Skip to content

Commit 842dfb4

Browse files
committed
feat(builtin): allow patching require in bootstrap scripts
* export BAZEL_WORKSPACE, BAZEL_NODE_LINKER & BAZEL_NODE_PATCH_REQUIRE env variables from node launcher * linker now users BAZEL_WORKSPACE & BAZEL_TARGET so that its runfiles resolve support works with non-test targets that don't have TEST_WORKSPACE & TEST_TARGET envs * adds resolveWorkspaceRelative() & patchRequire() to linker runfiles resolve support. The former is useful if you want to resolve without specifying the workspace. The latter is useful for nodejs_binary bootstrap scripts that want to patch require via the linker (such as e2e/jasmine/jasmine_shared_env_bootstrap.js) * tests targets can safely use TEST_TARGET & TEST_WORKSPACE build: s
1 parent 8a931d8 commit 842dfb4

11 files changed

Lines changed: 137 additions & 48 deletions

File tree

e2e/jasmine/BUILD.bazel

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,11 @@ jasmine_node_test(
55
srcs = ["test.spec.js"],
66
)
77

8-
# This requires the linker as jasmine_shared_env_bootstrap.js requires @bazel/jasmine
9-
# which only works with the linker as the --require script doesn't get the require.resolve
10-
# patches.
118
jasmine_node_test(
129
name = "shared_env_test",
1310
srcs = ["jasmine_shared_env_test.spec.js"],
1411
data = ["jasmine_shared_env_bootstrap.js"],
15-
# TODO(gregmagolan): fix Windows error: Error: Cannot find module 'e2e_jasmine/shared_env_test_devmode_srcs.MF'
16-
tags = ["fix-windows"],
1712
templated_args = [
18-
"--nobazel_patch_module_resolver",
1913
"--node_options=--require=$(rlocation $(location :jasmine_shared_env_bootstrap.js))",
2014
],
2115
deps = [

e2e/jasmine/jasmine_shared_env_bootstrap.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
// bootstrap the bazel require patch since this bootstrap script is loaded with
2+
// `--node_options=--require=$(rlocation $(location :jasmine_shared_env_bootstrap.js))`
3+
if (process.env['BAZEL_NODE_RUNFILES_HELPER']) {
4+
require(process.env['BAZEL_NODE_RUNFILES_HELPER']).patchRequire();
5+
}
6+
17
global.foobar = 1;
28

39
require('zone.js/dist/zone-node.js');

internal/golden_file_test/bin.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ ${prettyDiff}
2929
Update the golden file:
3030
3131
bazel run ${debugBuild ? '--compilation_mode=dbg ' : ''}${
32-
process.env['BAZEL_TARGET'].replace(/_bin$/, '')}.accept
32+
process.env['TEST_TARGET'].replace(/_bin$/, '')}.accept
3333
`);
3434
} else {
3535
throw new Error('unknown mode', mode);

internal/linker/BUILD.bazel

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ bzl_library(
2020
)
2121

2222
# END-INTERNAL
23-
exports_files(["index.js"])
23+
exports_files([
24+
"index.js",
25+
"runfiles_helper.js",
26+
])
2427

2528
filegroup(
2629
name = "package_contents",

internal/linker/index.js

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -165,16 +165,21 @@ Include as much of the build output as you can without disclosing anything confi
165165
If you want to test runfiles manifest behavior, add
166166
--spawn_strategy=standalone to the command line.`);
167167
}
168-
const wksp = env['TEST_WORKSPACE'];
169-
const target = env['TEST_TARGET'];
170-
if (!!wksp && !!target) {
171-
// //path/to:target -> //path/to
172-
const pkg = target.split(':')[0];
173-
this.packagePath = path.posix.join(wksp, pkg);
174-
}
175-
// If under runfiles (and not unit testing) then don't add a bin to taget for bin links
176-
this.noBin = !!env['TEST_TARGET'] && wksp !== 'build_bazel_rules_nodejs' &&
177-
target !== '//internal/linker/test:unit_tests';
168+
const wksp = env['BAZEL_WORKSPACE'];
169+
if (!!wksp) {
170+
this.workspace = wksp;
171+
}
172+
// If target is from an external workspace such as @npm//rollup/bin:rollup
173+
// resolvePackageRelative is not supported since package is in an external
174+
// workspace.
175+
const target = env['BAZEL_TARGET'];
176+
if (!!target && !target.startsWith('@')) {
177+
// //path/to:target -> path/to
178+
this.package = target.split(':')[0].replace(/^\/\//, '');
179+
}
180+
// We can derive if the process is being run in the execroot
181+
// if there is a bazel-out folder at the cwd.
182+
this.execroot = existsSync('bazel-out');
178183
}
179184
lookupDirectory(dir) {
180185
if (!this.manifest)
@@ -225,11 +230,27 @@ Include as much of the build output as you can without disclosing anything confi
225230
}
226231
throw new Error(`could not resolve modulePath ${modulePath}`);
227232
}
233+
resolveWorkspaceRelative(modulePath) {
234+
if (!this.workspace) {
235+
throw new Error('workspace could not be determined from the environment');
236+
}
237+
return this.resolve(path.posix.join(this.workspace, modulePath));
238+
}
228239
resolvePackageRelative(modulePath) {
229-
if (!this.packagePath) {
230-
throw new Error('packagePath could not be determined from the environment');
240+
if (!this.workspace) {
241+
throw new Error('workspace could not be determined from the environment');
242+
}
243+
if (!this.package) {
244+
throw new Error('package could not be determined from the environment');
245+
}
246+
return this.resolve(path.posix.join(this.workspace, this.package, modulePath));
247+
}
248+
patchRequire() {
249+
const requirePatch = process.env['BAZEL_NODE_PATCH_REQUIRE'];
250+
if (!requirePatch) {
251+
throw new Error('require patch location could not be determined from the environment');
231252
}
232-
return this.resolve(path.posix.join(this.packagePath, modulePath));
253+
require(requirePatch);
233254
}
234255
}
235256
exports.Runfiles = Runfiles;
@@ -249,6 +270,18 @@ Include as much of the build output as you can without disclosing anything confi
249270
}
250271
});
251272
}
273+
function existsSync(p) {
274+
try {
275+
fs.statSync(p);
276+
return true;
277+
}
278+
catch (e) {
279+
if (e.code === 'ENOENT') {
280+
return false;
281+
}
282+
throw e;
283+
}
284+
}
252285
/**
253286
* Given a set of module aliases returns an array of recursive `LinkerTreeElement`.
254287
*
@@ -424,9 +457,11 @@ Include as much of the build output as you can without disclosing anything confi
424457
let target = '<package linking failed>';
425458
switch (root) {
426459
case 'bin':
460+
// If we are in the execroot then add the bin path to the target; otherwise
461+
// we are in runfiles and the bin path should be omitted.
427462
// FIXME(#1196)
428-
target = runfiles.noBin ? path.join(workspaceAbs, toWorkspaceDir(modulePath)) :
429-
path.join(workspaceAbs, bin, toWorkspaceDir(modulePath));
463+
target = runfiles.execroot ? path.join(workspaceAbs, bin, toWorkspaceDir(modulePath)) :
464+
path.join(workspaceAbs, toWorkspaceDir(modulePath));
430465
break;
431466
case 'src':
432467
target = path.join(workspaceAbs, toWorkspaceDir(modulePath));

internal/linker/link_node_modules.ts

Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,15 @@ async function resolveRoot(root: string|undefined, runfiles: Runfiles) {
126126
export class Runfiles {
127127
manifest: Map<string, string>|undefined;
128128
dir: string|undefined;
129-
noBin: boolean;
129+
execroot: boolean;
130130
/**
131-
* If the environment gives us enough hints, we can know the path to the package
132-
* in the form workspace_name/path/to/package
131+
* If the environment gives us enough hints, we can know the workspace name
133132
*/
134-
packagePath: string|undefined;
133+
workspace: string|undefined;
134+
/**
135+
* If the environment gives us enough hints, we can know the package path
136+
*/
137+
package: string|undefined;
135138

136139
constructor(env: typeof process.env) {
137140
// If Bazel sets a variable pointing to a runfiles manifest,
@@ -159,18 +162,21 @@ export class Runfiles {
159162
If you want to test runfiles manifest behavior, add
160163
--spawn_strategy=standalone to the command line.`);
161164
}
162-
163-
const wksp = env['TEST_WORKSPACE'];
164-
const target = env['TEST_TARGET'];
165-
if (!!wksp && !!target) {
166-
// //path/to:target -> //path/to
167-
const pkg = target.split(':')[0];
168-
this.packagePath = path.posix.join(wksp, pkg);
165+
const wksp = env['BAZEL_WORKSPACE'];
166+
if (!!wksp) {
167+
this.workspace = wksp;
169168
}
170-
171-
// If under runfiles (and not unit testing) then don't add a bin to taget for bin links
172-
this.noBin = !!env['TEST_TARGET'] && wksp !== 'build_bazel_rules_nodejs' &&
173-
target !== '//internal/linker/test:unit_tests';
169+
// If target is from an external workspace such as @npm//rollup/bin:rollup
170+
// resolvePackageRelative is not supported since package is in an external
171+
// workspace.
172+
const target = env['BAZEL_TARGET'];
173+
if (!!target && !target.startsWith('@')) {
174+
// //path/to:target -> path/to
175+
this.package = target.split(':')[0].replace(/^\/\//, '');
176+
}
177+
// We can derive if the process is being run in the execroot
178+
// if there is a bazel-out folder at the cwd.
179+
this.execroot = existsSync('bazel-out');
174180
}
175181

176182
lookupDirectory(dir: string): string|undefined {
@@ -228,11 +234,29 @@ export class Runfiles {
228234
throw new Error(`could not resolve modulePath ${modulePath}`);
229235
}
230236

237+
resolveWorkspaceRelative(modulePath: string) {
238+
if (!this.workspace) {
239+
throw new Error('workspace could not be determined from the environment');
240+
}
241+
return this.resolve(path.posix.join(this.workspace, modulePath));
242+
}
243+
231244
resolvePackageRelative(modulePath: string) {
232-
if (!this.packagePath) {
233-
throw new Error('packagePath could not be determined from the environment');
245+
if (!this.workspace) {
246+
throw new Error('workspace could not be determined from the environment');
247+
}
248+
if (!this.package) {
249+
throw new Error('package could not be determined from the environment');
250+
}
251+
return this.resolve(path.posix.join(this.workspace, this.package, modulePath));
252+
}
253+
254+
patchRequire() {
255+
const requirePatch = process.env['BAZEL_NODE_PATCH_REQUIRE'];
256+
if (!requirePatch) {
257+
throw new Error('require patch location could not be determined from the environment');
234258
}
235-
return this.resolve(path.posix.join(this.packagePath, modulePath));
259+
require(requirePatch);
236260
}
237261
}
238262

@@ -257,6 +281,18 @@ async function exists(p: string) {
257281
}
258282
}
259283

284+
function existsSync(p: string) {
285+
try {
286+
fs.statSync(p)
287+
return true;
288+
} catch (e) {
289+
if (e.code === 'ENOENT') {
290+
return false;
291+
}
292+
throw e;
293+
}
294+
}
295+
260296
/**
261297
* Given a set of module aliases returns an array of recursive `LinkerTreeElement`.
262298
*
@@ -491,9 +527,11 @@ export async function main(args: string[], runfiles: Runfiles) {
491527
let target: string = '<package linking failed>';
492528
switch (root) {
493529
case 'bin':
530+
// If we are in the execroot then add the bin path to the target; otherwise
531+
// we are in runfiles and the bin path should be omitted.
494532
// FIXME(#1196)
495-
target = runfiles.noBin ? path.join(workspaceAbs, toWorkspaceDir(modulePath)) :
496-
path.join(workspaceAbs, bin, toWorkspaceDir(modulePath));
533+
target = runfiles.execroot ? path.join(workspaceAbs, bin, toWorkspaceDir(modulePath)) :
534+
path.join(workspaceAbs, toWorkspaceDir(modulePath));
497535
break;
498536
case 'src':
499537
target = path.join(workspaceAbs, toWorkspaceDir(modulePath));

internal/linker/runfiles_helper.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('./index.js').runfiles;

internal/node/node.bzl

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,8 @@ def _nodejs_binary_impl(ctx):
167167

168168
_write_loader_script(ctx)
169169

170-
script_path = _to_manifest_path(ctx, ctx.outputs.loader)
171-
172170
env_vars = "export BAZEL_TARGET=%s\n" % ctx.label
171+
env_vars += "export BAZEL_WORKSPACE=%s\n" % ctx.workspace_name
173172
for k in ctx.attr.configuration_env_vars + ctx.attr.default_env_vars:
174173
if k in ctx.var.keys():
175174
env_vars += "export %s=\"%s\"\n" % (k, ctx.var[k])
@@ -191,6 +190,7 @@ def _nodejs_binary_impl(ctx):
191190
node_tool_files.extend(ctx.toolchains["@build_bazel_rules_nodejs//toolchains/node:toolchain_type"].nodeinfo.tool_files)
192191

193192
node_tool_files.append(ctx.file._link_modules_script)
193+
node_tool_files.append(ctx.file._runfiles_helper_script)
194194
node_tool_files.append(ctx.file._bazel_require_script)
195195
node_tool_files.append(node_modules_manifest)
196196

@@ -205,9 +205,10 @@ def _nodejs_binary_impl(ctx):
205205
"TEMPLATED_env_vars": env_vars,
206206
"TEMPLATED_expected_exit_code": str(expected_exit_code),
207207
"TEMPLATED_link_modules_script": _to_manifest_path(ctx, ctx.file._link_modules_script),
208-
"TEMPLATED_loader_path": script_path,
208+
"TEMPLATED_loader_path": _to_manifest_path(ctx, ctx.outputs.loader),
209209
"TEMPLATED_modules_manifest": _to_manifest_path(ctx, node_modules_manifest),
210210
"TEMPLATED_repository_args": _to_manifest_path(ctx, ctx.file._repository_args),
211+
"TEMPLATED_runfiles_helper_script": _to_manifest_path(ctx, ctx.file._runfiles_helper_script),
211212
"TEMPLATED_script_path": _to_execroot_path(ctx, ctx.file.entry_point),
212213
"TEMPLATED_vendored_node": "" if is_builtin else strip_external(ctx.file._node.path),
213214
}
@@ -449,6 +450,10 @@ jasmine_node_test(
449450
default = Label("@nodejs//:bin/node_repo_args.sh"),
450451
allow_single_file = True,
451452
),
453+
"_runfiles_helper_script": attr.label(
454+
default = Label("//internal/linker:runfiles_helper.js"),
455+
allow_single_file = True,
456+
),
452457
"_source_map_support_files": attr.label_list(
453458
default = [
454459
Label("//third_party/github.com/buffer-from:contents"),

internal/node/node_launcher.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,13 @@ else
158158
fi
159159
fi
160160

161+
# Export the location of the runfiles helpers script
162+
export BAZEL_NODE_RUNFILES_HELPER=$(rlocation "TEMPLATED_runfiles_helper_script")
163+
164+
# Export the location of the loader script as it can be used to boostrap
165+
# node require patch if needed
166+
export BAZEL_NODE_PATCH_REQUIRE=$(rlocation "TEMPLATED_loader_path")
167+
161168
readonly repository_args=$(rlocation "TEMPLATED_repository_args")
162169
MAIN=$(rlocation "TEMPLATED_loader_path")
163170
readonly link_modules_script=$(rlocation "TEMPLATED_link_modules_script")

internal/npm_install/test/check.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ ${prettyDiff}
4040
4141
Update the golden file:
4242
43-
bazel run ${process.env['BAZEL_TARGET']}.accept`);
43+
bazel run ${process.env['TEST_TARGET']}.accept`);
4444
}
4545
}
4646
}

0 commit comments

Comments
 (0)