diff --git a/MODULE.bazel b/MODULE.bazel index b53443c6..80f7028d 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -10,7 +10,7 @@ register_toolchains( "//toolchains/unittest:bash_toolchain", ) -bazel_dep(name = "platforms", version = "0.0.4") +bazel_dep(name = "platforms", version = "0.0.9") bazel_dep(name = "rules_license", version = "0.0.7") ### INTERNAL ONLY - lines after this are not included in the release packaging. diff --git a/docs/native_binary_doc.md b/docs/native_binary_doc.md index 02152cfe..e8d4ad23 100755 --- a/docs/native_binary_doc.md +++ b/docs/native_binary_doc.md @@ -12,7 +12,7 @@ don't depend on Bash and work with --shell_executable="". ## native_binary
-native_binary(name, src, data, out)
+native_binary(name, src, data, out, env)
 
Wraps a pre-built binary or script with a binary rule. @@ -29,6 +29,7 @@ in genrule.tools for example. You can also augment the binary with runfiles. | src | path of the pre-built executable | Label | required | | | data | data dependencies. See https://bazel.build/reference/be/common-definitions#typical.data | List of labels | optional | `[]` | | out | An output name for the copy of the binary. Defaults to name.exe. (We add .exe to the name by default because it's required on Windows and tolerated on other platforms.) | String | optional | `""` | +| env | additional environment variables to set when the target is executed by `bazel`. Values are subject to location expansion for labels in `src` and `data`. | Dictionary: String -> String | optional | `{}` | @@ -36,7 +37,7 @@ in genrule.tools for example. You can also augment the binary with runfiles. ## native_test
-native_test(name, src, data, out)
+native_test(name, src, data, out, env, env_inherit)
 
Wraps a pre-built binary or script with a test rule. @@ -53,5 +54,7 @@ the binary with runfiles. | src | path of the pre-built executable | Label | required | | | data | data dependencies. See https://bazel.build/reference/be/common-definitions#typical.data | List of labels | optional | `[]` | | out | An output name for the copy of the binary. Defaults to name.exe. (We add .exe to the name by default because it's required on Windows and tolerated on other platforms.) | String | optional | `""` | +| env | additional environment variables to set when the target is executed by `bazel`. Values are subject to location expansion for labels in `src` and `data`. | Dictionary: String -> String | optional | `{}` | +| env_inherit | additional environment variables to inherit from the external environment when the test is executed by `bazel test`. | List of strings | optional | `[]` | diff --git a/rules/native_binary.bzl b/rules/native_binary.bzl index 7b1483ab..adaaa45b 100644 --- a/rules/native_binary.bzl +++ b/rules/native_binary.bzl @@ -29,23 +29,25 @@ def _impl_rule(ctx): ) runfiles = ctx.runfiles(files = ctx.files.data) - # Bazel 4.x LTS does not support `merge_all`. - # TODO: remove `merge` branch once we drop support for Bazel 4.x. - if hasattr(runfiles, "merge_all"): - runfiles = runfiles.merge_all([ - d[DefaultInfo].default_runfiles - for d in ctx.attr.data + [ctx.attr.src] - ]) - else: - for d in ctx.attr.data: - runfiles = runfiles.merge(d[DefaultInfo].default_runfiles) - runfiles = runfiles.merge(ctx.attr.src[DefaultInfo].default_runfiles) + runfiles = runfiles.merge_all([ + d[DefaultInfo].default_runfiles + for d in ctx.attr.data + [ctx.attr.src] + ]) - return DefaultInfo( - executable = out, - files = depset([out]), - runfiles = runfiles, - ) + targets = [ctx.attr.src] + ctx.attr.data + env = {k: ctx.expand_location(v, targets) for k, v in ctx.attr.env.items()} + + return [ + DefaultInfo( + executable = out, + files = depset([out]), + runfiles = runfiles, + ), + RunEnvironmentInfo( + environment = env, + inherited_environment = getattr(ctx.attr, "env_inherit", []), + ), + ] _ATTRS = { "src": attr.label( @@ -70,6 +72,12 @@ _ATTRS = { "name.exe. (We add .exe to the name by default because it's " + "required on Windows and tolerated on other platforms.)", ), + "env": attr.string_dict( + doc = "additional environment variables to set when the target is executed by " + + "`bazel`. Values are subject to location expansion for labels in `src` and " + + "`data`.", + default = {}, + ), } native_binary = rule( @@ -86,7 +94,14 @@ in genrule.tools for example. You can also augment the binary with runfiles. native_test = rule( implementation = _impl_rule, - attrs = _ATTRS, + attrs = dict( + _ATTRS, + env_inherit = attr.string_list( + doc = "additional environment variables to inherit from the external " + + "environment when the test is executed by `bazel test`. ", + default = [], + ), + ), test = True, doc = """ Wraps a pre-built binary or script with a test rule. diff --git a/tests/native_binary/BUILD b/tests/native_binary/BUILD index 5ec2d54a..114c83fb 100644 --- a/tests/native_binary/BUILD +++ b/tests/native_binary/BUILD @@ -30,6 +30,11 @@ cc_binary( deps = ["@bazel_tools//tools/cpp/runfiles"], ) +cc_binary( + name = "assertenv", + srcs = ["assertenv.cc"], +) + # A rule that copies "assertarg"'s output as an opaque executable, simulating a # binary that's not built from source and needs to be wrapped in native_binary. copy_file( @@ -54,6 +59,16 @@ copy_file( is_executable = True, ) +copy_file( + name = "copy_assertenv_exe", + src = ":assertenv", + # On Windows we need the ".exe" extension. + # On other platforms the extension doesn't matter. + # Therefore we can use ".exe" on every platform. + out = "assertenv_copy.exe", + is_executable = True, +) + _ARGS = [ "'a b'", "c\\ d", @@ -121,3 +136,17 @@ native_test( # Therefore we can use ".exe" on every platform. out = "data_from_binary_test.exe", ) + +native_test( + name = "env_test", + src = ":copy_assertenv_exe", + data = ["BUILD"], + env = { + "TEST_ENV_VAR": "test_env_var_value", + "TEST_ENV_VAR_WITH_EXPANSION": "|$(rootpath BUILD)|", + }, + env_inherit = [ + "HOME", # for POSIX + "APPDATA", # for Windows + ], +) diff --git a/tests/native_binary/assertenv.cc b/tests/native_binary/assertenv.cc new file mode 100644 index 00000000..0dd51f3c --- /dev/null +++ b/tests/native_binary/assertenv.cc @@ -0,0 +1,31 @@ +#include +#include +#include + +bool check(const char *var, const char *expected = nullptr); + +int main() { + bool ok = true; + ok = ok && check("TEST_ENV_VAR", "test_env_var_value"); +#ifdef _WIN32 + ok = ok && check("APPDATA"); +#else + ok = ok && check("HOME"); +#endif + ok = + ok && check("TEST_ENV_VAR_WITH_EXPANSION", "|tests/native_binary/BUILD|"); + return ok ? 0 : 1; +} + +bool check(const char *var, const char *expected) { + const char *actual = getenv(var); + if (actual == nullptr) { + fprintf(stderr, "expected %s\n", var); + return false; + } + if (expected && strcmp(actual, expected) != 0) { + fprintf(stderr, "expected %s=%s, got %s\n", var, expected, actual); + return false; + } + return true; +}