Skip to content

Commit 6d8c625

Browse files
authored
feat(builtin): npm_package_bin can produce directory output (#1164)
1 parent a2c6468 commit 6d8c625

7 files changed

Lines changed: 73 additions & 11 deletions

File tree

internal/node/npm_package_bin.bzl

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,44 @@ _ATTRS = {
88
"outs": attr.output_list(),
99
"args": attr.string_list(mandatory = True),
1010
"data": attr.label_list(allow_files = True, aspects = [module_mappings_aspect]),
11+
"out_dir": attr.string(),
1112
"tool": attr.label(
1213
executable = True,
1314
cfg = "host",
1415
mandatory = True,
1516
),
1617
}
1718

19+
# Need a custom expand_location function
20+
# because the out_dir is a tree artifact
21+
# so we weren't able to give it a label
22+
def _expand_location(ctx, s):
23+
s = s.replace("$@", "/".join([ctx.bin_dir.path, ctx.label.package, ctx.attr.out_dir]))
24+
return ctx.expand_location(s, targets = ctx.attr.data)
25+
1826
def _impl(ctx):
27+
if ctx.attr.out_dir and ctx.attr.outs:
28+
fail("Only one of out_dir and outs may be specified")
29+
if not ctx.attr.out_dir and not ctx.attr.outs:
30+
fail("One of out_dir and outs must be specified")
31+
1932
args = ctx.actions.args()
2033
inputs = ctx.files.data[:]
21-
outputs = ctx.outputs.outs
34+
outputs = []
35+
if ctx.attr.out_dir:
36+
outputs = [ctx.actions.declare_directory(ctx.attr.out_dir)]
37+
else:
38+
outputs = ctx.outputs.outs
2239
register_node_modules_linker(ctx, args, inputs)
2340
for a in ctx.attr.args:
24-
args.add(ctx.expand_location(a, targets = ctx.attr.data))
41+
args.add(_expand_location(ctx, a))
2542
ctx.actions.run(
2643
executable = ctx.executable.tool,
2744
inputs = inputs,
2845
outputs = outputs,
2946
arguments = [args],
3047
)
48+
return [DefaultInfo(files = depset(outputs))]
3149

3250
_npm_package_bin = rule(
3351
_impl,
@@ -45,17 +63,22 @@ def npm_package_bin(tool = None, package = None, package_bin = None, **kwargs):
4563
https://docs.bazel.build/versions/master/skylark/macros.html#full-example
4664
4765
Args:
48-
data: identical to [genrule.srcs](https://docs.bazel.build/versions/master/be/general.html#genrule.srcs)
66+
data: similar to [genrule.srcs](https://docs.bazel.build/versions/master/be/general.html#genrule.srcs)
4967
may also include targets that produce or reference npm packages which are needed by the tool
50-
outs: identical to [genrule.outs](https://docs.bazel.build/versions/master/be/general.html#genrule.outs)
68+
outs: similar to [genrule.outs](https://docs.bazel.build/versions/master/be/general.html#genrule.outs)
69+
out_dir: use this instead of `outs` if you want the output to be a directory
70+
Exactly one of `outs`, `out_dir` may be used.
71+
If you output a directory, there can only be one output.
5172
args: Command-line arguments to the tool.
5273
5374
Subject to 'Make variable' substitution.
5475
Can use $(location) expansion. See https://docs.bazel.build/versions/master/be/make-variables.html
76+
You may also refer to the location of the out_dir with the special `$@` replacement, like genrule.
5577
5678
package: an npm package whose binary to run, like "terser". Assumes your node_modules are installed in a workspace called "npm"
5779
package_bin: the "bin" entry from `package` that should be run. By default package_bin is the same string as `package`
58-
tool: a label for a binary to run, like `@npm//terser/bin:terser`. This is the longer form of package/package_bin
80+
tool: a label for a binary to run, like `@npm//terser/bin:terser`. This is the longer form of package/package_bin.
81+
Note that you can also refer to a binary in your local workspace.
5982
"""
6083
if not tool:
6184
if not package:

internal/node/test/BUILD.bazel

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,28 @@ npm_package_bin(
158158
package = "terser",
159159
)
160160

161+
nodejs_binary(
162+
name = "copy_to_directory",
163+
entry_point = "copy_to_directory.js",
164+
)
165+
166+
npm_package_bin(
167+
name = "produces_directory",
168+
args = [
169+
"--output-dir",
170+
"$@",
171+
"$(location terser_input.js)",
172+
],
173+
data = ["terser_input.js"],
174+
out_dir = "dir_output",
175+
tool = ":copy_to_directory",
176+
)
177+
161178
nodejs_test(
162179
name = "npm_package_bin_test",
163-
data = ["minified.js"],
180+
data = [
181+
"minified.js",
182+
"produces_directory",
183+
],
164184
entry_point = "npm_package_bin.spec.js",
165185
)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
4+
// argv[2] is always --output-dir
5+
const out_dir = process.argv[3];
6+
7+
try {
8+
fs.mkdirSync(out_dir);
9+
} catch {
10+
}
11+
12+
for (const input of process.argv.slice(4)) {
13+
fs.copyFileSync(input, path.join(out_dir, path.basename(input)));
14+
}
Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
const fs = require('fs');
22
const path = require('path');
3-
4-
const content = fs.readFileSync(path.join(require.resolve(__dirname + '/minified.js')), 'utf-8');
3+
const min_js = path.join(require.resolve(__dirname + '/minified.js'));
4+
const content = fs.readFileSync(min_js, 'utf-8');
55
if (!content.includes('{console.error("thing")}')) {
66
console.error(content);
77
process.exitCode = 1;
88
}
9+
10+
const dir = fs.readdirSync(path.join(path.dirname(min_js), 'dir_output'));
11+
if (!dir.includes('terser_input.js')) {
12+
console.error(dir), process.exitCode = 1;
13+
}

internal/npm_install/generate_build_file.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1063,7 +1063,7 @@ function printIndexBzl(pkg) {
10631063
10641064
# Generated helper macro to call ${name}
10651065
def ${name.replace(/-/g, '_')}(**kwargs):
1066-
if "outs" in kwargs:
1066+
if "outs" in kwargs or "out_dir" in kwargs:
10671067
npm_package_bin(tool = "@${WORKSPACE}//${pkg._dir}/bin:${name}", **kwargs)
10681068
else:
10691069
nodejs_binary(

internal/npm_install/test/golden/@gregmagolan/test-a/index.bzl.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary", "npm_package_bin")
22
def test(**kwargs):
3-
if "outs" in kwargs:
3+
if "outs" in kwargs or "out_dir" in kwargs:
44
npm_package_bin(tool = "@fine_grained_goldens//@gregmagolan/test-a/bin:test", **kwargs)
55
else:
66
nodejs_binary(

internal/npm_install/test/golden/jasmine/index.bzl.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary", "npm_package_bin")
22
def jasmine(**kwargs):
3-
if "outs" in kwargs:
3+
if "outs" in kwargs or "out_dir" in kwargs:
44
npm_package_bin(tool = "@fine_grained_goldens//jasmine/bin:jasmine", **kwargs)
55
else:
66
nodejs_binary(

0 commit comments

Comments
 (0)