Skip to content

Commit f19245b

Browse files
authored
feat(builtin): introduce copy_to_bin rule (#1450)
* feat(builtin): introduce copy_to_bin rule Based on meteorcloudy@ awesome work in bazelbuild/bazel-skylib#217 That PR isn't getting approved for upstream so we vendor it into our own ruleset * chore: migrate to copy_file and copy_to_bin
1 parent c65d9b7 commit f19245b

20 files changed

Lines changed: 1063 additions & 80 deletions

File tree

examples/kotlin/BUILD.bazel

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Add rules here to build your software
22
# See https://docs.bazel.build/versions/master/build-ref.html#BUILD_files
33

4-
load("@build_bazel_rules_nodejs//:index.bzl", "pkg_web")
4+
load("@build_bazel_rules_nodejs//:index.bzl", "copy_to_bin", "pkg_web")
55
load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_js_import", "kt_js_library")
66
load("@npm//http-server:index.bzl", "http_server")
77
load("@npm_bazel_jasmine//:index.bzl", "jasmine_node_test")
@@ -22,20 +22,19 @@ kt_js_library(
2222
deps = [":kotlinx-html-js"],
2323
)
2424

25-
# Copy bootstrap.js to the bin folder as _bootstrap.js
26-
# so that relative import to `./hello.js` is valid
27-
genrule(
25+
# Copy bootstrap.js to the output folder, so all files are next to each other at runtime
26+
# Allows the `./hello.js` relative import to work while referencing an output file
27+
copy_to_bin(
2828
name = "bootstrap",
2929
srcs = ["bootstrap.js"],
30-
outs = ["_bootstrap.js"],
31-
cmd = "cp $< $@",
3230
)
3331

3432
rollup_bundle(
3533
name = "bundle",
3634
srcs = ["hello.js"],
3735
config_file = "rollup.config.js",
38-
entry_point = "_bootstrap.js",
36+
# Reference the copy of bootstrap.js in the output folder
37+
entry_point = "bootstrap",
3938
# TODO: make this example work with format = "esm"
4039
format = "cjs",
4140
output_dir = True,

index.bzl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Users should not load files under "/internal"
1919

2020
load("//internal/common:check_bazel_version.bzl", _check_bazel_version = "check_bazel_version")
2121
load("//internal/common:check_version.bzl", "check_version")
22+
load("//internal/common:copy_to_bin.bzl", _copy_to_bin = "copy_to_bin")
2223
load("//internal/jasmine_node_test:jasmine_node_test.bzl", _jasmine_node_test = "jasmine_node_test")
2324
load(
2425
"//internal/node:node.bzl",
@@ -39,6 +40,7 @@ jasmine_node_test = _jasmine_node_test
3940
npm_package = _npm_package
4041
npm_package_bin = _npm_bin
4142
pkg_web = _pkg_web
43+
copy_to_bin = _copy_to_bin
4244
# ANY RULES ADDED HERE SHOULD BE DOCUMENTED, see index.for_docs.bzl
4345

4446
# Allows us to avoid a transitive dependency on bazel_skylib from leaking to users

index.for_docs.bzl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
This differs from :index.bzl because we don't have wrapping macros that hide the real doc"""
1818

1919
load("//internal/common:check_bazel_version.bzl", _check_bazel_version = "check_bazel_version")
20+
load("//internal/common:copy_to_bin.bzl", _copy_to_bin = "copy_to_bin")
2021
load("//internal/node:node.bzl", _nodejs_binary = "nodejs_binary", _nodejs_test = "nodejs_test")
2122
load("//internal/node:node_repositories.bzl", _node_repositories = "node_repositories_rule")
2223
load("//internal/node:npm_package_bin.bzl", _npm_bin = "npm_package_bin")
@@ -25,6 +26,7 @@ load("//internal/npm_package:npm_package.bzl", _npm_package = "npm_package")
2526
load("//internal/pkg_web:pkg_web.bzl", _pkg_web = "pkg_web")
2627

2728
check_bazel_version = _check_bazel_version
29+
copy_to_bin = _copy_to_bin
2830
nodejs_binary = _nodejs_binary
2931
nodejs_test = _nodejs_test
3032
node_repositories = _node_repositories

internal/common/BUILD.bazel

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ package(default_visibility = ["//internal:__subpackages__"])
2121

2222
bzl_library(
2323
name = "bzl",
24-
srcs = glob(["*.bzl"]),
24+
srcs = glob(["*.bzl"]) + [
25+
"//third_party/github.com/bazelbuild/bazel-skylib:bzl",
26+
],
2527
visibility = ["//visibility:public"],
2628
)
2729

internal/common/copy_to_bin.bzl

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Copyright 2019 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"copy_to_bin() rule"
16+
17+
load("//third_party/github.com/bazelbuild/bazel-skylib:rules/private/copy_file_private.bzl", "copy_bash", "copy_cmd")
18+
19+
def _copy_to_bin_impl(ctx):
20+
all_dst = []
21+
for src in ctx.files.srcs:
22+
if not src.is_source:
23+
fail("A source file must be specified in copy_to_bin rule, %s is not a source file." % src.path)
24+
dst = ctx.actions.declare_file(src.basename, sibling = src)
25+
if ctx.attr.is_windows:
26+
copy_cmd(ctx, src, dst)
27+
else:
28+
copy_bash(ctx, src, dst)
29+
all_dst.append(dst)
30+
return DefaultInfo(files = depset(all_dst), runfiles = ctx.runfiles(files = all_dst))
31+
32+
_copy_to_bin = rule(
33+
implementation = _copy_to_bin_impl,
34+
attrs = {
35+
"srcs": attr.label_list(mandatory = True, allow_files = True),
36+
"is_windows": attr.bool(mandatory = True, doc = "Automatically set by macro"),
37+
},
38+
)
39+
40+
def copy_to_bin(name, srcs, **kwargs):
41+
"""Copies a source file to bazel-bin at the same workspace-relative path path.
42+
43+
e.g. `<workspace_root>/foo/bar/a.txt -> <bazel-bin>/foo/bar/a.txt`
44+
45+
This is useful to populate the output folder with all files needed at runtime, even
46+
those which aren't outputs of a Bazel rule.
47+
48+
This way you can run a binary in the output folder (execroot or runfiles_root)
49+
without that program needing to rely on a runfiles helper library or be aware that
50+
files are divided between the source tree and the output tree.
51+
52+
Args:
53+
name: Name of the rule.
54+
srcs: A List of Labels. File(s) to to copy.
55+
**kwargs: further keyword arguments, e.g. `visibility`
56+
"""
57+
_copy_to_bin(
58+
name = name,
59+
srcs = srcs,
60+
is_windows = select({
61+
"@bazel_tools//src/conditions:host_windows": True,
62+
"//conditions:default": False,
63+
}),
64+
**kwargs
65+
)

internal/common/test/BUILD.bazel

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
load("//internal/common:copy_to_bin.bzl", "copy_to_bin")
2+
3+
licenses(["notice"])
4+
5+
package(default_testonly = 1)
6+
7+
sh_test(
8+
name = "copy_to_bin_tests",
9+
srcs = ["copy_to_bin_tests.sh"],
10+
data = [
11+
":a",
12+
"//third_party/github.com/bazelbuild/bazel-skylib:tests/unittest.bash",
13+
],
14+
deps = ["@bazel_tools//tools/bash/runfiles"],
15+
)
16+
17+
copy_to_bin(
18+
name = "a",
19+
srcs = ["foo/bar/a.txt"],
20+
)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Copyright 2019 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# --- begin runfiles.bash initialization ---
16+
# Copy-pasted from Bazel's Bash runfiles library (tools/bash/runfiles/runfiles.bash).
17+
set -euo pipefail
18+
if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
19+
if [[ -f "$0.runfiles_manifest" ]]; then
20+
export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest"
21+
elif [[ -f "$0.runfiles/MANIFEST" ]]; then
22+
export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST"
23+
elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
24+
export RUNFILES_DIR="$0.runfiles"
25+
fi
26+
fi
27+
if [[ -f "${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
28+
source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash"
29+
elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
30+
source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \
31+
"$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)"
32+
else
33+
echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash"
34+
exit 1
35+
fi
36+
# --- end runfiles.bash initialization ---
37+
38+
source "$(rlocation build_bazel_rules_nodejs/third_party/github.com/bazelbuild/bazel-skylib/tests/unittest.bash)" \
39+
|| { echo "Could not source build_bazel_rules_nodejs/third_party/github.com/bazelbuild/bazel-skylib/tests/unittest.bash" >&2; exit 1; }
40+
41+
function test_map_to_output() {
42+
echo "$(rlocation build_bazel_rules_nodejs/internal/common/test/foo/bar/a.txt)" >"$TEST_log"
43+
# Test the foo/bar/a.txt is copied to bazel-out/
44+
expect_log 'bazel-out/'
45+
cat "$(rlocation build_bazel_rules_nodejs/internal/common/test/foo/bar/a.txt)" >"$TEST_log"
46+
# Test the content of foo/bar/a.txt is correct
47+
expect_log '#!/bin/bash'
48+
expect_log '^echo aaa$'
49+
}
50+
51+
run_suite "map_to_output test suite"

internal/common/test/foo/bar/a.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/bin/bash
2+
echo aaa

internal/linker/test/BUILD.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ jasmine_node_test(
1515
name = "unit_tests",
1616
srcs = ["test_lib"],
1717
# NB: for better dev round-trip, we test against the ts_library target
18-
# rather than update the index.js it's transpiled from.
18+
# rather than update the index.js it's transpiled to.
1919
data = ["//internal/linker:linker_lib"],
2020
)

internal/linker/test/integration/BUILD.bazel

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ linked(
3333
out = "actual",
3434
program = ":run_program",
3535
deps = [
36-
"//%s/absolute_import:index.js" % package_name(),
36+
# NB: reference the copy of index.js in the output folder
37+
"//%s/absolute_import:copy_to_bin" % package_name(),
3738
":run_program",
3839
"//internal/linker/test/integration/dynamic_linked_pkg",
3940
"//internal/linker/test/integration/dynamic_linked_scoped_pkg",

0 commit comments

Comments
 (0)