diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index bd50460..6bdf62c 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -6,6 +6,8 @@ tasks: bazel: latest build_targets: - "..." + build_flags: + - "--incompatible_remap_main_repo" test_targets: - "..." test_flags: @@ -17,6 +19,8 @@ tasks: bazel: latest build_targets: - "..." + build_flags: + - "--incompatible_remap_main_repo" test_targets: - "..." test_flags: @@ -28,6 +32,8 @@ tasks: bazel: latest build_targets: - "..." + build_flags: + - "--incompatible_remap_main_repo" test_targets: - "..." test_flags: @@ -39,6 +45,8 @@ tasks: bazel: latest build_targets: - "..." + build_flags: + - "--incompatible_remap_main_repo" test_targets: - "--" - "..." @@ -52,6 +60,8 @@ tasks: bazel: last_green build_targets: - "..." + build_flags: + - "--incompatible_remap_main_repo" test_targets: - "..." test_flags: @@ -63,6 +73,8 @@ tasks: bazel: last_green build_targets: - "..." + build_flags: + - "--incompatible_remap_main_repo" test_targets: - "..." test_flags: @@ -74,6 +86,8 @@ tasks: bazel: last_green build_targets: - "..." + build_flags: + - "--incompatible_remap_main_repo" test_targets: - "..." test_flags: @@ -85,6 +99,8 @@ tasks: bazel: last_green build_targets: - "..." + build_flags: + - "--incompatible_remap_main_repo" test_targets: - "--" - "..." diff --git a/README.md b/README.md index 6f82d96..14066e9 100644 --- a/README.md +++ b/README.md @@ -56,22 +56,24 @@ s = shell.quote(p) ## List of modules (in lib/) -* [collections](lib/collections.bzl) -* [dicts](lib/dicts.bzl) -* [partial](lib/partial.bzl) -* [paths](lib/paths.bzl) -* [selects](lib/selects.bzl) +* [collections](docs/collections_doc.md) +* [dicts](docs/dicts_doc.md) +* [partial](docs/partial_doc.md) +* [paths](docs/paths_doc.md) +* [selects](docs/selects_doc.md) * [sets](lib/sets.bzl) - _deprecated_, use `new_sets` -* [new_sets](lib/new_sets.bzl) -* [shell](lib/shell.bzl) -* [structs](lib/structs.bzl) -* [types](lib/types.bzl) -* [unittest](lib/unittest.bzl) -* [versions](lib/versions.bzl) +* [new_sets](docs/new_sets.md) +* [shell](docs/shell_doc.md) +* [structs](docs/structs_doc.md) +* [types](docs/types_doc.md) +* [unittest](docs/unittest_doc.md) +* [versions](docs/versions_doc.md) ## List of rules (in rules/) -* [`cmd_maprule` and `bash_maprule`](lib/maprule.bzl) +* [analysis_test](docs/analysis_test_doc.md) +* [build_test](docs/build_test_doc.md) +* [`cmd_maprule` and `bash_maprule`](docs/maprule_doc.md) ## Writing a new module diff --git a/WORKSPACE b/WORKSPACE index e0f2a0a..4ae2edc 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -3,3 +3,16 @@ workspace(name = "bazel_skylib") load(":workspace.bzl", "bazel_skylib_workspace") bazel_skylib_workspace() + +# Below this line is for documentation generation only, +# and should thus not be included by dependencies on +# bazel-skylib. + +load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") +git_repository( + name = "io_bazel_skydoc", + remote = "https://github.com/bazelbuild/skydoc.git", + commit = "ac5c106412697ffb9364864070bac796b9bb63d3", # Feb 27, 2019 +) +load("@io_bazel_skydoc//skylark:skylark.bzl", "skydoc_repositories") +skydoc_repositories() diff --git a/docs/BUILD b/docs/BUILD new file mode 100644 index 0000000..865db0b --- /dev/null +++ b/docs/BUILD @@ -0,0 +1,101 @@ +load("@io_bazel_skydoc//stardoc:stardoc.bzl", "stardoc") + +# Note that due to a bug in Bazel 0.22.0, these targets cannot be built without +# the --incompatible_remap_main_repo flag. +stardoc( + name = "build_test_docs", + input = "//rules:build_test.bzl", + out = "build_test_doc_gen.md", + deps = ["//rules:build_test"], +) + +stardoc( + name = "analysis_test_docs", + input = "//rules:analysis_test.bzl", + out = "analysis_test_doc_gen.md", +) + +stardoc( + name = "collections_docs", + input = "//lib:collections.bzl", + out = "collections_doc_gen.md", + deps = ["//lib:collections"], +) + +stardoc( + name = "dicts_docs", + input = "//lib:dicts.bzl", + out = "dicts_doc_gen.md", + deps = ["//lib:dicts"], +) + +stardoc( + name = "partial_docs", + input = "//lib:partial.bzl", + out = "partial_doc_gen.md", + deps = ["//lib:partial"], +) + +stardoc( + name = "paths_docs", + input = "//lib:paths.bzl", + out = "paths_doc_gen.md", + deps = ["//lib:paths"], +) + +stardoc( + name = "selects_docs", + input = "//lib:selects.bzl", + out = "selects_doc_gen.md", + deps = ["//lib:selects"], +) + +stardoc( + name = "new_sets_docs", + input = "//lib:new_sets.bzl", + out = "new_sets_doc_gen.md", + deps = ["//lib:new_sets"], +) + +stardoc( + name = "shell_docs", + input = "//lib:shell.bzl", + out = "shell_doc_gen.md", + deps = ["//lib:shell"], +) + +stardoc( + name = "structs_docs", + input = "//lib:structs.bzl", + out = "structs_doc_gen.md", + deps = ["//lib:structs"], +) + +stardoc( + name = "types_docs", + input = "//lib:types.bzl", + out = "types_doc_gen.md", + deps = ["//lib:types"], +) + +stardoc( + name = "unittest_docs", + input = "//lib:unittest.bzl", + out = "unittest_doc_gen.md", + deps = ["//lib:unittest"], +) + +stardoc( + name = "versions_docs", + input = "//lib:versions.bzl", + out = "versions_doc_gen.md", + deps = ["//lib:versions"], +) + +stardoc( + name = "maprule_docs", + input = "//rules:maprule.bzl", + out = "maprule_doc_gen.md", + deps = ["//rules:maprule"], +) + diff --git a/docs/analysis_test_doc.md b/docs/analysis_test_doc.md new file mode 100755 index 0000000..762a82a --- /dev/null +++ b/docs/analysis_test_doc.md @@ -0,0 +1,62 @@ + +## analysis_test + +
+analysis_test(name, targets)
+
+ +Test rule checking that other targets can be successfully analyzed. + + This rule essentially verifies that all targets under `targets` would + generate no errors when analyzed with `bazel build [targets] --nobuild`. + Action success/failure for the targets and their transitive dependencies + are not verified. An analysis test simply ensures that each target in the transitive + dependencies propagate providers appropriately and register actions for their outputs + appropriately. + + NOTE: If the targets fail to analyze, instead of the analysis_test failing, the analysis_test + will fail to build. Ideally, it would instead result in a test failure. This is a current + infrastructure limitation that may be fixed in the future. + + Typical usage: + + load("@bazel_skylib//rules:analysis_test.bzl", "analysis_test") + analysis_test( + name = "my_analysis_test", + targets = [ + "//some/package:rule", + ], + ) + + Args: + name: The name of the test rule. + targets: A list of targets to ensure build. + + +### Attributes + + + + + + + + + + + + + + + + +
name + Name; required +

+ A unique name for this target. +

+
targets + List of labels; required +
+ + diff --git a/docs/build_test_doc.md b/docs/build_test_doc.md new file mode 100755 index 0000000..6f3c597 --- /dev/null +++ b/docs/build_test_doc.md @@ -0,0 +1,67 @@ +## build_test + +
+build_test(name, targets, kwargs)
+
+ +Test rule checking that other targets build. + +This works not by an instance of this test failing, but instead by +the targets it depends on failing to build, and hence failing +the attempt to run this test. + +NOTE: At the moment, this won't work on Windows; but someone adding +support would be welcomed. + +Typical usage: + +``` + load("@bazel_skylib//rules:build_test.bzl", "build_test") + build_test( + name = "my_build_test", + targets = [ + "//some/package:rule", + ], + ) +``` + + +### Parameters + + + + + + + + + + + + + + + + + + + + +
name + required. +

+ The name of the test rule. +

+
targets + required. +

+ A list of targets to ensure build. +

+
kwargs + optional. +

+ The common attributes for tests. +

+
+ + diff --git a/docs/collections_doc.md b/docs/collections_doc.md new file mode 100755 index 0000000..64ffb5e --- /dev/null +++ b/docs/collections_doc.md @@ -0,0 +1,108 @@ +## collections.after_each + +
+collections.after_each(separator, iterable)
+
+ +Inserts `separator` after each item in `iterable`. + +### Parameters + + + + + + + + + + + + + + + + +
separator + required. +

+ The value to insert after each item in `iterable`. +

+
iterable + required. +

+ The list into which to intersperse the separator. +

+
+ + +## collections.before_each + +
+collections.before_each(separator, iterable)
+
+ +Inserts `separator` before each item in `iterable`. + +### Parameters + + + + + + + + + + + + + + + + +
separator + required. +

+ The value to insert before each item in `iterable`. +

+
iterable + required. +

+ The list into which to intersperse the separator. +

+
+ + +## collections.uniq + +
+collections.uniq(iterable)
+
+ +Returns a list of unique elements in `iterable`. + +Requires all the elements to be hashable. + + +### Parameters + + + + + + + + + + + + +
iterable + required. +

+ An iterable to filter. +

+
+ + diff --git a/docs/dicts_doc.md b/docs/dicts_doc.md new file mode 100755 index 0000000..b266545 --- /dev/null +++ b/docs/dicts_doc.md @@ -0,0 +1,38 @@ +## dicts.add + +
+dicts.add(dictionaries)
+
+ +Returns a new `dict` that has all the entries of the given dictionaries. + +If the same key is present in more than one of the input dictionaries, the +last of them in the argument list overrides any earlier ones. + +This function is designed to take zero or one arguments as well as multiple +dictionaries, so that it follows arithmetic identities and callers can avoid +special cases for their inputs: the sum of zero dictionaries is the empty +dictionary, and the sum of a single dictionary is a copy of itself. + + +### Parameters + + + + + + + + + + + + +
dictionaries + optional. +

+ Zero or more dictionaries to be added. +

+
+ + diff --git a/docs/maprule_doc.md b/docs/maprule_doc.md new file mode 100755 index 0000000..cc438ed --- /dev/null +++ b/docs/maprule_doc.md @@ -0,0 +1,573 @@ + +## bash_maprule + +
+bash_maprule(name, add_env, cmd, foreach_srcs, message, outs_templates, srcs, tools)
+
+ + + +Maprule that runs a Bash command. + +This rule is the same as `cmd_maprule` except this one uses Bash to run the command, therefore the +`cmd` attribute must use Bash syntax. `bash_maprule` rules can only be built if Bash is installed on +the build machine. + + +Maprule runs a specific command for each of the "foreach" source files. This allows processing +source files in parallel, to produce some outputs for each of them. + +The name "maprule" indicates that this rule can be used to map source files to output files, and is +also a reference to "genrule" that inspired this rule's design (though there are significant +differences). + +Below you will find an Example, Tips and Tricks, and an FAQ. + +### Example + + + # This file is //projects/game:BUILD + + load("@bazel_skylib//rules:maprule.bzl", "bash_maprule") + + bash_maprule( + name = "process_assets", + foreach_srcs = [ + "rust.png", + "teapot.3ds", + "//assets:wood.jpg", + "//assets:models", + ], + outs_templates = { + "TAGS": "{src}.tags", + "DIGEST": "digests/{src_name_noext}.md5", + }, + tools = [ + "//bin:asset-tagger", + "//util:md5sum", + ], + add_env = { + "ASSET_TAGGER": "$(location //bin:asset-tagger)", + "MD5SUM": "$(location //util:md5sum)", + }, + + # Note: this command should live in a script file, we only inline it in the `cmd` attribute + # for the sake of demonstration. See Tips and Tricks section. + cmd = '"$MAPRULE_ASSET_TAGGER" --input="$MAPRULE_SRC" --output="$MAPRULE_TAGS" && ' + + '"$MAPRULE_MD5SUM" "$MAPRULE_SRC" > "$MAPRULE_DIGEST"', + ) + + +The "process_assets" rule above will execute the command in the `cmd` to process "rust.png", +"teapot.3ds", "//assets:wood.jpg", and every file in the "//assets:models" rule, producing the +corresponding .tags and .md5 files for each of them, under the following paths: + + bazel-bin/projects/game/process_assets_out/projects/game/rust.png.tags + bazel-bin/projects/game/process_assets_out/digests/rust.md5 + bazel-bin/projects/game/process_assets_out/projects/game/teapot.3ds.tags + bazel-bin/projects/game/process_assets_out/digests/teapot.md5 + bazel-bin/projects/game/process_assets_out/assets/wood.jpg.tags + bazel-bin/projects/game/process_assets_out/digests/wood.md5 + ... + +You can create downstream rules, for example a filegroup or genrule (or another maprule) that put +this rule in their `srcs` attribute and get all the .tags and .md5 files. + +## Tips and Tricks + +*(The Tips and Tricks section is the same for `cmd_maprule` and `bash_maprule`.)* + +### Use script files instead of inlining commands in the `cmd` attribute. + +Unless the command is trivial, don't try to write it in `cmd`. + +Properly quoting parts of the command is challenging enough, add to that escaping for the BUILD +file's syntax and the `cmd` attribute quickly gets unmaintainably complex. + +It's a lot easier and maintainable to create a script file such as "foo.sh" in `bash_maprule` or +"foo.bat" in `cmd_maprule` for the commands. To do that: + +1. move the commands to a script file +2. add the script file to the `tools` attribute +3. add an entry to the `add_env` attribute, e.g. "`TOOL: "$(location :tool.sh)"`" +4. replace the `cmd` with just "$MAPRULE_FOO" (in `bash_maprule`) or "%MAPRULE_FOO%" (in + `cmd_maprule`). + +Doing this also avoids hitting command line length limits. + +Example: + + cmd_maprule( + ... + srcs = ["//data:temperatures.txt"], + tools = [ + "command.bat", + ":weather-stats", + ], + add_env = { + "COMMAND": "$(location :command.bat)", + "STATS_TOOL": "$(location :weather-stats-computer)", + "TEMPERATURES": "$(location //data:temperatures.txt)", + }, + cmd = "%MAPRULE_COMMAND%", + ) + +### Use the `add_env` attribute to pass tool locations to the command. + +Entries in the `add_env` attribute may use "$(location)" references and may also use the same +placeholders as the `outs_templates` attribute. For example you can use this mechanism to pass extra +"$(location)" references of `tools` or `srcs` to the actions. + +Example: + + cmd_maprule( + ... + foreach_srcs = [...], + outs_templates = {"STATS_OUT": "{src}-stats.txt"}, + srcs = ["//data:temperatures.txt"], + tools = [":weather-stats"], + add_env = { + "STATS_TOOL": "$(location :weather-stats-computer)", + "TEMPERATURES": "$(location //data:temperatures.txt)", + }, + cmd = "%MAPRULE_STATS_TOOL% --src=%MAPRULE_SRC% --data=%MAPRULE_TEMPERATURES% > %MAPRULE_STATS_OUT%", + ) + + cc_binary( + name = "weather-stats", + ... + ) + +## Environment Variables + +*(The Environment Variables section is the same for `cmd_maprule` and `bash_maprule`.)* + +The rule defines several environment variables available to the command may reference. All of these +envvars names start with "MAPRULE_". You can add your own envvars with the `add_env` attribute. + +The command can use some envvars, all named "MAPRULE_*<something>*". + +The complete list of environment variables is: + +- "MAPRULE_SRC": the path of the current file from `foreach_srcs` +- "MAPRULE_SRCS": the space-separated paths of all files in the `srcs` attribute +- "MAPRULE_*<OUT>*": for each key name *<OUT>* in the `outs_templates` attribute, this + is the path of the respective output file for the current source +- "MAPRULE_*<ENV>*": for each key name *<ENV>* in the `add_env` attribute + +## FAQ + +*(The FAQ section is the same for `cmd_maprule` and `bash_maprule`.)* + +### What's the motivation for maprule? What's wrong with genrule? + +genrule creates a single action for all of its inputs. It requires specifying all output files. +Finally, it can only create Bash commands. + +Maprule supports parallel processing of its inputs and doesn't require specifying all outputs, just +templates for the outputs. And not only Bash is supported (via `bash_maprule`) but so are +cmd.exe-style commands via `cmd_maprule`. + +### `genrule.cmd` supports "$(location)" expressions, how come `*_maprule.cmd` doesn't? + +Maprule deliberately omits support for this feature to avoid pitfalls with quoting and escaping, and +potential issues with paths containing spaces. Instead, maprule exports environment variables for +the input and outputs of the action, and allows the user to define extra envvars. These extra +envvars do support "$(location)" expressions, so you can pass paths of labels in `srcs` and `tools`. + +### Why are all envvars exported with the "MAPRULE_" prefix? + +To avoid conflicts with other envvars, whose names could also be attractive outs_templates names. + +### Why do `outs_templates` and `add_env` keys have to be uppercase? + +Because they are exported as all-uppercase envvars, so requiring that they be declared as uppercase +gives a visual cue of that. It also avoids clashes resulting from mixed lower-upper-case names like +"foo" and "Foo". + +### Why don't `outs_templates` and `add_env` keys have to start with "MAPRULE_" even though they are exported as such? + +For convenience. It seems to bring no benefit to have the user always type "MAPRULE_" in front of +the name when the rule itself could as well add it. + +### Why are all outputs relative to "*<maprule_pkg>*/*<maprule_name>*_out/" ? + +To ensure that maprules in the same package and with the same outs_templates produce distinct output +files. + +### Why aren't `outs_templates` keys available as placeholders in the values of `add_env`? + +Because `add_env` is meant to be used for passing extra "$(location)" information to the action, and +the output paths are already available as envvars for the action. + + +### Attributes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
name + Name; required +

+ A unique name for this target. +

+
add_env + Dictionary: String -> String; optional +

+ Extra environment variables to define for the actions. Every variable's name must be uppercase. Bazel will automatically prepend "MAPRULE_" to the name when exporting the variable for the action. The values may use "$(location)" expressions for labels declared in the `srcs` and `tools` attribute, and may reference the same placeholders as the values of the `outs_templates` attribute. +

+
cmd + String; required +

+ The command to execute. It must be in the syntax corresponding to this maprule type, e.g. for `bash_maprule` this must be a Bash command, and for `cmd_maprule` a Windows Command Prompt (cmd.exe) command. Several environment variables are available for this command, storing values like the paths of the input and output files of the action. See the "Environment Variables" section for the complete list of environment variables available to this command. +

+
foreach_srcs + List of labels; required +

+ The set of sources that will be processed one by one in parallel, to produce the templated outputs. Each of these source files will will be processed individually by its own action. +

+
message + String; optional +

+ A custom progress message to display as the actions are executed. +

+
outs_templates + Dictionary: String -> String; required +

+

Templates for the output files. Each key defines a name for an output file and the value specifies a path template for that output. For each of the files in `foreach_srcs` this rule creates one action that produces all of these outputs. The paths of the particular output files for that input are computed from the template. The ensure the resolved templates yield unique paths, the following placeholders are supported in the path templates:

  • "{src}": same as "{src_dir}/{src_name}"
  • "{src_dir}": package path of the source file, and a trailing "/"
  • "{src_name}": base name of the source file
  • "{src_name_noext}": same as "{src_name}" without the file extension

You may also add extra path components to the templates, as long as the path template is relative and does not contain uplevel references (".."). Placeholders will be replaced by the values corresponding to the respective input file in `foreach_srcs`. Every output file is generated under <bazel_bin>/path/to/maprule/<maprule_name> + "_outs/".

+

+
srcs + List of labels; optional +

+ The set of source files common to all actions of this rule. +

+
tools + List of labels; optional +

+ Tools used by the command. The `cmd` attribute, and the values of the `add_env` attribute may reference these tools in "$(location)" expressions, similar to the genrule rule. +

+
+ + + +## cmd_maprule + +
+cmd_maprule(name, add_env, cmd, foreach_srcs, message, outs_templates, srcs, tools)
+
+ + + +Maprule that runs a Windows Command Prompt (`cmd.exe`) command. + +This rule is the same as `bash_maprule`, but uses `cmd.exe` instead of Bash, therefore the `cmd` +attribute must use Command Prompt syntax. `cmd_maprule` rules can only be built on Windows. + + +Maprule runs a specific command for each of the "foreach" source files. This allows processing +source files in parallel, to produce some outputs for each of them. + +The name "maprule" indicates that this rule can be used to map source files to output files, and is +also a reference to "genrule" that inspired this rule's design (though there are significant +differences). + +Below you will find an Example, Tips and Tricks, and an FAQ. + +### Example + + + # This file is //projects/game:BUILD + + load("@bazel_skylib//rules:maprule.bzl", "cmd_maprule") + + cmd_maprule( + name = "process_assets", + foreach_srcs = [ + "rust.png", + "teapot.3ds", + "//assets:wood.jpg", + "//assets:models", + ], + outs_templates = { + "TAGS": "{src}.tags", + "DIGEST": "digests/{src_name_noext}.md5", + }, + tools = [ + "//bin:asset-tagger", + "//util:md5sum", + ], + add_env = { + "ASSET_TAGGER": "$(location //bin:asset-tagger)", + "MD5SUM": "$(location //util:md5sum)", + }, + + # Note: this command should live in a script file, we only inline it in the `cmd` attribute + # for the sake of demonstration. See Tips and Tricks section. + cmd = "%MAPRULE_ASSET_TAGGER% --input=%MAPRULE_SRC% --output=%MAPRULE_TAGS% & " + + 'IF /I "%ERRORLEVEL%" EQU "0" ( %MAPRULE_MD5SUM% %MAPRULE_SRC% > %MAPRULE_DIGEST% )', + ) + + +The "process_assets" rule above will execute the command in the `cmd` to process "rust.png", +"teapot.3ds", "//assets:wood.jpg", and every file in the "//assets:models" rule, producing the +corresponding .tags and .md5 files for each of them, under the following paths: + + bazel-bin/projects/game/process_assets_out/projects/game/rust.png.tags + bazel-bin/projects/game/process_assets_out/digests/rust.md5 + bazel-bin/projects/game/process_assets_out/projects/game/teapot.3ds.tags + bazel-bin/projects/game/process_assets_out/digests/teapot.md5 + bazel-bin/projects/game/process_assets_out/assets/wood.jpg.tags + bazel-bin/projects/game/process_assets_out/digests/wood.md5 + ... + +You can create downstream rules, for example a filegroup or genrule (or another maprule) that put +this rule in their `srcs` attribute and get all the .tags and .md5 files. + +## Tips and Tricks + +*(The Tips and Tricks section is the same for `cmd_maprule` and `bash_maprule`.)* + +### Use script files instead of inlining commands in the `cmd` attribute. + +Unless the command is trivial, don't try to write it in `cmd`. + +Properly quoting parts of the command is challenging enough, add to that escaping for the BUILD +file's syntax and the `cmd` attribute quickly gets unmaintainably complex. + +It's a lot easier and maintainable to create a script file such as "foo.sh" in `bash_maprule` or +"foo.bat" in `cmd_maprule` for the commands. To do that: + +1. move the commands to a script file +2. add the script file to the `tools` attribute +3. add an entry to the `add_env` attribute, e.g. "`TOOL: "$(location :tool.sh)"`" +4. replace the `cmd` with just "$MAPRULE_FOO" (in `bash_maprule`) or "%MAPRULE_FOO%" (in + `cmd_maprule`). + +Doing this also avoids hitting command line length limits. + +Example: + + cmd_maprule( + ... + srcs = ["//data:temperatures.txt"], + tools = [ + "command.bat", + ":weather-stats", + ], + add_env = { + "COMMAND": "$(location :command.bat)", + "STATS_TOOL": "$(location :weather-stats-computer)", + "TEMPERATURES": "$(location //data:temperatures.txt)", + }, + cmd = "%MAPRULE_COMMAND%", + ) + +### Use the `add_env` attribute to pass tool locations to the command. + +Entries in the `add_env` attribute may use "$(location)" references and may also use the same +placeholders as the `outs_templates` attribute. For example you can use this mechanism to pass extra +"$(location)" references of `tools` or `srcs` to the actions. + +Example: + + cmd_maprule( + ... + foreach_srcs = [...], + outs_templates = {"STATS_OUT": "{src}-stats.txt"}, + srcs = ["//data:temperatures.txt"], + tools = [":weather-stats"], + add_env = { + "STATS_TOOL": "$(location :weather-stats-computer)", + "TEMPERATURES": "$(location //data:temperatures.txt)", + }, + cmd = "%MAPRULE_STATS_TOOL% --src=%MAPRULE_SRC% --data=%MAPRULE_TEMPERATURES% > %MAPRULE_STATS_OUT%", + ) + + cc_binary( + name = "weather-stats", + ... + ) + +## Environment Variables + +*(The Environment Variables section is the same for `cmd_maprule` and `bash_maprule`.)* + +The rule defines several environment variables available to the command may reference. All of these +envvars names start with "MAPRULE_". You can add your own envvars with the `add_env` attribute. + +The command can use some envvars, all named "MAPRULE_*<something>*". + +The complete list of environment variables is: + +- "MAPRULE_SRC": the path of the current file from `foreach_srcs` +- "MAPRULE_SRCS": the space-separated paths of all files in the `srcs` attribute +- "MAPRULE_*<OUT>*": for each key name *<OUT>* in the `outs_templates` attribute, this + is the path of the respective output file for the current source +- "MAPRULE_*<ENV>*": for each key name *<ENV>* in the `add_env` attribute + +## FAQ + +*(The FAQ section is the same for `cmd_maprule` and `bash_maprule`.)* + +### What's the motivation for maprule? What's wrong with genrule? + +genrule creates a single action for all of its inputs. It requires specifying all output files. +Finally, it can only create Bash commands. + +Maprule supports parallel processing of its inputs and doesn't require specifying all outputs, just +templates for the outputs. And not only Bash is supported (via `bash_maprule`) but so are +cmd.exe-style commands via `cmd_maprule`. + +### `genrule.cmd` supports "$(location)" expressions, how come `*_maprule.cmd` doesn't? + +Maprule deliberately omits support for this feature to avoid pitfalls with quoting and escaping, and +potential issues with paths containing spaces. Instead, maprule exports environment variables for +the input and outputs of the action, and allows the user to define extra envvars. These extra +envvars do support "$(location)" expressions, so you can pass paths of labels in `srcs` and `tools`. + +### Why are all envvars exported with the "MAPRULE_" prefix? + +To avoid conflicts with other envvars, whose names could also be attractive outs_templates names. + +### Why do `outs_templates` and `add_env` keys have to be uppercase? + +Because they are exported as all-uppercase envvars, so requiring that they be declared as uppercase +gives a visual cue of that. It also avoids clashes resulting from mixed lower-upper-case names like +"foo" and "Foo". + +### Why don't `outs_templates` and `add_env` keys have to start with "MAPRULE_" even though they are exported as such? + +For convenience. It seems to bring no benefit to have the user always type "MAPRULE_" in front of +the name when the rule itself could as well add it. + +### Why are all outputs relative to "*<maprule_pkg>*/*<maprule_name>*_out/" ? + +To ensure that maprules in the same package and with the same outs_templates produce distinct output +files. + +### Why aren't `outs_templates` keys available as placeholders in the values of `add_env`? + +Because `add_env` is meant to be used for passing extra "$(location)" information to the action, and +the output paths are already available as envvars for the action. + + +### Attributes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
name + Name; required +

+ A unique name for this target. +

+
add_env + Dictionary: String -> String; optional +

+ Extra environment variables to define for the actions. Every variable's name must be uppercase. Bazel will automatically prepend "MAPRULE_" to the name when exporting the variable for the action. The values may use "$(location)" expressions for labels declared in the `srcs` and `tools` attribute, and may reference the same placeholders as the values of the `outs_templates` attribute. +

+
cmd + String; required +

+ The command to execute. It must be in the syntax corresponding to this maprule type, e.g. for `bash_maprule` this must be a Bash command, and for `cmd_maprule` a Windows Command Prompt (cmd.exe) command. Several environment variables are available for this command, storing values like the paths of the input and output files of the action. See the "Environment Variables" section for the complete list of environment variables available to this command. +

+
foreach_srcs + List of labels; required +

+ The set of sources that will be processed one by one in parallel, to produce the templated outputs. Each of these source files will will be processed individually by its own action. +

+
message + String; optional +

+ A custom progress message to display as the actions are executed. +

+
outs_templates + Dictionary: String -> String; required +

+

Templates for the output files. Each key defines a name for an output file and the value specifies a path template for that output. For each of the files in `foreach_srcs` this rule creates one action that produces all of these outputs. The paths of the particular output files for that input are computed from the template. The ensure the resolved templates yield unique paths, the following placeholders are supported in the path templates:

  • "{src}": same as "{src_dir}/{src_name}"
  • "{src_dir}": package path of the source file, and a trailing "/"
  • "{src_name}": base name of the source file
  • "{src_name_noext}": same as "{src_name}" without the file extension

You may also add extra path components to the templates, as long as the path template is relative and does not contain uplevel references (".."). Placeholders will be replaced by the values corresponding to the respective input file in `foreach_srcs`. Every output file is generated under <bazel_bin>/path/to/maprule/<maprule_name> + "_outs/".

+

+
srcs + List of labels; optional +

+ The set of source files common to all actions of this rule. +

+
tools + List of labels; optional +

+ Tools used by the command. The `cmd` attribute, and the values of the `add_env` attribute may reference these tools in "$(location)" expressions, similar to the genrule rule. +

+
+ + diff --git a/docs/new_sets_doc.md b/docs/new_sets_doc.md new file mode 100755 index 0000000..2a3fb33 --- /dev/null +++ b/docs/new_sets_doc.md @@ -0,0 +1,519 @@ +## sets.make + +
+sets.make(elements)
+
+ +Creates a new set. + +All elements must be hashable. + + +### Parameters + + + + + + + + + + + + +
elements + optional. default is None +

+ Optional sequence to construct the set out of. +

+
+ + +## sets.copy + +
+sets.copy(s)
+
+ +Creates a new set from another set. + +### Parameters + + + + + + + + + + + + +
s + required. +

+ A set, as returned by `sets.make()`. +

+
+ + +## sets.to_list + +
+sets.to_list(s)
+
+ +Creates a list from the values in the set. + +### Parameters + + + + + + + + + + + + +
s + required. +

+ A set, as returned by `sets.make()`. +

+
+ + +## sets.insert + +
+sets.insert(s, e)
+
+ +Inserts an element into the set. + +Element must be hashable. This mutates the orginal set. + + +### Parameters + + + + + + + + + + + + + + + + +
s + required. +

+ A set, as returned by `sets.make()`. +

+
e + required. +

+ The element to be inserted. +

+
+ + +## sets.contains + +
+sets.contains(a, e)
+
+ +Checks for the existence of an element in a set. + +### Parameters + + + + + + + + + + + + + + + + +
a + required. +

+ A set, as returned by `sets.make()`. +

+
e + required. +

+ The element to look for. +

+
+ + +## sets.is_equal + +
+sets.is_equal(a, b)
+
+ +Returns whether two sets are equal. + +### Parameters + + + + + + + + + + + + + + + + +
a + required. +

+ A set, as returned by `sets.make()`. +

+
b + required. +

+ A set, as returned by `sets.make()`. +

+
+ + +## sets.is_subset + +
+sets.is_subset(a, b)
+
+ +Returns whether `a` is a subset of `b`. + +### Parameters + + + + + + + + + + + + + + + + +
a + required. +

+ A set, as returned by `sets.make()`. +

+
b + required. +

+ A set, as returned by `sets.make()`. +

+
+ + +## sets.disjoint + +
+sets.disjoint(a, b)
+
+ +Returns whether two sets are disjoint. + +Two sets are disjoint if they have no elements in common. + + +### Parameters + + + + + + + + + + + + + + + + +
a + required. +

+ A set, as returned by `sets.make()`. +

+
b + required. +

+ A set, as returned by `sets.make()`. +

+
+ + +## sets.intersection + +
+sets.intersection(a, b)
+
+ +Returns the intersection of two sets. + +### Parameters + + + + + + + + + + + + + + + + +
a + required. +

+ A set, as returned by `sets.make()`. +

+
b + required. +

+ A set, as returned by `sets.make()`. +

+
+ + +## sets.union + +
+sets.union(args)
+
+ +Returns the union of several sets. + +### Parameters + + + + + + + + + + + + +
args + optional. +

+ An arbitrary number of sets or lists. +

+
+ + +## sets.difference + +
+sets.difference(a, b)
+
+ +Returns the elements in `a` that are not in `b`. + +### Parameters + + + + + + + + + + + + + + + + +
a + required. +

+ A set, as returned by `sets.make()`. +

+
b + required. +

+ A set, as returned by `sets.make()`. +

+
+ + +## sets.length + +
+sets.length(s)
+
+ +Returns the number of elements in a set. + +### Parameters + + + + + + + + + + + + +
s + required. +

+ A set, as returned by `sets.make()`. +

+
+ + +## sets.remove + +
+sets.remove(s, e)
+
+ +Removes an element from the set. + +Element must be hashable. This mutates the orginal set. + + +### Parameters + + + + + + + + + + + + + + + + +
s + required. +

+ A set, as returned by `sets.make()`. +

+
e + required. +

+ The element to be removed. +

+
+ + +## sets.repr + +
+sets.repr(s)
+
+ +Returns a string value representing the set. + +### Parameters + + + + + + + + + + + + +
s + required. +

+ A set, as returned by `sets.make()`. +

+
+ + +## sets.str + +
+sets.str(s)
+
+ +Returns a string value representing the set. + +### Parameters + + + + + + + + + + + + +
s + required. +

+ A set, as returned by `sets.make()`. +

+
+ + diff --git a/docs/partial_doc.md b/docs/partial_doc.md new file mode 100755 index 0000000..4559ba3 --- /dev/null +++ b/docs/partial_doc.md @@ -0,0 +1,171 @@ +## partial.make + +
+partial.make(func, args, kwargs)
+
+ +Creates a partial that can be called using `call`. + +A partial can have args assigned to it at the make site, and can have args +passed to it at the call sites. + +A partial 'function' can be defined with positional args and kwargs: + + # function with no args + def function1(): + ... + + # function with 2 args + def function2(arg1, arg2): + ... + + # function with 2 args and keyword args + def function3(arg1, arg2, x, y): + ... + +The positional args passed to the function are the args passed into make +followed by any additional positional args given to call. The below example +illustrates a function with two positional arguments where one is supplied by +make and the other by call: + + # function demonstrating 1 arg at make site, and 1 arg at call site + def _foo(make_arg1, func_arg1): + print(make_arg1 + " " + func_arg1 + "!") + +For example: + + hi_func = partial.make(_foo, "Hello") + bye_func = partial.make(_foo, "Goodbye") + partial.call(hi_func, "Jennifer") + partial.call(hi_func, "Dave") + partial.call(bye_func, "Jennifer") + partial.call(bye_func, "Dave") + +prints: + + "Hello, Jennifer!" + "Hello, Dave!" + "Goodbye, Jennifer!" + "Goodbye, Dave!" + +The keyword args given to the function are the kwargs passed into make +unioned with the keyword args given to call. In case of a conflict, the +keyword args given to call take precedence. This allows you to set a default +value for keyword arguments and override it at the call site. + +Example with a make site arg, a call site arg, a make site kwarg and a +call site kwarg: + + def _foo(make_arg1, call_arg1, make_location, call_location): + print(make_arg1 + " is from " + make_location + " and " + + call_arg1 + " is from " + call_location + "!") + + func = partial.make(_foo, "Ben", make_location="Hollywood") + partial.call(func, "Jennifer", call_location="Denver") + +Prints "Ben is from Hollywood and Jennifer is from Denver!". + + partial.call(func, "Jennifer", make_location="LA", call_location="Denver") + +Prints "Ben is from LA and Jennifer is from Denver!". + +Note that keyword args may not overlap with positional args, regardless of +whether they are given during the make or call step. For instance, you can't +do: + +def foo(x): + pass + +func = partial.make(foo, 1) +partial.call(func, x=2) + + +### Parameters + + + + + + + + + + + + + + + + + + + + +
func + required. +

+ The function to be called. +

+
args + optional. +

+ Positional arguments to be passed to function. +

+
kwargs + optional. +

+ Keyword arguments to be passed to function. Note that these can + be overridden at the call sites. +

+
+ + +## partial.call + +
+partial.call(partial, args, kwargs)
+
+ +Calls a partial created using `make`. + +### Parameters + + + + + + + + + + + + + + + + + + + + +
partial + required. +

+ The partial to be called. +

+
args + optional. +

+ Additional positional arguments to be appended to the ones given to + make. +

+
kwargs + optional. +

+ Additional keyword arguments to augment and override the ones + given to make. +

+
+ + diff --git a/docs/paths_doc.md b/docs/paths_doc.md new file mode 100755 index 0000000..8797c01 --- /dev/null +++ b/docs/paths_doc.md @@ -0,0 +1,311 @@ +## paths.basename + +
+paths.basename(p)
+
+ +Returns the basename (i.e., the file portion) of a path. + +Note that if `p` ends with a slash, this function returns an empty string. +This matches the behavior of Python's `os.path.basename`, but differs from +the Unix `basename` command (which would return the path segment preceding +the final slash). + + +### Parameters + + + + + + + + + + + + +
p + required. +

+ The path whose basename should be returned. +

+
+ + +## paths.dirname + +
+paths.dirname(p)
+
+ +Returns the dirname of a path. + +The dirname is the portion of `p` up to but not including the file portion +(i.e., the basename). Any slashes immediately preceding the basename are not +included, unless omitting them would make the dirname empty. + + +### Parameters + + + + + + + + + + + + +
p + required. +

+ The path whose dirname should be returned. +

+
+ + +## paths.is_absolute + +
+paths.is_absolute(path)
+
+ +Returns `True` if `path` is an absolute path. + +### Parameters + + + + + + + + + + + + +
path + required. +

+ A path (which is a string). +

+
+ + +## paths.join + +
+paths.join(path, others)
+
+ +Joins one or more path components intelligently. + +This function mimics the behavior of Python's `os.path.join` function on POSIX +platform. It returns the concatenation of `path` and any members of `others`, +inserting directory separators before each component except the first. The +separator is not inserted if the path up until that point is either empty or +already ends in a separator. + +If any component is an absolute path, all previous components are discarded. + + +### Parameters + + + + + + + + + + + + + + + + +
path + required. +

+ A path segment. +

+
others + optional. +

+ Additional path segments. +

+
+ + +## paths.normalize + +
+paths.normalize(path)
+
+ +Normalizes a path, eliminating double slashes and other redundant segments. + +This function mimics the behavior of Python's `os.path.normpath` function on +POSIX platforms; specifically: + +- If the entire path is empty, "." is returned. +- All "." segments are removed, unless the path consists solely of a single + "." segment. +- Trailing slashes are removed, unless the path consists solely of slashes. +- ".." segments are removed as long as there are corresponding segments + earlier in the path to remove; otherwise, they are retained as leading ".." + segments. +- Single and double leading slashes are preserved, but three or more leading + slashes are collapsed into a single leading slash. +- Multiple adjacent internal slashes are collapsed into a single slash. + + +### Parameters + + + + + + + + + + + + +
path + required. +

+ A path. +

+
+ + +## paths.relativize + +
+paths.relativize(path, start)
+
+ +Returns the portion of `path` that is relative to `start`. + +Because we do not have access to the underlying file system, this +implementation differs slightly from Python's `os.path.relpath` in that it +will fail if `path` is not beneath `start` (rather than use parent segments to +walk up to the common file system root). + +Relativizing paths that start with parent directory references only works if +the path both start with the same initial parent references. + + +### Parameters + + + + + + + + + + + + + + + + +
path + required. +

+ The path to relativize. +

+
start + required. +

+ The ancestor path against which to relativize. +

+
+ + +## paths.replace_extension + +
+paths.replace_extension(p, new_extension)
+
+ +Replaces the extension of the file at the end of a path. + +If the path has no extension, the new extension is added to it. + + +### Parameters + + + + + + + + + + + + + + + + +
p + required. +

+ The path whose extension should be replaced. +

+
new_extension + required. +

+ The new extension for the file. The new extension should + begin with a dot if you want the new filename to have one. +

+
+ + +## paths.split_extension + +
+paths.split_extension(p)
+
+ +Splits the path `p` into a tuple containing the root and extension. + +Leading periods on the basename are ignored, so +`path.split_extension(".bashrc")` returns `(".bashrc", "")`. + + +### Parameters + + + + + + + + + + + + +
p + required. +

+ The path whose root and extension should be split. +

+
+ + diff --git a/docs/regenerate_docs.sh b/docs/regenerate_docs.sh new file mode 100755 index 0000000..2ab9a08 --- /dev/null +++ b/docs/regenerate_docs.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Copyright 2019 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. +# +# A script to manually regenerate the documentation contained in the docs/ directory. +# Should be run from the WORKSPACE root. + +set -euo pipefail + +bazel build docs:all --experimental_remap_main_repo + +for filename in bazel-bin/docs/*_gen.md; do + target_filename="$(echo $filename | sed -En "s/bazel-bin\/(.*)_gen.md/\1/p").md" + cp $filename $target_filename +done diff --git a/docs/selects_doc.md b/docs/selects_doc.md new file mode 100755 index 0000000..e26ed4c --- /dev/null +++ b/docs/selects_doc.md @@ -0,0 +1,87 @@ +## selects.with_or + +
+selects.with_or(input_dict, no_match_error)
+
+ +Drop-in replacement for `select()` that supports ORed keys. + +### Parameters + + + + + + + + + + + + + + + + +
input_dict + required. +

+ The same dictionary `select()` takes, except keys may take + either the usual form `"//foo:config1"` or + `("//foo:config1", "//foo:config2", ...)` to signify + `//foo:config1` OR `//foo:config2` OR `...`. +

+
no_match_error + optional. default is "" +

+ Optional custom error to report if no condition matches. + + Example: + + ```build + deps = selects.with_or({ + "//configs:one": [":dep1"], + ("//configs:two", "//configs:three"): [":dep2or3"], + "//configs:four": [":dep4"], + "//conditions:default": [":default"] + }) + ``` + + Key labels may appear at most once anywhere in the input. +

+
+ + +## selects.with_or_dict + +
+selects.with_or_dict(input_dict)
+
+ +Variation of `with_or` that returns the dict of the `select()`. + +Unlike `select()`, the contents of the dict can be inspected by Starlark +macros. + + +### Parameters + + + + + + + + + + + + +
input_dict + required. +

+ Same as `with_or`. +

+
+ + diff --git a/docs/shell_doc.md b/docs/shell_doc.md new file mode 100755 index 0000000..6c655be --- /dev/null +++ b/docs/shell_doc.md @@ -0,0 +1,71 @@ +## shell.array_literal + +
+shell.array_literal(iterable)
+
+ +Creates a string from a sequence that can be used as a shell array. + +For example, `shell.array_literal(["a", "b", "c"])` would return the string +`("a" "b" "c")`, which can be used in a shell script wherever an array +literal is needed. + +Note that all elements in the array are quoted (using `shell.quote`) for +safety, even if they do not need to be. + + +### Parameters + + + + + + + + + + + + +
iterable + required. +

+ A sequence of elements. Elements that are not strings will be + converted to strings first, by calling `str()`. +

+
+ + +## shell.quote + +
+shell.quote(s)
+
+ +Quotes the given string for use in a shell command. + +This function quotes the given string (in case it contains spaces or other +shell metacharacters.) + + +### Parameters + + + + + + + + + + + + +
s + required. +

+ The string to quote. +

+
+ + diff --git a/docs/structs_doc.md b/docs/structs_doc.md new file mode 100755 index 0000000..3b742cb --- /dev/null +++ b/docs/structs_doc.md @@ -0,0 +1,29 @@ +## structs.to_dict + +
+structs.to_dict(s)
+
+ +Converts a `struct` to a `dict`. + +### Parameters + + + + + + + + + + + + +
s + required. +

+ A `struct`. +

+
+ + diff --git a/docs/types_doc.md b/docs/types_doc.md new file mode 100755 index 0000000..038ae0a --- /dev/null +++ b/docs/types_doc.md @@ -0,0 +1,261 @@ +## types.is_list + +
+types.is_list(v)
+
+ +Returns True if v is an instance of a list. + +### Parameters + + + + + + + + + + + + +
v + required. +

+ The value whose type should be checked. +

+
+ + +## types.is_string + +
+types.is_string(v)
+
+ +Returns True if v is an instance of a string. + +### Parameters + + + + + + + + + + + + +
v + required. +

+ The value whose type should be checked. +

+
+ + +## types.is_bool + +
+types.is_bool(v)
+
+ +Returns True if v is an instance of a bool. + +### Parameters + + + + + + + + + + + + +
v + required. +

+ The value whose type should be checked. +

+
+ + +## types.is_none + +
+types.is_none(v)
+
+ +Returns True if v has the type of None. + +### Parameters + + + + + + + + + + + + +
v + required. +

+ The value whose type should be checked. +

+
+ + +## types.is_int + +
+types.is_int(v)
+
+ +Returns True if v is an instance of a signed integer. + +### Parameters + + + + + + + + + + + + +
v + required. +

+ The value whose type should be checked. +

+
+ + +## types.is_tuple + +
+types.is_tuple(v)
+
+ +Returns True if v is an instance of a tuple. + +### Parameters + + + + + + + + + + + + +
v + required. +

+ The value whose type should be checked. +

+
+ + +## types.is_dict + +
+types.is_dict(v)
+
+ +Returns True if v is an instance of a dict. + +### Parameters + + + + + + + + + + + + +
v + required. +

+ The value whose type should be checked. +

+
+ + +## types.is_function + +
+types.is_function(v)
+
+ +Returns True if v is an instance of a function. + +### Parameters + + + + + + + + + + + + +
v + required. +

+ The value whose type should be checked. +

+
+ + +## types.is_depset + +
+types.is_depset(v)
+
+ +Returns True if v is an instance of a `depset`. + +### Parameters + + + + + + + + + + + + +
v + required. +

+ The value whose type should be checked. +

+
+ + diff --git a/docs/unittest_doc.md b/docs/unittest_doc.md new file mode 100755 index 0000000..2256d12 --- /dev/null +++ b/docs/unittest_doc.md @@ -0,0 +1,859 @@ + +## unittest_toolchain + +
+unittest_toolchain(name, failure_templ, file_ext, join_on, success_templ)
+
+ + + +### Attributes + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
name + Name; required +

+ A unique name for this target. +

+
failure_templ + String; required +
file_ext + String; required +
join_on + String; required +
success_templ + String; required +
+ + +## analysistest.make + +
+analysistest.make(impl, expect_failure, config_settings)
+
+ +Creates an analysis test rule from its implementation function. + +An analysis test verifies the behavior of a "real" rule target by examining +and asserting on the providers given by the real target. + +Each analysis test is defined in an implementation function that must then be +associated with a rule so that a target can be built. This function handles +the boilerplate to create and return a test rule and captures the +implementation function's name so that it can be printed in test feedback. + +An example of an analysis test: + +``` +def _your_test(ctx): + env = analysistest.begin(ctx) + + # Assert statements go here + + return analysistest.end(env) + +your_test = analysistest.make(_your_test) +``` + +Recall that names of test rules must end in `_test`. + + +### Parameters + + + + + + + + + + + + + + + + + + + + +
impl + required. +

+ The implementation function of the unit test. +

+
expect_failure + optional. default is False +

+ If true, the analysis test will expect the target_under_test + to fail. Assertions can be made on the underlying failure using asserts.expect_failure +

+
config_settings + optional. default is {} +

+ A dictionary of configuration settings to change for the target under + test and its dependencies. This may be used to essentially change 'build flags' for + the target under test, and may thus be utilized to test multiple targets with different + flags in a single build +

+
+ + +## analysistest.begin + +
+analysistest.begin(ctx)
+
+ +Begins a unit test. + +This should be the first function called in a unit test implementation +function. It initializes a "test environment" that is used to collect +assertion failures so that they can be reported and logged at the end of the +test. + + +### Parameters + + + + + + + + + + + + +
ctx + required. +

+ The Skylark context. Pass the implementation function's `ctx` argument + in verbatim. +

+
+ + +## analysistest.end + +
+analysistest.end(env)
+
+ +Ends an analysis test and logs the results. + +This must be called and returned at the end of an analysis test implementation function so +that the results are reported. + + +### Parameters + + + + + + + + + + + + +
env + required. +

+ The test environment returned by `analysistest.begin`. +

+
+ + +## analysistest.fail + +
+analysistest.fail(env, msg)
+
+ +Unconditionally causes the current test to fail. + +### Parameters + + + + + + + + + + + + + + + + +
env + required. +

+ The test environment returned by `unittest.begin`. +

+
msg + required. +

+ The message to log describing the failure. +

+
+ + +## analysistest.target_actions + +
+analysistest.target_actions(env)
+
+ +Returns a list of actions registered by the target under test. + +### Parameters + + + + + + + + + + + + +
env + required. +

+ The test environment returned by `analysistest.begin`. +

+
+ + +## analysistest.target_under_test + +
+analysistest.target_under_test(env)
+
+ +Returns the target under test. + +### Parameters + + + + + + + + + + + + +
env + required. +

+ The test environment returned by `analysistest.begin`. +

+
+ + +## asserts.expect_failure + +
+asserts.expect_failure(env, expected_failure_msg)
+
+ +Asserts that the target under test has failed with a given error message. + +This requires that the analysis test is created with `analysistest.make()` and +`expect_failures = True` is specified. + + +### Parameters + + + + + + + + + + + + + + + + +
env + required. +

+ The test environment returned by `analysistest.begin`. +

+
expected_failure_msg + optional. default is "" +

+ The error message to expect as a result of analysis failures. +

+
+ + +## asserts.equals + +
+asserts.equals(env, expected, actual, msg)
+
+ +Asserts that the given `expected` and `actual` values are equal. + +### Parameters + + + + + + + + + + + + + + + + + + + + + + + + +
env + required. +

+ The test environment returned by `unittest.begin`. +

+
expected + required. +

+ The expected value of some computation. +

+
actual + required. +

+ The actual value returned by some computation. +

+
msg + optional. default is None +

+ An optional message that will be printed that describes the failure. + If omitted, a default will be used. +

+
+ + +## asserts.false + +
+asserts.false(env, condition, msg)
+
+ +Asserts that the given `condition` is false. + +### Parameters + + + + + + + + + + + + + + + + + + + + +
env + required. +

+ The test environment returned by `unittest.begin`. +

+
condition + required. +

+ A value that will be evaluated in a Boolean context. +

+
msg + optional. default is "Expected condition to be false, but was true." +

+ An optional message that will be printed that describes the failure. + If omitted, a default will be used. +

+
+ + +## asserts.set_equals + +
+asserts.set_equals(env, expected, actual, msg)
+
+ +Asserts that the given `expected` and `actual` sets are equal. + +### Parameters + + + + + + + + + + + + + + + + + + + + + + + + +
env + required. +

+ The test environment returned by `unittest.begin`. +

+
expected + required. +

+ The expected set resulting from some computation. +

+
actual + required. +

+ The actual set returned by some computation. +

+
msg + optional. default is None +

+ An optional message that will be printed that describes the failure. + If omitted, a default will be used. +

+
+ + +## asserts.new_set_equals + +
+asserts.new_set_equals(env, expected, actual, msg)
+
+ +Asserts that the given `expected` and `actual` sets are equal. + +### Parameters + + + + + + + + + + + + + + + + + + + + + + + + +
env + required. +

+ The test environment returned by `unittest.begin`. +

+
expected + required. +

+ The expected set resulting from some computation. +

+
actual + required. +

+ The actual set returned by some computation. +

+
msg + optional. default is None +

+ An optional message that will be printed that describes the failure. + If omitted, a default will be used. +

+
+ + +## asserts.true + +
+asserts.true(env, condition, msg)
+
+ +Asserts that the given `condition` is true. + +### Parameters + + + + + + + + + + + + + + + + + + + + +
env + required. +

+ The test environment returned by `unittest.begin`. +

+
condition + required. +

+ A value that will be evaluated in a Boolean context. +

+
msg + optional. default is "Expected condition to be true, but was false." +

+ An optional message that will be printed that describes the failure. + If omitted, a default will be used. +

+
+ + +## register_unittest_toolchains + +
+register_unittest_toolchains()
+
+ +Registers the toolchains for unittest users. + + + +## unittest.make + +
+unittest.make(impl, attrs)
+
+ +Creates a unit test rule from its implementation function. + +Each unit test is defined in an implementation function that must then be +associated with a rule so that a target can be built. This function handles +the boilerplate to create and return a test rule and captures the +implementation function's name so that it can be printed in test feedback. + +The optional `attrs` argument can be used to define dependencies for this +test, in order to form unit tests of rules. + +An example of a unit test: + +``` +def _your_test(ctx): + env = unittest.begin(ctx) + + # Assert statements go here + + return unittest.end(env) + +your_test = unittest.make(_your_test) +``` + +Recall that names of test rules must end in `_test`. + + +### Parameters + + + + + + + + + + + + + + + + +
impl + required. +

+ The implementation function of the unit test. +

+
attrs + optional. default is None +

+ An optional dictionary to supplement the attrs passed to the + unit test's `rule()` constructor. +

+
+ + +## unittest.suite + +
+unittest.suite(name, test_rules)
+
+ +Defines a `test_suite` target that contains multiple tests. + +After defining your test rules in a `.bzl` file, you need to create targets +from those rules so that `blaze test` can execute them. Doing this manually +in a BUILD file would consist of listing each test in your `load` statement +and then creating each target one by one. To reduce duplication, we recommend +writing a macro in your `.bzl` file to instantiate all targets, and calling +that macro from your BUILD file so you only have to load one symbol. + +For the case where your unit tests do not take any (non-default) attributes -- +i.e., if your unit tests do not test rules -- you can use this function to +create the targets and wrap them in a single test_suite target. In your +`.bzl` file, write: + +``` +def your_test_suite(): + unittest.suite( + "your_test_suite", + your_test, + your_other_test, + yet_another_test, + ) +``` + +Then, in your `BUILD` file, simply load the macro and invoke it to have all +of the targets created: + +``` +load("//path/to/your/package:tests.bzl", "your_test_suite") +your_test_suite() +``` + +If you pass _N_ unit test rules to `unittest.suite`, _N_ + 1 targets will be +created: a `test_suite` target named `${name}` (where `${name}` is the name +argument passed in here) and targets named `${name}_test_${i}`, where `${i}` +is the index of the test in the `test_rules` list, which is used to uniquely +name each target. + + +### Parameters + + + + + + + + + + + + + + + + +
name + required. +

+ The name of the `test_suite` target, and the prefix of all the test + target names. +

+
test_rules + optional. +

+ A list of test rules defines by `unittest.test`. +

+
+ + +## unittest.begin + +
+unittest.begin(ctx)
+
+ +Begins a unit test. + +This should be the first function called in a unit test implementation +function. It initializes a "test environment" that is used to collect +assertion failures so that they can be reported and logged at the end of the +test. + + +### Parameters + + + + + + + + + + + + +
ctx + required. +

+ The Skylark context. Pass the implementation function's `ctx` argument + in verbatim. +

+
+ + +## unittest.end + +
+unittest.end(env)
+
+ +Ends a unit test and logs the results. + +This must be called and returned at the end of a unit test implementation function so +that the results are reported. + + +### Parameters + + + + + + + + + + + + +
env + required. +

+ The test environment returned by `unittest.begin`. +

+
+ + +## unittest.fail + +
+unittest.fail(env, msg)
+
+ +Unconditionally causes the current test to fail. + +### Parameters + + + + + + + + + + + + + + + + +
env + required. +

+ The test environment returned by `unittest.begin`. +

+
msg + required. +

+ The message to log describing the failure. +

+
+ + diff --git a/docs/versions_doc.md b/docs/versions_doc.md new file mode 100755 index 0000000..d94076c --- /dev/null +++ b/docs/versions_doc.md @@ -0,0 +1,165 @@ +## versions.get + +
+versions.get()
+
+ +Returns the current Bazel version + + + +## versions.parse + +
+versions.parse(bazel_version)
+
+ +Parses a version string into a 3-tuple of ints + +int tuples can be compared directly using binary operators (<, >). + + +### Parameters + + + + + + + + + + + + +
bazel_version + required. +

+ the Bazel version string +

+
+ + +## versions.check + +
+versions.check(minimum_bazel_version, maximum_bazel_version, bazel_version)
+
+ +Check that the version of Bazel is valid within the specified range. + +### Parameters + + + + + + + + + + + + + + + + + + + + +
minimum_bazel_version + required. +

+ minimum version of Bazel expected +

+
maximum_bazel_version + optional. default is None +

+ maximum version of Bazel expected +

+
bazel_version + optional. default is None +

+ the version of Bazel to check. Used for testing, defaults to native.bazel_version +

+
+ + +## versions.is_at_most + +
+versions.is_at_most(threshold, version)
+
+ +Check that a version is lower or equals to a threshold. + +### Parameters + + + + + + + + + + + + + + + + +
threshold + required. +

+ the maximum version string +

+
version + required. +

+ the version string to be compared to the threshold +

+
+ + +## versions.is_at_least + +
+versions.is_at_least(threshold, version)
+
+ +Check that a version is higher or equals to a threshold. + +### Parameters + + + + + + + + + + + + + + + + +
threshold + required. +

+ the minimum version string +

+
version + required. +

+ the version string to be compared to the threshold +

+
+ + diff --git a/rules/BUILD b/rules/BUILD index e161093..e0d349b 100644 --- a/rules/BUILD +++ b/rules/BUILD @@ -7,6 +7,7 @@ package(default_visibility = ["//visibility:public"]) bzl_library( name = "build_test", srcs = ["build_test.bzl"], + deps = ["//lib:new_sets"], ) bzl_library( diff --git a/rules/build_test.bzl b/rules/build_test.bzl index 34c8657..edcb50a 100644 --- a/rules/build_test.bzl +++ b/rules/build_test.bzl @@ -28,6 +28,7 @@ def build_test(name, targets, **kwargs): Typical usage: + ``` load("@bazel_skylib//rules:build_test.bzl", "build_test") build_test( name = "my_build_test", @@ -35,6 +36,7 @@ def build_test(name, targets, **kwargs): "//some/package:rule", ], ) + ``` Args: name: The name of the test rule.