I am trying to make my build configurable with a custom flag.
It should be possible to specify the flag value when I build, e.g.
bazel build //test:bundle --//:foo_flag=bar
Here is my build target definition (using rules_js):
load("#npm//:defs.bzl", "npm_link_all_packages")
load("#npm//test:webpack-cli/package_json.bzl", "bin")
npm_link_all_packages(
name = "node_modules",
)
bin.webpack_cli(
name = "bundle",
outs = [
"out/index.html",
"out/main.js",
],
args = [
"--config",
"webpack.config.js",
"-o",
"out",
],
srcs = [
"webpack.config.js",
":node_modules",
] + glob([
"src/**/*.js",
]),
chdir = package_name(),
env = {
# ?
},
visibility = [
"//visibility:public",
],
)
What is unclear from the various Bazel examples is how to actually pass the flag to the rule.
I am looking for something like this:
# Invalid
env = {
"FOO": "$(value //:foo_flag)",
},
How do I pass a flag in Bazel?
To use the command-line flag, you want to use a config_setting() and then select() based on that.
config_setting(
name = "foo_flag_set",
flag_values = { "//:foo_flag": "bar" },
)
...
bin.webpack_cli(
name = "bundle",
...
env = select({
":foo_flag_set": {...},
"//conditions:default": None,
},
...
)
This might be the way to go, if you have a distinct set of values for your flag. Otherwise you might want to think about a --action_env flag.
Set them with the --define flag. Values set like that will be expanded in any context that performs "Make" variable expansion. That looks like this:
bin.webpack_cli(
name = "bundle",
...
env = {
"FOO": "$(FOO)",
},
)
and then build with --define=FOO=bar.
I can't find the docs for bin.webpack_cli to confirm it does "Make" variable expansion in env, but most env attributes do so I'm assuming it does. For example rules_docker's container_layer.env mentions this in its documentation.
Related
I'm using the please build tool and I'm having trouble using the target|file syntax with a filegroup. Specifically, I want to use specific files from the filegroup output in a genrule, but the syntax doesn't seem to work. Here's an example of what I'm trying to do:
# subdir/BUILD.plz
this = package_name()
a = text_file(
name = 'a_file',
content = "aaaaaaaaa",
)
b = text_file(
name = 'b_file',
content = "bbbbbbbbb",
)
c = text_file(
name = 'c_file',
content = "ccccccccc",
)
filegroup(
name = this,
srcs = {
'A': [a],
'B': [b],
'C': [c],
},
visibility = ['PUBLIC']
)
# BUILD.plz
sub = '#//subdir'
a_and_c_concat = genrule(
name = 'a_and_c_concat',
srcs = {
'A': [f'{sub}|A'],
'C': [f'{sub}|C'],
},
outs = ["out"],
cmd = """
set -eux
cat "${SRCS_A}" "${SRCS_C}" > "${OUTS}"
""",
visibility = ['PUBLIC'],
)
It does the expected behavior if I change the filegroup to be a genrule and 're-export' srcs to outs like so:
# subdir/BUILD.plz
...
genrule(
name = this,
srcs = {
'A': [a],
'B': [b],
'C': [c],
},
outs = {
'A': ['a'],
'B': ['b'],
'C': ['c'],
},
cmd = """
set -eux
cp "${SRCS_A}" "${OUTS_A}"
cp "${SRCS_B}" "${OUTS_B}"
cp "${SRCS_C}" "${OUTS_C}"
""",
visibility = ['PUBLIC']
)
Is there a way to use the target|file syntax with filegroup in Please build, or is there another way to achieve the same effect another way?
Any help or advice would be greatly appreciated. Thank you!
This isn't implemented at the moment, but I think it's a reasonable thing for Please to do.
Filegroups are just a thin wrapper around build_rule(), so they accept all the same arguments, however they don't expose named sources as named outputs. I've created an issue here to track this as a feature request:
https://github.com/thought-machine/please/issues/2701
I'm trying to let a repository rule to be run again in bazel when files used by the rule changes.
I have the following rule
def _irule_impl(ctx):
cmd = [ str(ctx.path(ctx.attr._tool)) ]
st = ctx.execute(cmd, environment = ctx.os.environ)
ctx.symlink(st.stdout,"my_r2")
ctx.execute(["echo",">>>>>>>>>>>>>>>>> Running implementation"], quiet=False)
"_tool_deps2": attr.label(
allow_single_file = True,
default = "//tools:tool3",
),
"_tool_deps3": attr.label(
allow_single_file = True,
default = "//tools:tool_filegroup",
),
}
)
//tools:tool3 is a file, and when I change that, the rule is executed again
//tools:tools_filesgroup is a filegroup
filegroup(
name = "tool_filegroup",
srcs = "tool2"
)
and when I change tool2 the repository rule is not executed again when I build.
Is there a way to get this working ?
I'm trying to build a rule for bazel which emulates the CMake *.in template system.
This has two challenges, the first is generate the template output. The second is make the output available to both genrules, filegroups and cc_* rules. The third is to get that dependency to transitively be passed to further downstream rules.
I have it generating the output file version.hpp in genfiles (or bazel-bin), and I can get the initial library rule to include it, but I can't seem to figure out how to make my cc_binary rule, which depends on the cc_library and transitively the header_template rule to find the header file.
I have the following .bzl rule:
def _header_template_impl(ctx):
# this generates the output from the template
ctx.actions.expand_template(
template = ctx.file.template,
output = ctx.outputs.out,
substitutions = ctx.attr.vars,
)
return [
# create a provider which says that this
# out file should be made available as a header
CcInfo(compilation_context=cc_common.create_compilation_context(
headers=depset([ctx.outputs.out])
)),
# Also create a provider referencing this header ???
DefaultInfo(files=depset(
[ctx.outputs.out]
))
]
header_template = rule(
implementation = _header_template_impl,
attrs = {
"vars": attr.string_dict(
mandatory = True
),
"extension": attr.string(default=".hpp"),
"template": attr.label(
mandatory = True,
allow_single_file = True,
),
},
outputs = {
"out": "%{name}%{extension}",
},
output_to_genfiles = True,
)
elsewhere I have a cc_library rule:
load("//:tools/header_template.bzl", "header_template")
# version control
BONSAI_MAJOR_VERSION = '2'
BONSAI_MINOR_VERSION = '0'
BONSAI_PATCH_VERSION = '9'
BONSAI_VERSION = \
BONSAI_MAJOR_VERSION + '.' + \
BONSAI_MINOR_VERSION + '.' + \
BONSAI_PATCH_VERSION
header_template(
name = "bonsai_version",
extension = ".hpp",
template = "version.hpp.in",
vars = {
"#BONSAI_MAJOR_VERSION#": BONSAI_MAJOR_VERSION,
"#BONSAI_MINOR_VERSION#": BONSAI_MINOR_VERSION,
"#BONSAI_PATCH_VERSION#": BONSAI_PATCH_VERSION,
"#BONSAI_VERSION#": BONSAI_VERSION,
},
)
# ...
private = glob([
"src/**/*.hpp",
"src/**/*.cpp",
"proto/**/*.hpp",
])
public = glob([
"include/*.hpp",
":bonsai_version",
])
cc_library(
# target name matches directory name so you can call:
# bazel build .
name = "bonsai",
srcs = private,
hdrs = public,
# public headers
includes = [
"include",
],
# ...
deps = [
":bonsai_version",
# ...
],
# ...
)
When I build, my source files need to be able to:
#include "bonsai_version.hpp"
I think the answer involves CcInfo but I'm grasping in the dark as to how it should be constructed.
I've already tried add "-I$(GENDIR)/" + package_name() to the copts, to no avail. The generated header still isn't available.
My expectation is that I should be able to return some kind of Info object that would allow me to add the dependency in srcs. Maybe it should be a DefaultInfo.
I've dug through the bazel rules examples and the source, but I'm missing something fundamental, and I can't find documentation that discuss this particular.
I'd like to be able to do the following:
header_template(
name = "some_header",
extension = ".hpp",
template = "some_header.hpp.in",
vars = {
"#SOMEVAR#": "value",
"{ANOTHERVAR}": "another_value",
},
)
cc_library(
name = "foo",
srcs = ["foo.src", ":some_header"],
...
)
cc_binary(
name = "bar",
srcs = ["bar.cpp"],
deps = [":foo"],
)
and include the generated header like so:
#include "some_header.hpp"
void bar(){
}
The answer looks like it is:
def _header_template_impl(ctx):
# this generates the output from the template
ctx.actions.expand_template(
template = ctx.file.template,
output = ctx.outputs.out,
substitutions = ctx.attr.vars,
)
return [
# create a provider which says that this
# out file should be made available as a header
CcInfo(compilation_context=cc_common.create_compilation_context(
# pass out the include path for finding this header
includes=depset([ctx.outputs.out.dirname]),
# and the actual header here.
headers=depset([ctx.outputs.out])
))
]
elsewhere:
header_template(
name = "some_header",
extension = ".hpp",
template = "some_header.hpp.in",
vars = {
"#SOMEVAR#": "value",
"{ANOTHERVAR}": "another_value",
},
)
cc_library(
name = "foo",
srcs = ["foo.cpp"],
deps = [":some_header"],
...
)
cc_binary(
name = "bar",
srcs = ["bar.cpp"],
deps = [":foo"],
)
If your header has a generic name (eg config.h) and you want it to be private (ie srcs instead of hdrs), you might need a different approach. I've seen this problem for gflags, which "leaked" config.h and affected libraries that depended on it (issue).
Of course, in both cases, the easiest solution is to generate and commit header files for the platforms you target.
Alternatively, you can set copts for the cc_library rule that uses the generated private header:
cc_library(
name = "foo",
srcs = ["foo.cpp", "some_header.hpp"],
copts = ["-I$(GENDIR)/my/package/name"],
...
)
If you want this to work when your repository is included as an external repository, you have a bit more work cut out for you due to bazel issue #4463.
PS. You might want to see if cc_fix_config from https://github.com/antonovvk/bazel_rules works for you. It's just a wrapper around perl but I found it useful.
I'm working on my first custom Bazel rules. The rules allow the running of bats command line tests.
I've included the rule definition below verbatim. I'm pretty happy with it so far but there's one part which feels really ugly and non-standard. If the rule user adds a binary dependency to the rule then I make sure that the binary appears on the PATH so that it can be tested. At the moment I do this by making a list of the binary paths and then appending them with $PWD which is expanded inside the script to the complete execution path. This feels hacky and error prone.
Is there a more idiomatic way to do this? I don't believe I can access the execution path in the rule due to it not being created until the execution phase.
Thanks for your help!
BATS_REPOSITORY_BUILD_FILE = """
package(default_visibility = [ "//visibility:public" ])
sh_binary(
name = "bats",
srcs = ["libexec/bats"],
data = [
"libexec/bats-exec-suite",
"libexec/bats-exec-test",
"libexec/bats-format-tap-stream",
"libexec/bats-preprocess",
],
)
"""
def bats_repositories(version="v0.4.0"):
native.new_git_repository(
name = "bats",
remote = "https://github.com/sstephenson/bats",
tag = version,
build_file_content = BATS_REPOSITORY_BUILD_FILE
)
BASH_TEMPLATE = """
#!/usr/bin/env bash
set -e
export TMPDIR="$TEST_TMPDIR"
export PATH="{bats_bins_path}":$PATH
"{bats}" "{test_paths}"
"""
def _dirname(path):
prefix, _, _ = path.rpartition("/")
return prefix.rstrip("/")
def _bats_test_impl(ctx):
runfiles = ctx.runfiles(
files = ctx.files.srcs,
collect_data = True,
)
tests = [f.short_path for f in ctx.files.srcs]
path = ["$PWD/" + _dirname(b.short_path) for b in ctx.files.deps]
sep = ctx.configuration.host_path_separator
ctx.file_action(
output = ctx.outputs.executable,
executable = True,
content = BASH_TEMPLATE.format(
bats = ctx.executable._bats.short_path,
test_paths = " ".join(tests),
bats_bins_path = sep.join(path),
),
)
runfiles = runfiles.merge(ctx.attr._bats.default_runfiles)
return DefaultInfo(
runfiles = runfiles,
)
bats_test = rule(
attrs = {
"srcs": attr.label_list(
allow_files = True,
),
"deps": attr.label_list(),
"_bats": attr.label(
default = Label("#bats//:bats"),
executable = True,
cfg = "host",
),
},
test = True,
implementation = _bats_test_impl,
)
This should be easy to support from Bazel 0.8.0 which will be released in ~2 weeks.
In your skylark implementation you should do ctx.expand_location(binary) where binary should be something like $(execpath :some-label) so you might want to just format the label you got from the user with the $(execpath) and bazel will make sure to give you the execution location of that label.
Some relevant resources:
$location expansion in Bazel
https://github.com/bazelbuild/bazel/issues/2475
https://github.com/bazelbuild/bazel/commit/cff0dc94f6a8e16492adf54c88d0b26abe903d4c
I have a code generator that produces three output files:
client.cpp
server.cpp
data.h
The genrule looks like this:
genrule(
name = 'code_gen',
tools = [ '//tools:code_gen.sh' ],
outs = [ 'client.cpp', 'server.cpp', 'data.h' ],
local = True,
cmd = '$(location //tools:code_gen.sh) $(#D)')
The 'client.cpp' and 'server.cpp' each have their own cc_library rule.
My question is how to depend on the genrule but only use a specific output file.
What I did is create a macro that defined the genrule with specific outs set to the file required, but this resulted in multiple execution of the genrule:
gen.bzl:
def code_generator(
name,
out):
native.genrule(
name = name,
tools = [ '//bazel:gen.sh' ],
outs = [ out ],
local = True,
cmd = '$(location //bazel:gen.sh) $(#D)')
BUILD
load(':gen.bzl', 'code_generator')
code_generator('client_cpp', 'client.cpp')
code_generator('server_cpp', 'server.cpp')
code_generator('data_h', 'data.h')
cc_library(
name = 'client',
srcs = [ ':client_cpp' ],
hdrs = [ ':data_h' ],
)
cc_library(
name = 'server',
srcs = [ ':server_cpp' ],
hdrs = [ ':data_h' ],
)
Is there a way to depend on a genrule making it run once and then use only selected outputs from it?
You should be able to just use the filename (e.g. :server.cpp) to depend on a specific output of a rule.