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

Add env and env_inherit to native_binary and native_test #482

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
3 changes: 3 additions & 0 deletions MODULE.bazel
Expand Up @@ -12,6 +12,9 @@ register_toolchains(

bazel_dep(name = "platforms", version = "0.0.4")

globals = use_extension("//:extension.bzl", "globals_extension")
use_repo(globals, "bazel_skylib_globals")

### INTERNAL ONLY - lines after this are not included in the release packaging.

# Build-only / test-only dependencies
Expand Down
2 changes: 1 addition & 1 deletion MODULE.bazel-remove-override.patch
@@ -1,6 +1,6 @@
--- MODULE.bazel
+++ MODULE.bazel
@@ -22,8 +22,8 @@
@@ -25,8 +25,8 @@
# Needed for bazelci and for building distribution tarballs.
# If using an unreleased version of bazel_skylib via git_override, apply
# MODULE.bazel-remove-override.patch to remove the following lines:
Expand Down
4 changes: 4 additions & 0 deletions docs/BUILD
Expand Up @@ -66,6 +66,10 @@ stardoc_with_diff_test(
name = "native_binary",
bzl_library_target = "//rules:native_binary",
out_label = "//docs:native_binary_doc.md",
deps = [
"//lib:versions.bzl",
"@bazel_skylib_globals//:globals.bzl",
],
)

stardoc_with_diff_test(
Expand Down
7 changes: 5 additions & 2 deletions docs/native_binary_doc.md
Expand Up @@ -13,7 +13,7 @@ don't depend on Bash and work with --shell_executable="".
## native_binary

<pre>
native_binary(<a href="#native_binary-name">name</a>, <a href="#native_binary-data">data</a>, <a href="#native_binary-out">out</a>, <a href="#native_binary-src">src</a>)
native_binary(<a href="#native_binary-name">name</a>, <a href="#native_binary-data">data</a>, <a href="#native_binary-env">env</a>, <a href="#native_binary-out">out</a>, <a href="#native_binary-src">src</a>)
</pre>


Expand All @@ -30,6 +30,7 @@ in genrule.tools for example. You can also augment the binary with runfiles.
| :------------- | :------------- | :------------- | :------------- | :------------- |
| <a id="native_binary-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
| <a id="native_binary-data"></a>data | data dependencies. See https://bazel.build/reference/be/common-definitions#typical.data | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | <code>[]</code> |
| <a id="native_binary-env"></a>env | additional environment variables to set when the target is executed by <code>bazel</code>. Setting this requires bazel version 5.3.0 or later. | <a href="https://bazel.build/rules/lib/dict">Dictionary: String -> String</a> | optional | <code>{}</code> |
| <a id="native_binary-out"></a>out | An output name for the copy of the binary | String | required | |
| <a id="native_binary-src"></a>src | path of the pre-built executable | <a href="https://bazel.build/concepts/labels">Label</a> | required | |

Expand All @@ -39,7 +40,7 @@ in genrule.tools for example. You can also augment the binary with runfiles.
## native_test

<pre>
native_test(<a href="#native_test-name">name</a>, <a href="#native_test-data">data</a>, <a href="#native_test-out">out</a>, <a href="#native_test-src">src</a>)
native_test(<a href="#native_test-name">name</a>, <a href="#native_test-data">data</a>, <a href="#native_test-env">env</a>, <a href="#native_test-env_inherit">env_inherit</a>, <a href="#native_test-out">out</a>, <a href="#native_test-src">src</a>)
</pre>


Expand All @@ -56,6 +57,8 @@ the binary with runfiles.
| :------------- | :------------- | :------------- | :------------- | :------------- |
| <a id="native_test-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
| <a id="native_test-data"></a>data | data dependencies. See https://bazel.build/reference/be/common-definitions#typical.data | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | <code>[]</code> |
| <a id="native_test-env"></a>env | additional environment variables to set when the target is executed by <code>bazel</code>. Setting this requires bazel version 5.3.0 or later. | <a href="https://bazel.build/rules/lib/dict">Dictionary: String -> String</a> | optional | <code>{}</code> |
| <a id="native_test-env_inherit"></a>env_inherit | additional environment variables to inherit from the external environment when the test is executed by <code>bazel test</code>. Setting this requires bazel version 5.3.0 or later. | List of strings | optional | <code>[]</code> |
| <a id="native_test-out"></a>out | An output name for the copy of the binary | String | required | |
| <a id="native_test-src"></a>src | path of the pre-built executable | <a href="https://bazel.build/concepts/labels">Label</a> | required | |

Expand Down
6 changes: 4 additions & 2 deletions docs/private/stardoc_with_diff_test.bzl
Expand Up @@ -30,7 +30,8 @@ load("@io_bazel_stardoc//stardoc:stardoc.bzl", "stardoc")
def stardoc_with_diff_test(
name,
bzl_library_target,
out_label):
out_label,
deps = None):
"""Creates a stardoc target coupled with a `diff_test` for a given `bzl_library`.

This is helpful for minimizing boilerplate in repos with lots of stardoc targets.
Expand All @@ -39,6 +40,7 @@ def stardoc_with_diff_test(
name: the stardoc target name
bzl_library_target: the label of the `bzl_library` target to generate documentation for
out_label: the label of the output MD file
deps: additional files loaded by the bazel library, if any
"""
out_file = out_label.replace("//", "").replace(":", "/")

Expand All @@ -47,7 +49,7 @@ def stardoc_with_diff_test(
name = name,
out = out_file.replace(".md", "-docgen.md"),
input = bzl_library_target + ".bzl",
deps = [bzl_library_target],
deps = [bzl_library_target] + (deps or []),
)

# Ensure that the generated MD has been updated in the local source tree
Expand Down
22 changes: 22 additions & 0 deletions extension.bzl
@@ -0,0 +1,22 @@
# Copyright 2023 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Module extension to load additional repositories"""

load("@bazel_skylib//:workspace.bzl", "globals_repo")

def _globals_extension_impl(
module_ctx): # @unused
globals_repo()

globals_extension = module_extension(_globals_extension_impl)
44 changes: 38 additions & 6 deletions rules/native_binary.bzl
Expand Up @@ -20,7 +20,14 @@ do, but they run the wrapped binary directly, instead of through Bash, so they
don't depend on Bash and work with --shell_executable="".
"""

load("@bazel_skylib_globals//:globals.bzl", "globals")

def _impl_rule(ctx):
if not globals.RunEnvironmentInfo:
for attr in ("env", "env_inherit"):
if getattr(ctx.attr, attr, None):
fail("Attribute %s specified for %s is only supported with bazel >= 5.3.0" %
(attr, ctx.label))
out = ctx.actions.declare_file(ctx.attr.out)
ctx.actions.symlink(
target_file = ctx.executable.src,
Expand All @@ -41,11 +48,21 @@ def _impl_rule(ctx):
runfiles = runfiles.merge(d[DefaultInfo].default_runfiles)
runfiles = runfiles.merge(ctx.attr.src[DefaultInfo].default_runfiles)

return DefaultInfo(
executable = out,
files = depset([out]),
runfiles = runfiles,
)
ret = [
DefaultInfo(
executable = out,
files = depset([out]),
runfiles = runfiles,
),
]
if globals.RunEnvironmentInfo:
ret.append(
globals.RunEnvironmentInfo(
environment = ctx.attr.env,
inherited_environment = getattr(ctx.attr, "env_inherit", []),
),
)
return ret

_ATTRS = {
"src": attr.label(
Expand All @@ -65,8 +82,23 @@ _ATTRS = {
),
# "out" is attr.string instead of attr.output, so that it is select()'able.
"out": attr.string(mandatory = True, doc = "An output name for the copy of the binary"),
"env": attr.string_dict(
doc = "additional environment variables to set when the target is executed by " +
"`bazel`. Setting this requires bazel version 5.3.0 or later.",
default = {},
),
}

_TEST_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`. " +
"Setting this requires bazel version 5.3.0 or later.",
default = [],
),
)

native_binary = rule(
implementation = _impl_rule,
attrs = _ATTRS,
Expand All @@ -81,7 +113,7 @@ in genrule.tools for example. You can also augment the binary with runfiles.

native_test = rule(
implementation = _impl_rule,
attrs = _ATTRS,
attrs = _TEST_ATTRS,
test = True,
doc = """
Wraps a pre-built binary or script with a test rule.
Expand Down
31 changes: 31 additions & 0 deletions tests/native_binary/BUILD
Expand Up @@ -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(
Expand All @@ -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",
Expand Down Expand Up @@ -115,3 +130,19 @@ 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",
# On Windows we need the ".exe" extension.
# On other platforms the extension doesn't matter.
# Therefore we can use ".exe" on every platform.
out = "env_test.exe",
env = {
"TEST_ENV_VAR": "test_env_var_value",
},
env_inherit = [
"HOME", # for POSIX
"APPDATA", # for Windows
],
)
31 changes: 31 additions & 0 deletions tests/native_binary/assertenv.cc
@@ -0,0 +1,31 @@
#include <stdio.h>
#include <string.h>

#ifdef _WIN32
#define OS_VAR "APPDATA"
#define strncasecmp _strnicmp
#else
#define OS_VAR "HOME"
#endif

int main(int argc, char **argv, char **envp) {
bool test_env_found = false;
bool inherited_var_found = false;
for (char **env = envp; *env != NULL; ++env) {
printf("%s\n", *env);
if (strcmp(*env, "TEST_ENV_VAR=test_env_var_value") == 0) {
test_env_found = true;
}
if (strncasecmp(*env, OS_VAR "=", strlen(OS_VAR "=")) == 0) {
inherited_var_found = true;
}
}
if (!test_env_found) {
fprintf(stderr,
"expected TEST_ENV_VAR=test_env_var_value in environment\n");
}
if (!inherited_var_found) {
fprintf(stderr, "expected " OS_VAR " in environment\n");
}
return test_env_found && inherited_var_found ? 0 : 1;
}
27 changes: 27 additions & 0 deletions workspace.bzl
Expand Up @@ -15,7 +15,34 @@
"""Dependency registration helpers for repositories which need to load bazel-skylib."""

load("@bazel_skylib//lib:unittest.bzl", "register_unittest_toolchains")
load("@bazel_skylib//lib:versions.bzl", "versions")

def _globals_repo_impl(repository_ctx):
globals = {
"RunEnvironmentInfo": "5.3.0",
}
globals = {
k: k if versions.is_at_least(v, versions.get()) else "None"
for k, v in globals.items()
}

repository_ctx.file(
"globals.bzl",
"globals = struct(\n%s\n)\n" % "\n".join(
[" %s = %s" % item for item in globals.items()],
),
)
repository_ctx.file("BUILD.bazel", "exports_files(['globals.bzl'])\n")

_globals_repo = repository_rule(
implementation = _globals_repo_impl,
local = True, # required to make sure the version is updated if the bazel server restarts
)

def globals_repo():
_globals_repo(name = "bazel_skylib_globals")

def bazel_skylib_workspace():
"""Registers toolchains and declares repository dependencies of the bazel_skylib repository."""
register_unittest_toolchains()
globals_repo()