Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(builtin): introduce a linker #1079

Merged
merged 1 commit into from
Sep 4, 2019
Merged

Conversation

alexeagle
Copy link
Collaborator

This is a separate process we run right before node programs.
Its job is to create symlinks so that the node_modules tree exists and
has all the packages we might want to load at runtime.

This will eventually replace the need for a custom module resolver,
custom typescript path mappings, and the existing module_mappings.bzl
support for TS/runtime mappings.

@buildsize
Copy link

buildsize bot commented Sep 3, 2019

File name Previous Size New Size Change
release.tar.gz 1.02 MB 1.02 MB 2.85 KB (0%)

Copy link
Collaborator

@soldair soldair left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm
just a couple suggestions for recursive directory traversal not blocking.

if (!!process.env['VERBOSE_LOGS']) console.error('[link_node_modules.js]', ...m);
}

function ls_r(d) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

risk of RangeError: Maximum call stack size exceeded with large trees.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the whole ls_r function, was really only needed while putting the code together

for (const p of fs.readdirSync(d)) {
const f = path.join(d, p);
debug(' ', f);
if (fs.statSync(f).isDirectory()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could use the new readdir Dirent api here.
also potential for infinite recursion with symlinks that link to a parent. change to lstat or use dirent.isSymbolicLink

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto, let's just drop this code


// Now add symlinks to each of our first-party packages so they appear under the node_modules tree
for (const m of Object.keys(modules)) {
if (fs.existsSync(m)) continue;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All fs operations could be done async and in parallel in this loop

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@soldair says he'll do some performance optimizations in a second PR once we've got tests in place that confirm this works

}

function ls_r(d) {
for (const p of fs.readdirSync(d)) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All fs operations could be done async and in parallel in this loop

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah good call


function symlink(target, path) {
debug(`symlink( ${path} -> ${target} )`);
fs.symlinkSync(target, path);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would there be any perf gains to know if a file dosent need to be symlinked? Either at a file or directory level?
This would be for the situation where no files have changed but the process needs to restart

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's what the logic does now, but we do it before calling the symlink helper.
I guess it would be more clear if the short-circuit just happens in the helper instead

@alexeagle
Copy link
Collaborator Author

Still to do here tomorrow:

  • move logic that reads the target_tool_path and target_tool from platform_common.ToolchainInfo[ nodeinfo] so it isn't duplicated
  • add unit tests for the runfiles manifest handling
  • add a README explaining how the linker compares with npm link or lerna

This is a separate process we run right before node programs.
Its job is to create symlinks so that the node_modules tree exists and
has all the packages we might want to load at runtime.

This will eventually replace the need for a custom module resolver,
custom typescript path mappings, and the existing module_mappings.bzl
support for TS/runtime mappings.
// k: npm/node_modules/semver/LICENSE
// v: /path/to/external/npm/node_modules/semver/LICENSE
// calculate l = length(`/semver/LICENSE`)
if (k.startsWith(dir)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will this collide /a/applefactory with /a/apple?
missing k.startsWith(dir+'/') || k === dir here i think

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed, will add a test for this in a followup

@alexeagle alexeagle merged commit 62037c9 into bazel-contrib:master Sep 4, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants