Commit f0bbcdc2 authored by brettw@chromium.org's avatar brettw@chromium.org

Improve GN handling of directory templates.

The previous implementation of the directory-based file templates (like {{source_out_dir}}}) was overly simplistic. It did not allow these templates to be used in the outputs section of a target since the directories were relative to the build directory (it was only useful for actions).

This new implementation changes the expansion of these values depending on where they're used, so they can be absolute when used in the outputs section, but relative to the build directory when used in the args section.

The implementation of outputs was changed to allow the use of these expansions. The previous implementation stored them as SourceFiles which didn't allow the use of directory expansions, since that happens after converting the input to a SourceFile. This new patch stores the outputs as a list of strings to allow this to be late-bound properly (and it turns out that almost all users of this wanted the strings anyway).

Changes the function that checks that files are in the output directory to optionally handle templates. Adds a test for this function.

Fixes a bug in get_target_outputs that it didn't work for copy() targets that contained templates. Fix this and add a test.

BUG=
R=viettrungluu@chromium.org

Review URL: https://codereview.chromium.org/387663003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@282639 0039d316-1c4b-4281-b951-d872f2087c98
parent bfe75748
...@@ -17,10 +17,10 @@ namespace { ...@@ -17,10 +17,10 @@ namespace {
// Returns true if the list of files looks like it might have a {{ }} pattern // Returns true if the list of files looks like it might have a {{ }} pattern
// in it. Used for error checking. // in it. Used for error checking.
bool FileListHasPattern(const Target::FileList& files) { bool StringListHasPattern(const std::vector<std::string>& files) {
for (size_t i = 0; i < files.size(); i++) { for (size_t i = 0; i < files.size(); i++) {
if (files[i].value().find("{{") != std::string::npos && if (files[i].find("{{") != std::string::npos &&
files[i].value().find("}}") != std::string::npos) files[i].find("}}") != std::string::npos)
return true; return true;
} }
return false; return false;
...@@ -108,10 +108,9 @@ void ActionTargetGenerator::FillScriptArgs() { ...@@ -108,10 +108,9 @@ void ActionTargetGenerator::FillScriptArgs() {
if (!value) if (!value)
return; return;
std::vector<std::string> args; if (!ExtractListOfStringValues(*value, &target_->action_values().args(),
if (!ExtractListOfStringValues(*value, &args, err_)) err_))
return; return;
target_->action_values().swap_in_args(&args);
} }
void ActionTargetGenerator::FillDepfile() { void ActionTargetGenerator::FillDepfile() {
...@@ -124,7 +123,7 @@ void ActionTargetGenerator::FillDepfile() { ...@@ -124,7 +123,7 @@ void ActionTargetGenerator::FillDepfile() {
} }
void ActionTargetGenerator::CheckOutputs() { void ActionTargetGenerator::CheckOutputs() {
const Target::FileList& outputs = target_->action_values().outputs(); const std::vector<std::string>& outputs = target_->action_values().outputs();
if (outputs.empty()) { if (outputs.empty()) {
*err_ = Err(function_call_, "Action has no outputs.", *err_ = Err(function_call_, "Action has no outputs.",
"If you have no outputs, the build system can not tell when your\n" "If you have no outputs, the build system can not tell when your\n"
...@@ -134,7 +133,7 @@ void ActionTargetGenerator::CheckOutputs() { ...@@ -134,7 +133,7 @@ void ActionTargetGenerator::CheckOutputs() {
if (output_type_ == Target::ACTION) { if (output_type_ == Target::ACTION) {
// Make sure the outputs for an action have no patterns in them. // Make sure the outputs for an action have no patterns in them.
if (FileListHasPattern(outputs)) { if (StringListHasPattern(outputs)) {
*err_ = Err(function_call_, "Action has patterns in the output.", *err_ = Err(function_call_, "Action has patterns in the output.",
"An action target should have the outputs completely specified. If\n" "An action target should have the outputs completely specified. If\n"
"you want to provide a mapping from source to output, use an\n" "you want to provide a mapping from source to output, use an\n"
...@@ -143,7 +142,7 @@ void ActionTargetGenerator::CheckOutputs() { ...@@ -143,7 +142,7 @@ void ActionTargetGenerator::CheckOutputs() {
} }
} else if (output_type_ == Target::ACTION_FOREACH) { } else if (output_type_ == Target::ACTION_FOREACH) {
// A foreach target should always have a pattern in the outputs. // A foreach target should always have a pattern in the outputs.
if (!FileListHasPattern(outputs)) { if (!StringListHasPattern(outputs)) {
*err_ = Err(function_call_, *err_ = Err(function_call_,
"action_foreach should have a pattern in the output.", "action_foreach should have a pattern in the output.",
"An action_foreach target should have a source expansion pattern in\n" "An action_foreach target should have a source expansion pattern in\n"
......
...@@ -25,12 +25,11 @@ class ActionValues { ...@@ -25,12 +25,11 @@ class ActionValues {
// Arguments to the script. // Arguments to the script.
std::vector<std::string>& args() { return args_; } std::vector<std::string>& args() { return args_; }
const std::vector<std::string>& args() const { return args_; } const std::vector<std::string>& args() const { return args_; }
void swap_in_args(std::vector<std::string>* a) { args_.swap(*a); }
// Files created by the script. // Files created by the script. These are strings rather than SourceFiles
std::vector<SourceFile>& outputs() { return outputs_; } // since they will often contain {{source expansions}}.
const std::vector<SourceFile>& outputs() const { return outputs_; } std::vector<std::string>& outputs() { return outputs_; }
void swap_in_outputs(std::vector<SourceFile>* op) { outputs_.swap(*op); } const std::vector<std::string>& outputs() const { return outputs_; }
// Depfile generated by the script. // Depfile generated by the script.
const SourceFile& depfile() const { return depfile_; } const SourceFile& depfile() const { return depfile_; }
...@@ -40,7 +39,7 @@ class ActionValues { ...@@ -40,7 +39,7 @@ class ActionValues {
private: private:
SourceFile script_; SourceFile script_;
std::vector<std::string> args_; std::vector<std::string> args_;
std::vector<SourceFile> outputs_; std::vector<std::string> outputs_;
SourceFile depfile_; SourceFile depfile_;
DISALLOW_COPY_AND_ASSIGN(ActionValues); DISALLOW_COPY_AND_ASSIGN(ActionValues);
......
...@@ -248,6 +248,25 @@ void PrintFileList(const Target::FileList& files, ...@@ -248,6 +248,25 @@ void PrintFileList(const Target::FileList& files,
OutputString(indent + sorted[i].value() + "\n"); OutputString(indent + sorted[i].value() + "\n");
} }
// This sorts the list.
void PrintStringList(const std::vector<std::string>& strings,
const std::string& header,
bool indent_extra,
bool display_header) {
if (strings.empty())
return;
if (display_header)
OutputString("\n" + header + ":\n");
std::string indent = indent_extra ? " " : " ";
std::vector<std::string> sorted = strings;
std::sort(sorted.begin(), sorted.end());
for (size_t i = 0; i < sorted.size(); i++)
OutputString(indent + sorted[i] + "\n");
}
void PrintSources(const Target* target, bool display_header) { void PrintSources(const Target* target, bool display_header) {
PrintFileList(target->sources(), "sources", false, display_header); PrintFileList(target->sources(), "sources", false, display_header);
} }
...@@ -259,16 +278,17 @@ void PrintInputs(const Target* target, bool display_header) { ...@@ -259,16 +278,17 @@ void PrintInputs(const Target* target, bool display_header) {
void PrintOutputs(const Target* target, bool display_header) { void PrintOutputs(const Target* target, bool display_header) {
if (target->output_type() == Target::ACTION) { if (target->output_type() == Target::ACTION) {
// Just display the outputs directly. // Just display the outputs directly.
PrintFileList(target->action_values().outputs(), "outputs", false, PrintStringList(target->action_values().outputs(), "outputs", false,
display_header); display_header);
} else if (target->output_type() == Target::ACTION_FOREACH) { } else if (target->output_type() == Target::ACTION_FOREACH ||
target->output_type() == Target::COPY_FILES) {
// Display both the output pattern and resolved list. // Display both the output pattern and resolved list.
if (display_header) if (display_header)
OutputString("\noutputs:\n"); OutputString("\noutputs:\n");
// Display the pattern. // Display the pattern.
OutputString(" Output pattern:\n"); OutputString(" Output pattern:\n");
PrintFileList(target->action_values().outputs(), "", true, false); PrintStringList(target->action_values().outputs(), "", true, false);
// Now display what that resolves to given the sources. // Now display what that resolves to given the sources.
OutputString("\n Resolved output file list:\n"); OutputString("\n Resolved output file list:\n");
...@@ -277,11 +297,7 @@ void PrintOutputs(const Target* target, bool display_header) { ...@@ -277,11 +297,7 @@ void PrintOutputs(const Target* target, bool display_header) {
FileTemplate file_template = FileTemplate::GetForTargetOutputs(target); FileTemplate file_template = FileTemplate::GetForTargetOutputs(target);
for (size_t i = 0; i < target->sources().size(); i++) for (size_t i = 0; i < target->sources().size(); i++)
file_template.Apply(target->sources()[i], &output_strings); file_template.Apply(target->sources()[i], &output_strings);
PrintStringList(output_strings, "", true, false);
std::sort(output_strings.begin(), output_strings.end());
for (size_t i = 0; i < output_strings.size(); i++) {
OutputString(" " + output_strings[i] + "\n");
}
} }
} }
......
This diff is collapsed.
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/containers/stack_container.h" #include "base/containers/stack_container.h"
#include "tools/gn/err.h" #include "tools/gn/err.h"
#include "tools/gn/source_dir.h"
#include "tools/gn/value.h" #include "tools/gn/value.h"
struct EscapeOptions; struct EscapeOptions;
...@@ -30,6 +31,11 @@ extern const char kSourceExpansion_Help[]; ...@@ -30,6 +31,11 @@ extern const char kSourceExpansion_Help[];
// substitutions. // substitutions.
class FileTemplate { class FileTemplate {
public: public:
enum OutputStyle {
OUTPUT_ABSOLUTE, // Dirs will be absolute "//foo/bar".
OUTPUT_RELATIVE, // Dirs will be relative to a given directory.
};
struct Subrange { struct Subrange {
// See the help in the .cc file for what these mean. // See the help in the .cc file for what these mean.
enum Type { enum Type {
...@@ -58,9 +64,19 @@ class FileTemplate { ...@@ -58,9 +64,19 @@ class FileTemplate {
// Constructs a template from the given value. On error, the err will be // Constructs a template from the given value. On error, the err will be
// set. In this case you should not use this object. // set. In this case you should not use this object.
FileTemplate(const Settings* settings, const Value& t, Err* err); FileTemplate(const Settings* settings,
FileTemplate(const Settings* settings, const std::vector<std::string>& t); const Value& t,
FileTemplate(const Settings* settings, const std::vector<SourceFile>& t); OutputStyle output_style,
const SourceDir& relative_to,
Err* err);
FileTemplate(const Settings* settings,
const std::vector<std::string>& t,
OutputStyle output_style,
const SourceDir& relative_to);
FileTemplate(const Settings* settings,
const std::vector<SourceFile>& t,
OutputStyle output_style,
const SourceDir& relative_to);
~FileTemplate(); ~FileTemplate();
...@@ -105,7 +121,6 @@ class FileTemplate { ...@@ -105,7 +121,6 @@ class FileTemplate {
// (see GetWithNinjaExpansions). // (see GetWithNinjaExpansions).
void WriteNinjaVariablesForSubstitution( void WriteNinjaVariablesForSubstitution(
std::ostream& out, std::ostream& out,
const Settings* settings,
const SourceFile& source, const SourceFile& source,
const EscapeOptions& escape_options) const; const EscapeOptions& escape_options) const;
...@@ -113,13 +128,18 @@ class FileTemplate { ...@@ -113,13 +128,18 @@ class FileTemplate {
// substitute for the given type. // substitute for the given type.
static const char* GetNinjaVariableNameForType(Subrange::Type type); static const char* GetNinjaVariableNameForType(Subrange::Type type);
// Extracts the given type of substitution from the given source. The source // Extracts the given type of substitution from the given source file.
// should be the file name relative to the output directory. // If output_style is RELATIVE, relative_to indicates the directory that the
// relative directories should be relative to, otherwise it is ignored.
static std::string GetSubstitution(const Settings* settings, static std::string GetSubstitution(const Settings* settings,
const SourceFile& source, const SourceFile& source,
Subrange::Type type); Subrange::Type type,
OutputStyle output_style,
const SourceDir& relative_to);
// Known template types, these include the "{{ }}" // Known template types, these include the "{{ }}".
// IF YOU ADD NEW ONES: If the expansion expands to something inside the
// output directory, also update EnsureStringIsInOutputDir.
static const char kSource[]; static const char kSource[];
static const char kSourceNamePart[]; static const char kSourceNamePart[];
static const char kSourceFilePart[]; static const char kSourceFilePart[];
...@@ -138,6 +158,8 @@ class FileTemplate { ...@@ -138,6 +158,8 @@ class FileTemplate {
void ParseOneTemplateString(const std::string& str); void ParseOneTemplateString(const std::string& str);
const Settings* settings_; const Settings* settings_;
OutputStyle output_style_;
SourceDir relative_to_;
TemplateVector templates_; TemplateVector templates_;
......
...@@ -14,7 +14,8 @@ TEST(FileTemplate, Static) { ...@@ -14,7 +14,8 @@ TEST(FileTemplate, Static) {
std::vector<std::string> templates; std::vector<std::string> templates;
templates.push_back("something_static"); templates.push_back("something_static");
FileTemplate t(setup.settings(), templates); FileTemplate t(setup.settings(), templates,
FileTemplate::OUTPUT_ABSOLUTE, SourceDir("//"));
EXPECT_FALSE(t.has_substitutions()); EXPECT_FALSE(t.has_substitutions());
std::vector<std::string> result; std::vector<std::string> result;
...@@ -29,7 +30,8 @@ TEST(FileTemplate, Typical) { ...@@ -29,7 +30,8 @@ TEST(FileTemplate, Typical) {
std::vector<std::string> templates; std::vector<std::string> templates;
templates.push_back("foo/{{source_name_part}}.cc"); templates.push_back("foo/{{source_name_part}}.cc");
templates.push_back("foo/{{source_name_part}}.h"); templates.push_back("foo/{{source_name_part}}.h");
FileTemplate t(setup.settings(), templates); FileTemplate t(setup.settings(), templates,
FileTemplate::OUTPUT_ABSOLUTE, SourceDir("//"));
EXPECT_TRUE(t.has_substitutions()); EXPECT_TRUE(t.has_substitutions());
std::vector<std::string> result; std::vector<std::string> result;
...@@ -44,7 +46,9 @@ TEST(FileTemplate, Weird) { ...@@ -44,7 +46,9 @@ TEST(FileTemplate, Weird) {
std::vector<std::string> templates; std::vector<std::string> templates;
templates.push_back("{{{source}}{{source}}{{"); templates.push_back("{{{source}}{{source}}{{");
FileTemplate t(setup.settings(), templates); FileTemplate t(setup.settings(), templates,
FileTemplate::OUTPUT_RELATIVE,
setup.settings()->build_settings()->build_dir());
EXPECT_TRUE(t.has_substitutions()); EXPECT_TRUE(t.has_substitutions());
std::vector<std::string> result; std::vector<std::string> result;
...@@ -62,7 +66,9 @@ TEST(FileTemplate, NinjaExpansions) { ...@@ -62,7 +66,9 @@ TEST(FileTemplate, NinjaExpansions) {
templates.push_back("--out=foo bar\"{{source_name_part}}\".o"); templates.push_back("--out=foo bar\"{{source_name_part}}\".o");
templates.push_back(""); // Test empty string. templates.push_back(""); // Test empty string.
FileTemplate t(setup.settings(), templates); FileTemplate t(setup.settings(), templates,
FileTemplate::OUTPUT_RELATIVE,
setup.settings()->build_settings()->build_dir());
std::ostringstream out; std::ostringstream out;
t.WriteWithNinjaExpansions(out); t.WriteWithNinjaExpansions(out);
...@@ -95,13 +101,15 @@ TEST(FileTemplate, NinjaVariables) { ...@@ -95,13 +101,15 @@ TEST(FileTemplate, NinjaVariables) {
templates.push_back("{{source_gen_dir}}"); templates.push_back("{{source_gen_dir}}");
templates.push_back("{{source_out_dir}}"); templates.push_back("{{source_out_dir}}");
FileTemplate t(setup.settings(), templates); FileTemplate t(setup.settings(), templates,
FileTemplate::OUTPUT_RELATIVE,
setup.settings()->build_settings()->build_dir());
std::ostringstream out; std::ostringstream out;
EscapeOptions options; EscapeOptions options;
options.mode = ESCAPE_NINJA_COMMAND; options.mode = ESCAPE_NINJA_COMMAND;
t.WriteNinjaVariablesForSubstitution(out, setup.settings(), t.WriteNinjaVariablesForSubstitution(out, SourceFile("//foo/bar.txt"),
SourceFile("//foo/bar.txt"), options); options);
// Just the variables used above should be written. // Just the variables used above should be written.
EXPECT_EQ( EXPECT_EQ(
...@@ -120,27 +128,56 @@ TEST(FileTemplate, NinjaVariables) { ...@@ -120,27 +128,56 @@ TEST(FileTemplate, NinjaVariables) {
TEST(FileTemplate, Substitutions) { TEST(FileTemplate, Substitutions) {
TestWithScope setup; TestWithScope setup;
#define GetSubst(str, what) \ // Call to get substitutions relative to the build dir.
FileTemplate::GetSubstitution(setup.settings(), \ #define GetRelSubst(str, what) \
SourceFile(str), \ FileTemplate::GetSubstitution( \
FileTemplate::Subrange::what) setup.settings(), \
SourceFile(str), \
FileTemplate::Subrange::what, \
FileTemplate::OUTPUT_RELATIVE, \
setup.settings()->build_settings()->build_dir())
// Call to get absolute directory substitutions.
#define GetAbsSubst(str, what) \
FileTemplate::GetSubstitution( \
setup.settings(), \
SourceFile(str), \
FileTemplate::Subrange::what, \
FileTemplate::OUTPUT_ABSOLUTE, \
SourceDir())
// Try all possible templates with a normal looking string. // Try all possible templates with a normal looking string.
EXPECT_EQ("../../foo/bar/baz.txt", GetSubst("//foo/bar/baz.txt", SOURCE)); EXPECT_EQ("../../foo/bar/baz.txt", GetRelSubst("//foo/bar/baz.txt", SOURCE));
EXPECT_EQ("baz", GetSubst("//foo/bar/baz.txt", NAME_PART)); EXPECT_EQ("//foo/bar/baz.txt", GetAbsSubst("//foo/bar/baz.txt", SOURCE));
EXPECT_EQ("baz.txt", GetSubst("//foo/bar/baz.txt", FILE_PART));
EXPECT_EQ("../../foo/bar", GetSubst("//foo/bar/baz.txt", SOURCE_DIR)); EXPECT_EQ("baz", GetRelSubst("//foo/bar/baz.txt", NAME_PART));
EXPECT_EQ("foo/bar", GetSubst("//foo/bar/baz.txt", ROOT_RELATIVE_DIR)); EXPECT_EQ("baz", GetAbsSubst("//foo/bar/baz.txt", NAME_PART));
EXPECT_EQ("gen/foo/bar", GetSubst("//foo/bar/baz.txt", SOURCE_GEN_DIR));
EXPECT_EQ("obj/foo/bar", GetSubst("//foo/bar/baz.txt", SOURCE_OUT_DIR)); EXPECT_EQ("baz.txt", GetRelSubst("//foo/bar/baz.txt", FILE_PART));
EXPECT_EQ("baz.txt", GetAbsSubst("//foo/bar/baz.txt", FILE_PART));
EXPECT_EQ("../../foo/bar", GetRelSubst("//foo/bar/baz.txt", SOURCE_DIR));
EXPECT_EQ("//foo/bar", GetAbsSubst("//foo/bar/baz.txt", SOURCE_DIR));
EXPECT_EQ("foo/bar", GetRelSubst("//foo/bar/baz.txt", ROOT_RELATIVE_DIR));
EXPECT_EQ("foo/bar", GetAbsSubst("//foo/bar/baz.txt", ROOT_RELATIVE_DIR));
EXPECT_EQ("gen/foo/bar", GetRelSubst("//foo/bar/baz.txt", SOURCE_GEN_DIR));
EXPECT_EQ("//out/Debug/gen/foo/bar",
GetAbsSubst("//foo/bar/baz.txt", SOURCE_GEN_DIR));
EXPECT_EQ("obj/foo/bar", GetRelSubst("//foo/bar/baz.txt", SOURCE_OUT_DIR));
EXPECT_EQ("//out/Debug/obj/foo/bar",
GetAbsSubst("//foo/bar/baz.txt", SOURCE_OUT_DIR));
// Operations on an absolute path. // Operations on an absolute path.
EXPECT_EQ("/baz.txt", GetSubst("/baz.txt", SOURCE)); EXPECT_EQ("/baz.txt", GetRelSubst("/baz.txt", SOURCE));
EXPECT_EQ("/.", GetSubst("/baz.txt", SOURCE_DIR)); EXPECT_EQ("/.", GetRelSubst("/baz.txt", SOURCE_DIR));
EXPECT_EQ("gen", GetSubst("/baz.txt", SOURCE_GEN_DIR)); EXPECT_EQ("gen", GetRelSubst("/baz.txt", SOURCE_GEN_DIR));
EXPECT_EQ("obj", GetSubst("/baz.txt", SOURCE_OUT_DIR)); EXPECT_EQ("obj", GetRelSubst("/baz.txt", SOURCE_OUT_DIR));
EXPECT_EQ(".", GetSubst("//baz.txt", ROOT_RELATIVE_DIR)); EXPECT_EQ(".", GetRelSubst("//baz.txt", ROOT_RELATIVE_DIR));
#undef GetSubst #undef GetAbsSubst
#undef GetRelSubst
} }
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "tools/gn/file_template.h"
#include "tools/gn/location.h" #include "tools/gn/location.h"
#include "tools/gn/settings.h" #include "tools/gn/settings.h"
#include "tools/gn/source_dir.h" #include "tools/gn/source_dir.h"
...@@ -334,23 +335,28 @@ base::StringPiece FindLastDirComponent(const SourceDir& dir) { ...@@ -334,23 +335,28 @@ base::StringPiece FindLastDirComponent(const SourceDir& dir) {
bool EnsureStringIsInOutputDir(const SourceDir& dir, bool EnsureStringIsInOutputDir(const SourceDir& dir,
const std::string& str, const std::string& str,
const Value& originating, const Value& originating,
bool allow_templates,
Err* err) { Err* err) {
// The last char of the dir will be a slash. We don't care if the input ends
// in a slash or not, so just compare up until there.
//
// This check will be wrong for all proper prefixes "e.g. "/output" will // This check will be wrong for all proper prefixes "e.g. "/output" will
// match "/out" but we don't really care since this is just a sanity check. // match "/out" but we don't really care since this is just a sanity check.
const std::string& dir_str = dir.value(); const std::string& dir_str = dir.value();
if (str.compare(0, dir_str.length() - 1, dir_str, 0, dir_str.length() - 1) if (str.compare(0, dir_str.length(), dir_str, 0, dir_str.length()) == 0)
!= 0) { return true; // Output directory is hardcoded.
*err = Err(originating, "File is not inside output directory.",
"The given file should be in the output directory. Normally you would " if (allow_templates) {
"specify\n\"$target_out_dir/foo\" or " // Allow the string to begin with any source expansion inside the output
"\"$target_gen_dir/foo\". I interpreted this as\n\"" // directory.
+ str + "\"."); if (StartsWithASCII(str, FileTemplate::kSourceGenDir, true) ||
return false; StartsWithASCII(str, FileTemplate::kSourceOutDir, true))
return true;
} }
return true;
*err = Err(originating, "File is not inside output directory.",
"The given file should be in the output directory. Normally you would "
"specify\n\"$target_out_dir/foo\" or "
"\"$target_gen_dir/foo\". I interpreted this as\n\""
+ str + "\".");
return false;
} }
bool IsPathAbsolute(const base::StringPiece& path) { bool IsPathAbsolute(const base::StringPiece& path) {
......
...@@ -104,11 +104,16 @@ base::StringPiece FindLastDirComponent(const SourceDir& dir); ...@@ -104,11 +104,16 @@ base::StringPiece FindLastDirComponent(const SourceDir& dir);
// //
// The originating value will be blamed in the error. // The originating value will be blamed in the error.
// //
// Setting allow_templates indicates that the result will be put into a
// FileTemplate and it will allow the strings to be prefixed with expansions
// that would include the output path.
//
// If the file isn't in the dir, returns false and sets the error. Otherwise // If the file isn't in the dir, returns false and sets the error. Otherwise
// returns true and leaves the error untouched. // returns true and leaves the error untouched.
bool EnsureStringIsInOutputDir(const SourceDir& dir, bool EnsureStringIsInOutputDir(const SourceDir& dir,
const std::string& str, const std::string& str,
const Value& originating, const Value& originating,
bool allow_templates,
Err* err); Err* err);
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
......
...@@ -90,6 +90,50 @@ TEST(FilesystemUtils, FindLastDirComponent) { ...@@ -90,6 +90,50 @@ TEST(FilesystemUtils, FindLastDirComponent) {
EXPECT_EQ("bar", FindLastDirComponent(regular2)); EXPECT_EQ("bar", FindLastDirComponent(regular2));
} }
TEST(FilesystemUtils, EnsureStringIsInOutputDir) {
SourceDir output_dir("//out/Debug/");
// Some outside.
Err err;
EXPECT_FALSE(EnsureStringIsInOutputDir(output_dir, "//foo", Value(), false,
&err));
EXPECT_TRUE(err.has_error());
err = Err();
EXPECT_FALSE(EnsureStringIsInOutputDir(output_dir, "//out/Debugit", Value(),
false, &err));
EXPECT_TRUE(err.has_error());
// Some inside.
err = Err();
EXPECT_TRUE(EnsureStringIsInOutputDir(output_dir, "//out/Debug/", Value(),
false, &err));
EXPECT_FALSE(err.has_error());
EXPECT_TRUE(EnsureStringIsInOutputDir(output_dir, "//out/Debug/foo", Value(),
false, &err));
EXPECT_FALSE(err.has_error());
// Pattern but no template expansions are allowed.
EXPECT_FALSE(EnsureStringIsInOutputDir(output_dir, "{{source_gen_dir}}",
Value(), false, &err));
EXPECT_TRUE(err.has_error());
// Pattern with template expansions allowed.
err = Err();
EXPECT_TRUE(EnsureStringIsInOutputDir(output_dir, "{{source_gen_dir}}",
Value(), true, &err));
EXPECT_FALSE(err.has_error());
// Template expansion that doesn't include the absolute directory.
EXPECT_FALSE(EnsureStringIsInOutputDir(output_dir, "{{source}}",
Value(), true, &err));
EXPECT_TRUE(err.has_error());
err = Err();
EXPECT_FALSE(EnsureStringIsInOutputDir(output_dir,
"{{source_root_relative_dir}}",
Value(), true, &err));
EXPECT_TRUE(err.has_error());
}
TEST(FilesystemUtils, IsPathAbsolute) { TEST(FilesystemUtils, IsPathAbsolute) {
EXPECT_TRUE(IsPathAbsolute("/foo/bar")); EXPECT_TRUE(IsPathAbsolute("/foo/bar"));
EXPECT_TRUE(IsPathAbsolute("/")); EXPECT_TRUE(IsPathAbsolute("/"));
......
...@@ -19,19 +19,20 @@ void GetOutputsForTarget(const Settings* settings, ...@@ -19,19 +19,20 @@ void GetOutputsForTarget(const Settings* settings,
const Target* target, const Target* target,
std::vector<std::string>* ret) { std::vector<std::string>* ret) {
switch (target->output_type()) { switch (target->output_type()) {
case Target::ACTION: case Target::ACTION: {
case Target::COPY_FILES: { // Actions: return the outputs specified.
// Actions and copy targets: return the outputs specified. const std::vector<std::string>& outs = target->action_values().outputs();
const std::vector<SourceFile>& outs = target->action_values().outputs();
ret->reserve(outs.size()); ret->reserve(outs.size());
for (size_t i = 0; i < outs.size(); i++) for (size_t i = 0; i < outs.size(); i++)
ret->push_back(outs[i].value()); ret->push_back(outs[i]);
break; break;
} }
case Target::COPY_FILES:
case Target::ACTION_FOREACH: { case Target::ACTION_FOREACH: {
// Action_foreach: return the result of the template in the outputs. // Copy/action_foreach: return the result of the template in the outputs.
FileTemplate file_template(settings, target->action_values().outputs()); FileTemplate file_template(settings, target->action_values().outputs(),
FileTemplate::OUTPUT_ABSOLUTE, SourceDir());
const std::vector<SourceFile>& sources = target->sources(); const std::vector<SourceFile>& sources = target->sources();
for (size_t i = 0; i < sources.size(); i++) for (size_t i = 0; i < sources.size(); i++)
file_template.Apply(sources[i], ret); file_template.Apply(sources[i], ret);
......
...@@ -82,11 +82,26 @@ TEST_F(GetTargetOutputsTest, SourceSet) { ...@@ -82,11 +82,26 @@ TEST_F(GetTargetOutputsTest, SourceSet) {
AssertSingleStringEquals(result, "//out/Debug/obj/foo/bar.stamp"); AssertSingleStringEquals(result, "//out/Debug/obj/foo/bar.stamp");
} }
TEST_F(GetTargetOutputsTest, Copy) {
Target* action = new Target(setup_.settings(), GetLabel("//foo/", "bar"));
action->set_output_type(Target::COPY_FILES);
action->sources().push_back(SourceFile("//file.txt"));
action->action_values().outputs().push_back(
"//out/Debug/{{source_file_part}}.one");
items_.push_back(new scoped_ptr<Item>(action));
Err err;
Value result = GetTargetOutputs("//foo:bar", &err);
ASSERT_FALSE(err.has_error());
AssertSingleStringEquals(result, "//out/Debug/file.txt.one");
}
TEST_F(GetTargetOutputsTest, Action) { TEST_F(GetTargetOutputsTest, Action) {
Target* action = new Target(setup_.settings(), GetLabel("//foo/", "bar")); Target* action = new Target(setup_.settings(), GetLabel("//foo/", "bar"));
action->set_output_type(Target::ACTION); action->set_output_type(Target::ACTION);
action->action_values().outputs().push_back(SourceFile("//output1.txt")); action->action_values().outputs().push_back("//output1.txt");
action->action_values().outputs().push_back(SourceFile("//output2.txt")); action->action_values().outputs().push_back("//output2.txt");
items_.push_back(new scoped_ptr<Item>(action)); items_.push_back(new scoped_ptr<Item>(action));
...@@ -101,9 +116,9 @@ TEST_F(GetTargetOutputsTest, ActionForeach) { ...@@ -101,9 +116,9 @@ TEST_F(GetTargetOutputsTest, ActionForeach) {
action->set_output_type(Target::ACTION_FOREACH); action->set_output_type(Target::ACTION_FOREACH);
action->sources().push_back(SourceFile("//file.txt")); action->sources().push_back(SourceFile("//file.txt"));
action->action_values().outputs().push_back( action->action_values().outputs().push_back(
SourceFile("//out/Debug/{{source_file_part}}.one")); "//out/Debug/{{source_file_part}}.one");
action->action_values().outputs().push_back( action->action_values().outputs().push_back(
SourceFile("//out/Debug/{{source_file_part}}.two")); "//out/Debug/{{source_file_part}}.two");
items_.push_back(new scoped_ptr<Item>(action)); items_.push_back(new scoped_ptr<Item>(action));
......
...@@ -71,7 +71,8 @@ Value RunProcessFileTemplate(Scope* scope, ...@@ -71,7 +71,8 @@ Value RunProcessFileTemplate(Scope* scope,
return Value(); return Value();
} }
FileTemplate file_template(scope->settings(), args[1], err); FileTemplate file_template(scope->settings(), args[1],
FileTemplate::OUTPUT_ABSOLUTE, SourceDir(), err);
if (err->has_error()) if (err->has_error())
return Value(); return Value();
......
...@@ -60,7 +60,7 @@ Value RunWriteFile(Scope* scope, ...@@ -60,7 +60,7 @@ Value RunWriteFile(Scope* scope,
SourceFile source_file = cur_dir.ResolveRelativeFile(args[0].string_value()); SourceFile source_file = cur_dir.ResolveRelativeFile(args[0].string_value());
if (!EnsureStringIsInOutputDir( if (!EnsureStringIsInOutputDir(
scope->settings()->build_settings()->build_dir(), scope->settings()->build_settings()->build_dir(),
source_file.value(), args[0], err)) source_file.value(), args[0], false, err))
return Value(); return Value();
// Compute output. // Compute output.
......
...@@ -23,8 +23,11 @@ NinjaActionTargetWriter::~NinjaActionTargetWriter() { ...@@ -23,8 +23,11 @@ NinjaActionTargetWriter::~NinjaActionTargetWriter() {
} }
void NinjaActionTargetWriter::Run() { void NinjaActionTargetWriter::Run() {
FileTemplate args_template(target_->settings(), FileTemplate args_template(
target_->action_values().args()); target_->settings(),
target_->action_values().args(),
FileTemplate::OUTPUT_RELATIVE,
target_->settings()->build_settings()->build_dir());
std::string custom_rule_name = WriteRuleDefinition(args_template); std::string custom_rule_name = WriteRuleDefinition(args_template);
// Collect our deps to pass as "extra hard dependencies" for input deps. This // Collect our deps to pass as "extra hard dependencies" for input deps. This
...@@ -58,10 +61,11 @@ void NinjaActionTargetWriter::Run() { ...@@ -58,10 +61,11 @@ void NinjaActionTargetWriter::Run() {
// Write a rule that invokes the script once with the outputs as outputs, // Write a rule that invokes the script once with the outputs as outputs,
// and the data as inputs. // and the data as inputs.
out_ << "build"; out_ << "build";
const Target::FileList& outputs = target_->action_values().outputs(); const std::vector<std::string>& outputs =
target_->action_values().outputs();
for (size_t i = 0; i < outputs.size(); i++) { for (size_t i = 0; i < outputs.size(); i++) {
OutputFile output_path( OutputFile output_path(
RemovePrefix(outputs[i].value(), RemovePrefix(outputs[i],
settings_->build_settings()->build_dir().value())); settings_->build_settings()->build_dir().value()));
output_files.push_back(output_path); output_files.push_back(output_path);
out_ << " "; out_ << " ";
...@@ -141,7 +145,7 @@ void NinjaActionTargetWriter::WriteArgsSubstitutions( ...@@ -141,7 +145,7 @@ void NinjaActionTargetWriter::WriteArgsSubstitutions(
template_escape_options.mode = ESCAPE_NINJA_COMMAND; template_escape_options.mode = ESCAPE_NINJA_COMMAND;
args_template.WriteNinjaVariablesForSubstitution( args_template.WriteNinjaVariablesForSubstitution(
out_, target_->settings(), source, template_escape_options); out_, source, template_escape_options);
} }
void NinjaActionTargetWriter::WriteSourceRules( void NinjaActionTargetWriter::WriteSourceRules(
...@@ -149,7 +153,7 @@ void NinjaActionTargetWriter::WriteSourceRules( ...@@ -149,7 +153,7 @@ void NinjaActionTargetWriter::WriteSourceRules(
const std::string& implicit_deps, const std::string& implicit_deps,
const FileTemplate& args_template, const FileTemplate& args_template,
std::vector<OutputFile>* output_files) { std::vector<OutputFile>* output_files) {
FileTemplate output_template(GetOutputTemplate()); FileTemplate output_template = FileTemplate::GetForTargetOutputs(target_);
const Target::FileList& sources = target_->sources(); const Target::FileList& sources = target_->sources();
for (size_t i = 0; i < sources.size(); i++) { for (size_t i = 0; i < sources.size(); i++) {
...@@ -208,7 +212,11 @@ void NinjaActionTargetWriter::WriteOutputFilesForBuildLine( ...@@ -208,7 +212,11 @@ void NinjaActionTargetWriter::WriteOutputFilesForBuildLine(
std::vector<std::string> output_template_result; std::vector<std::string> output_template_result;
output_template.Apply(source, &output_template_result); output_template.Apply(source, &output_template_result);
for (size_t out_i = 0; out_i < output_template_result.size(); out_i++) { for (size_t out_i = 0; out_i < output_template_result.size(); out_i++) {
OutputFile output_path(output_template_result[out_i]); // All output files should be in the build directory, so we can rebase
// them just by trimming the prefix.
OutputFile output_path(
RemovePrefix(output_template_result[out_i],
settings_->build_settings()->build_dir().value()));
output_files->push_back(output_path); output_files->push_back(output_path);
out_ << " "; out_ << " ";
path_output_.WriteFile(out_, output_path); path_output_.WriteFile(out_, output_path);
...@@ -227,5 +235,6 @@ FileTemplate NinjaActionTargetWriter::GetDepfileTemplate() const { ...@@ -227,5 +235,6 @@ FileTemplate NinjaActionTargetWriter::GetDepfileTemplate() const {
RemovePrefix(target_->action_values().depfile().value(), RemovePrefix(target_->action_values().depfile().value(),
settings_->build_settings()->build_dir().value()); settings_->build_settings()->build_dir().value());
template_args.push_back(depfile_relative_to_build_dir); template_args.push_back(depfile_relative_to_build_dir);
return FileTemplate(settings_, template_args); return FileTemplate(settings_, template_args, FileTemplate::OUTPUT_ABSOLUTE,
SourceDir());
} }
...@@ -16,14 +16,14 @@ TEST(NinjaActionTargetWriter, WriteOutputFilesForBuildLine) { ...@@ -16,14 +16,14 @@ TEST(NinjaActionTargetWriter, WriteOutputFilesForBuildLine) {
Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
target.action_values().outputs().push_back( target.action_values().outputs().push_back(
SourceFile("//out/Debug/gen/a b{{source_name_part}}.h")); "//out/Debug/gen/a b{{source_name_part}}.h");
target.action_values().outputs().push_back( target.action_values().outputs().push_back(
SourceFile("//out/Debug/gen/{{source_name_part}}.cc")); "//out/Debug/gen/{{source_name_part}}.cc");
std::ostringstream out; std::ostringstream out;
NinjaActionTargetWriter writer(&target, setup.toolchain(), out); NinjaActionTargetWriter writer(&target, setup.toolchain(), out);
FileTemplate output_template = writer.GetOutputTemplate(); FileTemplate output_template = FileTemplate::GetForTargetOutputs(&target);
SourceFile source("//foo/bar.in"); SourceFile source("//foo/bar.in");
std::vector<OutputFile> output_files; std::vector<OutputFile> output_files;
...@@ -44,7 +44,9 @@ TEST(NinjaActionTargetWriter, WriteArgsSubstitutions) { ...@@ -44,7 +44,9 @@ TEST(NinjaActionTargetWriter, WriteArgsSubstitutions) {
args.push_back("-i"); args.push_back("-i");
args.push_back("{{source}}"); args.push_back("{{source}}");
args.push_back("--out=foo bar{{source_name_part}}.o"); args.push_back("--out=foo bar{{source_name_part}}.o");
FileTemplate args_template(setup.settings(), args); FileTemplate args_template(setup.settings(), args,
FileTemplate::OUTPUT_RELATIVE,
setup.settings()->build_settings()->build_dir());
writer.WriteArgsSubstitutions(SourceFile("//foo/b ar.in"), args_template); writer.WriteArgsSubstitutions(SourceFile("//foo/b ar.in"), args_template);
#if defined(OS_WIN) #if defined(OS_WIN)
...@@ -71,8 +73,7 @@ TEST(NinjaActionTargetWriter, ActionWithSources) { ...@@ -71,8 +73,7 @@ TEST(NinjaActionTargetWriter, ActionWithSources) {
target.sources().push_back(SourceFile("//foo/source.txt")); target.sources().push_back(SourceFile("//foo/source.txt"));
target.inputs().push_back(SourceFile("//foo/included.txt")); target.inputs().push_back(SourceFile("//foo/included.txt"));
target.action_values().outputs().push_back( target.action_values().outputs().push_back("//out/Debug/foo.out");
SourceFile("//out/Debug/foo.out"));
// Posix. // Posix.
{ {
...@@ -156,7 +157,7 @@ TEST(NinjaActionTargetWriter, ForEach) { ...@@ -156,7 +157,7 @@ TEST(NinjaActionTargetWriter, ForEach) {
"--out=foo bar{{source_name_part}}.o"); "--out=foo bar{{source_name_part}}.o");
target.action_values().outputs().push_back( target.action_values().outputs().push_back(
SourceFile("//out/Debug/{{source_name_part}}.out")); "//out/Debug/{{source_name_part}}.out");
target.inputs().push_back(SourceFile("//foo/included.txt")); target.inputs().push_back(SourceFile("//foo/included.txt"));
...@@ -265,7 +266,7 @@ TEST(NinjaActionTargetWriter, ForEachWithDepfile) { ...@@ -265,7 +266,7 @@ TEST(NinjaActionTargetWriter, ForEachWithDepfile) {
"--out=foo bar{{source_name_part}}.o"); "--out=foo bar{{source_name_part}}.o");
target.action_values().outputs().push_back( target.action_values().outputs().push_back(
SourceFile("//out/Debug/{{source_name_part}}.out")); "//out/Debug/{{source_name_part}}.out");
target.inputs().push_back(SourceFile("//foo/included.txt")); target.inputs().push_back(SourceFile("//foo/included.txt"));
......
...@@ -19,7 +19,7 @@ NinjaCopyTargetWriter::~NinjaCopyTargetWriter() { ...@@ -19,7 +19,7 @@ NinjaCopyTargetWriter::~NinjaCopyTargetWriter() {
void NinjaCopyTargetWriter::Run() { void NinjaCopyTargetWriter::Run() {
CHECK(target_->action_values().outputs().size() == 1); CHECK(target_->action_values().outputs().size() == 1);
FileTemplate output_template(GetOutputTemplate()); FileTemplate output_template = FileTemplate::GetForTargetOutputs(target_);
std::vector<OutputFile> output_files; std::vector<OutputFile> output_files;
...@@ -35,8 +35,12 @@ void NinjaCopyTargetWriter::Run() { ...@@ -35,8 +35,12 @@ void NinjaCopyTargetWriter::Run() {
std::vector<std::string> template_result; std::vector<std::string> template_result;
output_template.Apply(input_file, &template_result); output_template.Apply(input_file, &template_result);
CHECK(template_result.size() == 1); CHECK(template_result.size() == 1);
OutputFile output_file(template_result[0]);
// All output files should be in the build directory, so we can rebase
// them just by trimming the prefix.
OutputFile output_file(
RemovePrefix(template_result[0],
settings_->build_settings()->build_dir().value()));
output_files.push_back(output_file); output_files.push_back(output_file);
out_ << "build "; out_ << "build ";
......
...@@ -22,7 +22,7 @@ TEST(NinjaCopyTargetWriter, Run) { ...@@ -22,7 +22,7 @@ TEST(NinjaCopyTargetWriter, Run) {
target.sources().push_back(SourceFile("//foo/input2.txt")); target.sources().push_back(SourceFile("//foo/input2.txt"));
target.action_values().outputs().push_back( target.action_values().outputs().push_back(
SourceFile("//out/Debug/{{source_name_part}}.out")); "//out/Debug/{{source_name_part}}.out");
std::ostringstream out; std::ostringstream out;
NinjaCopyTargetWriter writer(&target, setup.toolchain(), out); NinjaCopyTargetWriter writer(&target, setup.toolchain(), out);
...@@ -56,8 +56,7 @@ TEST(NinjaCopyTargetWriter, ToolchainDeps) { ...@@ -56,8 +56,7 @@ TEST(NinjaCopyTargetWriter, ToolchainDeps) {
target.sources().push_back(SourceFile("//foo/input1.txt")); target.sources().push_back(SourceFile("//foo/input1.txt"));
target.action_values().outputs().push_back( target.action_values().outputs().push_back("//out/Debug/output.out");
SourceFile("//out/Debug/output.out"));
std::ostringstream out; std::ostringstream out;
NinjaCopyTargetWriter writer(&target, setup.toolchain(), out); NinjaCopyTargetWriter writer(&target, setup.toolchain(), out);
......
...@@ -168,15 +168,3 @@ std::string NinjaTargetWriter::WriteInputDepsStampAndGetDep( ...@@ -168,15 +168,3 @@ std::string NinjaTargetWriter::WriteInputDepsStampAndGetDep(
out_ << "\n"; out_ << "\n";
return " | " + stamp_file_string; return " | " + stamp_file_string;
} }
FileTemplate NinjaTargetWriter::GetOutputTemplate() const {
const Target::FileList& outputs = target_->action_values().outputs();
std::vector<std::string> output_template_args;
for (size_t i = 0; i < outputs.size(); i++) {
// All outputs should be in the output dir.
output_template_args.push_back(
RemovePrefix(outputs[i].value(),
settings_->build_settings()->build_dir().value()));
}
return FileTemplate(target_->settings(), output_template_args);
}
...@@ -38,12 +38,6 @@ class NinjaTargetWriter { ...@@ -38,12 +38,6 @@ class NinjaTargetWriter {
std::string WriteInputDepsStampAndGetDep( std::string WriteInputDepsStampAndGetDep(
const std::vector<const Target*>& extra_hard_deps) const; const std::vector<const Target*>& extra_hard_deps) const;
// Returns the FileTemplate constructed from the outputs variable. This is
// like FileTemplate::GetForTargetOutputs except this additionally trims the
// build directory from the front so we can just write the names without
// further processing.
FileTemplate GetOutputTemplate() const;
const Settings* settings_; // Non-owning. const Settings* settings_; // Non-owning.
const Target* target_; // Non-owning. const Target* target_; // Non-owning.
const Toolchain* toolchain_; // Non-owning. const Toolchain* toolchain_; // Non-owning.
......
...@@ -222,20 +222,20 @@ void TargetGenerator::FillOutputs() { ...@@ -222,20 +222,20 @@ void TargetGenerator::FillOutputs() {
if (!value) if (!value)
return; return;
Target::FileList outputs; std::vector<std::string>& outputs = target_->action_values().outputs();
if (!ExtractListOfRelativeFiles(scope_->settings()->build_settings(), *value, if (!ExtractListOfStringValues(*value, &outputs, err_))
scope_->GetSourceDir(), &outputs, err_))
return; return;
// Validate that outputs are in the output dir. // Validate that outputs are in the output dir.
bool allow_templates = target_->output_type() == Target::ACTION_FOREACH ||
target_->output_type() == Target::COPY_FILES;
CHECK(outputs.size() == value->list_value().size()); CHECK(outputs.size() == value->list_value().size());
for (size_t i = 0; i < outputs.size(); i++) { for (size_t i = 0; i < outputs.size(); i++) {
if (!EnsureStringIsInOutputDir( if (!EnsureStringIsInOutputDir(
GetBuildSettings()->build_dir(), GetBuildSettings()->build_dir(),
outputs[i].value(), value->list_value()[i], err_)) outputs[i], value->list_value()[i], allow_templates, err_))
return; return;
} }
target_->action_values().outputs().swap(outputs);
} }
void TargetGenerator::FillGenericConfigs(const char* var_name, void TargetGenerator::FillGenericConfigs(const char* var_name,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment