Skip to main content

String parameter macros

Many rule attributes (the ones with type attrs.arg) support expanding references to other rules using a mechanism called string parameter macros. All expanded build rules are automatically added as dependencies.

Note that the paths returned by these macros are relative paths. Using relative paths ensures that your builds are hermetic, that is, they are reproducible across different machine environments.

$(location //path/to:target)

Expands to the location of the output of the specified build rule. This means that you can refer to the output without needing to be aware of how Buck is storing data on the disk mid-build.

For example:

cxx_test(
name = "my_test",
srcs = ["main.cpp"],
preprocessor_flags = ["-DTEST_DIR=$(location :test_dir)"],
)

filegroup(
name = "test_dir",
srcs = [
"test_files/foo.json",
"test_files/bar.toml",
],
)

$(location_exec //path/to:target)

Identical to $(location //path/to:target), but the configuration is transitioned to the execution platform. This can be useful when using genrule to wrap another build system with buck.

$(source relative/path/to/source)

Expands to the location of the specified source. The difference with using $(location path/to:export_file_target) is that the path points to the file in the source tree, rather than a copy or symlink in buck-out.

For example:

cxx_test(
name = "my_test",
srcs = ["main.cpp"],
preprocessor_flags = ["-DMY_SOURCE_FILE=$(source path/to/my_source_file)"],
)

$(exe //path/to:target)

Expands a build rule that results in an executable to the commands necessary to run that executable as part of the build.

For example, a java_binary() might expand to a call to java -jar path/to/target.jar. Files that are executable (perhaps generated by a genrule()) are also expanded.

If the build rule does not generate an executable output, then an exception is thrown and the build breaks.

If the $(exe my_dependency) dependency should actually be built with the target platform, use $(exe_target my_dependency) instead, which will stick to the same platform as the target.

$(exe_target //path/to:target)

Identical to $(exe //path/to:target), except that the target is built using the target platform, rather than the execution platform.

This is for example useful to get the paths to executables to be run as part of tests. For example:

sh_test(
name = "my_test",
args = [
"$(exe_target //path/to:target_to_test)",
],
# `my_test.sh` takes a single argument, which is the path to an executable
# to test.
test = "my_test.sh",
visibility = ["//risk/tap_enricher/..."],
)

$(query_targets queryfunction(//path/to:target))

Runs a query on the given target and replaces the macro with the matching targets.

For example:

my_rule(
name = "example",
# Will be replaced by all dependencies of `some_target`.
foo = "$(query_targets deps(:some_target))"
)

$(query_outputs queryfunction(//path/to:target))

Runs a query on the given target and replaces the macro with the outputs of the matching targets.

For example:

my_rule(
name = "example",
# Will be replaced by the outputs of all dependencies of `some_target`.
foo = "$(query_outputs deps(:some_target))"
)

$(query_targets_and_outputs [separator] queryfunction(//path/to:target))

Runs a query on the given target and replaces the macro with matching targets and their outputs, which are separated by an optional separator (defaults to a space).

For example:

my_rule(
name = "example",
# Will be replaced by the space-separated dependencies of `some_target`, as
# well as their outputs.
foo = "$(query_targets_and_outputs deps(:some_target))"
)

$(classpath //path/to:target)

Expands to the transitive classpath of the specified build rule, provided that the rule has a Java classpath. If the rule does not have (or contribute to) a classpath, then an exception is thrown and the build breaks. It takes an optional second argument to limit the depth of the traversal, but only a depth of 1 is currently supported. Its behavior is similar to the corresponding operation in the buck audit command.

$(location //path/to:target[output])

Expands to the named output file or directory of the given target, for rules that expose supplementary outputs.

How to manage long expanded values

In some cases, the results of the expanded macro might be so long that they exceed a limit in your operating environment. For example, if you use the results of an expanded macro in Bash, it could exceed Bash's command-line limits.

To work around these limits, prefix the macro name with the @ character. Buck then writes the results of the expanded macro to a temporary file and replaces the macro with the path to that file while keeping the @ prefix. For example:

$(@query_targets_and_outputs //...)

expands to something similar to:

@/tmp/tempfile

Many applications recognize the @ prefix to mean: read the contents of this file to obtain the necessary arguments.

How to prevent expansion

If you need to prevent expansion of a string parameter macro, prefix the macro with a backslash.

\$(dirname ...)

For example, the following rule passes the dirname command to the shell to execute in a subshell; dirname is a Unix/Linux command-line utility that returns the directory of a specified file. We need to use an escape because the syntax for subshells is the same as the syntax for string parameter macros:

genrule(
name = 'gen',
out = 'out.txt',
cmd = 'cp $SRCS \$(dirname $OUT)',
srcs = [
'test1.txt',
'test2.txt',
],
)

Query functions

The query_* macros accept a quoted query expression which supports the following query functions:

  • attrfilter
  • attrregexfilter
  • deps
  • except (set-difference)
  • filter
  • intersect
  • kind
  • rdeps
  • set
  • union

Frequently Asked Questions (FAQ)

  • When are macros evaluated? Macros are evaluated before the command is passed to the shell for execution. You can think of them as simple string replacements.
  • Can macros be nested? Macros cannot be nested. If you need to run an additional macro on the output of a previous macro, create a nested genrule definition and use the $(location) macro to read the output of the previous macro.
  • Are parentheses okay inside a macro? Inside a macro, parentheses must be balanced. Parentheses which are part of a quoted string are ignored.
  • Is white space okay inside a macro? Macro arguments are white space separated, so arguments which contain white space must be quoted.
  • Are nested quotes allowed? A single level of nested quotes is allowed, such as "My name is 'Buck'." or 'My name is "Buck".'. Note that when you use a macro in a BUCK file, you must ensure that quotes are properly escaped, so that the shell command that uses the macro forms a proper string.

Extended Backus-Naur form

The Extended Backus-Naur form (EBNF) grammar for a macro is as follows:

macro = "$(", macro_name, whitespace, [arg_list], ")";
macro_name = {all_ascii_chars - whitespace - parens};
whitespace = "\t" | "\n" | " " | "\r";
parens = "(" | ")";
arg_list = arg | arg, whitespace, arg_list;
arg = {all_ascii_chars - whitespace - parens}
| "(", arg, ")"
| "\"", [{-"\""}], "\""
| "'", [{-"'"}], "'";