Support for source_target_relative expansion in GN This can be used to get a path to the source file relative to the target's directory. Review-Url: https://codereview.chromium.org/2387763002 Cr-Original-Commit-Position: refs/heads/master@{#439541} Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src Cr-Mirrored-Commit: 7d88638248b10cec98364cf561f268cbed62587e
diff --git a/tools/gn/action_values.cc b/tools/gn/action_values.cc index a7ce83d..28c4a11 100644 --- a/tools/gn/action_values.cc +++ b/tools/gn/action_values.cc
@@ -22,7 +22,7 @@ target->output_type() == Target::ACTION_FOREACH) { // Copy and foreach applies the outputs to the sources. SubstitutionWriter::ApplyListToSources( - target->settings(), outputs_, target->sources(), result); + target, target->settings(), outputs_, target->sources(), result); } else { // Actions (and anything else that happens to specify an output) just use // the output list with no substitution.
diff --git a/tools/gn/bundle_file_rule.cc b/tools/gn/bundle_file_rule.cc index e23361f..5c4fc27 100644 --- a/tools/gn/bundle_file_rule.cc +++ b/tools/gn/bundle_file_rule.cc
@@ -46,7 +46,7 @@ break; default: output_path.append(SubstitutionWriter::GetSourceSubstitution( - settings, source_file, subrange.type, + target_, target_->settings(), source_file, subrange.type, SubstitutionWriter::OUTPUT_ABSOLUTE, SourceDir())); break; }
diff --git a/tools/gn/desc_builder.cc b/tools/gn/desc_builder.cc index 3e2aaa0..a1a5710 100644 --- a/tools/gn/desc_builder.cc +++ b/tools/gn/desc_builder.cc
@@ -616,8 +616,9 @@ res->SetWithoutPathExpansion("output_patterns", std::move(patterns)); } std::vector<SourceFile> output_files; - SubstitutionWriter::ApplyListToSources(target_->settings(), outputs, - target_->sources(), &output_files); + SubstitutionWriter::ApplyListToSources(target_, target_->settings(), + outputs, target_->sources(), + &output_files); res->SetWithoutPathExpansion(variables::kOutputs, RenderValue(output_files)); } else {
diff --git a/tools/gn/docs/reference.md b/tools/gn/docs/reference.md index 1aa56f1..26ba5c6 100644 --- a/tools/gn/docs/reference.md +++ b/tools/gn/docs/reference.md
@@ -6584,6 +6584,13 @@ build.gn file. "//foo/bar/baz.txt" => "obj/foo/bar" + {{source_target_relative}}\n" + The path to the source file relative to the target's directory. This will + generally be used for replicating the source directory layout in the + output directory. This can only be used in actions and it is an error to + use in process_file_template where there is no "target". + "//foo/bar/baz.txt" => "baz.txt" + ``` ### **(*) Note on directories**
diff --git a/tools/gn/function_process_file_template.cc b/tools/gn/function_process_file_template.cc index 78889c9..28e3e46 100644 --- a/tools/gn/function_process_file_template.cc +++ b/tools/gn/function_process_file_template.cc
@@ -93,8 +93,15 @@ return Value(); } + auto& types = subst.required_types(); + if (std::find(types.begin(), types.end(), + SUBSTITUTION_SOURCE_TARGET_RELATIVE) != types.end()) { + *err = Err(template_arg, "Not a valid substitution type for the function."); + return Value(); + } + SubstitutionWriter::ApplyListToSourcesAsString( - scope->settings(), subst, input_files, &result_files); + nullptr, scope->settings(), subst, input_files, &result_files); // Convert the list of strings to the return Value. Value ret(function, Value::LIST);
diff --git a/tools/gn/ninja_action_target_writer.cc b/tools/gn/ninja_action_target_writer.cc index 1743632..77ee2b4 100644 --- a/tools/gn/ninja_action_target_writer.cc +++ b/tools/gn/ninja_action_target_writer.cc
@@ -184,11 +184,11 @@ // very unusual (normally the substitutions will go in one place or the // other) and the redundant assignment won't bother Ninja. SubstitutionWriter::WriteNinjaVariablesForSource( - settings_, sources[i], + target_, settings_, sources[i], target_->action_values().args().required_types(), args_escape_options, out_); SubstitutionWriter::WriteNinjaVariablesForSource( - settings_, sources[i], + target_, settings_, sources[i], target_->action_values().rsp_file_contents().required_types(), args_escape_options, out_); @@ -206,7 +206,8 @@ size_t first_output_index = output_files->size(); SubstitutionWriter::ApplyListToSourceAsOutputFile( - settings_, target_->action_values().outputs(), source, output_files); + target_, settings_, target_->action_values().outputs(), source, + output_files); for (size_t i = first_output_index; i < output_files->size(); i++) { out_ << " "; @@ -217,5 +218,5 @@ void NinjaActionTargetWriter::WriteDepfile(const SourceFile& source) { path_output_.WriteFile(out_, SubstitutionWriter::ApplyPatternToSourceAsOutputFile( - settings_, target_->action_values().depfile(), source)); + target_, settings_, target_->action_values().depfile(), source)); }
diff --git a/tools/gn/ninja_copy_target_writer.cc b/tools/gn/ninja_copy_target_writer.cc index b0313be..9ffd0fc 100644 --- a/tools/gn/ninja_copy_target_writer.cc +++ b/tools/gn/ninja_copy_target_writer.cc
@@ -103,7 +103,7 @@ for (const auto& input_file : target_->sources()) { OutputFile output_file = SubstitutionWriter::ApplyPatternToSourceAsOutputFile( - target_->settings(), output_subst, input_file); + target_, target_->settings(), output_subst, input_file); output_files->push_back(output_file); out_ << "build ";
diff --git a/tools/gn/substitution_type.cc b/tools/gn/substitution_type.cc index 7730070..c6ea385 100644 --- a/tools/gn/substitution_type.cc +++ b/tools/gn/substitution_type.cc
@@ -21,6 +21,7 @@ "{{source_root_relative_dir}}", // SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR "{{source_gen_dir}}", // SUBSTITUTION_SOURCE_GEN_DIR "{{source_out_dir}}", // SUBSTITUTION_SOURCE_OUT_DIR + "{{source_target_relative}}", // SUBSTITUTION_SOURCE_TARGET_RELATIVE "{{label}}", // SUBSTITUTION_LABEL "{{label_name}}", // SUBSTITUTION_LABEL_NAME @@ -70,6 +71,7 @@ "source_root_relative_dir", // SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR "source_gen_dir", // SUBSTITUTION_SOURCE_GEN_DIR "source_out_dir", // SUBSTITUTION_SOURCE_OUT_DIR + "source_target_relative", // SUBSTITUTION_SOURCE_TARGET_RELATIVE "label", // SUBSTITUTION_LABEL "label_name", // SUBSTITUTION_LABEL_NAME @@ -160,7 +162,8 @@ type == SUBSTITUTION_SOURCE_DIR || type == SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR || type == SUBSTITUTION_SOURCE_GEN_DIR || - type == SUBSTITUTION_SOURCE_OUT_DIR; + type == SUBSTITUTION_SOURCE_OUT_DIR || + type == SUBSTITUTION_SOURCE_TARGET_RELATIVE; } bool IsValidToolSubstitution(SubstitutionType type) {
diff --git a/tools/gn/substitution_type.h b/tools/gn/substitution_type.h index 84524fb..74ab507 100644 --- a/tools/gn/substitution_type.h +++ b/tools/gn/substitution_type.h
@@ -30,6 +30,7 @@ SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR, // {{root_relative_dir}} SUBSTITUTION_SOURCE_GEN_DIR, // {{source_gen_dir}} SUBSTITUTION_SOURCE_OUT_DIR, // {{source_out_dir}} + SUBSTITUTION_SOURCE_TARGET_RELATIVE, // {{source_target_relative}} // Valid for all compiler and linker tools. These depend on the target and // do not vary on a per-file basis.
diff --git a/tools/gn/substitution_writer.cc b/tools/gn/substitution_writer.cc index 4d818cd..7a3534e 100644 --- a/tools/gn/substitution_writer.cc +++ b/tools/gn/substitution_writer.cc
@@ -98,6 +98,13 @@ build.gn file. "//foo/bar/baz.txt" => "obj/foo/bar" + {{source_target_relative}}\n" + The path to the source file relative to the target's directory. This will + generally be used for replicating the source directory layout in the + output directory. This can only be used in actions and it is an error to + use in process_file_template where there is no "target". + "//foo/bar/baz.txt" => "baz.txt" + (*) Note on directories Paths containing directories (except the source_root_relative_dir) will be @@ -196,11 +203,12 @@ // static SourceFile SubstitutionWriter::ApplyPatternToSource( + const Target* target, const Settings* settings, const SubstitutionPattern& pattern, const SourceFile& source) { std::string result_value = ApplyPatternToSourceAsString( - settings, pattern, source); + target, settings, pattern, source); CHECK(!result_value.empty() && result_value[0] == '/') << "The result of the pattern \"" << pattern.AsString() @@ -210,6 +218,7 @@ // static std::string SubstitutionWriter::ApplyPatternToSourceAsString( + const Target* target, const Settings* settings, const SubstitutionPattern& pattern, const SourceFile& source) { @@ -219,7 +228,7 @@ result_value.append(subrange.literal); } else { result_value.append( - GetSourceSubstitution(settings, source, subrange.type, + GetSourceSubstitution(target, settings, source, subrange.type, OUTPUT_ABSOLUTE, SourceDir())); } } @@ -228,78 +237,89 @@ // static OutputFile SubstitutionWriter::ApplyPatternToSourceAsOutputFile( + const Target* target, const Settings* settings, const SubstitutionPattern& pattern, const SourceFile& source) { - SourceFile result_as_source = ApplyPatternToSource(settings, pattern, source); + SourceFile result_as_source = ApplyPatternToSource( + target, settings, pattern, source); return OutputFile(settings->build_settings(), result_as_source); } // static void SubstitutionWriter::ApplyListToSource( + const Target* target, const Settings* settings, const SubstitutionList& list, const SourceFile& source, std::vector<SourceFile>* output) { for (const auto& item : list.list()) - output->push_back(ApplyPatternToSource(settings, item, source)); + output->push_back(ApplyPatternToSource(target, settings, item, source)); } // static void SubstitutionWriter::ApplyListToSourceAsString( + const Target* target, const Settings* settings, const SubstitutionList& list, const SourceFile& source, std::vector<std::string>* output) { for (const auto& item : list.list()) - output->push_back(ApplyPatternToSourceAsString(settings, item, source)); + output->push_back(ApplyPatternToSourceAsString( + target, settings, item, source)); } // static void SubstitutionWriter::ApplyListToSourceAsOutputFile( + const Target* target, const Settings* settings, const SubstitutionList& list, const SourceFile& source, std::vector<OutputFile>* output) { for (const auto& item : list.list()) - output->push_back(ApplyPatternToSourceAsOutputFile(settings, item, source)); + output->push_back(ApplyPatternToSourceAsOutputFile( + target, settings, item, source)); } // static void SubstitutionWriter::ApplyListToSources( + const Target* target, const Settings* settings, const SubstitutionList& list, const std::vector<SourceFile>& sources, std::vector<SourceFile>* output) { output->clear(); for (const auto& source : sources) - ApplyListToSource(settings, list, source, output); + ApplyListToSource(target, settings, list, source, output); } // static void SubstitutionWriter::ApplyListToSourcesAsString( + const Target* target, const Settings* settings, const SubstitutionList& list, const std::vector<SourceFile>& sources, std::vector<std::string>* output) { output->clear(); for (const auto& source : sources) - ApplyListToSourceAsString(settings, list, source, output); + ApplyListToSourceAsString(target, settings, list, source, output); } // static void SubstitutionWriter::ApplyListToSourcesAsOutputFile( + const Target* target, const Settings* settings, const SubstitutionList& list, const std::vector<SourceFile>& sources, std::vector<OutputFile>* output) { output->clear(); for (const auto& source : sources) - ApplyListToSourceAsOutputFile(settings, list, source, output); + ApplyListToSourceAsOutputFile(target, settings, list, source, output); } // static void SubstitutionWriter::WriteNinjaVariablesForSource( + const Target* target, const Settings* settings, const SourceFile& source, const std::vector<SubstitutionType>& types, @@ -314,7 +334,8 @@ out << " " << kSubstitutionNinjaNames[type] << " = "; EscapeStringToStream( out, - GetSourceSubstitution(settings, source, type, OUTPUT_RELATIVE, + GetSourceSubstitution(target, settings, source, type, + OUTPUT_RELATIVE, settings->build_settings()->build_dir()), escape_options); out << std::endl; @@ -324,6 +345,7 @@ // static std::string SubstitutionWriter::GetSourceSubstitution( + const Target* target, const Settings* settings, const SourceFile& source, SubstitutionType type, @@ -366,6 +388,16 @@ BuildDirContext(settings), source.GetDir(), BuildDirType::OBJ)); break; + case SUBSTITUTION_SOURCE_TARGET_RELATIVE: + if (target) { + return RebasePath(source.value(), target->label().dir(), + settings->build_settings()->root_path_utf8()); + } + NOTREACHED() + << "Cannot use substitution " << kSubstitutionNames[type] + << " without target"; + return std::string(); + default: NOTREACHED() << "Unsupported substitution for this function: " @@ -502,7 +534,7 @@ // Fall-through to the source ones. return GetSourceSubstitution( - target->settings(), source, type, OUTPUT_RELATIVE, + target, target->settings(), source, type, OUTPUT_RELATIVE, target->settings()->build_settings()->build_dir()); }
diff --git a/tools/gn/substitution_writer.h b/tools/gn/substitution_writer.h index a450bbb..92f09c4 100644 --- a/tools/gn/substitution_writer.h +++ b/tools/gn/substitution_writer.h
@@ -46,6 +46,10 @@ // The compiler and linker specific substitutions do NOT include the various // cflags, ldflags, libraries, etc. These are written by the ninja target // writer since they depend on traversing the dependency tree. +// +// The methods which take a target as an argument can accept null target +// pointer if there is no target context, in which case the substitutions +// requiring target context will not work. class SubstitutionWriter { public: enum OutputStyle { @@ -81,15 +85,20 @@ // expected to be a SourceFile or an OutputFile, this will CHECK if the // result isn't in the correct directory. The caller should validate this // first (see for example IsFileInOuputDir). + // + // The target can be null (see class comment above). static SourceFile ApplyPatternToSource( + const Target* target, const Settings* settings, const SubstitutionPattern& pattern, const SourceFile& source); static std::string ApplyPatternToSourceAsString( + const Target* target, const Settings* settings, const SubstitutionPattern& pattern, const SourceFile& source); static OutputFile ApplyPatternToSourceAsOutputFile( + const Target* target, const Settings* settings, const SubstitutionPattern& pattern, const SourceFile& source); @@ -98,17 +107,22 @@ // given output vector. It works this way so one can call multiple times to // apply to multiple files and create a list. The result can either be // SourceFiles or OutputFiles. + // + // The target can be null (see class comment above). static void ApplyListToSource( + const Target* target, const Settings* settings, const SubstitutionList& list, const SourceFile& source, std::vector<SourceFile>* output); static void ApplyListToSourceAsString( + const Target* target, const Settings* settings, const SubstitutionList& list, const SourceFile& source, std::vector<std::string>* output); static void ApplyListToSourceAsOutputFile( + const Target* target, const Settings* settings, const SubstitutionList& list, const SourceFile& source, @@ -116,17 +130,22 @@ // Like ApplyListToSource but applies the list to all sources and replaces // rather than appends the output (this produces the complete output). + // + // The target can be null (see class comment above). static void ApplyListToSources( + const Target* target, const Settings* settings, const SubstitutionList& list, const std::vector<SourceFile>& sources, std::vector<SourceFile>* output); static void ApplyListToSourcesAsString( + const Target* target, const Settings* settings, const SubstitutionList& list, const std::vector<SourceFile>& sources, std::vector<std::string>* output); static void ApplyListToSourcesAsOutputFile( + const Target* target, const Settings* settings, const SubstitutionList& list, const std::vector<SourceFile>& sources, @@ -138,7 +157,10 @@ // Ninja files, paths will be relative to the build dir, and no definition // for {{source}} will be written since that maps to Ninja's implicit $in // variable. + // + // The target can be null (see class comment above). static void WriteNinjaVariablesForSource( + const Target* target, const Settings* settings, const SourceFile& source, const std::vector<SubstitutionType>& types, @@ -149,7 +171,10 @@ // given source file. If output_style is OUTPUT_RELATIVE, relative_to // indicates the directory that the relative directories should be relative // to, otherwise it is ignored. + // + // The target can be null (see class comment above). static std::string GetSourceSubstitution( + const Target* target, const Settings* settings, const SourceFile& source, SubstitutionType type,
diff --git a/tools/gn/substitution_writer_unittest.cc b/tools/gn/substitution_writer_unittest.cc index d252c79..d98d4ce 100644 --- a/tools/gn/substitution_writer_unittest.cc +++ b/tools/gn/substitution_writer_unittest.cc
@@ -43,7 +43,7 @@ nullptr, &err)); SourceFile result = SubstitutionWriter::ApplyPatternToSource( - setup.settings(), pattern, SourceFile("//foo/bar/myfile.txt")); + nullptr, setup.settings(), pattern, SourceFile("//foo/bar/myfile.txt")); ASSERT_EQ("//out/Debug/gen/foo/bar/myfile.tmp", result.value()); } @@ -56,7 +56,7 @@ nullptr, &err)); OutputFile result = SubstitutionWriter::ApplyPatternToSourceAsOutputFile( - setup.settings(), pattern, SourceFile("//foo/bar/myfile.txt")); + nullptr, setup.settings(), pattern, SourceFile("//foo/bar/myfile.txt")); ASSERT_EQ("gen/foo/bar/myfile.tmp", result.value()); } @@ -73,7 +73,8 @@ std::ostringstream out; SubstitutionWriter::WriteNinjaVariablesForSource( - setup.settings(), SourceFile("//foo/bar/baz.txt"), types, options, out); + nullptr, setup.settings(), SourceFile("//foo/bar/baz.txt"), types, + options, out); // The "source" should be skipped since that will expand to $in which is // implicit. @@ -103,10 +104,17 @@ TEST(SubstitutionWriter, SourceSubstitutions) { TestWithScope setup; + Err err; + + Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz")); + target.set_output_type(Target::STATIC_LIBRARY); + target.SetToolchain(setup.toolchain()); + ASSERT_TRUE(target.OnResolved(&err)); // Call to get substitutions relative to the build dir. #define GetRelSubst(str, what) \ SubstitutionWriter::GetSourceSubstitution( \ + &target, \ setup.settings(), \ SourceFile(str), \ what, \ @@ -116,6 +124,7 @@ // Call to get absolute directory substitutions. #define GetAbsSubst(str, what) \ SubstitutionWriter::GetSourceSubstitution( \ + &target, \ setup.settings(), \ SourceFile(str), \ what, \ @@ -175,6 +184,11 @@ EXPECT_EQ(".", GetRelSubst("//baz.txt", SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR)); + EXPECT_EQ("baz.txt", + GetRelSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_TARGET_RELATIVE)); + EXPECT_EQ("baz.txt", + GetAbsSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_TARGET_RELATIVE)); + #undef GetAbsSubst #undef GetRelSubst }