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

fix: don't link runfiles node_modules to execroot node_modules if there is an external workspace node_modules #3060

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions internal/linker/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,9 @@ function symlink(target, p) {
}
});
}
function resolveWorkspaceNodeModules(workspace, startCwd, isExecroot, execroot, runfiles) {
function resolveWorkspaceNodeModules(externalWorkspace, startCwd, isExecroot, execroot, runfiles) {
return __awaiter(this, void 0, void 0, function* () {
const targetManifestPath = `${workspace}/node_modules`;
const targetManifestPath = `${externalWorkspace}/node_modules`;
if (isExecroot) {
return `${execroot}/external/${targetManifestPath}`;
}
Expand Down Expand Up @@ -319,10 +319,10 @@ function main(args, runfiles) {
});
}
for (const packagePath of Object.keys(roots)) {
const workspace = roots[packagePath];
let workspaceNodeModules = yield resolveWorkspaceNodeModules(workspace, startCwd, isExecroot, execroot, runfiles);
const externalWorkspace = roots[packagePath];
let workspaceNodeModules = yield resolveWorkspaceNodeModules(externalWorkspace, startCwd, isExecroot, execroot, runfiles);
if (yield exists(workspaceNodeModules)) {
log_verbose(`resolved ${workspace} workspace node modules path to ${workspaceNodeModules}`);
log_verbose(`resolved ${externalWorkspace} external workspace node modules path to ${workspaceNodeModules}`);
}
else {
workspaceNodeModules = undefined;
Expand All @@ -345,14 +345,14 @@ function main(args, runfiles) {
if (!isExecroot) {
const runfilesPackagePath = path.posix.join(startCwd, packagePath);
yield mkdirp(`${runfilesPackagePath}`);
yield symlinkWithUnlink(execrootNodeModules, `${runfilesPackagePath}/node_modules`);
yield symlinkWithUnlink(!packagePath && workspaceNodeModules ? workspaceNodeModules : execrootNodeModules, `${runfilesPackagePath}/node_modules`);
}
if (process.env['RUNFILES']) {
const stat = yield gracefulLstat(process.env['RUNFILES']);
if (stat && stat.isDirectory()) {
const runfilesPackagePath = path.posix.join(process.env['RUNFILES'], packagePath);
const runfilesPackagePath = path.posix.join(process.env['RUNFILES'], workspace, packagePath);
yield mkdirp(`${runfilesPackagePath}`);
yield symlinkWithUnlink(execrootNodeModules, `${runfilesPackagePath}/node_modules`);
yield symlinkWithUnlink(!packagePath && workspaceNodeModules ? workspaceNodeModules : execrootNodeModules, `${runfilesPackagePath}/node_modules`);
}
}
}
Expand Down
22 changes: 13 additions & 9 deletions internal/linker/link_node_modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,9 @@ async function symlink(target: string, p: string): Promise<boolean> {

/** Determines an absolute path to the given workspace if it contains node modules. */
async function resolveWorkspaceNodeModules(
workspace: string, startCwd: string, isExecroot: boolean, execroot: string|undefined,
externalWorkspace: string, startCwd: string, isExecroot: boolean, execroot: string|undefined,
runfiles: Runfiles) {
const targetManifestPath = `${workspace}/node_modules`;
const targetManifestPath = `${externalWorkspace}/node_modules`;

if (isExecroot) {
// Under execroot, the npm workspace will be under an external folder from the startCwd
Expand Down Expand Up @@ -471,11 +471,11 @@ export async function main(args: string[], runfiles: Runfiles) {
// Symlink all node_modules roots defined. These are 3rd party deps in external npm workspaces
// lined to node_modules folders at the root or in sub-directories
for (const packagePath of Object.keys(roots)) {
const workspace = roots[packagePath];
const externalWorkspace = roots[packagePath];
let workspaceNodeModules: string | undefined = await resolveWorkspaceNodeModules(
workspace, startCwd, isExecroot, execroot, runfiles);
externalWorkspace, startCwd, isExecroot, execroot, runfiles);
if (await exists(workspaceNodeModules)) {
log_verbose(`resolved ${workspace} workspace node modules path to ${workspaceNodeModules}`);
log_verbose(`resolved ${externalWorkspace} external workspace node modules path to ${workspaceNodeModules}`);
} else {
// There are no third party node_modules to symlink to
workspaceNodeModules = undefined;
Expand Down Expand Up @@ -511,21 +511,25 @@ export async function main(args: string[], runfiles: Runfiles) {
await mkdirp(`${packagePathBin}`);
await symlinkWithUnlink(execrootNodeModules, `${packagePathBin}/node_modules`);
}

// Start CWD symlink -> execroot node_modules
if (!isExecroot) {
const runfilesPackagePath = path.posix.join(startCwd, packagePath);
await mkdirp(`${runfilesPackagePath}`);
await symlinkWithUnlink(execrootNodeModules, `${runfilesPackagePath}/node_modules`);
// Don't link to the root execroot node_modules if there is a workspace node_modules.
// Bazel will delete that symlink on rebuild in the ibazel run context.
await symlinkWithUnlink(!packagePath && workspaceNodeModules ? workspaceNodeModules : execrootNodeModules, `${runfilesPackagePath}/node_modules`);
}

// RUNFILES symlink -> execroot node_modules
if (process.env['RUNFILES']) {
const stat = await gracefulLstat(process.env['RUNFILES']);
if (stat && stat.isDirectory()) {
const runfilesPackagePath = path.posix.join(process.env['RUNFILES'], packagePath);
const runfilesPackagePath = path.posix.join(process.env['RUNFILES'], workspace, packagePath);
await mkdirp(`${runfilesPackagePath}`);
await symlinkWithUnlink(execrootNodeModules, `${runfilesPackagePath}/node_modules`);
// Don't link to the root execroot node_modules if there is a workspace node_modules.
// Bazel will delete that symlink on rebuild in the ibazel run context.
await symlinkWithUnlink(!packagePath && workspaceNodeModules ? workspaceNodeModules : execrootNodeModules, `${runfilesPackagePath}/node_modules`);
}
}
}
Expand Down
1 change: 1 addition & 0 deletions internal/linker/test/link_node_modules.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ describe('link_node_modules', () => {

// No first-party packages
writeManifest({
workspace: workspace,
bin: BIN_DIR,
roots: {'': 'npm'},
});
Expand Down
6 changes: 6 additions & 0 deletions internal/node/launcher.sh
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,12 @@ if [[ "${RUNFILES_ROOT}" ]]; then
# (e.g., /private/.../execroot/build_bazel_rules_nodejs/bazel-out/darwin-fastbuild/bin/internal/linker/test/multi_linker/test.sh.runfiles/build_bazel_rules_nodejs/node_modules)
export BAZEL_PATCH_ROOTS="${BAZEL_PATCH_ROOTS},${RUNFILES_ROOT}/${BAZEL_WORKSPACE}/node_modules"
fi
if [[ "${RUNFILES}" ]]; then
# If in runfiles, guard the RUNFILES root itself
export BAZEL_PATCH_ROOTS="${BAZEL_PATCH_ROOTS},${RUNFILES}"
# If RUNFILES is set, guard the RUNFILES node_modules as well
export BAZEL_PATCH_ROOTS="${BAZEL_PATCH_ROOTS},${RUNFILES}/${BAZEL_WORKSPACE}/node_modules"
fi
if [[ -n "${BAZEL_NODE_MODULES_ROOTS:-}" ]]; then
# BAZEL_NODE_MODULES_ROOTS is in the format "<path>:<workspace>,<path>:<workspace>"
# (e.g., "internal/linker/test:npm_internal_linker_test,:npm")
Expand Down
12 changes: 11 additions & 1 deletion internal/node/test/env.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ function normPath(p) {
// On Windows, we normalize to lowercase for so path mismatches such as 'C:/Users' and
// 'c:/users' don't break the specs.
result = result.toLowerCase();
if (/[a-zA-Z]\:/.test(result)) {
if (/^[a-z]\:/.test(result)) {
// Handle c:/ and /c/ mismatch
result = `/${result[0]}${result.slice(2)}`;
} else if (/^[a-z];[a-z]\:/.test(result)) {
// Handle c;b:/ and /c/b/ mismatch
result = `/${result[0]}/${result[2]}${result.slice(4)}`;
}
}
return result;
Expand Down Expand Up @@ -40,11 +43,14 @@ describe('launcher.sh environment', function() {
expectPathsToMatch(process.cwd(), `${process.env['RUNFILES_DIR']}/build_bazel_rules_nodejs`);
expectPathsToMatch(process.env['PWD'], `${process.env['RUNFILES_DIR']}/build_bazel_rules_nodejs`);
expectPathsToMatch(process.env['BAZEL_NODE_MODULES_ROOTS'], ':npm');
console.log(process.env['RUNFILES'])
const expectedRoots = [
`${execroot}`,
`${execroot}/node_modules`,
`${runfilesRoot}`,
`${runfilesRoot}/build_bazel_rules_nodejs/node_modules`,
`${process.env['RUNFILES']}`,
`${process.env['RUNFILES']}/${process.env['BAZEL_WORKSPACE']}/node_modules`,
`${execroot}/external/npm/node_modules`,
`${runfilesRoot}/npm/node_modules`,
`${runfilesRoot}/build_bazel_rules_nodejs/external/npm/node_modules`,
Expand All @@ -71,6 +77,8 @@ describe('launcher.sh environment', function() {
const expectedRoots = [
`${execroot}`,
`${execroot}/node_modules`,
`${env['RUNFILES']}`,
`${env['RUNFILES']}/${env['BAZEL_WORKSPACE']}/node_modules`,
]
expectPathsToMatch(env['BAZEL_PATCH_ROOTS'].split(','), expectedRoots);
});
Expand All @@ -92,6 +100,8 @@ describe('launcher.sh environment', function() {
const expectedRoots = [
`${execroot}`,
`${execroot}/node_modules`,
`${env['RUNFILES']}`,
`${env['RUNFILES']}/${env['BAZEL_WORKSPACE']}/node_modules`,
`${execroot}/external/npm/node_modules`,
]
expectPathsToMatch(env['BAZEL_PATCH_ROOTS'].split(','), expectedRoots);
Expand Down