Delete maprule. Fix Buildifier lint errors. (#192)
* Delete maprule. Fix Buildifier lint errors. Delete maprule and its tests: I wrote this rule, and I no longer plan to release it. Alternative rules exist that serve users' needs better. Fix also Buildifier lint errors that were making BuildKite red: https://buildkite.com/bazel/bazel-skylib/builds/659#ab98ac31-6e1c-415e-b8a8-5f8868340c7d
This commit is contained in:
parent
90ea6feaf3
commit
58068fe0cc
11
WORKSPACE
11
WORKSPACE
|
@ -28,3 +28,14 @@ bazel_skylib_internal_deps()
|
|||
load("//:internal_setup.bzl", "bazel_skylib_internal_setup")
|
||||
|
||||
bazel_skylib_internal_setup()
|
||||
|
||||
http_archive(
|
||||
name = "rules_cc",
|
||||
sha256 = "b4b2a2078bdb7b8328d843e8de07d7c13c80e6c89e86a09d6c4b424cfd1aaa19",
|
||||
strip_prefix = "rules_cc-cb2dfba6746bfa3c3705185981f3109f0ae1b893",
|
||||
urls = [
|
||||
"https://mirror.bazel.build/github.com/bazelbuild/rules_cc/archive/cb2dfba6746bfa3c3705185981f3109f0ae1b893.zip",
|
||||
"https://github.com/bazelbuild/rules_cc/archive/cb2dfba6746bfa3c3705185981f3109f0ae1b893.zip",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -133,6 +133,6 @@ stardoc(
|
|||
name = "common_settings_docs",
|
||||
out = "common_settings_doc_gen.md",
|
||||
input = "//rules:common_settings.bzl",
|
||||
deps = ["//rules:common_settings"],
|
||||
semantic_flags = ["--experimental_build_setting_api=True"],
|
||||
deps = ["//rules:common_settings"],
|
||||
)
|
||||
|
|
|
@ -29,18 +29,19 @@ def _diff_test_impl(ctx):
|
|||
test_bin = ctx.actions.declare_file(ctx.label.name + "-test.bat")
|
||||
ctx.actions.write(
|
||||
output = test_bin,
|
||||
content = r"""@echo off
|
||||
content = """@rem Generated by diff_test.bzl, do not edit.
|
||||
@echo off
|
||||
SETLOCAL ENABLEEXTENSIONS
|
||||
SETLOCAL ENABLEDELAYEDEXPANSION
|
||||
set MF=%RUNFILES_MANIFEST_FILE:/=\%
|
||||
set PATH=%SYSTEMROOT%\system32
|
||||
set MF=%RUNFILES_MANIFEST_FILE:/=\\%
|
||||
set PATH=%SYSTEMROOT%\\system32
|
||||
set F1={file1}
|
||||
set F2={file2}
|
||||
if "!F1:~0,9!" equ "external/" (set F1=!F1:~9!) else (set F1=!TEST_WORKSPACE!/!F1!)
|
||||
if "!F2:~0,9!" equ "external/" (set F2=!F2:~9!) else (set F2=!TEST_WORKSPACE!/!F2!)
|
||||
for /F "tokens=2* usebackq" %%i in (`findstr.exe /l /c:"!F1! " "%MF%"`) do (
|
||||
set RF1=%%i
|
||||
set RF1=!RF1:/=\!
|
||||
set RF1=!RF1:/=\\!
|
||||
)
|
||||
if "!RF1!" equ "" (
|
||||
echo>&2 ERROR: !F1! not found
|
||||
|
@ -48,7 +49,7 @@ if "!RF1!" equ "" (
|
|||
)
|
||||
for /F "tokens=2* usebackq" %%i in (`findstr.exe /l /c:"!F2! " "%MF%"`) do (
|
||||
set RF2=%%i
|
||||
set RF2=!RF2:/=\!
|
||||
set RF2=!RF2:/=\\!
|
||||
)
|
||||
if "!RF2!" equ "" (
|
||||
echo>&2 ERROR: !F2! not found
|
||||
|
|
|
@ -14,23 +14,6 @@ bzl_library(
|
|||
visibility = ["//rules:__pkg__"],
|
||||
)
|
||||
|
||||
bzl_library(
|
||||
name = "maprule",
|
||||
srcs = ["maprule.bzl"],
|
||||
visibility = ["//tests:__pkg__"],
|
||||
deps = [":maprule_private"],
|
||||
)
|
||||
|
||||
bzl_library(
|
||||
name = "maprule_private",
|
||||
srcs = ["maprule_private.bzl"],
|
||||
deps = [
|
||||
":maprule_util",
|
||||
"//lib:dicts",
|
||||
"//lib:paths",
|
||||
],
|
||||
)
|
||||
|
||||
bzl_library(
|
||||
name = "maprule_util",
|
||||
srcs = ["maprule_util.bzl"],
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""Maprule implementation in Starlark.
|
||||
|
||||
This module exports the cmd_maprule() and bash_maprule() build rules.
|
||||
|
||||
They are the same except for the interpreter they use (cmd.exe and Bash respectively) and for the
|
||||
expected language of their `cmd` attribute.
|
||||
|
||||
You can read more about these rules in "maprule_private.bzl".
|
||||
"""
|
||||
|
||||
load(":maprule_private.bzl", _bash_maprule = "bash_maprule", _cmd_maprule = "cmd_maprule")
|
||||
|
||||
cmd_maprule = _cmd_maprule
|
||||
bash_maprule = _bash_maprule
|
|
@ -1,574 +0,0 @@
|
|||
# Copyright 2018 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.
|
||||
|
||||
"""Maprule implementation in Starlark.
|
||||
|
||||
This module exports:
|
||||
|
||||
- The cmd_maprule() and bash_maprule() build rules.
|
||||
They are the same except for the interpreter they use (cmd.exe and Bash respectively) and for the
|
||||
expected language of their `cmd` attribute. We will refer to them collectively as `maprule`.
|
||||
|
||||
- The maprule_testing struct. This should only be used by maprule's own unittests.
|
||||
"""
|
||||
|
||||
load("//lib:dicts.bzl", "dicts")
|
||||
load("//lib:paths.bzl", "paths")
|
||||
load(
|
||||
":maprule_util.bzl",
|
||||
"BASH_STRATEGY",
|
||||
"CMD_STRATEGY",
|
||||
"fail_if_errors",
|
||||
"resolve_locations",
|
||||
)
|
||||
|
||||
_cmd_maprule_intro = """
|
||||
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.
|
||||
"""
|
||||
|
||||
_bash_maprule_intro = """
|
||||
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.
|
||||
"""
|
||||
|
||||
_cmd_maprule_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% )',
|
||||
)
|
||||
"""
|
||||
|
||||
_bash_maprule_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"',
|
||||
)
|
||||
"""
|
||||
|
||||
_rule_doc_template = """
|
||||
{intro}
|
||||
|
||||
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
|
||||
|
||||
{example}
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
def _is_relative_path(p):
|
||||
"""Returns True if `p` is a relative path (considering Unix and Windows semantics)."""
|
||||
return p and p[0] != "/" and p[0] != "\\" and (
|
||||
len(p) < 2 or not p[0].isalpha() or p[1] != ":"
|
||||
)
|
||||
|
||||
def _validate_attributes(ctx_attr_outs_templates, ctx_attr_add_env):
|
||||
"""Validates rule attributes and returns a list of error messages if there were errors."""
|
||||
errors = []
|
||||
|
||||
envvars = {
|
||||
"MAPRULE_SRC": "the source file",
|
||||
"MAPRULE_SRCS": "the space-joined paths of the common sources",
|
||||
}
|
||||
|
||||
if not ctx_attr_outs_templates:
|
||||
errors.append("ERROR: \"outs_templates\" must not be empty")
|
||||
|
||||
names_to_paths = {}
|
||||
paths_to_names = {}
|
||||
|
||||
# Check entries in "outs_templates".
|
||||
for name, path in ctx_attr_outs_templates.items():
|
||||
# Check the entry's name.
|
||||
envvar_for_name = "MAPRULE_" + name.upper()
|
||||
error_prefix = "ERROR: In \"outs_templates\" entry {\"%s\": \"%s\"}: " % (name, path)
|
||||
if not name:
|
||||
errors.append("ERROR: Bad entry in the \"outs_templates\" attribute: the name " +
|
||||
"should not be empty.")
|
||||
elif name.upper() != name:
|
||||
errors.append(error_prefix + "the name should be all upper-case.")
|
||||
elif envvar_for_name in envvars:
|
||||
errors.append((error_prefix +
|
||||
"please rename it, otherwise this output path would be exported " +
|
||||
"as the environment variable \"%s\", conflicting with the " +
|
||||
"environment variable of %s.") % (
|
||||
envvar_for_name,
|
||||
envvars[envvar_for_name],
|
||||
))
|
||||
elif not path:
|
||||
errors.append(error_prefix + "output path should not be empty.")
|
||||
elif not _is_relative_path(path):
|
||||
errors.append(error_prefix + "output path should be relative.")
|
||||
elif ".." in path:
|
||||
errors.append(error_prefix + "output path should not contain uplevel references.")
|
||||
elif path in paths_to_names:
|
||||
errors.append(error_prefix +
|
||||
"output path is already used for \"%s\"." % paths_to_names[path])
|
||||
envvars[envvar_for_name] = "the \"%s\" output file declared in the \"outs_templates\" attribute" % name
|
||||
names_to_paths[name] = path
|
||||
paths_to_names[path] = name
|
||||
|
||||
# Check envvar names in "add_env".
|
||||
for name, value in ctx_attr_add_env.items():
|
||||
envvar_for_name = "MAPRULE_" + name.upper()
|
||||
error_prefix = "ERROR: In \"add_env\" entry {\"%s\": \"%s\"}: " % (name, value)
|
||||
if not name:
|
||||
errors.append("ERROR: Bad entry in the \"add_env\" attribute: the name should not be " +
|
||||
"empty.")
|
||||
elif name.upper() != name:
|
||||
errors.append(error_prefix + "the name should be all upper-case.")
|
||||
elif envvar_for_name in envvars:
|
||||
errors.append((error_prefix +
|
||||
"please rename it, otherwise it would be exported as \"%s\", " +
|
||||
"conflicting with the environment variable of %s.") % (
|
||||
envvar_for_name,
|
||||
envvars[envvar_for_name],
|
||||
))
|
||||
elif "$(location" in value:
|
||||
tokens = value.split("$(location")
|
||||
if len(tokens) != 2:
|
||||
errors.append(error_prefix + "use only one $(location) or $(locations) function.")
|
||||
elif ")" not in tokens[1]:
|
||||
errors.append(error_prefix +
|
||||
"incomplete $(location) or $(locations) function, missing closing " +
|
||||
"parenthesis")
|
||||
envvars[name] = "an additional environment declared in \"add_env\" as \"%s\"" % name
|
||||
|
||||
return errors
|
||||
|
||||
def _src_placeholders(src, strategy):
|
||||
return {
|
||||
"src": strategy.as_path(src.short_path),
|
||||
"src_dir": strategy.as_path(paths.dirname(src.short_path) + "/"),
|
||||
"src_name": src.basename,
|
||||
"src_name_noext": (src.basename[:-len(src.extension) - 1] if len(src.extension) else src.basename),
|
||||
}
|
||||
|
||||
def _create_outputs(ctx, ctx_label_name, ctx_attr_outs_templates, strategy, foreach_srcs):
|
||||
errors = []
|
||||
outs_dicts = {}
|
||||
output_generated_by = {}
|
||||
all_output_files = []
|
||||
src_placeholders_dicts = {}
|
||||
output_path_prefix = ctx_label_name + "_out/"
|
||||
for src in foreach_srcs:
|
||||
src_placeholders_dicts[src] = _src_placeholders(src, strategy)
|
||||
|
||||
outputs_for_src = {}
|
||||
for template_name, path in ctx_attr_outs_templates.items():
|
||||
output_path = path.format(**src_placeholders_dicts[src])
|
||||
if output_path in output_generated_by:
|
||||
existing_generator = output_generated_by[output_path]
|
||||
errors.append("\n".join([
|
||||
"ERROR: output file generated multiple times:",
|
||||
" output file: " + output_path,
|
||||
" foreach_srcs file 1: " + existing_generator[0].short_path,
|
||||
" outs_templates entry 1: " + existing_generator[1],
|
||||
" foreach_srcs file 2: " + src.short_path,
|
||||
" outs_templates entry 2: " + template_name,
|
||||
]))
|
||||
output_generated_by[output_path] = (src, template_name)
|
||||
output = ctx.actions.declare_file(output_path_prefix + output_path)
|
||||
outputs_for_src[template_name] = output
|
||||
all_output_files.append(output)
|
||||
outs_dicts[src] = outputs_for_src
|
||||
|
||||
if errors:
|
||||
# For sake of Starlark unittest we return all_output_files, so the test can create dummy
|
||||
# generating actions for the files even in case of errors.
|
||||
return None, all_output_files, None, errors
|
||||
else:
|
||||
return outs_dicts, all_output_files, src_placeholders_dicts, None
|
||||
|
||||
def _custom_envmap(ctx, strategy, src_placeholders, outs_dict, resolved_add_env):
|
||||
return dicts.add(
|
||||
{
|
||||
"MAPRULE_" + k.upper(): strategy.as_path(v)
|
||||
for k, v in src_placeholders.items()
|
||||
},
|
||||
{
|
||||
"MAPRULE_" + k.upper(): strategy.as_path(v.path)
|
||||
for k, v in outs_dict.items()
|
||||
},
|
||||
{
|
||||
"MAPRULE_" + k.upper(): v
|
||||
for k, v in resolved_add_env.items()
|
||||
},
|
||||
)
|
||||
|
||||
def _maprule_main(ctx, strategy):
|
||||
errors = _validate_attributes(ctx.attr.outs_templates, ctx.attr.add_env)
|
||||
fail_if_errors(errors)
|
||||
|
||||
# From "srcs": merge the depsets in the DefaultInfo.files of the targets.
|
||||
common_srcs = depset(transitive = [t[DefaultInfo].files for t in ctx.attr.srcs])
|
||||
common_srcs_list = common_srcs.to_list()
|
||||
|
||||
# From "foreach_srcs": by accessing the attribute's value through ctx.files (a list, not a
|
||||
# depset), we flatten the depsets of DefaultInfo.files of the targets and merge them to a single
|
||||
# list. This is fine, we would have to do this anyway, because we iterate over them later.
|
||||
foreach_srcs = ctx.files.foreach_srcs
|
||||
|
||||
# Create the outputs for each file in "foreach_srcs".
|
||||
foreach_src_outs_dicts, all_outputs, src_placeholders_dicts, errors = _create_outputs(
|
||||
ctx,
|
||||
ctx.label.name,
|
||||
ctx.attr.outs_templates,
|
||||
strategy,
|
||||
foreach_srcs,
|
||||
)
|
||||
fail_if_errors(errors)
|
||||
|
||||
progress_message = (ctx.attr.message or "Executing maprule") + " for %s" % ctx.label
|
||||
|
||||
# Create the part of the environment variables map that all actions will share.
|
||||
common_envmap = dicts.add(
|
||||
ctx.configuration.default_shell_env,
|
||||
{"MAPRULE_SRCS": " ".join([strategy.as_path(p.path) for p in common_srcs_list])},
|
||||
)
|
||||
|
||||
# Resolve "tools" runfiles and $(location) references in "add_env".
|
||||
inputs_from_tools, manifests_from_tools = ctx.resolve_tools(tools = ctx.attr.tools)
|
||||
add_env = resolve_locations(ctx, strategy, ctx.attr.add_env)
|
||||
|
||||
# Create actions for each of the "foreach" sources.
|
||||
for src in foreach_srcs:
|
||||
strategy.create_action(
|
||||
ctx,
|
||||
inputs = depset(direct = [src], transitive = [common_srcs, inputs_from_tools]),
|
||||
outputs = foreach_src_outs_dicts[src].values(),
|
||||
# The custom envmap contains envvars specific to the current "src", such as MAPRULE_SRC.
|
||||
env = dicts.add(
|
||||
common_envmap,
|
||||
_custom_envmap(
|
||||
ctx,
|
||||
strategy,
|
||||
src_placeholders_dicts[src],
|
||||
foreach_src_outs_dicts[src],
|
||||
add_env,
|
||||
),
|
||||
),
|
||||
command = ctx.attr.cmd,
|
||||
progress_message = progress_message,
|
||||
mnemonic = "Maprule",
|
||||
manifests_from_tools = manifests_from_tools,
|
||||
)
|
||||
|
||||
return [DefaultInfo(files = depset(all_outputs))]
|
||||
|
||||
def _cmd_maprule_impl(ctx):
|
||||
return _maprule_main(ctx, CMD_STRATEGY)
|
||||
|
||||
def _bash_maprule_impl(ctx):
|
||||
return _maprule_main(ctx, BASH_STRATEGY)
|
||||
|
||||
_ATTRS = {
|
||||
"srcs": attr.label_list(
|
||||
allow_files = True,
|
||||
doc = "The set of source files common to all actions of this rule.",
|
||||
),
|
||||
"add_env": attr.string_dict(
|
||||
doc = "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": attr.string(
|
||||
mandatory = True,
|
||||
doc = "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": attr.label_list(
|
||||
allow_files = True,
|
||||
mandatory = True,
|
||||
doc = "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": attr.string(
|
||||
doc = "A custom progress message to display as the actions are executed.",
|
||||
),
|
||||
"outs_templates": attr.string_dict(
|
||||
allow_empty = False,
|
||||
mandatory = True,
|
||||
doc = "<p>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:</p>" +
|
||||
"<ul>" +
|
||||
"<li>\"{src}\": same as \"{src_dir}/{src_name}\"</li>" +
|
||||
"<li>\"{src_dir}\": package path of the source file, and a trailing \"/\"</li>" +
|
||||
"<li>\"{src_name}\": base name of the source file</li>" +
|
||||
"<li>\"{src_name_noext}\": same as \"{src_name}\" without the file extension</li>" +
|
||||
"</ul>" +
|
||||
"<p>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/\".</p>",
|
||||
),
|
||||
"tools": attr.label_list(
|
||||
cfg = "host",
|
||||
allow_files = True,
|
||||
doc = "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.",
|
||||
),
|
||||
}
|
||||
|
||||
# Maprule that uses Windows cmd.exe as the interpreter.
|
||||
cmd_maprule = rule(
|
||||
implementation = _cmd_maprule_impl,
|
||||
doc = _rule_doc_template.format(
|
||||
intro = _cmd_maprule_intro,
|
||||
example = _cmd_maprule_example,
|
||||
),
|
||||
attrs = _ATTRS,
|
||||
)
|
||||
|
||||
# Maprule that uses Bash as the interpreter.
|
||||
bash_maprule = rule(
|
||||
implementation = _bash_maprule_impl,
|
||||
doc = _rule_doc_template.format(
|
||||
intro = _bash_maprule_intro,
|
||||
example = _bash_maprule_example,
|
||||
),
|
||||
attrs = _ATTRS,
|
||||
)
|
||||
|
||||
# Only used in unittesting maprule.
|
||||
maprule_testing = struct(
|
||||
cmd_strategy = CMD_STRATEGY,
|
||||
bash_strategy = BASH_STRATEGY,
|
||||
src_placeholders = _src_placeholders,
|
||||
validate_attributes = _validate_attributes,
|
||||
is_relative_path = _is_relative_path,
|
||||
custom_envmap = _custom_envmap,
|
||||
create_outputs = _create_outputs,
|
||||
)
|
|
@ -1,24 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""Maprule implementation in Starlark.
|
||||
|
||||
This module exports:
|
||||
|
||||
This module exports the maprule_testing struct. This should only be used by maprule's own unittests.
|
||||
"""
|
||||
|
||||
load(":maprule_private.bzl", _maprule_testing = "maprule_testing")
|
||||
|
||||
maprule_testing = _maprule_testing
|
|
@ -2,7 +2,6 @@ load("//:bzl_library.bzl", "bzl_library")
|
|||
load(":build_test_tests.bzl", "build_test_test_suite")
|
||||
load(":collections_tests.bzl", "collections_test_suite")
|
||||
load(":dicts_tests.bzl", "dicts_test_suite")
|
||||
load(":maprule_tests.bzl", "maprule_test_suite")
|
||||
load(":old_sets_tests.bzl", "old_sets_test_suite")
|
||||
load(":new_sets_tests.bzl", "new_sets_test_suite")
|
||||
load(":partial_tests.bzl", "partial_test_suite")
|
||||
|
@ -27,8 +26,6 @@ collections_test_suite()
|
|||
|
||||
dicts_test_suite()
|
||||
|
||||
maprule_test_suite()
|
||||
|
||||
old_sets_test_suite()
|
||||
|
||||
new_sets_test_suite()
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
"""Unit tests for build_test.bzl."""
|
||||
|
||||
load("//rules:build_test.bzl", "build_test")
|
||||
load("@rules_cc//cc:defs.bzl", "cc_library")
|
||||
|
||||
def build_test_test_suite():
|
||||
# Since the rules doesn't do anything really, it just makes some targets
|
||||
|
@ -29,7 +30,7 @@ def build_test_test_suite():
|
|||
)
|
||||
|
||||
# Use it in a non-test target
|
||||
native.cc_library(
|
||||
cc_library(
|
||||
name = "build_test__build_target",
|
||||
srcs = [":build_test__make_src"],
|
||||
)
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
# This package aids testing the 'maprule' rule.
|
||||
#
|
||||
# ATTENTION: As of 2019-03-20, maprule is not ready for public use.
|
||||
# @laszlocsomor is planning incompatible changes to the rule.
|
||||
|
||||
load("//rules/private:maprule.bzl", "bash_maprule")
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_testonly = 1)
|
||||
|
||||
sh_test(
|
||||
name = "maprule_tests",
|
||||
srcs = ["maprule_tests.sh"],
|
||||
data = [
|
||||
# Use DefaultInfo.files from 'mr_bash' (via 'file_deps').
|
||||
":file_deps",
|
||||
"//tests:unittest.bash",
|
||||
],
|
||||
deps = ["@bazel_tools//tools/bash/runfiles"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "file_deps",
|
||||
# Use DefaultInfo.files from 'mr_bash'.
|
||||
srcs = [":mr_bash"],
|
||||
)
|
||||
|
||||
bash_maprule(
|
||||
name = "mr_bash",
|
||||
srcs = ["common.txt"],
|
||||
add_env = {
|
||||
"TOOL": "$(location :mr_bash_tool)",
|
||||
},
|
||||
# TODO(laszlocsomor): add quotes around $MAPRULE_TOOL after
|
||||
# https://github.com/bazelbuild/bazel/issues/7454 is fixed.
|
||||
cmd = "$MAPRULE_TOOL",
|
||||
foreach_srcs = [
|
||||
"foo.txt",
|
||||
"b/bar.txt",
|
||||
],
|
||||
outs_templates = {
|
||||
"OUT1": "{src}.out1",
|
||||
"OUT2": "out2/{src_name_noext}",
|
||||
},
|
||||
tools = [":mr_bash_tool"],
|
||||
)
|
||||
|
||||
sh_binary(
|
||||
name = "mr_bash_tool",
|
||||
srcs = ["mr_bash_tool.sh"],
|
||||
)
|
|
@ -1 +0,0 @@
|
|||
bar file
|
|
@ -1 +0,0 @@
|
|||
common file
|
|
@ -1 +0,0 @@
|
|||
foo file
|
|
@ -1,61 +0,0 @@
|
|||
#!/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.
|
||||
|
||||
# --- begin runfiles.bash initialization ---
|
||||
set -euo pipefail
|
||||
if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
|
||||
if [[ -f "$0.runfiles_manifest" ]]; then
|
||||
export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest"
|
||||
elif [[ -f "$0.runfiles/MANIFEST" ]]; then
|
||||
export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST"
|
||||
elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
|
||||
export RUNFILES_DIR="$0.runfiles"
|
||||
fi
|
||||
fi
|
||||
if [[ -f "${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
|
||||
source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash"
|
||||
elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
|
||||
source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \
|
||||
"$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)"
|
||||
else
|
||||
echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash"
|
||||
exit 1
|
||||
fi
|
||||
# --- end runfiles.bash initialization ---
|
||||
|
||||
source "$(rlocation bazel_skylib/tests/unittest.bash)" \
|
||||
|| { echo "Could not source bazel_skylib/tests/unittest.bash" >&2; exit 1; }
|
||||
|
||||
function test_bash_maprule() {
|
||||
local -r out1foo="$(rlocation bazel_skylib/tests/maprule/mr_bash_out/tests/maprule/foo.txt.out1)"
|
||||
local -r out1bar="$(rlocation bazel_skylib/tests/maprule/mr_bash_out/tests/maprule/b/bar.txt.out1)"
|
||||
local -r out2foo="$(rlocation bazel_skylib/tests/maprule/mr_bash_out/out2/foo)"
|
||||
local -r out2bar="$(rlocation bazel_skylib/tests/maprule/mr_bash_out/out2/bar)"
|
||||
|
||||
cat "$out1foo" | tr '\n\r' ' ' > "$TEST_log"
|
||||
expect_log "^tests/maprule/common.txt *tests/maprule/foo.txt *$"
|
||||
|
||||
cat "$out1bar" | tr '\n\r' ' ' > "$TEST_log"
|
||||
expect_log "^tests/maprule/common.txt *tests/maprule/b/bar.txt *$"
|
||||
|
||||
cat "$out2foo" | tr '\n\r' ' ' > "$TEST_log"
|
||||
expect_log "^common file *foo file *$"
|
||||
|
||||
cat "$out2bar" | tr '\n\r' ' ' > "$TEST_log"
|
||||
expect_log "^common file *bar file *$"
|
||||
}
|
||||
|
||||
run_suite "maprule test suite"
|
|
@ -1,20 +0,0 @@
|
|||
#!/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.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
echo "$MAPRULE_SRCS" "$MAPRULE_SRC" > "$MAPRULE_OUT1"
|
||||
cat "$MAPRULE_SRCS" "$MAPRULE_SRC" > "$MAPRULE_OUT2"
|
|
@ -1,498 +0,0 @@
|
|||
# Copyright 2018 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.
|
||||
|
||||
"""Unit tests for maprule.bzl."""
|
||||
|
||||
load("//lib:unittest.bzl", "asserts", "unittest")
|
||||
load("//rules/private:maprule_testing.bzl", "maprule_testing")
|
||||
|
||||
def _dummy_generating_action(ctx, path):
|
||||
ctx.actions.write(path, "hello")
|
||||
|
||||
def _mock_file(ctx, path):
|
||||
f = ctx.actions.declare_file(path)
|
||||
_dummy_generating_action(ctx, f)
|
||||
return f
|
||||
|
||||
def _lstrip_until(s, until):
|
||||
return s[s.find(until):]
|
||||
|
||||
def _assert_dict_keys(env, expected, actual, msg):
|
||||
asserts.equals(env, {k: None for k in expected}, {k: None for k in actual}, msg)
|
||||
|
||||
def _assert_ends_with(env, expected_ending, s, msg):
|
||||
if not s.endswith(expected_ending):
|
||||
unittest.fail(env, msg + ": expected \"%s\" to end with \"%s\"" % (s, expected_ending))
|
||||
|
||||
def _assert_no_error(env, errors, msg):
|
||||
if errors:
|
||||
unittest.fail(env, msg + ": expected no errors, got: [%s]" % "\n".join(errors))
|
||||
|
||||
def _assert_error(env, errors, expected_fragment, msg):
|
||||
for e in errors:
|
||||
if expected_fragment in e:
|
||||
return
|
||||
unittest.fail(env, msg + ": did not find \"%s\" in: [%s]" % (expected_fragment, "\n".join(errors)))
|
||||
|
||||
def _contains_substrings_in_order(s, substrings):
|
||||
index = 0
|
||||
for ss in substrings:
|
||||
index = s.find(ss, index)
|
||||
if index < 0:
|
||||
return False
|
||||
index += len(ss)
|
||||
return True
|
||||
|
||||
def _assert_error_fragments(env, errors, expected_fragments, msg):
|
||||
for e in errors:
|
||||
if _contains_substrings_in_order(e, expected_fragments):
|
||||
return
|
||||
unittest.fail(env, msg + ": did not find expected fragments in \"%s\" in order" % "\n".join(errors))
|
||||
|
||||
def _src_placeholders_test(ctx):
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
for language, strategy in [
|
||||
("cmd", maprule_testing.cmd_strategy),
|
||||
("bash", maprule_testing.bash_strategy),
|
||||
]:
|
||||
for basename, basename_noext in [("bar.txt", "bar"), ("bar.pb.h", "bar.pb")]:
|
||||
actual = maprule_testing.src_placeholders(
|
||||
_mock_file(ctx, language + "/foo/" + basename),
|
||||
strategy,
|
||||
)
|
||||
_assert_dict_keys(
|
||||
env,
|
||||
["src", "src_dir", "src_name", "src_name_noext"],
|
||||
actual,
|
||||
"assertion #1 (language: %s, basename: %s)" % (language, basename),
|
||||
)
|
||||
_assert_ends_with(
|
||||
env,
|
||||
strategy.as_path(language + "/foo/" + basename),
|
||||
actual["src"],
|
||||
"assertion #2 (language: %s, basename: %s)" % (language, basename),
|
||||
)
|
||||
_assert_ends_with(
|
||||
env,
|
||||
strategy.as_path(language + "/foo/"),
|
||||
actual["src_dir"],
|
||||
"assertion #3 (language: %s, basename: %s)" % (language, basename),
|
||||
)
|
||||
asserts.equals(
|
||||
env,
|
||||
basename,
|
||||
actual["src_name"],
|
||||
"assertion #4 (language: %s, basename: %s)" % (language, basename),
|
||||
)
|
||||
asserts.equals(
|
||||
env,
|
||||
basename_noext,
|
||||
actual["src_name_noext"],
|
||||
"assertion #5 (language: %s, basename: %s)" % (language, basename),
|
||||
)
|
||||
|
||||
return unittest.end(env)
|
||||
|
||||
src_placeholders_test = unittest.make(_src_placeholders_test)
|
||||
|
||||
def _validate_attributes_test(ctx):
|
||||
"""Unit tests for maprule_testing.validate_attributes."""
|
||||
env = unittest.begin(ctx)
|
||||
_assert_no_error(
|
||||
env,
|
||||
maprule_testing.validate_attributes({"FOO": "bar"}, {"BAR": "value1"}),
|
||||
"assertion #1",
|
||||
)
|
||||
_assert_no_error(
|
||||
env,
|
||||
maprule_testing.validate_attributes({"FOO": "bar"}, {}),
|
||||
"assertion #2",
|
||||
)
|
||||
|
||||
_assert_error(
|
||||
env,
|
||||
maprule_testing.validate_attributes({}, {}),
|
||||
"\"outs_templates\" must not be empty",
|
||||
"assertion #3",
|
||||
)
|
||||
_assert_error(
|
||||
env,
|
||||
maprule_testing.validate_attributes({"": "foo"}, {}),
|
||||
"name should not be empty",
|
||||
"assertion #4",
|
||||
)
|
||||
_assert_error(
|
||||
env,
|
||||
maprule_testing.validate_attributes({"foo": "bar"}, {}),
|
||||
"name should be all upper-case",
|
||||
"assertion #5",
|
||||
)
|
||||
_assert_error(
|
||||
env,
|
||||
maprule_testing.validate_attributes({"SRC": "bar"}, {}),
|
||||
"conflicting with the environment variable of the source file",
|
||||
"assertion #6",
|
||||
)
|
||||
|
||||
_assert_error(
|
||||
env,
|
||||
maprule_testing.validate_attributes({"FOO": ""}, {}),
|
||||
"output path should not be empty",
|
||||
"assertion #7",
|
||||
)
|
||||
_assert_error(
|
||||
env,
|
||||
maprule_testing.validate_attributes({"FOO": "/usr/bin"}, {}),
|
||||
"output path should be relative",
|
||||
"assertion #8",
|
||||
)
|
||||
_assert_error(
|
||||
env,
|
||||
maprule_testing.validate_attributes({"FOO": "c:/usr/bin"}, {}),
|
||||
"output path should be relative",
|
||||
"assertion #9",
|
||||
)
|
||||
_assert_error(
|
||||
env,
|
||||
maprule_testing.validate_attributes({"FOO": "../foo"}, {}),
|
||||
"output path should not contain uplevel references",
|
||||
"assertion #10",
|
||||
)
|
||||
_assert_no_error(
|
||||
env,
|
||||
maprule_testing.validate_attributes({"FOO": "./foo"}, {}),
|
||||
"assertion #11",
|
||||
)
|
||||
_assert_error(
|
||||
env,
|
||||
maprule_testing.validate_attributes({"BAR": "foo", "FOO": "foo"}, {}),
|
||||
"output path is already used for \"BAR\"",
|
||||
"assertion #12",
|
||||
)
|
||||
|
||||
_assert_error(
|
||||
env,
|
||||
maprule_testing.validate_attributes({"FOO": "bar"}, {"": "baz"}),
|
||||
"name should not be empty",
|
||||
"assertion #13",
|
||||
)
|
||||
_assert_error(
|
||||
env,
|
||||
maprule_testing.validate_attributes({"FOO": "bar"}, {"Bar": "baz"}),
|
||||
"name should be all upper-case",
|
||||
"assertion #14",
|
||||
)
|
||||
_assert_error(
|
||||
env,
|
||||
maprule_testing.validate_attributes({"FOO": "bar"}, {"FOO": "baz"}),
|
||||
"conflicting with the environment variable of the \"FOO\" output file",
|
||||
"assertion #15",
|
||||
)
|
||||
|
||||
_assert_error(
|
||||
env,
|
||||
maprule_testing.validate_attributes({"FOO": "bar"}, {"BAR": "$(location x) $(location y)"}),
|
||||
"use only one $(location)",
|
||||
"assertion #16",
|
||||
)
|
||||
_assert_error(
|
||||
env,
|
||||
maprule_testing.validate_attributes({"FOO": "bar"}, {"BAR": "a $(location b"}),
|
||||
"missing closing parenthesis",
|
||||
"assertion #17",
|
||||
)
|
||||
|
||||
return unittest.end(env)
|
||||
|
||||
validate_attributes_test = unittest.make(_validate_attributes_test)
|
||||
|
||||
def _as_path_test(ctx):
|
||||
"""Unit tests for maprule_testing.as_path."""
|
||||
env = unittest.begin(ctx)
|
||||
asserts.equals(
|
||||
env,
|
||||
"Foo\\Bar\\Baz\\Qux",
|
||||
maprule_testing.cmd_strategy.as_path("Foo/Bar/Baz\\Qux"),
|
||||
msg = "assertion #1",
|
||||
)
|
||||
asserts.equals(
|
||||
env,
|
||||
"Foo/Bar/Baz\\Qux",
|
||||
maprule_testing.bash_strategy.as_path("Foo/Bar/Baz\\Qux"),
|
||||
msg = "assertion #2",
|
||||
)
|
||||
return unittest.end(env)
|
||||
|
||||
as_path_test = unittest.make(_as_path_test)
|
||||
|
||||
def _assert_relative_path(env, path, index):
|
||||
asserts.true(
|
||||
env,
|
||||
maprule_testing.is_relative_path(path),
|
||||
msg = "assertion #%d" % index,
|
||||
)
|
||||
|
||||
def _assert_not_relative_path(env, path, index):
|
||||
asserts.false(
|
||||
env,
|
||||
maprule_testing.is_relative_path(path),
|
||||
msg = "assertion #%d" % index,
|
||||
)
|
||||
|
||||
def _is_relative_path_test(ctx):
|
||||
"""Unit tests for maprule_testing.is_relative_path."""
|
||||
env = unittest.begin(ctx)
|
||||
_assert_relative_path(env, "Foo/Bar/Baz", 1)
|
||||
_assert_relative_path(env, "Foo\\Bar\\Baz", 2)
|
||||
_assert_relative_path(env, "Foo/Bar\\Baz", 3)
|
||||
_assert_not_relative_path(env, "d:/Foo/Bar", 4)
|
||||
_assert_not_relative_path(env, "D:/Foo/Bar", 5)
|
||||
_assert_not_relative_path(env, "/Foo/Bar", 6)
|
||||
_assert_not_relative_path(env, "\\Foo\\Bar", 7)
|
||||
return unittest.end(env)
|
||||
|
||||
is_relative_path_test = unittest.make(_is_relative_path_test)
|
||||
|
||||
def _custom_envmap_test(ctx):
|
||||
"""Unit tests for maprule_testing.custom_envmap."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
actual = {}
|
||||
|
||||
for language, strategy in [
|
||||
("cmd", maprule_testing.cmd_strategy),
|
||||
("bash", maprule_testing.bash_strategy),
|
||||
]:
|
||||
actual[language] = maprule_testing.custom_envmap(
|
||||
ctx,
|
||||
strategy,
|
||||
src_placeholders = {"src_ph1": "Src/Ph1-value", "src_ph2": "Src/Ph2-value"},
|
||||
outs_dict = {
|
||||
"out1": _mock_file(ctx, language + "/Foo/Out1"),
|
||||
"out2": _mock_file(ctx, language + "/Foo/Out2"),
|
||||
},
|
||||
resolved_add_env = {"ENV1": "Env1"},
|
||||
)
|
||||
_assert_dict_keys(
|
||||
env,
|
||||
["MAPRULE_SRC_PH1", "MAPRULE_SRC_PH2", "MAPRULE_OUT1", "MAPRULE_OUT2", "MAPRULE_ENV1"],
|
||||
actual[language],
|
||||
msg = "assertion #1 (language: %s)" % language,
|
||||
)
|
||||
actual[language]["MAPRULE_OUT1"] = _lstrip_until(actual[language]["MAPRULE_OUT1"], "Foo")
|
||||
actual[language]["MAPRULE_OUT2"] = _lstrip_until(actual[language]["MAPRULE_OUT2"], "Foo")
|
||||
|
||||
asserts.equals(
|
||||
env,
|
||||
{
|
||||
"MAPRULE_ENV1": "Env1",
|
||||
"MAPRULE_OUT1": "Foo\\Out1",
|
||||
"MAPRULE_OUT2": "Foo\\Out2",
|
||||
"MAPRULE_SRC_PH1": "Src\\Ph1-value",
|
||||
"MAPRULE_SRC_PH2": "Src\\Ph2-value",
|
||||
},
|
||||
actual["cmd"],
|
||||
msg = "assertion #2",
|
||||
)
|
||||
|
||||
asserts.equals(
|
||||
env,
|
||||
{
|
||||
"MAPRULE_ENV1": "Env1",
|
||||
"MAPRULE_OUT1": "Foo/Out1",
|
||||
"MAPRULE_OUT2": "Foo/Out2",
|
||||
"MAPRULE_SRC_PH1": "Src/Ph1-value",
|
||||
"MAPRULE_SRC_PH2": "Src/Ph2-value",
|
||||
},
|
||||
actual["bash"],
|
||||
msg = "assertion #3",
|
||||
)
|
||||
|
||||
return unittest.end(env)
|
||||
|
||||
custom_envmap_test = unittest.make(_custom_envmap_test)
|
||||
|
||||
def _create_outputs_test(ctx):
|
||||
"""Unit tests for maprule_testing.create_outputs."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
for language, strategy in [
|
||||
("cmd", maprule_testing.cmd_strategy),
|
||||
("bash", maprule_testing.bash_strategy),
|
||||
]:
|
||||
src1 = _mock_file(ctx, language + "/foo/src1.txt")
|
||||
src2 = _mock_file(ctx, language + "/foo/src2.pb.h")
|
||||
src3 = _mock_file(ctx, language + "/bar/src1.txt")
|
||||
foreach_srcs = [src1, src2, src3]
|
||||
|
||||
outs_dicts, all_output_files, _, errors = (
|
||||
maprule_testing.create_outputs(
|
||||
ctx,
|
||||
"my_maprule",
|
||||
{
|
||||
"OUT1": "{src}.out1",
|
||||
"OUT2": "{src_dir}/out2/{src_name_noext}.out2",
|
||||
},
|
||||
strategy,
|
||||
foreach_srcs,
|
||||
)
|
||||
)
|
||||
|
||||
_assert_no_error(env, errors, "assertion #1 (language: %s)" % language)
|
||||
|
||||
for output in all_output_files:
|
||||
_dummy_generating_action(ctx, output)
|
||||
|
||||
_assert_dict_keys(
|
||||
env,
|
||||
foreach_srcs,
|
||||
outs_dicts,
|
||||
"assertion #2 (language: %s)" % language,
|
||||
)
|
||||
for src in foreach_srcs:
|
||||
_assert_dict_keys(
|
||||
env,
|
||||
["OUT1", "OUT2"],
|
||||
outs_dicts[src],
|
||||
"assertion #3 (language: %s, src: %s)" % (language, src),
|
||||
)
|
||||
|
||||
_assert_ends_with(
|
||||
env,
|
||||
"my_maprule_out/tests/%s/foo/src1.txt.out1" % language,
|
||||
outs_dicts[src1]["OUT1"].path,
|
||||
"assertion #4 (language: %s)" % language,
|
||||
)
|
||||
_assert_ends_with(
|
||||
env,
|
||||
"my_maprule_out/tests/%s/foo/out2/src1.out2" % language,
|
||||
outs_dicts[src1]["OUT2"].path,
|
||||
"assertion #5 (language: %s)" % language,
|
||||
)
|
||||
|
||||
_assert_ends_with(
|
||||
env,
|
||||
"my_maprule_out/tests/%s/foo/src2.pb.h.out1" % language,
|
||||
outs_dicts[src2]["OUT1"].path,
|
||||
"assertion #6 (language: %s)" % language,
|
||||
)
|
||||
_assert_ends_with(
|
||||
env,
|
||||
"my_maprule_out/tests/%s/foo/out2/src2.pb.out2" % language,
|
||||
outs_dicts[src2]["OUT2"].path,
|
||||
"assertion #7 (language: %s)" % language,
|
||||
)
|
||||
|
||||
_assert_ends_with(
|
||||
env,
|
||||
"my_maprule_out/tests/%s/bar/src1.txt.out1" % language,
|
||||
outs_dicts[src3]["OUT1"].path,
|
||||
"assertion #8 (language: %s)" % language,
|
||||
)
|
||||
_assert_ends_with(
|
||||
env,
|
||||
"my_maprule_out/tests/%s/bar/out2/src1.out2" % language,
|
||||
outs_dicts[src3]["OUT2"].path,
|
||||
"assertion #9 (language: %s)" % language,
|
||||
)
|
||||
|
||||
expected = [
|
||||
"my_maprule_out/tests/%s/foo/src1.txt.out1" % language,
|
||||
"my_maprule_out/tests/%s/foo/out2/src1.out2" % language,
|
||||
"my_maprule_out/tests/%s/foo/src2.pb.h.out1" % language,
|
||||
"my_maprule_out/tests/%s/foo/out2/src2.pb.out2" % language,
|
||||
"my_maprule_out/tests/%s/bar/src1.txt.out1" % language,
|
||||
"my_maprule_out/tests/%s/bar/out2/src1.out2" % language,
|
||||
]
|
||||
for i in range(0, len(all_output_files)):
|
||||
actual = _lstrip_until(all_output_files[i].path, "my_maprule_out")
|
||||
asserts.equals(
|
||||
env,
|
||||
expected[i],
|
||||
actual,
|
||||
"assertion #10 (language: %s, index: %d)" % (language, i),
|
||||
)
|
||||
|
||||
return unittest.end(env)
|
||||
|
||||
create_outputs_test = unittest.make(_create_outputs_test)
|
||||
|
||||
def _conflicting_outputs_test(ctx):
|
||||
"""Unit tests for maprule_testing.create_outputs catching conflicting outputs."""
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
for language, strategy in [
|
||||
("cmd", maprule_testing.cmd_strategy),
|
||||
("bash", maprule_testing.bash_strategy),
|
||||
]:
|
||||
src1 = _mock_file(ctx, language + "/foo/src1.txt")
|
||||
src2 = _mock_file(ctx, language + "/foo/src2.pb.h")
|
||||
src3 = _mock_file(ctx, language + "/bar/src1.txt")
|
||||
foreach_srcs = [src1, src2, src3]
|
||||
|
||||
_, all_output_files, _, errors = (
|
||||
maprule_testing.create_outputs(
|
||||
ctx,
|
||||
"my_maprule",
|
||||
{
|
||||
"OUT1": "out1", # 3 conflicts
|
||||
"OUT2": "{src_dir}/out2", # 2 conflicts
|
||||
"OUT3": "out3/{src_name}", # 2 conflicts
|
||||
},
|
||||
strategy,
|
||||
foreach_srcs,
|
||||
)
|
||||
)
|
||||
|
||||
for output in all_output_files:
|
||||
_dummy_generating_action(ctx, output)
|
||||
|
||||
_assert_error_fragments(
|
||||
env,
|
||||
errors,
|
||||
["out1", language + "/foo/src1.txt", "OUT1", language + "/foo/src2.pb.h", "OUT1"],
|
||||
msg = "assertion #1 (language: %s)" % language,
|
||||
)
|
||||
|
||||
_assert_error_fragments(
|
||||
env,
|
||||
errors,
|
||||
["out2", language + "/foo/src1.txt", "OUT2", language + "/foo/src2.pb.h", "OUT2"],
|
||||
msg = "assertion #2 (language: %s)" % language,
|
||||
)
|
||||
|
||||
_assert_error_fragments(
|
||||
env,
|
||||
errors,
|
||||
["out3/src1.txt", language + "/foo/src1.txt", "OUT3", language + "/bar/src1.txt", "OUT3"],
|
||||
msg = "assertion #5 (language: %s)" % language,
|
||||
)
|
||||
|
||||
return unittest.end(env)
|
||||
|
||||
conflicting_outputs_test = unittest.make(_conflicting_outputs_test)
|
||||
|
||||
def maprule_test_suite():
|
||||
"""Creates the test targets and test suite for maprule.bzl tests."""
|
||||
|
||||
unittest.suite(
|
||||
"maprule_tests",
|
||||
src_placeholders_test,
|
||||
validate_attributes_test,
|
||||
as_path_test,
|
||||
is_relative_path_test,
|
||||
custom_envmap_test,
|
||||
conflicting_outputs_test,
|
||||
)
|
|
@ -1,5 +1,6 @@
|
|||
load("//rules:copy_file.bzl", "copy_file")
|
||||
load("//rules:native_binary.bzl", "native_binary", "native_test")
|
||||
load("@rules_cc//cc:defs.bzl", "cc_binary")
|
||||
|
||||
package(
|
||||
default_testonly = 1,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
load("//rules:diff_test.bzl", "diff_test")
|
||||
load("//rules:run_binary.bzl", "run_binary")
|
||||
load("//rules:write_file.bzl", "write_file")
|
||||
load("@rules_cc//cc:defs.bzl", "cc_binary")
|
||||
|
||||
package(
|
||||
default_testonly = 1,
|
||||
|
|
Loading…
Reference in New Issue