summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/concepts/built-in-rules.md29
-rw-r--r--src/buildtool/build_engine/target_map/built_in_rules.cpp47
2 files changed, 62 insertions, 14 deletions
diff --git a/doc/concepts/built-in-rules.md b/doc/concepts/built-in-rules.md
index 5591c174..fc644a03 100644
--- a/doc/concepts/built-in-rules.md
+++ b/doc/concepts/built-in-rules.md
@@ -91,20 +91,23 @@ precedence to the artifacts over the runfiles; conflicts within
artifacts or runfiles are resolved in a latest-wins fashion using the
order of the targets in the evaluated `"deps"` argument.
-The fields `"cmds"`, `"out_dirs"`, `"outs"`, and `"env"` are evaluated
-fields where `"cmds"`, `"out_dirs"`, and `"outs"` have to evaluate to a
-list of strings, and `"env"` has to evaluate to a map of strings. During
-their evaluation, the functions `"outs"` and `"runfiles"`
-can be used to access the logical paths of the artifacts
+The fields `"cmds"`, `"sh -c"`, `"out_dirs"`, `"outs"`, and `"env"`
+are evaluated fields where `"cmds"`, `"out_dirs"`, and `"outs"`
+have to evaluate to a list of strings, `"sh -c"` has to evalute to
+a list of strings or `null`, and `"env"` has to evaluate to a map
+of strings. During their evaluation, the functions `"outs"` and
+`"runfiles"` can be used to access the logical paths of the artifacts
and runfiles, respectively, of a target specified in `"deps"`. Here,
-`"env"` specifies the environment in which the action is carried out.
-`"out_dirs"` and `"outs"` define the output directories and files,
-respectively, the action has to produce. Since some artifacts are to be
-produced, at least one of `"out_dirs"` or `"outs"` must be a non-empty
-list of strings. It is an error if one or more paths are present in both
-the `"out_dirs"` and `"outs"`. Finally, the strings in `"cmds"` are
-extended by a newline character and joined, and command of the action is
-interpreting this string by `sh`.
+`"env"` specifies the environment in which the action is carried
+out. `"out_dirs"` and `"outs"` define the output directories and
+files, respectively, the action has to produce. Since some artifacts
+are to be produced, at least one of `"out_dirs"` or `"outs"` must
+be a non-empty list of strings. It is an error if one or more paths
+are present in both the `"out_dirs"` and `"outs"`. Finally, the
+strings in `"cmds"` are extended by a newline character and joined,
+and command of the action is the result of evaluating the field
+`"sh -c"` (or `["sh", "-c"]` if `"sh -c"` evaluates to `null` or
+`[]`) extended by this string.
The artifacts of this target are the outputs (as declared by
`"out_dirs"` and `"outs"`) of this action. Runfiles and provider map are
diff --git a/src/buildtool/build_engine/target_map/built_in_rules.cpp b/src/buildtool/build_engine/target_map/built_in_rules.cpp
index 29eba7bf..7a057927 100644
--- a/src/buildtool/build_engine/target_map/built_in_rules.cpp
+++ b/src/buildtool/build_engine/target_map/built_in_rules.cpp
@@ -43,6 +43,7 @@ auto const kGenericRuleFields =
"deps",
"env",
"execution properties",
+ "sh -c",
"tainted",
"timeout scaling",
"type",
@@ -1092,6 +1093,43 @@ void GenericRuleWithDeps(
}
}
+ auto sh_exp = desc->ReadOptionalExpression("sh -c", Expression::kEmptyList);
+ if (not sh_exp) {
+ return;
+ }
+ auto sh_val = sh_exp.Evaluate(
+ param_config, string_fields_fcts, [&logger](auto const& msg) {
+ (*logger)(fmt::format("While evaluating sh:\n{}", msg), true);
+ });
+ if (not sh_val) {
+ return;
+ }
+ if (sh_val->IsNone()) {
+ sh_val = Expression::kEmptyList;
+ }
+ if (not sh_val->IsList()) {
+ (*logger)(fmt::format("sh has evaluate to list of strings or null, but "
+ "found {}",
+ sh_val->ToString()),
+ true);
+ return;
+ }
+ for (auto const& entry : sh_val->List()) {
+ if (not entry->IsString()) {
+ (*logger)(fmt::format("sh has evaluate to list of strings or null, "
+ "but found {}\nwith non-string entry {}",
+ sh_val->ToString(),
+ entry->ToString()),
+ true);
+ return;
+ }
+ }
+ static ExpressionPtr const kShC =
+ Expression::FromJson(R"( ["sh", "-c"] )"_json);
+ if (sh_val->List().empty()) {
+ sh_val = kShC;
+ }
+
auto scale_exp =
desc->ReadOptionalExpression("timeout scaling", Expression::kOne);
if (not scale_exp) {
@@ -1159,11 +1197,18 @@ void GenericRuleWithDeps(
inputs = ExpressionPtr{Expression::map_t{inputs, (*dep)->Artifacts()}};
}
+ std::vector<std::string> argv{};
+ argv.reserve(sh_val->List().size() + 1);
+ for (auto const& entry : sh_val->List()) {
+ argv.emplace_back(entry->String());
+ }
+ argv.emplace_back(cmd_ss.str());
+
// Construct our single action, and its artifacts
auto action = BuildMaps::Target::Utils::createAction(
outs,
out_dirs,
- {"sh", "-c", cmd_ss.str()},
+ argv,
env_val,
std::nullopt,
false,