Make more variables available to scripts.
This change makes certain extra variables available to 'action' target types,
i.e. scripts. Those variables are:
* defines
* include_dirs
* rustenv
They are deduced from any configs applied to the target, so
configs also now applies to 'action' targets.
These variables are normally exclusively used by compilers, and no compiler-
specific variables have been made available to scripts previously. Here's
why they're needed.
rustenv:
This variable represents environment variables passed to the Rust
build, and in the Chromium tree this is assembled using various transitive
'config's. These environment variables are typically used to allow `env!`
directives within the Rust code to work properly; often (but not always)
this is to support pre-existing third party Rust code ("crates").
Such pre-existing third party Rust code also expects the same environment
variables to be present when the crates' build scripts are run. In a gn
environment, these build scripts are run using an 'action', and therefore
we need to make this rustenv value available to 'action' targets.
This is described in https://chromium/1304254.
defines/include_dirs:
In Rust development it is common to produce Rust bindings to pre-existing
C/C++ headers. This is not built into rustc, but is instead a facility
provided by separate codegen tools - the most commonly known example being
bindgen (https://rust-lang.github.io/rust-bindgen/). In a gn environment,
these tools are run as a gn 'action' target.
Such tools involve parsing C/C++ headers (using something like libclang).
In order to parse headers properly, such tools obviously need to be aware
of the full list of -I and -D flags active in the current environment.
In Chromium, such -I and -D flags are assembled using a graph of complex
transitive gn 'config's, and there's no realistic prospect of hard-coding
equivalent command lines.
'bindgen' therefore absolutely has to have access to the 'include_dirs'
and 'defines' set using gn configs. One solution here is to define a
whole new toolchain where we inform GN that bindgen is a compiler - this
solution is outlined in https://chromium/1296241 - however this is not
ideal. To parse (say) just base/cpu_info.h, we would need to depend upon
//base(//toolchains/fake_bindgen_toolchain)
which will run a command to 'compile' every .cc file within //base.
This can be 10,000s of extra ninja rules, and even if most of them
are just a 'touch' command, this is still a great deal of extra complexity.
Bug: chromium/1272780, chromium/1296155, chromium/1296241, chromium/1304254
Change-Id: Ib60067b310d8508f61d04fe7ddc537a5d2c2917f
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/13220
Reviewed-by: Brett Wilson <brettw@chromium.org>
Commit-Queue: Brett Wilson <brettw@chromium.org>
diff --git a/docs/reference.md b/docs/reference.md
index 566b3ed..51dcecc 100644
--- a/docs/reference.md
+++ b/docs/reference.md
@@ -1312,6 +1312,12 @@
and stuff like other Python files required to run your script in the "inputs"
variable.
+ Actions can take the configs and public_configs lists, as well as any of the
+ configs variables (defines, include_dirs, etc.) set directly on the target.
+ These behave exactly as they would on a binary target and can be accessed
+ using substitution patterns in the script args (see "gn help args") to
+ implement custom compiler-like tools.
+
The "deps" and "public_deps" for an action will always be
completed before any part of the action is run so it can depend on
the output of previous steps. The "data_deps" will be built if the
@@ -1354,8 +1360,11 @@
#### **Variables**
```
- args, data, data_deps, depfile, deps, inputs, metadata, outputs*, pool,
- response_file_contents, script*, sources
+ args, asmflags, bridge_header, cflags, cflags_c, cflags_cc, cflags_objc,
+ cflags_objcc, configs, data, data_deps, defines, depfile, deps,
+ framework_dirs, include_dirs, inputs, metadata, module_deps, module_name,
+ outputs*, pool, response_file_contents, rustenv, rustflags, script*, sources,
+ swiftflags
* = required
```
@@ -1443,9 +1452,11 @@
#### **Variables**
```
- args, data, data_deps, depfile, deps, inputs, metadata, outputs*, pool,
- response_file_contents, script*, sources*
- * = required
+ args, asmflags, bridge_header, cflags, cflags_c, cflags_cc, cflags_objc,
+ cflags_objcc, configs, data, data_deps, defines, depfile, deps,
+ framework_dirs, include_dirs, inputs, metadata, module_deps, module_name,
+ outputs*, pool, response_file_contents, rustenv, rustflags, script*, sources,
+ swiftflags
```
#### **Example**
@@ -4696,6 +4707,14 @@
to the script. Typically you would use source expansion (see "gn help
source_expansion") to insert the source file names.
+ Args can also expand the substitution patterns corresponding to config
+ variables in the same way that compiler tools (see "gn help tool") do. These
+ allow actions that run compiler or compiler-like tools to access the results
+ of propagating configs through the build graph. For example:
+
+ args = [ "{{defines}}", "{{include_dirs}}", "{{rustenv}}", "--input-file",
+ "{{source}}" ]
+
See also "gn help action" and "gn help action_foreach".
```
### <a name="var_asmflags"></a>**asmflags**: Flags passed to the assembler.
diff --git a/src/gn/action_target_generator.cc b/src/gn/action_target_generator.cc
index 73fa288..ff66765 100644
--- a/src/gn/action_target_generator.cc
+++ b/src/gn/action_target_generator.cc
@@ -6,6 +6,7 @@
#include "base/stl_util.h"
#include "gn/build_settings.h"
+#include "gn/config_values_generator.h"
#include "gn/err.h"
#include "gn/filesystem_utils.h"
#include "gn/functions.h"
@@ -63,9 +64,19 @@
if (!FillCheckIncludes())
return;
+ if (!FillConfigs())
+ return;
+
if (!CheckOutputs())
return;
+ // Config values (compiler flags, etc.) set directly on this target.
+ ConfigValuesGenerator gen(&target_->config_values(), scope_,
+ scope_->GetSourceDir(), err_);
+ gen.Run();
+ if (err_->has_error())
+ return;
+
// Action outputs don't depend on the current toolchain so we can skip adding
// that dependency.
diff --git a/src/gn/c_substitution_type.cc b/src/gn/c_substitution_type.cc
index ea47ce9..971d87a 100644
--- a/src/gn/c_substitution_type.cc
+++ b/src/gn/c_substitution_type.cc
@@ -92,6 +92,19 @@
type == &CSubstitutionModuleDepsNoSelf;
}
+bool IsValidCompilerScriptArgsSubstitution(const Substitution* type) {
+ return type == &CSubstitutionAsmFlags || type == &CSubstitutionCFlags ||
+ type == &CSubstitutionCFlagsC || type == &CSubstitutionCFlagsCc ||
+ type == &CSubstitutionCFlagsObjC ||
+ type == &CSubstitutionCFlagsObjCc || type == &CSubstitutionDefines ||
+ type == &CSubstitutionFrameworkDirs ||
+ type == &CSubstitutionIncludeDirs ||
+ type == &CSubstitutionSwiftModuleName ||
+ type == &CSubstitutionSwiftBridgeHeader ||
+ type == &CSubstitutionSwiftModuleDirs ||
+ type == &CSubstitutionSwiftFlags;
+}
+
bool IsValidCompilerOutputsSubstitution(const Substitution* type) {
// All tool types except "output" (which would be infinitely recursive).
return (IsValidToolSubstitution(type) && type != &SubstitutionOutput) ||
diff --git a/src/gn/c_substitution_type.h b/src/gn/c_substitution_type.h
index 1f4e90e..d0d1f8c 100644
--- a/src/gn/c_substitution_type.h
+++ b/src/gn/c_substitution_type.h
@@ -54,4 +54,7 @@
bool IsValidLinkerOutputsSubstitution(const Substitution* type);
bool IsValidALinkSubstitution(const Substitution* type);
+// action targets may rely on some compiler substitutions.
+bool IsValidCompilerScriptArgsSubstitution(const Substitution* type);
+
#endif // TOOLS_GN_C_SUBSTITUTION_TYPE_H_
diff --git a/src/gn/compile_commands_writer.cc b/src/gn/compile_commands_writer.cc
index 16f2a1a..c65d378 100644
--- a/src/gn/compile_commands_writer.cc
+++ b/src/gn/compile_commands_writer.cc
@@ -105,7 +105,7 @@
std::ostringstream out;
WriteOneFlag(config, target, substitution, has_precompiled_headers,
tool_name, getter, opts, path_output, out,
- /*write_substitution=*/false);
+ /*write_substitution=*/false, /*indent=*/false);
base::EscapeJSONString(out.str(), false, &result);
return result;
};
diff --git a/src/gn/functions_target.cc b/src/gn/functions_target.cc
index 7120b3f..74f53e9 100644
--- a/src/gn/functions_target.cc
+++ b/src/gn/functions_target.cc
@@ -138,6 +138,12 @@
It is recommended you put inputs to your script in the "sources" variable,
and stuff like other Python files required to run your script in the "inputs"
variable.
+
+ Actions can take the configs and public_configs lists, as well as any of the
+ configs variables (defines, include_dirs, etc.) set directly on the target.
+ These behave exactly as they would on a binary target and can be accessed
+ using substitution patterns in the script args (see "gn help args") to
+ implement custom compiler-like tools.
)"
ACTION_DEPS
@@ -160,8 +166,11 @@
R"(
Variables
- args, data, data_deps, depfile, deps, inputs, metadata, outputs*, pool,
- response_file_contents, script*, sources
+ args, asmflags, bridge_header, cflags, cflags_c, cflags_cc, cflags_objc,
+ cflags_objcc, configs, data, data_deps, defines, depfile, deps,
+ framework_dirs, include_dirs, inputs, metadata, module_deps, module_name,
+ outputs*, pool, response_file_contents, rustenv, rustflags, script*, sources,
+ swiftflags
* = required
Example
@@ -230,9 +239,11 @@
R"(
Variables
- args, data, data_deps, depfile, deps, inputs, metadata, outputs*, pool,
- response_file_contents, script*, sources*
- * = required
+ args, asmflags, bridge_header, cflags, cflags_c, cflags_cc, cflags_objc,
+ cflags_objcc, configs, data, data_deps, defines, depfile, deps,
+ framework_dirs, include_dirs, inputs, metadata, module_deps, module_name,
+ outputs*, pool, response_file_contents, rustenv, rustflags, script*, sources,
+ swiftflags
Example
diff --git a/src/gn/ninja_action_target_writer.cc b/src/gn/ninja_action_target_writer.cc
index bfceef9..e717d33 100644
--- a/src/gn/ninja_action_target_writer.cc
+++ b/src/gn/ninja_action_target_writer.cc
@@ -80,6 +80,9 @@
if (target_->action_values().has_depfile()) {
WriteDepfile(SourceFile());
}
+
+ WriteNinjaVariablesForAction();
+
if (target_->action_values().pool().ptr) {
out_ << " pool = ";
out_ << target_->action_values().pool().ptr->GetNinjaName(
@@ -149,7 +152,8 @@
out_ << std::endl;
out_ << " description = ACTION " << target_label << std::endl;
out_ << " restat = 1" << std::endl;
- const Tool* tool = target_->toolchain()->GetTool(GeneralTool::kGeneralToolAction);
+ const Tool* tool =
+ target_->toolchain()->GetTool(GeneralTool::kGeneralToolAction);
if (tool && tool->pool().ptr) {
out_ << " pool = ";
out_ << tool->pool().ptr->GetNinjaName(
@@ -204,6 +208,7 @@
target_, settings_, sources[i],
target_->action_values().rsp_file_contents().required_types(),
args_escape_options, out_);
+ WriteNinjaVariablesForAction();
if (target_->action_values().has_depfile()) {
WriteDepfile(sources[i]);
@@ -248,3 +253,10 @@
out_ << " deps = gcc" << std::endl;
}
}
+
+void NinjaActionTargetWriter::WriteNinjaVariablesForAction() {
+ SubstitutionBits subst;
+ target_->action_values().args().FillRequiredTypes(&subst);
+ WriteRustCompilerVars(subst, /*indent=*/true, /*always_write=*/false);
+ WriteCCompilerVars(subst, /*indent=*/true, /*respect_source_types=*/false);
+}
diff --git a/src/gn/ninja_action_target_writer.h b/src/gn/ninja_action_target_writer.h
index ef91f4e..58b9489 100644
--- a/src/gn/ninja_action_target_writer.h
+++ b/src/gn/ninja_action_target_writer.h
@@ -50,6 +50,10 @@
void WriteDepfile(const SourceFile& source);
+ // Writes variables that we make available to all actions, irrespective
+ // of whether they're associated with a specific source file.
+ void WriteNinjaVariablesForAction();
+
// Path output writer that doesn't do any escaping or quoting. It does,
// however, convert slashes. Used for
// computing intermediate strings.
diff --git a/src/gn/ninja_action_target_writer_unittest.cc b/src/gn/ninja_action_target_writer_unittest.cc
index 80b125b..d363b5e 100644
--- a/src/gn/ninja_action_target_writer_unittest.cc
+++ b/src/gn/ninja_action_target_writer_unittest.cc
@@ -5,6 +5,7 @@
#include <algorithm>
#include <sstream>
+#include "gn/config.h"
#include "gn/ninja_action_target_writer.h"
#include "gn/pool.h"
#include "gn/substitution_list.h"
@@ -486,3 +487,63 @@
EXPECT_EQ(expected_linux, out.str());
}
}
+
+TEST(NinjaActionTargetWriter, SeesConfig) {
+ Err err;
+ TestWithScope setup;
+
+ setup.build_settings()->set_python_path(
+ base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
+
+ Config farcfg(setup.settings(), Label(SourceDir("//foo/"), "farcfg"));
+ farcfg.own_values().defines().push_back("MY_DEFINE2");
+ farcfg.own_values().cflags().push_back("-isysroot=baz");
+ farcfg.visibility().SetPublic();
+ ASSERT_TRUE(farcfg.OnResolved(&err));
+
+ Config cfgdep(setup.settings(), Label(SourceDir("//foo/"), "cfgdep"));
+ cfgdep.own_values().rustenv().push_back("my_rustenv");
+ cfgdep.own_values().include_dirs().push_back(SourceDir("//my_inc_dir/"));
+ cfgdep.own_values().defines().push_back("MY_DEFINE");
+ cfgdep.visibility().SetPublic();
+ cfgdep.configs().push_back(LabelConfigPair(&farcfg));
+ ASSERT_TRUE(cfgdep.OnResolved(&err));
+
+ Target foo(setup.settings(), Label(SourceDir("//foo/"), "foo"));
+ foo.set_output_type(Target::ACTION);
+ foo.visibility().SetPublic();
+ foo.sources().push_back(SourceFile("//foo/input1.txt"));
+ foo.action_values().set_script(SourceFile("//foo/script.py"));
+ foo.action_values().args() = SubstitutionList::MakeForTest(
+ "{{rustenv}}", "{{include_dirs}}", "{{defines}}", "{{cflags}}");
+ foo.configs().push_back(LabelConfigPair(&cfgdep));
+ foo.SetToolchain(setup.toolchain());
+ foo.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/foo.out");
+ ASSERT_TRUE(foo.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaActionTargetWriter writer(&foo, out);
+ writer.Run();
+
+ const char expected[] =
+ "rule __foo_foo___rule\n"
+ // These come from the args.
+ " command = /usr/bin/python ../../foo/script.py ${rustenv} "
+ "${include_dirs} ${defines} ${cflags}\n"
+ " description = ACTION //foo:foo()\n"
+ " restat = 1\n"
+ "\n"
+ "build foo.out: __foo_foo___rule | ../../foo/script.py"
+ " ../../foo/input1.txt\n"
+ " rustenv = my_rustenv\n"
+ " defines = -DMY_DEFINE -DMY_DEFINE2\n"
+ " include_dirs = -I../../my_inc_dir\n"
+ " cflags = -isysroot=baz\n"
+ "\n"
+ "build obj/foo/foo.stamp: stamp foo.out\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
diff --git a/src/gn/ninja_c_binary_target_writer.cc b/src/gn/ninja_c_binary_target_writer.cc
index 5f3afcb..052f03a 100644
--- a/src/gn/ninja_c_binary_target_writer.cc
+++ b/src/gn/ninja_c_binary_target_writer.cc
@@ -224,42 +224,8 @@
const std::vector<ModuleDep>& module_dep_info) {
const SubstitutionBits& subst = target_->toolchain()->substitution_bits();
- // Defines.
- if (subst.used.count(&CSubstitutionDefines)) {
- out_ << CSubstitutionDefines.ninja_name << " =";
- RecursiveTargetConfigToStream<std::string>(kRecursiveWriterSkipDuplicates,
- target_, &ConfigValues::defines,
- DefineWriter(), out_);
- out_ << std::endl;
- }
-
- // Framework search path.
- if (subst.used.count(&CSubstitutionFrameworkDirs)) {
- const Tool* tool = target_->toolchain()->GetTool(CTool::kCToolLink);
-
- out_ << CSubstitutionFrameworkDirs.ninja_name << " =";
- PathOutput framework_dirs_output(
- path_output_.current_dir(),
- settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
- RecursiveTargetConfigToStream<SourceDir>(
- kRecursiveWriterSkipDuplicates, target_, &ConfigValues::framework_dirs,
- FrameworkDirsWriter(framework_dirs_output,
- tool->framework_dir_switch()),
- out_);
- out_ << std::endl;
- }
-
- // Include directories.
- if (subst.used.count(&CSubstitutionIncludeDirs)) {
- out_ << CSubstitutionIncludeDirs.ninja_name << " =";
- PathOutput include_path_output(
- path_output_.current_dir(),
- settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
- RecursiveTargetConfigToStream<SourceDir>(
- kRecursiveWriterSkipDuplicates, target_, &ConfigValues::include_dirs,
- IncludeWriter(include_path_output), out_);
- out_ << std::endl;
- }
+ WriteCCompilerVars(subst, /*indent=*/false,
+ /*respect_source_types_used=*/true);
if (!module_dep_info.empty()) {
// TODO(scottmg): Currently clang modules only working for C++.
@@ -272,89 +238,6 @@
}
}
- bool has_precompiled_headers =
- target_->config_values().has_precompiled_headers();
-
- EscapeOptions opts = GetFlagOptions();
- if (target_->source_types_used().Get(SourceFile::SOURCE_S) ||
- target_->source_types_used().Get(SourceFile::SOURCE_ASM)) {
- WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
- &CSubstitutionAsmFlags, false, Tool::kToolNone,
- &ConfigValues::asmflags, opts, path_output_, out_);
- }
- if (target_->source_types_used().Get(SourceFile::SOURCE_C) ||
- target_->source_types_used().Get(SourceFile::SOURCE_CPP) ||
- target_->source_types_used().Get(SourceFile::SOURCE_M) ||
- target_->source_types_used().Get(SourceFile::SOURCE_MM) ||
- target_->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) {
- WriteOneFlag(kRecursiveWriterKeepDuplicates, target_, &CSubstitutionCFlags,
- false, Tool::kToolNone, &ConfigValues::cflags, opts,
- path_output_, out_);
- }
- if (target_->source_types_used().Get(SourceFile::SOURCE_C)) {
- WriteOneFlag(kRecursiveWriterKeepDuplicates, target_, &CSubstitutionCFlagsC,
- has_precompiled_headers, CTool::kCToolCc,
- &ConfigValues::cflags_c, opts, path_output_, out_);
- }
- if (target_->source_types_used().Get(SourceFile::SOURCE_CPP) ||
- target_->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) {
- WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
- &CSubstitutionCFlagsCc, has_precompiled_headers,
- CTool::kCToolCxx, &ConfigValues::cflags_cc, opts, path_output_,
- out_);
- }
- if (target_->source_types_used().Get(SourceFile::SOURCE_M)) {
- WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
- &CSubstitutionCFlagsObjC, has_precompiled_headers,
- CTool::kCToolObjC, &ConfigValues::cflags_objc, opts,
- path_output_, out_);
- }
- if (target_->source_types_used().Get(SourceFile::SOURCE_MM)) {
- WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
- &CSubstitutionCFlagsObjCc, has_precompiled_headers,
- CTool::kCToolObjCxx, &ConfigValues::cflags_objcc, opts,
- path_output_, out_);
- }
- if (target_->source_types_used().SwiftSourceUsed()) {
- if (subst.used.count(&CSubstitutionSwiftModuleName)) {
- out_ << CSubstitutionSwiftModuleName.ninja_name << " = ";
- EscapeStringToStream(out_, target_->swift_values().module_name(), opts);
- out_ << std::endl;
- }
-
- if (subst.used.count(&CSubstitutionSwiftBridgeHeader)) {
- out_ << CSubstitutionSwiftBridgeHeader.ninja_name << " = ";
- if (!target_->swift_values().bridge_header().is_null()) {
- path_output_.WriteFile(out_, target_->swift_values().bridge_header());
- } else {
- out_ << R"("")";
- }
- out_ << std::endl;
- }
-
- if (subst.used.count(&CSubstitutionSwiftModuleDirs)) {
- // Uniquify the list of swiftmodule dirs (in case multiple swiftmodules
- // are generated in the same directory).
- UniqueVector<SourceDir> swiftmodule_dirs;
- for (const Target* dep : target_->swift_values().modules())
- swiftmodule_dirs.push_back(dep->swift_values().module_output_dir());
-
- out_ << CSubstitutionSwiftModuleDirs.ninja_name << " =";
- PathOutput swiftmodule_path_output(
- path_output_.current_dir(),
- settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
- IncludeWriter swiftmodule_path_writer(swiftmodule_path_output);
- for (const SourceDir& swiftmodule_dir : swiftmodule_dirs) {
- swiftmodule_path_writer(swiftmodule_dir, out_);
- }
- out_ << std::endl;
- }
-
- WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
- &CSubstitutionSwiftFlags, false, CTool::kCToolSwift,
- &ConfigValues::swiftflags, opts, path_output_, out_);
- }
-
WriteSharedVars(subst);
}
diff --git a/src/gn/ninja_c_binary_target_writer_unittest.cc b/src/gn/ninja_c_binary_target_writer_unittest.cc
index 2aafe92..e4ec800 100644
--- a/src/gn/ninja_c_binary_target_writer_unittest.cc
+++ b/src/gn/ninja_c_binary_target_writer_unittest.cc
@@ -2505,10 +2505,10 @@
const char expected[] = R"(defines =
include_dirs =
-module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm
-module_deps_no_self = -Xclang -fmodules-embed-all-files
cflags =
cflags_cc =
+module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm
+module_deps_no_self = -Xclang -fmodules-embed-all-files
label = //blah$:a
root_out_dir = withmodules
target_out_dir = obj/blah
@@ -2550,10 +2550,10 @@
const char expected[] = R"(defines =
include_dirs =
-module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/stuff/libb.b.pcm
-module_deps_no_self = -Xclang -fmodules-embed-all-files
cflags =
cflags_cc =
+module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/stuff/libb.b.pcm
+module_deps_no_self = -Xclang -fmodules-embed-all-files
label = //stuff$:b
root_out_dir = withmodules
target_out_dir = obj/stuff
@@ -2594,10 +2594,10 @@
const char expected[] = R"(defines =
include_dirs =
-module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/stuff/libc.c.pcm -fmodule-file=obj/blah/liba.a.pcm
-module_deps_no_self = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm
cflags =
cflags_cc =
+module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/stuff/libc.c.pcm -fmodule-file=obj/blah/liba.a.pcm
+module_deps_no_self = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm
label = //things$:c
root_out_dir = withmodules
target_out_dir = obj/things
@@ -2635,10 +2635,10 @@
const char expected[] = R"(defines =
include_dirs =
-module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm -fmodule-file=obj/stuff/libb.b.pcm
-module_deps_no_self = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm -fmodule-file=obj/stuff/libb.b.pcm
cflags =
cflags_cc =
+module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm -fmodule-file=obj/stuff/libb.b.pcm
+module_deps_no_self = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm -fmodule-file=obj/stuff/libb.b.pcm
label = //zap$:c
root_out_dir = withmodules
target_out_dir = obj/zap
diff --git a/src/gn/ninja_rust_binary_target_writer.cc b/src/gn/ninja_rust_binary_target_writer.cc
index 0fd26c0..f3ff21f 100644
--- a/src/gn/ninja_rust_binary_target_writer.cc
+++ b/src/gn/ninja_rust_binary_target_writer.cc
@@ -212,13 +212,7 @@
EscapeOptions opts = GetFlagOptions();
WriteCrateVars(target_, tool_, opts, out_);
- WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
- &kRustSubstitutionRustFlags, false, Tool::kToolNone,
- &ConfigValues::rustflags, opts, path_output_, out_);
-
- WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
- &kRustSubstitutionRustEnv, false, Tool::kToolNone,
- &ConfigValues::rustenv, opts, path_output_, out_);
+ WriteRustCompilerVars(subst, /*indent=*/false, /*always_write=*/true);
WriteSharedVars(subst);
}
diff --git a/src/gn/ninja_target_command_util.cc b/src/gn/ninja_target_command_util.cc
index b542fb2..a691ae3 100644
--- a/src/gn/ninja_target_command_util.cc
+++ b/src/gn/ninja_target_command_util.cc
@@ -51,10 +51,13 @@
EscapeOptions flag_escape_options,
PathOutput& path_output,
std::ostream& out,
- bool write_substitution) {
+ bool write_substitution,
+ bool indent) {
if (!target->toolchain()->substitution_bits().used.count(subst_enum))
return;
+ if (indent)
+ out << " ";
if (write_substitution)
out << subst_enum->ninja_name << " =";
diff --git a/src/gn/ninja_target_command_util.h b/src/gn/ninja_target_command_util.h
index b0179a1..93666fb 100644
--- a/src/gn/ninja_target_command_util.h
+++ b/src/gn/ninja_target_command_util.h
@@ -102,7 +102,8 @@
EscapeOptions flag_escape_options,
PathOutput& path_output,
std::ostream& out,
- bool write_substitution = true);
+ bool write_substitution = true,
+ bool indent = false);
// Fills |outputs| with the object or gch file for the precompiled header of the
// given type (flag type and tool type must match).
diff --git a/src/gn/ninja_target_writer.cc b/src/gn/ninja_target_writer.cc
index efcf991..0f2514b 100644
--- a/src/gn/ninja_target_writer.cc
+++ b/src/gn/ninja_target_writer.cc
@@ -8,6 +8,7 @@
#include "base/files/file_util.h"
#include "base/strings/string_util.h"
+#include "gn/c_substitution_type.h"
#include "gn/config_values_extractors.h"
#include "gn/err.h"
#include "gn/escape.h"
@@ -20,8 +21,10 @@
#include "gn/ninja_create_bundle_target_writer.h"
#include "gn/ninja_generated_file_target_writer.h"
#include "gn/ninja_group_target_writer.h"
+#include "gn/ninja_target_command_util.h"
#include "gn/ninja_utils.h"
#include "gn/output_file.h"
+#include "gn/rust_substitution_type.h"
#include "gn/scheduler.h"
#include "gn/string_output_buffer.h"
#include "gn/string_utils.h"
@@ -188,6 +191,178 @@
out_ << std::endl;
}
+void NinjaTargetWriter::WriteCCompilerVars(const SubstitutionBits& bits,
+ bool indent,
+ bool respect_source_used) {
+ // Defines.
+ if (bits.used.count(&CSubstitutionDefines)) {
+ if (indent)
+ out_ << " ";
+ out_ << CSubstitutionDefines.ninja_name << " =";
+ RecursiveTargetConfigToStream<std::string>(kRecursiveWriterSkipDuplicates,
+ target_, &ConfigValues::defines,
+ DefineWriter(), out_);
+ out_ << std::endl;
+ }
+
+ // Framework search path.
+ if (bits.used.count(&CSubstitutionFrameworkDirs)) {
+ const Tool* tool = target_->toolchain()->GetTool(CTool::kCToolLink);
+
+ if (indent)
+ out_ << " ";
+ out_ << CSubstitutionFrameworkDirs.ninja_name << " =";
+ PathOutput framework_dirs_output(
+ path_output_.current_dir(),
+ settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
+ RecursiveTargetConfigToStream<SourceDir>(
+ kRecursiveWriterSkipDuplicates, target_, &ConfigValues::framework_dirs,
+ FrameworkDirsWriter(framework_dirs_output,
+ tool->framework_dir_switch()),
+ out_);
+ out_ << std::endl;
+ }
+
+ // Include directories.
+ if (bits.used.count(&CSubstitutionIncludeDirs)) {
+ if (indent)
+ out_ << " ";
+ out_ << CSubstitutionIncludeDirs.ninja_name << " =";
+ PathOutput include_path_output(
+ path_output_.current_dir(),
+ settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
+ RecursiveTargetConfigToStream<SourceDir>(
+ kRecursiveWriterSkipDuplicates, target_, &ConfigValues::include_dirs,
+ IncludeWriter(include_path_output), out_);
+ out_ << std::endl;
+ }
+
+ bool has_precompiled_headers =
+ target_->config_values().has_precompiled_headers();
+
+ EscapeOptions opts;
+ opts.mode = ESCAPE_NINJA_COMMAND;
+ if (respect_source_used
+ ? target_->source_types_used().Get(SourceFile::SOURCE_S)
+ : bits.used.count(&CSubstitutionAsmFlags)) {
+ WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
+ &CSubstitutionAsmFlags, false, Tool::kToolNone,
+ &ConfigValues::asmflags, opts, path_output_, out_, true,
+ indent);
+ }
+ if (respect_source_used
+ ? (target_->source_types_used().Get(SourceFile::SOURCE_C) ||
+ target_->source_types_used().Get(SourceFile::SOURCE_CPP) ||
+ target_->source_types_used().Get(SourceFile::SOURCE_M) ||
+ target_->source_types_used().Get(SourceFile::SOURCE_MM) ||
+ target_->source_types_used().Get(SourceFile::SOURCE_MODULEMAP))
+ : bits.used.count(&CSubstitutionCFlags)) {
+ WriteOneFlag(kRecursiveWriterKeepDuplicates, target_, &CSubstitutionCFlags,
+ false, Tool::kToolNone, &ConfigValues::cflags, opts,
+ path_output_, out_, true, indent);
+ }
+ if (respect_source_used
+ ? target_->source_types_used().Get(SourceFile::SOURCE_C)
+ : bits.used.count(&CSubstitutionCFlagsC)) {
+ WriteOneFlag(kRecursiveWriterKeepDuplicates, target_, &CSubstitutionCFlagsC,
+ has_precompiled_headers, CTool::kCToolCc,
+ &ConfigValues::cflags_c, opts, path_output_, out_, true,
+ indent);
+ }
+ if (respect_source_used
+ ? (target_->source_types_used().Get(SourceFile::SOURCE_CPP) ||
+ target_->source_types_used().Get(SourceFile::SOURCE_MODULEMAP))
+ : bits.used.count(&CSubstitutionCFlagsCc)) {
+ WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
+ &CSubstitutionCFlagsCc, has_precompiled_headers,
+ CTool::kCToolCxx, &ConfigValues::cflags_cc, opts, path_output_,
+ out_, true, indent);
+ }
+ if (respect_source_used
+ ? target_->source_types_used().Get(SourceFile::SOURCE_M)
+ : bits.used.count(&CSubstitutionCFlagsObjC)) {
+ WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
+ &CSubstitutionCFlagsObjC, has_precompiled_headers,
+ CTool::kCToolObjC, &ConfigValues::cflags_objc, opts,
+ path_output_, out_, true, indent);
+ }
+ if (respect_source_used
+ ? target_->source_types_used().Get(SourceFile::SOURCE_MM)
+ : bits.used.count(&CSubstitutionCFlagsObjCc)) {
+ WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
+ &CSubstitutionCFlagsObjCc, has_precompiled_headers,
+ CTool::kCToolObjCxx, &ConfigValues::cflags_objcc, opts,
+ path_output_, out_, true, indent);
+ }
+ if (target_->source_types_used().SwiftSourceUsed() || !respect_source_used) {
+ if (bits.used.count(&CSubstitutionSwiftModuleName)) {
+ if (indent)
+ out_ << " ";
+ out_ << CSubstitutionSwiftModuleName.ninja_name << " = ";
+ EscapeStringToStream(out_, target_->swift_values().module_name(), opts);
+ out_ << std::endl;
+ }
+
+ if (bits.used.count(&CSubstitutionSwiftBridgeHeader)) {
+ if (indent)
+ out_ << " ";
+ out_ << CSubstitutionSwiftBridgeHeader.ninja_name << " = ";
+ if (!target_->swift_values().bridge_header().is_null()) {
+ path_output_.WriteFile(out_, target_->swift_values().bridge_header());
+ } else {
+ out_ << R"("")";
+ }
+ out_ << std::endl;
+ }
+
+ if (bits.used.count(&CSubstitutionSwiftModuleDirs)) {
+ // Uniquify the list of swiftmodule dirs (in case multiple swiftmodules
+ // are generated in the same directory).
+ UniqueVector<SourceDir> swiftmodule_dirs;
+ for (const Target* dep : target_->swift_values().modules())
+ swiftmodule_dirs.push_back(dep->swift_values().module_output_dir());
+
+ if (indent)
+ out_ << " ";
+ out_ << CSubstitutionSwiftModuleDirs.ninja_name << " =";
+ PathOutput swiftmodule_path_output(
+ path_output_.current_dir(),
+ settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
+ IncludeWriter swiftmodule_path_writer(swiftmodule_path_output);
+ for (const SourceDir& swiftmodule_dir : swiftmodule_dirs) {
+ swiftmodule_path_writer(swiftmodule_dir, out_);
+ }
+ out_ << std::endl;
+ }
+
+ WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
+ &CSubstitutionSwiftFlags, false, CTool::kCToolSwift,
+ &ConfigValues::swiftflags, opts, path_output_, out_, true,
+ indent);
+ }
+}
+
+void NinjaTargetWriter::WriteRustCompilerVars(const SubstitutionBits& bits,
+ bool indent,
+ bool always_write) {
+ EscapeOptions opts;
+ opts.mode = ESCAPE_NINJA_COMMAND;
+
+ if (bits.used.count(&kRustSubstitutionRustFlags) || always_write) {
+ WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
+ &kRustSubstitutionRustFlags, false, Tool::kToolNone,
+ &ConfigValues::rustflags, opts, path_output_, out_, true,
+ indent);
+ }
+
+ if (bits.used.count(&kRustSubstitutionRustEnv) || always_write) {
+ WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
+ &kRustSubstitutionRustEnv, false, Tool::kToolNone,
+ &ConfigValues::rustenv, opts, path_output_, out_, true,
+ indent);
+ }
+}
+
std::vector<OutputFile> NinjaTargetWriter::WriteInputDepsStampAndGetDep(
const std::vector<const Target*>& additional_hard_deps,
size_t num_stamp_uses) const {
diff --git a/src/gn/ninja_target_writer.h b/src/gn/ninja_target_writer.h
index 7a339ab..3d38a14 100644
--- a/src/gn/ninja_target_writer.h
+++ b/src/gn/ninja_target_writer.h
@@ -39,6 +39,23 @@
// identified by the given bits will be written.
void WriteSharedVars(const SubstitutionBits& bits);
+ // Writes out the substitution values that are shared between C compiler tools
+ // and action tools. Only the substitutions identified by the given bits will
+ // be written.
+ // If respect_source_used is set, the generated substitution values will
+ // respect the types of source code used; otherwise they will respect the bits
+ // passed in.
+ void WriteCCompilerVars(const SubstitutionBits& bits,
+ bool indent,
+ bool respect_source_used);
+
+ // Writes out the substitution values that are shared between Rust tools
+ // and action tools. Only the substitutions identified by the given bits will
+ // be written, unless 'always_write' is specified.
+ void WriteRustCompilerVars(const SubstitutionBits& bits,
+ bool indent,
+ bool always_write);
+
// Writes to the output stream a stamp rule for input dependencies, and
// returns the file to be appended to source rules that encodes the
// order-only dependencies for the current target.
diff --git a/src/gn/rust_substitution_type.cc b/src/gn/rust_substitution_type.cc
index 69e6cf6..5423d0d 100644
--- a/src/gn/rust_substitution_type.cc
+++ b/src/gn/rust_substitution_type.cc
@@ -42,6 +42,12 @@
type == &kRustSubstitutionSources;
}
+// Rust substitution types which we also make available to action targets.
+bool IsValidRustScriptArgsSubstitution(const Substitution* type) {
+ return type == &kRustSubstitutionRustEnv ||
+ type == &kRustSubstitutionRustFlags;
+}
+
bool IsValidRustLinkerSubstitution(const Substitution* type) {
return IsValidRustSubstitution(type) ||
type == &CSubstitutionLdFlags;
diff --git a/src/gn/rust_substitution_type.h b/src/gn/rust_substitution_type.h
index 36ba953..5a6ac6c 100644
--- a/src/gn/rust_substitution_type.h
+++ b/src/gn/rust_substitution_type.h
@@ -23,6 +23,7 @@
extern const Substitution kRustSubstitutionSources;
bool IsValidRustSubstitution(const Substitution* type);
+bool IsValidRustScriptArgsSubstitution(const Substitution* type);
bool IsValidRustLinkerSubstitution(const Substitution* type);
#endif // TOOLS_GN_RUST_SUBSTITUTION_TYPE_H_
diff --git a/src/gn/substitution_list.cc b/src/gn/substitution_list.cc
index 21311c6..aed66a0 100644
--- a/src/gn/substitution_list.cc
+++ b/src/gn/substitution_list.cc
@@ -49,13 +49,16 @@
SubstitutionList SubstitutionList::MakeForTest(const char* a,
const char* b,
- const char* c) {
+ const char* c,
+ const char* d) {
std::vector<std::string> input_strings;
input_strings.push_back(a);
if (b)
input_strings.push_back(b);
if (c)
input_strings.push_back(c);
+ if (d)
+ input_strings.push_back(d);
Err err;
SubstitutionList result;
diff --git a/src/gn/substitution_list.h b/src/gn/substitution_list.h
index 559b25d..0dbf206 100644
--- a/src/gn/substitution_list.h
+++ b/src/gn/substitution_list.h
@@ -27,7 +27,8 @@
// Makes a SubstitutionList from the given hardcoded patterns.
static SubstitutionList MakeForTest(const char* a,
const char* b = nullptr,
- const char* c = nullptr);
+ const char* c = nullptr,
+ const char* d = nullptr);
const std::vector<SubstitutionPattern>& list() const { return list_; }
diff --git a/src/gn/substitution_type.cc b/src/gn/substitution_type.cc
index 06c21b3..723bf8a 100644
--- a/src/gn/substitution_type.cc
+++ b/src/gn/substitution_type.cc
@@ -163,7 +163,9 @@
}
bool IsValidScriptArgsSubstitution(const Substitution* type) {
- return IsValidSourceSubstitution(type) || type == &SubstitutionRspFileName;
+ return IsValidSourceSubstitution(type) || type == &SubstitutionRspFileName ||
+ IsValidCompilerScriptArgsSubstitution(type) ||
+ IsValidRustScriptArgsSubstitution(type);
}
bool IsValidToolSubstitution(const Substitution* type) {
diff --git a/src/gn/variables.cc b/src/gn/variables.cc
index fba20e0..ca64776 100644
--- a/src/gn/variables.cc
+++ b/src/gn/variables.cc
@@ -561,6 +561,14 @@
to the script. Typically you would use source expansion (see "gn help
source_expansion") to insert the source file names.
+ Args can also expand the substitution patterns corresponding to config
+ variables in the same way that compiler tools (see "gn help tool") do. These
+ allow actions that run compiler or compiler-like tools to access the results
+ of propagating configs through the build graph. For example:
+
+ args = [ "{{defines}}", "{{include_dirs}}", "{{rustenv}}", "--input-file",
+ "{{source}}" ]
+
See also "gn help action" and "gn help action_foreach".
)";