From 2fbe101fd45323476bc2f25a7abd29af9da22b5c Mon Sep 17 00:00:00 2001 From: "brettw@chromium.org" Date: Thu, 20 Mar 2014 17:59:15 +0000 Subject: GN: Split custom into action and action_foreach Previously the custom target type implemented two modes: iterate over a list of sources, and run a script once with a set of inputs. This proved a bit confusing since to differentiate these cases you had to either fill in sources or not. This patch splits that apart into two targets: an action for running a script once, and an action_foreach for running a script over a set of inputs. This makes the differentiation explicit and allows more clear error reporting when you mess it up. I also added additional checks. BUG= R=scottmg@chromium.org Review URL: https://codereview.chromium.org/200923007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@258342 0039d316-1c4b-4281-b951-d872f2087c98 --- tools/gn/BUILD.gn | 22 +- tools/gn/action_target_generator.cc | 154 ++++++++++ tools/gn/action_target_generator.h | 37 +++ tools/gn/action_values.cc | 11 + tools/gn/action_values.h | 49 ++++ tools/gn/copy_target_generator.cc | 2 +- tools/gn/file_template.cc | 12 +- tools/gn/function_rebase_path.cc | 4 +- tools/gn/function_template.cc | 2 +- tools/gn/functions.cc | 3 +- tools/gn/functions.h | 24 +- tools/gn/functions_target.cc | 227 +++++++++------ tools/gn/gn.gyp | 24 +- tools/gn/gyp_action_target_writer.cc | 110 +++++++ tools/gn/gyp_action_target_writer.h | 30 ++ tools/gn/gyp_action_target_writer_unittest.cc | 58 ++++ tools/gn/gyp_script_target_writer.cc | 110 ------- tools/gn/gyp_script_target_writer.h | 30 -- tools/gn/gyp_script_target_writer_unittest.cc | 58 ---- tools/gn/gyp_target_writer.cc | 7 +- tools/gn/ninja_action_target_writer.cc | 228 +++++++++++++++ tools/gn/ninja_action_target_writer.h | 83 ++++++ tools/gn/ninja_action_target_writer_unittest.cc | 372 ++++++++++++++++++++++++ tools/gn/ninja_copy_target_writer.cc | 2 +- tools/gn/ninja_copy_target_writer_unittest.cc | 2 +- tools/gn/ninja_helper.cc | 3 +- tools/gn/ninja_script_target_writer.cc | 213 -------------- tools/gn/ninja_script_target_writer.h | 83 ------ tools/gn/ninja_script_target_writer_unittest.cc | 297 ------------------- tools/gn/ninja_target_writer.cc | 9 +- tools/gn/script_target_generator.cc | 95 ------ tools/gn/script_target_generator.h | 31 -- tools/gn/script_values.cc | 11 - tools/gn/script_values.h | 49 ---- tools/gn/target.cc | 6 +- tools/gn/target.h | 11 +- tools/gn/target_generator.cc | 13 +- tools/gn/variables.cc | 67 ++--- 38 files changed, 1391 insertions(+), 1158 deletions(-) create mode 100644 tools/gn/action_target_generator.cc create mode 100644 tools/gn/action_target_generator.h create mode 100644 tools/gn/action_values.cc create mode 100644 tools/gn/action_values.h create mode 100644 tools/gn/gyp_action_target_writer.cc create mode 100644 tools/gn/gyp_action_target_writer.h create mode 100644 tools/gn/gyp_action_target_writer_unittest.cc delete mode 100644 tools/gn/gyp_script_target_writer.cc delete mode 100644 tools/gn/gyp_script_target_writer.h delete mode 100644 tools/gn/gyp_script_target_writer_unittest.cc create mode 100644 tools/gn/ninja_action_target_writer.cc create mode 100644 tools/gn/ninja_action_target_writer.h create mode 100644 tools/gn/ninja_action_target_writer_unittest.cc delete mode 100644 tools/gn/ninja_script_target_writer.cc delete mode 100644 tools/gn/ninja_script_target_writer.h delete mode 100644 tools/gn/ninja_script_target_writer_unittest.cc delete mode 100644 tools/gn/script_target_generator.cc delete mode 100644 tools/gn/script_target_generator.h delete mode 100644 tools/gn/script_values.cc delete mode 100644 tools/gn/script_values.h (limited to 'tools/gn') diff --git a/tools/gn/BUILD.gn b/tools/gn/BUILD.gn index 1c2f162..badfb0b 100644 --- a/tools/gn/BUILD.gn +++ b/tools/gn/BUILD.gn @@ -15,6 +15,10 @@ if (!is_gyp) { static_library("gn_lib") { sources = [ + "action_target_generator.cc", + "action_target_generator.h", + "action_values.cc", + "action_values.h", "args.cc", "args.h", "binary_target_generator.cc", @@ -69,8 +73,8 @@ static_library("gn_lib") { "gyp_binary_target_writer.h", "gyp_helper.cc", "gyp_helper.h", - "gyp_script_target_writer.cc", - "gyp_script_target_writer.h", + "gyp_action_target_writer.cc", + "gyp_action_target_writer.h", "gyp_target_writer.cc", "gyp_target_writer.h", "import_manager.cc", @@ -90,6 +94,8 @@ static_library("gn_lib") { "loader.h", "location.cc", "location.h", + "ninja_action_target_writer.cc", + "ninja_action_target_writer.h", "ninja_binary_target_writer.cc", "ninja_binary_target_writer.h", "ninja_build_writer.cc", @@ -100,8 +106,6 @@ static_library("gn_lib") { "ninja_group_target_writer.h", "ninja_helper.cc", "ninja_helper.h", - "ninja_script_target_writer.cc", - "ninja_script_target_writer.h", "ninja_target_writer.cc", "ninja_target_writer.h", "ninja_toolchain_writer.cc", @@ -125,10 +129,6 @@ static_library("gn_lib") { "scope.h", "scope_per_file_provider.cc", "scope_per_file_provider.h", - "script_target_generator.cc", - "script_target_generator.h", - "script_values.cc", - "script_values.h", "settings.cc", "settings.h", "setup.cc", @@ -185,18 +185,18 @@ test("gn_unittests") { sources = [ "builder_unittest.cc", "escape_unittest.cc", - "file_template_unittest.cc", "filesystem_utils_unittest.cc", + "file_template_unittest.cc", "function_rebase_path_unittest.cc", + "gyp_action_target_writer_unittest.cc", "gyp_binary_target_writer_unittest.cc", - "gyp_script_target_writer_unittest.cc", "input_conversion_unittest.cc", "label_unittest.cc", "loader_unittest.cc", + "ninja_action_target_writer_unittest.cc", "ninja_binary_target_writer_unittest.cc", "ninja_copy_target_writer_unittest.cc", "ninja_helper_unittest.cc", - "ninja_script_target_writer_unittest.cc", "operators_unittest.cc", "parser_unittest.cc", "path_output_unittest.cc", diff --git a/tools/gn/action_target_generator.cc b/tools/gn/action_target_generator.cc new file mode 100644 index 0000000..5f8f959 --- /dev/null +++ b/tools/gn/action_target_generator.cc @@ -0,0 +1,154 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "tools/gn/action_target_generator.h" + +#include "tools/gn/build_settings.h" +#include "tools/gn/err.h" +#include "tools/gn/filesystem_utils.h" +#include "tools/gn/parse_tree.h" +#include "tools/gn/scope.h" +#include "tools/gn/value.h" +#include "tools/gn/value_extractors.h" +#include "tools/gn/variables.h" + +namespace { + +// Returns true if the list of files looks like it might have a {{ }} pattern +// in it. Used for error checking. +bool FileListHasPattern(const Target::FileList& files) { + for (size_t i = 0; i < files.size(); i++) { + if (files[i].value().find("{{") != std::string::npos && + files[i].value().find("}}") != std::string::npos) + return true; + } + return false; +} + +} // namespace + +ActionTargetGenerator::ActionTargetGenerator( + Target* target, + Scope* scope, + const FunctionCallNode* function_call, + Target::OutputType type, + Err* err) + : TargetGenerator(target, scope, function_call, err), + output_type_(type) { +} + +ActionTargetGenerator::~ActionTargetGenerator() { +} + +void ActionTargetGenerator::DoRun() { + target_->set_output_type(output_type_); + + FillExternal(); + if (err_->has_error()) + return; + + FillSources(); + if (err_->has_error()) + return; + if (output_type_ == Target::ACTION_FOREACH && target_->sources().empty()) { + // Foreach rules must always have some sources to have an effect. + *err_ = Err(function_call_, "action_foreach target has no sources.", + "If you don't specify any sources, there is nothing to run your\n" + "script over."); + return; + } + + FillSourcePrereqs(); + if (err_->has_error()) + return; + + FillScript(); + if (err_->has_error()) + return; + + FillScriptArgs(); + if (err_->has_error()) + return; + + FillOutputs(); + if (err_->has_error()) + return; + + FillDepfile(); + if (err_->has_error()) + return; + + CheckOutputs(); + if (err_->has_error()) + return; + + // Action outputs don't depend on the current toolchain so we can skip adding + // that dependency. +} + +void ActionTargetGenerator::FillScript() { + // If this gets called, the target type requires a script, so error out + // if it doesn't have one. + const Value* value = scope_->GetValue(variables::kScript, true); + if (!value) { + *err_ = Err(function_call_, "This target type requires a \"script\"."); + return; + } + if (!value->VerifyTypeIs(Value::STRING, err_)) + return; + + target_->action_values().set_script( + scope_->GetSourceDir().ResolveRelativeFile(value->string_value())); +} + +void ActionTargetGenerator::FillScriptArgs() { + const Value* value = scope_->GetValue(variables::kArgs, true); + if (!value) + return; + + std::vector args; + if (!ExtractListOfStringValues(*value, &args, err_)) + return; + target_->action_values().swap_in_args(&args); +} + +void ActionTargetGenerator::FillDepfile() { + const Value* value = scope_->GetValue(variables::kDepfile, true); + if (!value) + return; + target_->action_values().set_depfile( + scope_->settings()->build_settings()->build_dir().ResolveRelativeFile( + value->string_value())); +} + +void ActionTargetGenerator::CheckOutputs() { + const Target::FileList& outputs = target_->action_values().outputs(); + if (outputs.empty()) { + *err_ = Err(function_call_, "Action has no outputs.", + "If you have no outputs, the build system can not tell when your\n" + "script needs to be run."); + return; + } + + if (output_type_ == Target::ACTION) { + // Make sure the outputs for an action have no patterns in them. + if (FileListHasPattern(outputs)) { + *err_ = Err(function_call_, "Action has patterns in the output.", + "An action target should have the outputs completely specified. If\n" + "you want to provide a mapping from source to output, use an\n" + "\"action_foreach\" target."); + return; + } + } else if (output_type_ == Target::ACTION_FOREACH) { + // A foreach target should always have a pattern in the outputs. + if (!FileListHasPattern(outputs)) { + *err_ = Err(function_call_, + "action_foreach should have a pattern in the output.", + "An action_foreach target should have a source expansion pattern in\n" + "it to map source file to unique output file name. Otherwise, the\n" + "build system can't determine when your script needs to be run."); + return; + } + } +} diff --git a/tools/gn/action_target_generator.h b/tools/gn/action_target_generator.h new file mode 100644 index 0000000..aa81c3d --- /dev/null +++ b/tools/gn/action_target_generator.h @@ -0,0 +1,37 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_GN_ACTION_TARGET_GENERATOR_H_ +#define TOOLS_GN_ACTION_TARGET_GENERATOR_H_ + +#include "base/compiler_specific.h" +#include "tools/gn/target_generator.h" + +// Populates a Target with the values from an action[_foreach] rule. +class ActionTargetGenerator : public TargetGenerator { + public: + ActionTargetGenerator(Target* target, + Scope* scope, + const FunctionCallNode* function_call, + Target::OutputType type, + Err* err); + virtual ~ActionTargetGenerator(); + + protected: + virtual void DoRun() OVERRIDE; + + private: + void FillScript(); + void FillScriptArgs(); + void FillDepfile(); + + // Checks for errors in the outputs variable. + void CheckOutputs(); + + Target::OutputType output_type_; + + DISALLOW_COPY_AND_ASSIGN(ActionTargetGenerator); +}; + +#endif // TOOLS_GN_ACTION_TARGET_GENERATOR_H_ diff --git a/tools/gn/action_values.cc b/tools/gn/action_values.cc new file mode 100644 index 0000000..80bea62 --- /dev/null +++ b/tools/gn/action_values.cc @@ -0,0 +1,11 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "tools/gn/action_values.h" + +ActionValues::ActionValues() { +} + +ActionValues::~ActionValues() { +} diff --git a/tools/gn/action_values.h b/tools/gn/action_values.h new file mode 100644 index 0000000..8b883c7 --- /dev/null +++ b/tools/gn/action_values.h @@ -0,0 +1,49 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_GN_ACTION_VALUES_H_ +#define TOOLS_GN_ACTION_VALUES_H_ + +#include +#include + +#include "base/basictypes.h" +#include "tools/gn/source_file.h" + +// Holds the values (outputs, args, script name, etc.) for either an action or +// an action_foreach target. +class ActionValues { + public: + ActionValues(); + ~ActionValues(); + + // Filename of the script to execute. + const SourceFile& script() const { return script_; } + void set_script(const SourceFile& s) { script_ = s; } + + // Arguments to the script. + std::vector& args() { return args_; } + const std::vector& args() const { return args_; } + void swap_in_args(std::vector* a) { args_.swap(*a); } + + // Files created by the script. + std::vector& outputs() { return outputs_; } + const std::vector& outputs() const { return outputs_; } + void swap_in_outputs(std::vector* op) { outputs_.swap(*op); } + + // Depfile generated by the script. + const SourceFile& depfile() const { return depfile_; } + bool has_depfile() const { return !depfile_.is_null(); } + void set_depfile(const SourceFile& depfile) { depfile_ = depfile; } + + private: + SourceFile script_; + std::vector args_; + std::vector outputs_; + SourceFile depfile_; + + DISALLOW_COPY_AND_ASSIGN(ActionValues); +}; + +#endif // TOOLS_GN_ACTION_VALUES_H_ diff --git a/tools/gn/copy_target_generator.cc b/tools/gn/copy_target_generator.cc index b1b9076..c88b0c4 100644 --- a/tools/gn/copy_target_generator.cc +++ b/tools/gn/copy_target_generator.cc @@ -38,7 +38,7 @@ void CopyTargetGenerator::DoRun() { "You have to specify at least one file to copy in the \"sources\"."); return; } - if (target_->script_values().outputs().size() != 1) { + if (target_->action_values().outputs().size() != 1) { *err_ = Err(function_call_, "Copy command must have exactly one output.", "You must specify exactly one value in the \"outputs\" array for the " "destination of the copy\n(see \"gn help copy\"). If there are " diff --git a/tools/gn/file_template.cc b/tools/gn/file_template.cc index 92a44a9..ec34993 100644 --- a/tools/gn/file_template.cc +++ b/tools/gn/file_template.cc @@ -19,7 +19,7 @@ const char FileTemplate::kSourceFilePart[] = "{{source_file_part}}"; const char kSourceExpansion_Help[] = "How Source Expansion Works\n" "\n" - " Source expansion is used for the custom script and copy target types\n" + " Source expansion is used for the action_foreach and copy target types\n" " to map source file names to output file names or arguments.\n" "\n" " To perform source expansion in the outputs, GN maps every entry in the\n" @@ -34,8 +34,8 @@ const char kSourceExpansion_Help[] = " as a static list of literal file names that do not depend on the\n" " sources.\n" "\n" - " See \"gn help copy\" and \"gn help custom\" for more on how this is\n" - " applied.\n" + " See \"gn help copy\" and \"gn help action_foreach\" for more on how\n" + " this is applied.\n" "\n" "Placeholders\n" "\n" @@ -59,7 +59,7 @@ const char kSourceExpansion_Help[] = "Examples\n" "\n" " Non-varying outputs:\n" - " script(\"hardcoded_outputs\") {\n" + " action(\"hardcoded_outputs\") {\n" " sources = [ \"input1.idl\", \"input2.idl\" ]\n" " outputs = [ \"$target_out_dir/output1.dat\",\n" " \"$target_out_dir/output2.dat\" ]\n" @@ -67,7 +67,7 @@ const char kSourceExpansion_Help[] = " The outputs in this case will be the two literal files given.\n" "\n" " Varying outputs:\n" - " script(\"varying_outputs\") {\n" + " action_foreach(\"varying_outputs\") {\n" " sources = [ \"input1.idl\", \"input2.idl\" ]\n" " outputs = [ \"$target_out_dir/{{source_name_part}}.h\",\n" " \"$target_out_dir/{{source_name_part}}.cc\" ]\n" @@ -96,7 +96,7 @@ FileTemplate::~FileTemplate() { // static FileTemplate FileTemplate::GetForTargetOutputs(const Target* target) { - const Target::FileList& outputs = target->script_values().outputs(); + const Target::FileList& outputs = target->action_values().outputs(); std::vector output_template_args; for (size_t i = 0; i < outputs.size(); i++) output_template_args.push_back(outputs[i].value()); diff --git a/tools/gn/function_rebase_path.cc b/tools/gn/function_rebase_path.cc index aefedd5..a167cda 100644 --- a/tools/gn/function_rebase_path.cc +++ b/tools/gn/function_rebase_path.cc @@ -211,7 +211,7 @@ const char kRebasePath_Help[] = " foo = rebase_path(\"source/myfile.txt\", \".\", \".\", \"to_system\")\n" "\n" " # Typical usage for converting to the build directory for a script.\n" - " custom(\"myscript\") {\n" + " action(\"myscript\") {\n" " # Don't convert sources, GN will automatically convert these to be\n" " # relative to the build directory when it contructs the command\n" " # line for your script.\n" @@ -224,7 +224,7 @@ const char kRebasePath_Help[] = " rebase_path(\"//mything/data/input.dat\", root_build_dir),\n" " \"--rel\",\n" " rebase_path(\"relative_path.txt\", root_build_dir)\n" - " ]\n" + " ] + sources\n" " }\n"; Value RunRebasePath(Scope* scope, diff --git a/tools/gn/function_template.cc b/tools/gn/function_template.cc index 0947305..f726529 100644 --- a/tools/gn/function_template.cc +++ b/tools/gn/function_template.cc @@ -58,7 +58,7 @@ const char kTemplate_Help[] = " \"$target_gen_dir/{{source_name_part}}.h\" ]\n" "\n" " # Intermediate target to compile IDL to C source.\n" - " custom(\"${target_name}_code_gen\") {\n" + " action_foreach(\"${target_name}_code_gen\") {\n" " # The |sources| will be inherited from the surrounding scope so\n" " # we don't need to redefine it.\n" " script = \"foo.py\"\n" diff --git a/tools/gn/functions.cc b/tools/gn/functions.cc index 2443ad9..8d146b2 100644 --- a/tools/gn/functions.cc +++ b/tools/gn/functions.cc @@ -574,11 +574,12 @@ struct FunctionInfoInitializer { #define INSERT_FUNCTION(command) \ map[k##command] = FunctionInfo(&Run##command, k##command##_Help); + INSERT_FUNCTION(Action) + INSERT_FUNCTION(ActionForEach) INSERT_FUNCTION(Assert) INSERT_FUNCTION(Component) INSERT_FUNCTION(Config) INSERT_FUNCTION(Copy) - INSERT_FUNCTION(Custom) INSERT_FUNCTION(DeclareArgs) INSERT_FUNCTION(Defined) INSERT_FUNCTION(ExecScript) diff --git a/tools/gn/functions.h b/tools/gn/functions.h index 60eb202..2a5d0a2 100644 --- a/tools/gn/functions.h +++ b/tools/gn/functions.h @@ -54,6 +54,22 @@ typedef Value (*NoBlockFunction)(Scope* scope, const std::vector& args, Err* err); +extern const char kAction[]; +extern const char kAction_Help[]; +Value RunAction(Scope* scope, + const FunctionCallNode* function, + const std::vector& args, + BlockNode* block, + Err* err); + +extern const char kActionForEach[]; +extern const char kActionForEach_Help[]; +Value RunActionForEach(Scope* scope, + const FunctionCallNode* function, + const std::vector& args, + BlockNode* block, + Err* err); + extern const char kAssert[]; extern const char kAssert_Help[]; Value RunAssert(Scope* scope, @@ -83,14 +99,6 @@ Value RunCopy(const FunctionCallNode* function, Scope* block_scope, Err* err); -extern const char kCustom[]; -extern const char kCustom_Help[]; -Value RunCustom(Scope* scope, - const FunctionCallNode* function, - const std::vector& args, - BlockNode* block, - Err* err); - extern const char kDeclareArgs[]; extern const char kDeclareArgs_Help[]; Value RunDeclareArgs(Scope* scope, diff --git a/tools/gn/functions_target.cc b/tools/gn/functions_target.cc index 8c61f3b..9b47cf74 100644 --- a/tools/gn/functions_target.cc +++ b/tools/gn/functions_target.cc @@ -52,6 +52,148 @@ Value ExecuteGenericTarget(const char* target_type, } // namespace +// action ---------------------------------------------------------------------- + +// Common help paragraph on script runtime execution directories. +#define SCRIPT_EXECUTION_CONTEXT \ + " The script will be executed with the given arguments with the current\n"\ + " directory being that of the root build directory. If you pass files\n"\ + " to your script, see \"gn help to_build_path\" for how to convert\n" \ + " file names to be relative to the build directory (file names in the\n" \ + " sources, outputs, and source_prereqs will be all treated as relative\n" \ + " to the current build file and converted as needed automatically).\n" + +// Common help paragraph on script output directories. +#define SCRIPT_EXECUTION_OUTPUTS \ + " All output files must be inside the output directory of the build.\n" \ + " You would generally use |$target_out_dir| or |$target_gen_dir| to\n" \ + " reference the output or generated intermediate file directories,\n" \ + " respectively.\n" + +const char kAction[] = "action"; +const char kAction_Help[] = + "action: Declare a target that runs a script a single time.\n" + "\n" + " This target type allows you to run a script a single time to produce\n" + " or more output files. If you want to run a script once for each of a\n" + " set of input files, see \"gn help action_foreach\".\n" + "\n" + " In an action the \"sources\" and \"source_prereqs\" are treated the\n" + " same: they're both input dependencies on script execution with no\n" + " special handling. If you want to pass the sources to your script, you\n" + " must do so explicitly by including them in the \"args\".\n" + "\n" + " It is recommended you put inputs to your script in the \"sources\"\n" + " variable, and stuff like other Python files required to run your\n" + " script in the \"source_prereqs\" variable.\n" + "\n" + " You should specify files created by your script by specifying them in\n" + " the \"outputs\".\n" + "\n" + SCRIPT_EXECUTION_CONTEXT + "\n" + "File name handling\n" + "\n" + SCRIPT_EXECUTION_OUTPUTS + "\n" + "Variables\n" + "\n" + " args, data, datadeps, depfile, deps, outputs*, script*,\n" + " source_prereqs, sources\n" + " * = required\n" + "\n" + "Example\n" + "\n" + " action(\"run_this_guy_once\") {\n" + " script = \"doprocessing.py\"\n" + " sources = [ \"my_configuration.txt\" ]\n" + " outputs = [ \"$target_gen_dir/insightful_output.txt\" ]\n" + "\n" + " # Our script imports this Python file so we want to rebuild if it\n" + " # changes.\n" + " source_prereqs = [ \"helper_library.py\" ]\n" + "\n" + " # Note that we have to manually pass the sources to our script if\n" + " # the script needs them as inputs.\n" + " args = [ \"--out\", to_build_path(target_gen_dir) ] + sources\n" + " }\n"; + +Value RunAction(Scope* scope, + const FunctionCallNode* function, + const std::vector& args, + BlockNode* block, + Err* err) { + return ExecuteGenericTarget(functions::kAction, scope, function, args, + block, err); +} + +// action_foreach -------------------------------------------------------------- + +const char kActionForEach[] = "action_foreach"; +const char kActionForEach_Help[] = + "action_foreach: Run a script over a set of input files.\n" + "\n" + " This target type allows you to run a script once-per-file over a set\n" + " of sources. If you want to run a script once that takes many files as\n" + " input, see \"gn help action\".\n" + "\n" + " The script will be run once per file in the \"sources\" variable. The\n" + " \"outputs\" variable should specify one or more files with a source\n" + " expansion pattern in it (see \"gn help source_expansion\"). The output\n" + " file(s) for each script invocation should be unique. Normally you\n" + " use \"{{source_name_part}}\" in each output file.\n" + "\n" + " If your script takes additional data as input, such as a shared\n" + " configuration file or a Python module it uses, those files should be\n" + " listed in the \"source_prereqs\" variable. These files are treated as\n" + " dependencies of each script invocation.\n" + "\n" + SCRIPT_EXECUTION_CONTEXT + "\n" + "File name handling\n" + "\n" + SCRIPT_EXECUTION_OUTPUTS + "\n" + "Variables\n" + "\n" + " args, data, datadeps, depfile, deps, outputs*, script*,\n" + " source_prereqs, sources*\n" + " * = required\n" + "\n" + "Example\n" + "\n" + " # Runs the script over each IDL file. The IDL script will generate\n" + " # both a .cc and a .h file for each input.\n" + " action_foreach(\"my_idl\") {\n" + " script = \"idl_processor.py\"\n" + " sources = [ \"foo.idl\", \"bar.idl\" ]\n" + "\n" + " # Our script reads this file each time, so we need to list is as a\n" + " # dependency so we can rebuild if it changes.\n" + " source_prereqs = [ \"my_configuration.txt\" ]\n" + "\n" + " # Transformation from source file name to output file names.\n" + " outputs = [ \"$target_gen_dir/{{source_name_part}}.h\",\n" + " \"$target_gen_dir/{{source_name_part}}.cc\" ]\n" + "\n" + " # Note that since \"args\" is opaque to GN, if you specify paths\n" + " # here, you will need to convert it to be relative to the build\n" + " # directory using \"to_build_path()\".\n" + " args = [ \"{{source}}\",\n" + " \"-o\",\n" + " to_build_path(relative_target_gen_dir) + \"/\" +\n" + " {{source_name_part}}.h\" ]\n" + " }\n" + "\n"; +Value RunActionForEach(Scope* scope, + const FunctionCallNode* function, + const std::vector& args, + BlockNode* block, + Err* err) { + return ExecuteGenericTarget(functions::kActionForEach, scope, function, args, + block, err); +} + // component ------------------------------------------------------------------- const char kComponent[] = "component"; @@ -167,91 +309,6 @@ Value RunCopy(const FunctionCallNode* function, return Value(); } -// custom ---------------------------------------------------------------------- - -const char kCustom[] = "custom"; -const char kCustom_Help[] = - "custom: Declare a script-generated target.\n" - "\n" - " This target type allows you to run a script over a set of source\n" - " files and generate a set of output files.\n" - "\n" - " The script will be executed with the given arguments with the current\n" - " directory being that of the root build directory. If you pass files\n" - " to your script, see \"gn help to_build_path\" for how to convert\n" - " file names to be relative to the build directory (file names in the\n" - " sources, outputs, and source_prereqs will be all treated as relative\n" - " to the current build file and converted as needed automatically).\n" - "\n" - " There are two modes. The first mode is the \"per-file\" mode where you\n" - " specify a list of sources and the script is run once for each one as a\n" - " build rule. In this case, each file specified in the |outputs|\n" - " variable must be unique when applied to each source file (normally you\n" - " would reference |{{source_name_part}}| from within each one) or the\n" - " build system will get confused about how to build those files. You\n" - " should use the |source_prereqs| variable to list all additional\n" - " dependencies of your script: these will be added as dependencies for\n" - " each build step.\n" - "\n" - " The second mode is when you just want to run a script once rather than\n" - " as a general rule over a set of files. In this case you don't list any\n" - " sources. Dependencies of your script are specified only in the\n" - " |source_prereqs| variable and your |outputs| variable should just list\n" - " all outputs.\n" - "\n" - "File name handling\n" - "\n" - " All output files must be inside the output directory of the build.\n" - " You would generally use |$target_out_dir| or |$target_gen_dir| to\n" - " reference the output or generated intermediate file directories,\n" - " respectively.\n" - "\n" - " You can specify a mapping from source files to output files using\n" - " source expansion (see \"gn help source_expansion\"). The placeholders\n" - " will look like \"{{source}}\", for example, and can appear in\n" - " either the outputs or the args lists.\n" - "\n" - "Variables\n" - "\n" - " args, deps, outputs, script*, source_prereqs, sources\n" - " * = required\n" - "\n" - "Examples\n" - "\n" - " # Runs the script over each IDL file. The IDL script will generate\n" - " # both a .cc and a .h file for each input.\n" - " custom(\"general_rule\") {\n" - " script = \"idl_processor.py\"\n" - " sources = [ \"foo.idl\", \"bar.idl\" ]\n" - " source_prereqs = [ \"my_configuration.txt\" ]\n" - " outputs = [ \"$target_gen_dir/{{source_name_part}}.h\",\n" - " \"$target_gen_dir/{{source_name_part}}.cc\" ]\n" - "\n" - " # Note that since \"args\" is opaque to GN, if you specify paths\n" - " # here, you will need to convert it to be relative to the build\n" - " # directory using \"to_build_path()\".\n" - " args = [ \"{{source}}\",\n" - " \"-o\",\n" - " to_build_path(relative_target_gen_dir) + \"/\" +\n" - " {{source_name_part}}.h\" ]\n" - " }\n" - "\n" - " custom(\"just_run_this_guy_once\") {\n" - " script = \"doprocessing.py\"\n" - " source_prereqs = [ \"my_configuration.txt\" ]\n" - " outputs = [ \"$target_gen_dir/insightful_output.txt\" ]\n" - " args = [ \"--output_dir\", to_build_path(target_gen_dir) ]\n" - " }\n"; - -Value RunCustom(Scope* scope, - const FunctionCallNode* function, - const std::vector& args, - BlockNode* block, - Err* err) { - return ExecuteGenericTarget(functions::kCustom, scope, function, args, - block, err); -} - // executable ------------------------------------------------------------------ const char kExecutable[] = "executable"; diff --git a/tools/gn/gn.gyp b/tools/gn/gn.gyp index 0670ad4..fe305c1 100644 --- a/tools/gn/gn.gyp +++ b/tools/gn/gn.gyp @@ -10,6 +10,10 @@ '../../base/base.gyp:base', ], 'sources': [ + 'action_target_generator.cc', + 'action_target_generator.h', + 'action_values.cc', + 'action_values.h', 'args.cc', 'args.h', 'binary_target_generator.cc', @@ -60,12 +64,12 @@ 'function_write_file.cc', 'group_target_generator.cc', 'group_target_generator.h', + 'gyp_action_target_writer.cc', + 'gyp_action_target_writer.h', 'gyp_binary_target_writer.cc', 'gyp_binary_target_writer.h', 'gyp_helper.cc', 'gyp_helper.h', - 'gyp_script_target_writer.cc', - 'gyp_script_target_writer.h', 'gyp_target_writer.cc', 'gyp_target_writer.h', 'import_manager.cc', @@ -85,6 +89,8 @@ 'loader.h', 'location.cc', 'location.h', + 'ninja_action_target_writer.cc', + 'ninja_action_target_writer.h', 'ninja_binary_target_writer.cc', 'ninja_binary_target_writer.h', 'ninja_build_writer.cc', @@ -95,8 +101,6 @@ 'ninja_group_target_writer.h', 'ninja_helper.cc', 'ninja_helper.h', - 'ninja_script_target_writer.cc', - 'ninja_script_target_writer.h', 'ninja_target_writer.cc', 'ninja_target_writer.h', 'ninja_toolchain_writer.cc', @@ -120,10 +124,6 @@ 'scope.h', 'scope_per_file_provider.cc', 'scope_per_file_provider.h', - 'script_target_generator.cc', - 'script_target_generator.h', - 'script_values.cc', - 'script_values.h', 'settings.cc', 'settings.h', 'setup.cc', @@ -174,18 +174,18 @@ 'sources': [ 'builder_unittest.cc', 'escape_unittest.cc', - 'file_template_unittest.cc', 'filesystem_utils_unittest.cc', + 'file_template_unittest.cc', 'function_rebase_path_unittest.cc', + 'gyp_action_target_writer_unittest.cc', 'gyp_binary_target_writer_unittest.cc', - 'gyp_script_target_writer_unittest.cc', 'input_conversion_unittest.cc', 'label_unittest.cc', 'loader_unittest.cc', + 'ninja_action_target_writer_unittest.cc', 'ninja_binary_target_writer_unittest.cc', - 'ninja_helper_unittest.cc', 'ninja_copy_target_writer_unittest.cc', - 'ninja_script_target_writer_unittest.cc', + 'ninja_helper_unittest.cc', 'operators_unittest.cc', 'parser_unittest.cc', 'path_output_unittest.cc', diff --git a/tools/gn/gyp_action_target_writer.cc b/tools/gn/gyp_action_target_writer.cc new file mode 100644 index 0000000..7df2b9d --- /dev/null +++ b/tools/gn/gyp_action_target_writer.cc @@ -0,0 +1,110 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "tools/gn/gyp_action_target_writer.h" + +#include "tools/gn/builder_record.h" +#include "tools/gn/err.h" +#include "tools/gn/file_template.h" +#include "tools/gn/filesystem_utils.h" +#include "tools/gn/settings.h" +#include "tools/gn/target.h" + +// Write action targets as GYP actions that just invoke Ninja. This allows us +// to not have to worry about duplicating the precise GN action execution +// semantices in GYP for each platform (GYP varies a bit). + +GypActionTargetWriter::GypActionTargetWriter(const TargetGroup& group, + const Toolchain* toolchain, + const SourceDir& gyp_dir, + std::ostream& out) + : GypTargetWriter(group.debug->item()->AsTarget(), toolchain, + gyp_dir, out) { +} + +GypActionTargetWriter::~GypActionTargetWriter() { +} + +void GypActionTargetWriter::Run() { + int indent = 4; + std::string name = helper_.GetNameForTarget(target_); + + // Put the ninja build for this action target in this directory. + SourceDir ninja_dir(GetTargetOutputDir(target_).value() + name + "_ninja/"); + + Indent(indent) << "{\n"; + + Indent(indent + kExtraIndent) << "'target_name': '" << name << "',\n"; + Indent(indent + kExtraIndent) << "'type': 'none',\n"; + Indent(indent + kExtraIndent) << "'actions': [{\n"; + + Indent(indent + kExtraIndent * 2) << "'action_name': '" << name + << " action',\n"; + + Indent(indent + kExtraIndent * 2) << "'action': [\n"; + Indent(indent + kExtraIndent * 3) << "'ninja',\n"; + Indent(indent + kExtraIndent * 3) << "'-C', '"; + path_output_.WriteDir(out_, ninja_dir, PathOutput::DIR_NO_LAST_SLASH); + out_ << "',\n"; + Indent(indent + kExtraIndent * 3) << "'" << name << "',\n"; + Indent(indent + kExtraIndent * 2) << "],\n"; + + WriteActionInputs(indent + kExtraIndent * 2); + WriteActionOutputs(indent + kExtraIndent * 2); + + Indent(indent + kExtraIndent) << "}],\n"; + Indent(indent) << "},\n"; +} + +void GypActionTargetWriter::WriteActionInputs(int indent) { + Indent(indent) << "'inputs': [\n"; + + // Write everything that should be considered an input for dependency + // purposes, which is all sources as well as the prereqs. + const Target::FileList& sources = target_->sources(); + for (size_t i = 0; i < sources.size(); i++) { + Indent(indent + kExtraIndent) << "'"; + path_output_.WriteFile(out_, sources[i]); + out_ << "',\n"; + } + + const Target::FileList& prereqs = target_->source_prereqs(); + for (size_t i = 0; i < prereqs.size(); i++) { + Indent(indent + kExtraIndent) << "'"; + path_output_.WriteFile(out_, prereqs[i]); + out_ << "',\n"; + } + + Indent(indent) << "],\n"; +} + +void GypActionTargetWriter::WriteActionOutputs(int indent) { + Indent(indent) << "'outputs': [\n"; + + const Target::FileList& sources = target_->sources(); + if (sources.empty()) { + // Just write outputs directly if there are no sources. + const Target::FileList& output = target_->action_values().outputs(); + for (size_t output_i = 0; output_i < output.size(); output_i++) { + Indent(indent + kExtraIndent) << "'"; + path_output_.WriteFile(out_, output[output_i]); + out_ << "',\n"; + } + } else { + // There are sources, the outputs should be a template to apply to each. + FileTemplate output_template = FileTemplate::GetForTargetOutputs(target_); + + std::vector output; + for (size_t source_i = 0; source_i < sources.size(); source_i++) { + output_template.ApplyString(sources[source_i].value(), &output); + for (size_t output_i = 0; output_i < output.size(); output_i++) { + Indent(indent + kExtraIndent) << "'"; + path_output_.WriteFile(out_, SourceFile(output[output_i])); + out_ << "',\n"; + } + } + } + + Indent(indent) << "],\n"; +} diff --git a/tools/gn/gyp_action_target_writer.h b/tools/gn/gyp_action_target_writer.h new file mode 100644 index 0000000..1ef3a7c --- /dev/null +++ b/tools/gn/gyp_action_target_writer.h @@ -0,0 +1,30 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_GN_GYP_ACTION_TARGET_WRITER_H_ +#define TOOLS_GN_GYP_ACTION_TARGET_WRITER_H_ + +#include "base/compiler_specific.h" +#include "tools/gn/gyp_target_writer.h" +#include "tools/gn/target.h" +#include "tools/gn/toolchain.h" + +class GypActionTargetWriter : public GypTargetWriter { + public: + GypActionTargetWriter(const TargetGroup& group, + const Toolchain* toolchain, + const SourceDir& gyp_dir, + std::ostream& out); + virtual ~GypActionTargetWriter(); + + virtual void Run() OVERRIDE; + + private: + void WriteActionInputs(int indent); + void WriteActionOutputs(int indent); + + DISALLOW_COPY_AND_ASSIGN(GypActionTargetWriter); +}; + +#endif // TOOLS_GN_GYP_ACTION_TARGET_WRITER_H_ diff --git a/tools/gn/gyp_action_target_writer_unittest.cc b/tools/gn/gyp_action_target_writer_unittest.cc new file mode 100644 index 0000000..14c2930 --- /dev/null +++ b/tools/gn/gyp_action_target_writer_unittest.cc @@ -0,0 +1,58 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "testing/gtest/include/gtest/gtest.h" +#include "tools/gn/builder_record.h" +#include "tools/gn/gyp_action_target_writer.h" +#include "tools/gn/test_with_scope.h" + +TEST(GypActionTargetWriter, Run) { + TestWithScope setup; + setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); + scoped_ptr target( + new Target(setup.settings(), Label(SourceDir("//foo/"), "bar"))); + target->set_output_type(Target::ACTION); + + target->sources().push_back(SourceFile("//foo/input1.txt")); + target->sources().push_back(SourceFile("//foo/input2.txt")); + + target->action_values().outputs().push_back( + SourceFile("//out/Debug/{{source_file_part}}.out")); + + BuilderRecord record(BuilderRecord::ITEM_TARGET, target->label()); + record.set_item(target.PassAs()); + GypTargetWriter::TargetGroup group; + group.debug = &record; + + setup.settings()->set_target_os(Settings::WIN); + + std::ostringstream out; + GypActionTargetWriter writer(group, setup.toolchain(), + SourceDir("//out/gn_gyp/"), out); + writer.Run(); + + const char expected[] = + " {\n" + " 'target_name': 'bar',\n" + " 'type': 'none',\n" + " 'actions': [{\n" + " 'action_name': 'bar action',\n" + " 'action': [\n" + " 'ninja',\n" + " '-C', '../../out/Debug/obj/foo/bar_ninja',\n" + " 'bar',\n" + " ],\n" + " 'inputs': [\n" + " '../../foo/input1.txt',\n" + " '../../foo/input2.txt',\n" + " ],\n" + " 'outputs': [\n" + " '../../out/Debug/input1.txt.out',\n" + " '../../out/Debug/input2.txt.out',\n" + " ],\n" + " }],\n" + " },\n"; + std::string out_str = out.str(); + EXPECT_EQ(expected, out_str); +} diff --git a/tools/gn/gyp_script_target_writer.cc b/tools/gn/gyp_script_target_writer.cc deleted file mode 100644 index 472a23b..0000000 --- a/tools/gn/gyp_script_target_writer.cc +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "tools/gn/gyp_script_target_writer.h" - -#include "tools/gn/builder_record.h" -#include "tools/gn/err.h" -#include "tools/gn/file_template.h" -#include "tools/gn/filesystem_utils.h" -#include "tools/gn/settings.h" -#include "tools/gn/target.h" - -// Write script targets as GYP actions that just invoke Ninja. This allows us -// to not have to worry about duplicating the precise GN script execution -// semantices in GYP for each platform (GYP varies a bit). - -GypScriptTargetWriter::GypScriptTargetWriter(const TargetGroup& group, - const Toolchain* toolchain, - const SourceDir& gyp_dir, - std::ostream& out) - : GypTargetWriter(group.debug->item()->AsTarget(), toolchain, - gyp_dir, out) { -} - -GypScriptTargetWriter::~GypScriptTargetWriter() { -} - -void GypScriptTargetWriter::Run() { - int indent = 4; - std::string name = helper_.GetNameForTarget(target_); - - // Put the ninja build for this script target in this directory. - SourceDir ninja_dir(GetTargetOutputDir(target_).value() + name + "_ninja/"); - - Indent(indent) << "{\n"; - - Indent(indent + kExtraIndent) << "'target_name': '" << name << "',\n"; - Indent(indent + kExtraIndent) << "'type': 'none',\n"; - Indent(indent + kExtraIndent) << "'actions': [{\n"; - - Indent(indent + kExtraIndent * 2) << "'action_name': '" << name - << " action',\n"; - - Indent(indent + kExtraIndent * 2) << "'action': [\n"; - Indent(indent + kExtraIndent * 3) << "'ninja',\n"; - Indent(indent + kExtraIndent * 3) << "'-C', '"; - path_output_.WriteDir(out_, ninja_dir, PathOutput::DIR_NO_LAST_SLASH); - out_ << "',\n"; - Indent(indent + kExtraIndent * 3) << "'" << name << "',\n"; - Indent(indent + kExtraIndent * 2) << "],\n"; - - WriteActionInputs(indent + kExtraIndent * 2); - WriteActionOutputs(indent + kExtraIndent * 2); - - Indent(indent + kExtraIndent) << "}],\n"; - Indent(indent) << "},\n"; -} - -void GypScriptTargetWriter::WriteActionInputs(int indent) { - Indent(indent) << "'inputs': [\n"; - - // Write everything that should be considered an input for dependency - // purposes, which is all sources as well as the prereqs. - const Target::FileList& sources = target_->sources(); - for (size_t i = 0; i < sources.size(); i++) { - Indent(indent + kExtraIndent) << "'"; - path_output_.WriteFile(out_, sources[i]); - out_ << "',\n"; - } - - const Target::FileList& prereqs = target_->source_prereqs(); - for (size_t i = 0; i < prereqs.size(); i++) { - Indent(indent + kExtraIndent) << "'"; - path_output_.WriteFile(out_, prereqs[i]); - out_ << "',\n"; - } - - Indent(indent) << "],\n"; -} - -void GypScriptTargetWriter::WriteActionOutputs(int indent) { - Indent(indent) << "'outputs': [\n"; - - const Target::FileList& sources = target_->sources(); - if (sources.empty()) { - // Just write outputs directly if there are no sources. - const Target::FileList& output = target_->script_values().outputs(); - for (size_t output_i = 0; output_i < output.size(); output_i++) { - Indent(indent + kExtraIndent) << "'"; - path_output_.WriteFile(out_, output[output_i]); - out_ << "',\n"; - } - } else { - // There are sources, the outputs should be a template to apply to each. - FileTemplate output_template = FileTemplate::GetForTargetOutputs(target_); - - std::vector output; - for (size_t source_i = 0; source_i < sources.size(); source_i++) { - output_template.ApplyString(sources[source_i].value(), &output); - for (size_t output_i = 0; output_i < output.size(); output_i++) { - Indent(indent + kExtraIndent) << "'"; - path_output_.WriteFile(out_, SourceFile(output[output_i])); - out_ << "',\n"; - } - } - } - - Indent(indent) << "],\n"; -} diff --git a/tools/gn/gyp_script_target_writer.h b/tools/gn/gyp_script_target_writer.h deleted file mode 100644 index 9f277f1..0000000 --- a/tools/gn/gyp_script_target_writer.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef TOOLS_GN_GYP_SCRIPT_TARGET_WRITER_H_ -#define TOOLS_GN_GYP_SCRIPT_TARGET_WRITER_H_ - -#include "base/compiler_specific.h" -#include "tools/gn/gyp_target_writer.h" -#include "tools/gn/target.h" -#include "tools/gn/toolchain.h" - -class GypScriptTargetWriter : public GypTargetWriter { - public: - GypScriptTargetWriter(const TargetGroup& group, - const Toolchain* toolchain, - const SourceDir& gyp_dir, - std::ostream& out); - virtual ~GypScriptTargetWriter(); - - virtual void Run() OVERRIDE; - - private: - void WriteActionInputs(int indent); - void WriteActionOutputs(int indent); - - DISALLOW_COPY_AND_ASSIGN(GypScriptTargetWriter); -}; - -#endif // TOOLS_GN_GYP_SCRIPT_TARGET_WRITER_H_ diff --git a/tools/gn/gyp_script_target_writer_unittest.cc b/tools/gn/gyp_script_target_writer_unittest.cc deleted file mode 100644 index ed07a5f..0000000 --- a/tools/gn/gyp_script_target_writer_unittest.cc +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "testing/gtest/include/gtest/gtest.h" -#include "tools/gn/builder_record.h" -#include "tools/gn/gyp_script_target_writer.h" -#include "tools/gn/test_with_scope.h" - -TEST(GypScriptTargetWriter, Run) { - TestWithScope setup; - setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); - scoped_ptr target( - new Target(setup.settings(), Label(SourceDir("//foo/"), "bar"))); - target->set_output_type(Target::CUSTOM); - - target->sources().push_back(SourceFile("//foo/input1.txt")); - target->sources().push_back(SourceFile("//foo/input2.txt")); - - target->script_values().outputs().push_back( - SourceFile("//out/Debug/{{source_file_part}}.out")); - - BuilderRecord record(BuilderRecord::ITEM_TARGET, target->label()); - record.set_item(target.PassAs()); - GypTargetWriter::TargetGroup group; - group.debug = &record; - - setup.settings()->set_target_os(Settings::WIN); - - std::ostringstream out; - GypScriptTargetWriter writer(group, setup.toolchain(), - SourceDir("//out/gn_gyp/"), out); - writer.Run(); - - const char expected[] = - " {\n" - " 'target_name': 'bar',\n" - " 'type': 'none',\n" - " 'actions': [{\n" - " 'action_name': 'bar action',\n" - " 'action': [\n" - " 'ninja',\n" - " '-C', '../../out/Debug/obj/foo/bar_ninja',\n" - " 'bar',\n" - " ],\n" - " 'inputs': [\n" - " '../../foo/input1.txt',\n" - " '../../foo/input2.txt',\n" - " ],\n" - " 'outputs': [\n" - " '../../out/Debug/input1.txt.out',\n" - " '../../out/Debug/input2.txt.out',\n" - " ],\n" - " }],\n" - " },\n"; - std::string out_str = out.str(); - EXPECT_EQ(expected, out_str); -} diff --git a/tools/gn/gyp_target_writer.cc b/tools/gn/gyp_target_writer.cc index 0f362d4..789b6b0 100644 --- a/tools/gn/gyp_target_writer.cc +++ b/tools/gn/gyp_target_writer.cc @@ -11,8 +11,8 @@ #include "tools/gn/build_settings.h" #include "tools/gn/builder_record.h" #include "tools/gn/filesystem_utils.h" +#include "tools/gn/gyp_action_target_writer.h" #include "tools/gn/gyp_binary_target_writer.h" -#include "tools/gn/gyp_script_target_writer.h" #include "tools/gn/scheduler.h" #include "tools/gn/settings.h" #include "tools/gn/target.h" @@ -77,8 +77,9 @@ void GypTargetWriter::WriteFile(const SourceFile& gyp_file, switch (cur->output_type()) { case Target::COPY_FILES: break; // TODO(brettw) - case Target::CUSTOM: { - GypScriptTargetWriter writer(targets[i], debug_toolchain, + case Target::ACTION: + case Target::ACTION_FOREACH: { + GypActionTargetWriter writer(targets[i], debug_toolchain, gyp_file.GetDir(), file); writer.Run(); break; diff --git a/tools/gn/ninja_action_target_writer.cc b/tools/gn/ninja_action_target_writer.cc new file mode 100644 index 0000000..fef58ac --- /dev/null +++ b/tools/gn/ninja_action_target_writer.cc @@ -0,0 +1,228 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "tools/gn/ninja_action_target_writer.h" + +#include "base/strings/string_util.h" +#include "tools/gn/err.h" +#include "tools/gn/file_template.h" +#include "tools/gn/string_utils.h" +#include "tools/gn/target.h" + +NinjaActionTargetWriter::NinjaActionTargetWriter(const Target* target, + const Toolchain* toolchain, + std::ostream& out) + : NinjaTargetWriter(target, toolchain, out), + path_output_no_escaping_( + target->settings()->build_settings()->build_dir(), + ESCAPE_NONE, false) { +} + +NinjaActionTargetWriter::~NinjaActionTargetWriter() { +} + +void NinjaActionTargetWriter::Run() { + FileTemplate args_template(target_->action_values().args()); + std::string custom_rule_name = WriteRuleDefinition(args_template); + std::string implicit_deps = GetSourcesImplicitDeps(); + + // Collects all output files for writing below. + std::vector output_files; + + if (target_->output_type() == Target::ACTION_FOREACH) { + // Write separate build lines for each input source file. + WriteSourceRules(custom_rule_name, implicit_deps, args_template, + &output_files); + } else { + DCHECK(target_->output_type() == Target::ACTION); + + // Write a rule that invokes the script once with the outputs as outputs, + // and the data as inputs. + out_ << "build"; + if (target_->action_values().has_depfile()) { + out_ << " "; + WriteDepfile(SourceFile()); + } + const Target::FileList& outputs = target_->action_values().outputs(); + for (size_t i = 0; i < outputs.size(); i++) { + OutputFile output_path( + RemovePrefix(outputs[i].value(), + settings_->build_settings()->build_dir().value())); + output_files.push_back(output_path); + out_ << " "; + path_output_.WriteFile(out_, output_path); + } + + out_ << ": " << custom_rule_name << implicit_deps; + + // In the case of running the script once, we allow you to write the input + // dependencies in both sources and source_prereqs. source_prereqs are + // already in the implicit deps written above, but the sources aren't + // (since treating the sources this was is unique to an action). + const Target::FileList& sources = target_->sources(); + for (size_t i = 0; i < sources.size(); i++) { + out_ << " "; + path_output_.WriteFile(out_, sources[i]); + } + out_ << std::endl; + + if (target_->action_values().has_depfile()) { + out_ << " depfile = "; + WriteDepfile(SourceFile()); + out_ << std::endl; + } + } + out_ << std::endl; + + WriteStamp(output_files); +} + +std::string NinjaActionTargetWriter::WriteRuleDefinition( + const FileTemplate& args_template) { + // Make a unique name for this rule. + // + // Use a unique name for the response file when there are multiple build + // steps so that they don't stomp on each other. When there are no sources, + // there will be only one invocation so we can use a simple name. + std::string target_label = target_->label().GetUserVisibleName(true); + std::string custom_rule_name(target_label); + base::ReplaceChars(custom_rule_name, ":/()", "_", &custom_rule_name); + custom_rule_name.append("_rule"); + + if (settings_->IsWin()) { + // Send through gyp-win-tool and use a response file. + std::string rspfile = custom_rule_name; + if (has_sources()) + rspfile += ".$unique_name"; + rspfile += ".rsp"; + + out_ << "rule " << custom_rule_name << std::endl; + out_ << " command = "; + path_output_.WriteFile(out_, settings_->build_settings()->python_path()); + // TODO(brettw) this hardcodes "environment.x86" which is something that + // the Chrome Windows toolchain writes. We should have a way to invoke + // python without requiring this gyp_win_tool thing. + out_ << " gyp-win-tool action-wrapper environment.x86 " << rspfile + << std::endl; + out_ << " description = ACTION " << target_label << std::endl; + out_ << " restat = 1" << std::endl; + out_ << " rspfile = " << rspfile << std::endl; + + // The build command goes in the rsp file. + out_ << " rspfile_content = "; + path_output_.WriteFile(out_, settings_->build_settings()->python_path()); + out_ << " "; + path_output_.WriteFile(out_, target_->action_values().script()); + args_template.WriteWithNinjaExpansions(out_); + out_ << std::endl; + } else { + // Posix can execute Python directly. + out_ << "rule " << custom_rule_name << std::endl; + out_ << " command = "; + path_output_.WriteFile(out_, settings_->build_settings()->python_path()); + out_ << " "; + path_output_.WriteFile(out_, target_->action_values().script()); + args_template.WriteWithNinjaExpansions(out_); + out_ << std::endl; + out_ << " description = ACTION " << target_label << std::endl; + out_ << " restat = 1" << std::endl; + } + + out_ << std::endl; + return custom_rule_name; +} + +void NinjaActionTargetWriter::WriteArgsSubstitutions( + const SourceFile& source, + const FileTemplate& args_template) { + std::ostringstream source_file_stream; + path_output_no_escaping_.WriteFile(source_file_stream, source); + + EscapeOptions template_escape_options; + template_escape_options.mode = ESCAPE_NINJA_SHELL; + template_escape_options.inhibit_quoting = true; + + args_template.WriteNinjaVariablesForSubstitution( + out_, source_file_stream.str(), template_escape_options); +} + +void NinjaActionTargetWriter::WriteSourceRules( + const std::string& custom_rule_name, + const std::string& implicit_deps, + const FileTemplate& args_template, + std::vector* output_files) { + FileTemplate output_template(GetOutputTemplate()); + + const Target::FileList& sources = target_->sources(); + for (size_t i = 0; i < sources.size(); i++) { + out_ << "build"; + WriteOutputFilesForBuildLine(output_template, sources[i], output_files); + + out_ << ": " << custom_rule_name << " "; + path_output_.WriteFile(out_, sources[i]); + out_ << implicit_deps << std::endl; + + // Windows needs a unique ID for the response file. + if (target_->settings()->IsWin()) + out_ << " unique_name = " << i << std::endl; + + if (args_template.has_substitutions()) + WriteArgsSubstitutions(sources[i], args_template); + + if (target_->action_values().has_depfile()) { + out_ << " depfile = "; + WriteDepfile(sources[i]); + out_ << std::endl; + } + } +} + +void NinjaActionTargetWriter::WriteStamp( + const std::vector& output_files) { + out_ << "build "; + path_output_.WriteFile(out_, helper_.GetTargetOutputFile(target_)); + out_ << ": " + << helper_.GetRulePrefix(target_->settings()) + << "stamp"; + for (size_t i = 0; i < output_files.size(); i++) { + out_ << " "; + path_output_.WriteFile(out_, output_files[i]); + } + out_ << std::endl; +} + +void NinjaActionTargetWriter::WriteOutputFilesForBuildLine( + const FileTemplate& output_template, + const SourceFile& source, + std::vector* output_files) { + // If there is a depfile specified we need to list it as the first output as + // that is what ninja will expect the depfile to refer to itself as. + if (target_->action_values().has_depfile()) { + out_ << " "; + WriteDepfile(source); + } + std::vector output_template_result; + output_template.ApplyString(source.value(), &output_template_result); + for (size_t out_i = 0; out_i < output_template_result.size(); out_i++) { + OutputFile output_path(output_template_result[out_i]); + output_files->push_back(output_path); + out_ << " "; + path_output_.WriteFile(out_, output_path); + } +} + +void NinjaActionTargetWriter::WriteDepfile(const SourceFile& source) { + std::vector result; + GetDepfileTemplate().ApplyString(source.value(), &result); + path_output_.WriteFile(out_, OutputFile(result[0])); +} + +FileTemplate NinjaActionTargetWriter::GetDepfileTemplate() const { + std::vector template_args; + std::string depfile_relative_to_build_dir = + RemovePrefix(target_->action_values().depfile().value(), + settings_->build_settings()->build_dir().value()); + template_args.push_back(depfile_relative_to_build_dir); + return FileTemplate(template_args); +} diff --git a/tools/gn/ninja_action_target_writer.h b/tools/gn/ninja_action_target_writer.h new file mode 100644 index 0000000..e5ece40 --- /dev/null +++ b/tools/gn/ninja_action_target_writer.h @@ -0,0 +1,83 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_GN_NINJA_ACTION_TARGET_WRITER_H_ +#define TOOLS_GN_NINJA_ACTION_TARGET_WRITER_H_ + +#include + +#include "base/compiler_specific.h" +#include "base/gtest_prod_util.h" +#include "tools/gn/ninja_target_writer.h" + +class FileTemplate; +class OutputFile; + +// Writes a .ninja file for a action target type. +class NinjaActionTargetWriter : public NinjaTargetWriter { + public: + NinjaActionTargetWriter(const Target* target, + const Toolchain* toolchain, + std::ostream& out); + virtual ~NinjaActionTargetWriter(); + + virtual void Run() OVERRIDE; + + private: + FRIEND_TEST_ALL_PREFIXES(NinjaActionTargetWriter, + WriteOutputFilesForBuildLine); + FRIEND_TEST_ALL_PREFIXES(NinjaActionTargetWriter, + WriteOutputFilesForBuildLineWithDepfile); + FRIEND_TEST_ALL_PREFIXES(NinjaActionTargetWriter, + WriteArgsSubstitutions); + + bool has_sources() const { return !target_->sources().empty(); } + + // Writes the Ninja rule for invoking the script. + // + // Returns the name of the custom rule generated. This will be based on the + // target name, and will include the string "$unique_name" if there are + // multiple inputs. + std::string WriteRuleDefinition(const FileTemplate& args_template); + + // Writes the rules for compiling each source, writing all output files + // to the given vector. + // + // implicit_deps is a precomputed string of all ninja files that are common + // to each build step, it starts with a "|" if it's nonempty. + void WriteSourceRules(const std::string& custom_rule_name, + const std::string& implicit_deps, + const FileTemplate& args_template, + std::vector* output_files); + + // Writes the Ninja variables that expand the substitutions required by the + // arguments for the given source file. + void WriteArgsSubstitutions(const SourceFile& source, + const FileTemplate& args_template); + + // Writes the .stamp rule that names this target and collects all invocations + // of the script into one thing that other targets can depend on. + void WriteStamp(const std::vector& output_files); + + // Writes the output files generated by the output template for the given + // source file. This will start with a space and will not include a newline. + // Appends the output files to the given vector. + void WriteOutputFilesForBuildLine(const FileTemplate& output_template, + const SourceFile& source, + std::vector* output_files); + + void WriteDepfile(const SourceFile& source); + + // Returns the FileTemplate for the depfile variable. + FileTemplate GetDepfileTemplate() const; + + // Path output writer that doesn't do any escaping or quoting. It does, + // however, convert slashes. Used for + // computing intermediate strings. + PathOutput path_output_no_escaping_; + + DISALLOW_COPY_AND_ASSIGN(NinjaActionTargetWriter); +}; + +#endif // TOOLS_GN_NINJA_ACTION_TARGET_WRITER_H_ diff --git a/tools/gn/ninja_action_target_writer_unittest.cc b/tools/gn/ninja_action_target_writer_unittest.cc new file mode 100644 index 0000000..c1aa6f4 --- /dev/null +++ b/tools/gn/ninja_action_target_writer_unittest.cc @@ -0,0 +1,372 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "tools/gn/file_template.h" +#include "tools/gn/ninja_action_target_writer.h" +#include "tools/gn/test_with_scope.h" + +TEST(NinjaActionTargetWriter, WriteOutputFilesForBuildLine) { + TestWithScope setup; + setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); + Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); + + target.action_values().outputs().push_back( + SourceFile("//out/Debug/gen/a b{{source_name_part}}.h")); + target.action_values().outputs().push_back( + SourceFile("//out/Debug/gen/{{source_name_part}}.cc")); + + std::ostringstream out; + NinjaActionTargetWriter writer(&target, setup.toolchain(), out); + + FileTemplate output_template = writer.GetOutputTemplate(); + + SourceFile source("//foo/bar.in"); + std::vector output_files; + writer.WriteOutputFilesForBuildLine(output_template, source, &output_files); + + std::string out_str = out.str(); +#if defined(OS_WIN) + std::replace(out_str.begin(), out_str.end(), '\\', '/'); +#endif + EXPECT_EQ(" gen/a$ bbar.h gen/bar.cc", out_str); +} + +TEST(NinjaActionTargetWriter, WriteOutputFilesForBuildLineWithDepfile) { + TestWithScope setup; + setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); + Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); + + target.action_values().set_depfile( + SourceFile("//out/Debug/gen/{{source_name_part}}.d")); + target.action_values().outputs().push_back( + SourceFile("//out/Debug/gen/{{source_name_part}}.h")); + target.action_values().outputs().push_back( + SourceFile("//out/Debug/gen/{{source_name_part}}.cc")); + + std::ostringstream out; + NinjaActionTargetWriter writer(&target, setup.toolchain(), out); + + FileTemplate output_template = writer.GetOutputTemplate(); + + SourceFile source("//foo/bar.in"); + std::vector output_files; + writer.WriteOutputFilesForBuildLine(output_template, source, &output_files); + + std::string out_str = out.str(); +#if defined(OS_WIN) + std::replace(out_str.begin(), out_str.end(), '\\', '/'); +#endif + EXPECT_EQ(" gen/bar.d gen/bar.h gen/bar.cc", out_str); +} + +TEST(NinjaActionTargetWriter, WriteArgsSubstitutions) { + TestWithScope setup; + setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); + Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); + + std::ostringstream out; + NinjaActionTargetWriter writer(&target, setup.toolchain(), out); + + std::vector args; + args.push_back("-i"); + args.push_back("{{source}}"); + args.push_back("--out=foo bar{{source_name_part}}.o"); + FileTemplate args_template(args); + + writer.WriteArgsSubstitutions(SourceFile("//foo/b ar.in"), args_template); + + std::string out_str = out.str(); +#if defined(OS_WIN) + std::replace(out_str.begin(), out_str.end(), '\\', '/'); +#endif + EXPECT_EQ(" source = ../../foo/b$ ar.in\n source_name_part = b$ ar\n", + out_str); +} + +// Makes sure that we write sources as input dependencies for actions with +// both sources and source_prereqs (ACTION_FOREACH treats the sources +// differently). +TEST(NinjaActionTargetWriter, ActionWithSources) { + TestWithScope setup; + setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); + Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); + target.set_output_type(Target::ACTION); + + target.action_values().set_script(SourceFile("//foo/script.py")); + + target.sources().push_back(SourceFile("//foo/source.txt")); + target.source_prereqs().push_back(SourceFile("//foo/included.txt")); + + target.action_values().outputs().push_back( + SourceFile("//out/Debug/foo.out")); + + // Posix. + { + setup.settings()->set_target_os(Settings::LINUX); + setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL( + "/usr/bin/python"))); + + std::ostringstream out; + NinjaActionTargetWriter writer(&target, setup.toolchain(), out); + writer.Run(); + + const char expected_linux[] = + "rule __foo_bar___rule\n" + " command = /usr/bin/python ../../foo/script.py\n" + " description = ACTION //foo:bar()\n" + " restat = 1" + "\n" + "\n" + "build foo.out: __foo_bar___rule | ../../foo/included.txt ../../foo/source.txt\n" + "\n" + "build obj/foo/bar.stamp: stamp foo.out\n"; + std::string out_str = out.str(); +#if defined(OS_WIN) + std::replace(out_str.begin(), out_str.end(), '\\', '/'); +#endif + EXPECT_EQ(expected_linux, out_str); + } + + // Windows. + { + // Note: we use forward slashes here so that the output will be the same on + // Linux and Windows. + setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL( + "C:/python/python.exe"))); + setup.settings()->set_target_os(Settings::WIN); + + std::ostringstream out; + NinjaActionTargetWriter writer(&target, setup.toolchain(), out); + writer.Run(); + + // TODO(brettw) I think we'll need to worry about backslashes here + // depending if we're on actual Windows or Linux pretending to be Windows. + const char expected_win[] = + "rule __foo_bar___rule\n" + " command = C:/python/python.exe gyp-win-tool action-wrapper environment.x86 __foo_bar___rule.$unique_name.rsp\n" + " description = ACTION //foo:bar()\n" + " restat = 1\n" + " rspfile = __foo_bar___rule.$unique_name.rsp\n" + " rspfile_content = C:/python/python.exe ../../foo/script.py\n" + "\n" + "build foo.out: __foo_bar___rule | ../../foo/included.txt ../../foo/source.txt\n" + "\n" + "build obj/foo/bar.stamp: stamp foo.out\n"; + std::string out_str = out.str(); +#if defined(OS_WIN) + std::replace(out_str.begin(), out_str.end(), '\\', '/'); +#endif + EXPECT_EQ(expected_win, out_str); + } +} + +TEST(NinjaActionTargetWriter, ForEach) { + TestWithScope setup; + setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); + Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); + target.set_output_type(Target::ACTION_FOREACH); + + target.sources().push_back(SourceFile("//foo/input1.txt")); + target.sources().push_back(SourceFile("//foo/input2.txt")); + + target.action_values().set_script(SourceFile("//foo/script.py")); + + target.action_values().args().push_back("-i"); + target.action_values().args().push_back("{{source}}"); + target.action_values().args().push_back( + "--out=foo bar{{source_name_part}}.o"); + + target.action_values().outputs().push_back( + SourceFile("//out/Debug/{{source_name_part}}.out")); + + target.source_prereqs().push_back(SourceFile("//foo/included.txt")); + + // Posix. + { + setup.settings()->set_target_os(Settings::LINUX); + setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL( + "/usr/bin/python"))); + + std::ostringstream out; + NinjaActionTargetWriter writer(&target, setup.toolchain(), out); + writer.Run(); + + const char expected_linux[] = + "rule __foo_bar___rule\n" + " command = /usr/bin/python ../../foo/script.py -i ${source} " + "\"--out=foo$ bar${source_name_part}.o\"\n" + " description = ACTION //foo:bar()\n" + " restat = 1\n" + "\n" + "build input1.out: __foo_bar___rule ../../foo/input1.txt | " + "../../foo/included.txt\n" + " source = ../../foo/input1.txt\n" + " source_name_part = input1\n" + "build input2.out: __foo_bar___rule ../../foo/input2.txt | " + "../../foo/included.txt\n" + " source = ../../foo/input2.txt\n" + " source_name_part = input2\n" + "\n" + "build obj/foo/bar.stamp: stamp input1.out input2.out\n"; + + std::string out_str = out.str(); +#if defined(OS_WIN) + std::replace(out_str.begin(), out_str.end(), '\\', '/'); +#endif + EXPECT_EQ(expected_linux, out_str); + } + + // Windows. + { + // Note: we use forward slashes here so that the output will be the same on + // Linux and Windows. + setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL( + "C:/python/python.exe"))); + setup.settings()->set_target_os(Settings::WIN); + + std::ostringstream out; + NinjaActionTargetWriter writer(&target, setup.toolchain(), out); + writer.Run(); + + // TODO(brettw) I think we'll need to worry about backslashes here + // depending if we're on actual Windows or Linux pretending to be Windows. + const char expected_win[] = + "rule __foo_bar___rule\n" + " command = C:/python/python.exe gyp-win-tool action-wrapper " + "environment.x86 __foo_bar___rule.$unique_name.rsp\n" + " description = ACTION //foo:bar()\n" + " restat = 1\n" + " rspfile = __foo_bar___rule.$unique_name.rsp\n" + " rspfile_content = C:/python/python.exe ../../foo/script.py -i " + "${source} \"--out=foo$ bar${source_name_part}.o\"\n" + "\n" + "build input1.out: __foo_bar___rule ../../foo/input1.txt | " + "../../foo/included.txt\n" + " unique_name = 0\n" + " source = ../../foo/input1.txt\n" + " source_name_part = input1\n" + "build input2.out: __foo_bar___rule ../../foo/input2.txt | " + "../../foo/included.txt\n" + " unique_name = 1\n" + " source = ../../foo/input2.txt\n" + " source_name_part = input2\n" + "\n" + "build obj/foo/bar.stamp: stamp input1.out input2.out\n"; + std::string out_str = out.str(); +#if defined(OS_WIN) + std::replace(out_str.begin(), out_str.end(), '\\', '/'); +#endif + EXPECT_EQ(expected_win, out_str); + } +} + +TEST(NinjaActionTargetWriter, ForEachWithDepfile) { + TestWithScope setup; + setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); + Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); + target.set_output_type(Target::ACTION_FOREACH); + + target.sources().push_back(SourceFile("//foo/input1.txt")); + target.sources().push_back(SourceFile("//foo/input2.txt")); + + target.action_values().set_script(SourceFile("//foo/script.py")); + target.action_values().set_depfile( + SourceFile("//out/Debug/gen/{{source_name_part}}.d")); + + target.action_values().args().push_back("-i"); + target.action_values().args().push_back("{{source}}"); + target.action_values().args().push_back( + "--out=foo bar{{source_name_part}}.o"); + + target.action_values().outputs().push_back( + SourceFile("//out/Debug/{{source_name_part}}.out")); + + target.source_prereqs().push_back(SourceFile("//foo/included.txt")); + + // Posix. + { + setup.settings()->set_target_os(Settings::LINUX); + setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL( + "/usr/bin/python"))); + + std::ostringstream out; + NinjaActionTargetWriter writer(&target, setup.toolchain(), out); + writer.Run(); + + const char expected_linux[] = + "rule __foo_bar___rule\n" + " command = /usr/bin/python ../../foo/script.py -i ${source} " + "\"--out=foo$ bar${source_name_part}.o\"\n" + " description = ACTION //foo:bar()\n" + " restat = 1\n" + "\n" + "build gen/input1.d input1.out: __foo_bar___rule ../../foo/input1.txt" + " | ../../foo/included.txt\n" + " source = ../../foo/input1.txt\n" + " source_name_part = input1\n" + " depfile = gen/input1.d\n" + "build gen/input2.d input2.out: __foo_bar___rule ../../foo/input2.txt" + " | ../../foo/included.txt\n" + " source = ../../foo/input2.txt\n" + " source_name_part = input2\n" + " depfile = gen/input2.d\n" + "\n" + "build obj/foo/bar.stamp: stamp input1.out input2.out\n"; + + std::string out_str = out.str(); +#if defined(OS_WIN) + std::replace(out_str.begin(), out_str.end(), '\\', '/'); +#endif + EXPECT_EQ(expected_linux, out_str); + } + + // Windows. + { + // Note: we use forward slashes here so that the output will be the same on + // Linux and Windows. + setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL( + "C:/python/python.exe"))); + setup.settings()->set_target_os(Settings::WIN); + + std::ostringstream out; + NinjaActionTargetWriter writer(&target, setup.toolchain(), out); + writer.Run(); + + // TODO(brettw) I think we'll need to worry about backslashes here + // depending if we're on actual Windows or Linux pretending to be Windows. + const char expected_win[] = + "rule __foo_bar___rule\n" + " command = C:/python/python.exe gyp-win-tool action-wrapper " + "environment.x86 __foo_bar___rule.$unique_name.rsp\n" + " description = ACTION //foo:bar()\n" + " restat = 1\n" + " rspfile = __foo_bar___rule.$unique_name.rsp\n" + " rspfile_content = C:/python/python.exe ../../foo/script.py -i " + "${source} \"--out=foo$ bar${source_name_part}.o\"\n" + "\n" + "build gen/input1.d input1.out: __foo_bar___rule ../../foo/input1.txt" + " | ../../foo/included.txt\n" + " unique_name = 0\n" + " source = ../../foo/input1.txt\n" + " source_name_part = input1\n" + " depfile = gen/input1.d\n" + "build gen/input2.d input2.out: __foo_bar___rule ../../foo/input2.txt" + " | ../../foo/included.txt\n" + " unique_name = 1\n" + " source = ../../foo/input2.txt\n" + " source_name_part = input2\n" + " depfile = gen/input2.d\n" + "\n" + "build obj/foo/bar.stamp: stamp input1.out input2.out\n"; + std::string out_str = out.str(); +#if defined(OS_WIN) + std::replace(out_str.begin(), out_str.end(), '\\', '/'); +#endif + EXPECT_EQ(expected_win, out_str); + } +} diff --git a/tools/gn/ninja_copy_target_writer.cc b/tools/gn/ninja_copy_target_writer.cc index 89b3e3f..9ab88e4 100644 --- a/tools/gn/ninja_copy_target_writer.cc +++ b/tools/gn/ninja_copy_target_writer.cc @@ -18,7 +18,7 @@ NinjaCopyTargetWriter::~NinjaCopyTargetWriter() { } void NinjaCopyTargetWriter::Run() { - CHECK(target_->script_values().outputs().size() == 1); + CHECK(target_->action_values().outputs().size() == 1); FileTemplate output_template(GetOutputTemplate()); std::vector output_files; diff --git a/tools/gn/ninja_copy_target_writer_unittest.cc b/tools/gn/ninja_copy_target_writer_unittest.cc index 5642d9d..b86508b 100644 --- a/tools/gn/ninja_copy_target_writer_unittest.cc +++ b/tools/gn/ninja_copy_target_writer_unittest.cc @@ -19,7 +19,7 @@ TEST(NinjaCopyTargetWriter, Run) { target.sources().push_back(SourceFile("//foo/input1.txt")); target.sources().push_back(SourceFile("//foo/input2.txt")); - target.script_values().outputs().push_back( + target.action_values().outputs().push_back( SourceFile("//out/Debug/{{source_name_part}}.out")); // Posix. diff --git a/tools/gn/ninja_helper.cc b/tools/gn/ninja_helper.cc index eddab213..849ec4d 100644 --- a/tools/gn/ninja_helper.cc +++ b/tools/gn/ninja_helper.cc @@ -135,7 +135,8 @@ OutputFile NinjaHelper::GetTargetOutputFile(const Target* target) const { if (target->output_type() == Target::GROUP || target->output_type() == Target::SOURCE_SET || target->output_type() == Target::COPY_FILES || - target->output_type() == Target::CUSTOM) { + target->output_type() == Target::ACTION || + target->output_type() == Target::ACTION_FOREACH) { extension = "stamp"; } else { extension = GetExtensionForOutputType(target->output_type(), diff --git a/tools/gn/ninja_script_target_writer.cc b/tools/gn/ninja_script_target_writer.cc deleted file mode 100644 index f2004e3..0000000 --- a/tools/gn/ninja_script_target_writer.cc +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "tools/gn/ninja_script_target_writer.h" - -#include "base/strings/string_util.h" -#include "tools/gn/err.h" -#include "tools/gn/file_template.h" -#include "tools/gn/string_utils.h" -#include "tools/gn/target.h" - -NinjaScriptTargetWriter::NinjaScriptTargetWriter(const Target* target, - const Toolchain* toolchain, - std::ostream& out) - : NinjaTargetWriter(target, toolchain, out), - path_output_no_escaping_( - target->settings()->build_settings()->build_dir(), - ESCAPE_NONE, false) { -} - -NinjaScriptTargetWriter::~NinjaScriptTargetWriter() { -} - -void NinjaScriptTargetWriter::Run() { - FileTemplate args_template(target_->script_values().args()); - std::string custom_rule_name = WriteRuleDefinition(args_template); - std::string implicit_deps = GetSourcesImplicitDeps(); - - // Collects all output files for writing below. - std::vector output_files; - - if (has_sources()) { - // Write separate build lines for each input source file. - WriteSourceRules(custom_rule_name, implicit_deps, args_template, - &output_files); - } else { - // No sources, write a rule that invokes the script once with the - // outputs as outputs, and the data as inputs. - out_ << "build"; - if (target_->script_values().has_depfile()) { - out_ << " "; - WriteDepfile(SourceFile()); - } - const Target::FileList& outputs = target_->script_values().outputs(); - for (size_t i = 0; i < outputs.size(); i++) { - OutputFile output_path( - RemovePrefix(outputs[i].value(), - settings_->build_settings()->build_dir().value())); - output_files.push_back(output_path); - out_ << " "; - path_output_.WriteFile(out_, output_path); - } - out_ << ": " << custom_rule_name << implicit_deps << std::endl; - if (target_->script_values().has_depfile()) { - out_ << " depfile = "; - WriteDepfile(SourceFile()); - out_ << std::endl; - } - } - out_ << std::endl; - - WriteStamp(output_files); -} - -std::string NinjaScriptTargetWriter::WriteRuleDefinition( - const FileTemplate& args_template) { - // Make a unique name for this rule. - // - // Use a unique name for the response file when there are multiple build - // steps so that they don't stomp on each other. When there are no sources, - // there will be only one invocation so we can use a simple name. - std::string target_label = target_->label().GetUserVisibleName(true); - std::string custom_rule_name(target_label); - base::ReplaceChars(custom_rule_name, ":/()", "_", &custom_rule_name); - custom_rule_name.append("_rule"); - - if (settings_->IsWin()) { - // Send through gyp-win-tool and use a response file. - std::string rspfile = custom_rule_name; - if (has_sources()) - rspfile += ".$unique_name"; - rspfile += ".rsp"; - - out_ << "rule " << custom_rule_name << std::endl; - out_ << " command = "; - path_output_.WriteFile(out_, settings_->build_settings()->python_path()); - // TODO(brettw) this hardcodes "environment.x86" which is something that - // the Chrome Windows toolchain writes. We should have a way to invoke - // python without requiring this gyp_win_tool thing. - out_ << " gyp-win-tool action-wrapper environment.x86 " << rspfile - << std::endl; - out_ << " description = CUSTOM " << target_label << std::endl; - out_ << " restat = 1" << std::endl; - out_ << " rspfile = " << rspfile << std::endl; - - // The build command goes in the rsp file. - out_ << " rspfile_content = "; - path_output_.WriteFile(out_, settings_->build_settings()->python_path()); - out_ << " "; - path_output_.WriteFile(out_, target_->script_values().script()); - args_template.WriteWithNinjaExpansions(out_); - out_ << std::endl; - } else { - // Posix can execute Python directly. - out_ << "rule " << custom_rule_name << std::endl; - out_ << " command = "; - path_output_.WriteFile(out_, settings_->build_settings()->python_path()); - out_ << " "; - path_output_.WriteFile(out_, target_->script_values().script()); - args_template.WriteWithNinjaExpansions(out_); - out_ << std::endl; - out_ << " description = CUSTOM " << target_label << std::endl; - out_ << " restat = 1" << std::endl; - } - - out_ << std::endl; - return custom_rule_name; -} - -void NinjaScriptTargetWriter::WriteArgsSubstitutions( - const SourceFile& source, - const FileTemplate& args_template) { - std::ostringstream source_file_stream; - path_output_no_escaping_.WriteFile(source_file_stream, source); - - EscapeOptions template_escape_options; - template_escape_options.mode = ESCAPE_NINJA_SHELL; - template_escape_options.inhibit_quoting = true; - - args_template.WriteNinjaVariablesForSubstitution( - out_, source_file_stream.str(), template_escape_options); -} - -void NinjaScriptTargetWriter::WriteSourceRules( - const std::string& custom_rule_name, - const std::string& implicit_deps, - const FileTemplate& args_template, - std::vector* output_files) { - FileTemplate output_template(GetOutputTemplate()); - - const Target::FileList& sources = target_->sources(); - for (size_t i = 0; i < sources.size(); i++) { - out_ << "build"; - WriteOutputFilesForBuildLine(output_template, sources[i], output_files); - - out_ << ": " << custom_rule_name << " "; - path_output_.WriteFile(out_, sources[i]); - out_ << implicit_deps << std::endl; - - // Windows needs a unique ID for the response file. - if (target_->settings()->IsWin()) - out_ << " unique_name = " << i << std::endl; - - if (args_template.has_substitutions()) - WriteArgsSubstitutions(sources[i], args_template); - - if (target_->script_values().has_depfile()) { - out_ << " depfile = "; - WriteDepfile(sources[i]); - out_ << std::endl; - } - } -} - -void NinjaScriptTargetWriter::WriteStamp( - const std::vector& output_files) { - out_ << "build "; - path_output_.WriteFile(out_, helper_.GetTargetOutputFile(target_)); - out_ << ": " - << helper_.GetRulePrefix(target_->settings()) - << "stamp"; - for (size_t i = 0; i < output_files.size(); i++) { - out_ << " "; - path_output_.WriteFile(out_, output_files[i]); - } - out_ << std::endl; -} - -void NinjaScriptTargetWriter::WriteOutputFilesForBuildLine( - const FileTemplate& output_template, - const SourceFile& source, - std::vector* output_files) { - // If there is a depfile specified we need to list it as the first output as - // that is what ninja will expect the depfile to refer to itself as. - if (target_->script_values().has_depfile()) { - out_ << " "; - WriteDepfile(source); - } - std::vector output_template_result; - output_template.ApplyString(source.value(), &output_template_result); - for (size_t out_i = 0; out_i < output_template_result.size(); out_i++) { - OutputFile output_path(output_template_result[out_i]); - output_files->push_back(output_path); - out_ << " "; - path_output_.WriteFile(out_, output_path); - } -} - -void NinjaScriptTargetWriter::WriteDepfile(const SourceFile& source) { - std::vector result; - GetDepfileTemplate().ApplyString(source.value(), &result); - path_output_.WriteFile(out_, OutputFile(result[0])); -} - -FileTemplate NinjaScriptTargetWriter::GetDepfileTemplate() const { - std::vector template_args; - std::string depfile_relative_to_build_dir = - RemovePrefix(target_->script_values().depfile().value(), - settings_->build_settings()->build_dir().value()); - template_args.push_back(depfile_relative_to_build_dir); - return FileTemplate(template_args); -} diff --git a/tools/gn/ninja_script_target_writer.h b/tools/gn/ninja_script_target_writer.h deleted file mode 100644 index e32a4a6..0000000 --- a/tools/gn/ninja_script_target_writer.h +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef TOOLS_GN_NINJA_SCRIPT_TARGET_WRITER_H_ -#define TOOLS_GN_NINJA_SCRIPT_TARGET_WRITER_H_ - -#include - -#include "base/compiler_specific.h" -#include "base/gtest_prod_util.h" -#include "tools/gn/ninja_target_writer.h" - -class FileTemplate; -class OutputFile; - -// Writes a .ninja file for a custom script target type. -class NinjaScriptTargetWriter : public NinjaTargetWriter { - public: - NinjaScriptTargetWriter(const Target* target, - const Toolchain* toolchain, - std::ostream& out); - virtual ~NinjaScriptTargetWriter(); - - virtual void Run() OVERRIDE; - - private: - FRIEND_TEST_ALL_PREFIXES(NinjaScriptTargetWriter, - WriteOutputFilesForBuildLine); - FRIEND_TEST_ALL_PREFIXES(NinjaScriptTargetWriter, - WriteOutputFilesForBuildLineWithDepfile); - FRIEND_TEST_ALL_PREFIXES(NinjaScriptTargetWriter, - WriteArgsSubstitutions); - - bool has_sources() const { return !target_->sources().empty(); } - - // Writes the Ninja rule for invoking the script. - // - // Returns the name of the custom rule generated. This will be based on the - // target name, and will include the string "$unique_name" if there are - // multiple inputs. - std::string WriteRuleDefinition(const FileTemplate& args_template); - - // Writes the rules for compiling each source, writing all output files - // to the given vector. - // - // implicit_deps is a precomputed string of all ninja files that are common - // to each build step, it starts with a "|" if it's nonempty. - void WriteSourceRules(const std::string& custom_rule_name, - const std::string& implicit_deps, - const FileTemplate& args_template, - std::vector* output_files); - - // Writes the Ninja variables that expand the substitutions required by the - // arguments for the given source file. - void WriteArgsSubstitutions(const SourceFile& source, - const FileTemplate& args_template); - - // Writes the .stamp rule that names this target and collects all invocations - // of the script into one thing that other targets can depend on. - void WriteStamp(const std::vector& output_files); - - // Writes the output files generated by the output template for the given - // source file. This will start with a space and will not include a newline. - // Appends the output files to the given vector. - void WriteOutputFilesForBuildLine(const FileTemplate& output_template, - const SourceFile& source, - std::vector* output_files); - - void WriteDepfile(const SourceFile& source); - - // Returns the FileTemplate for the depfile variable. - FileTemplate GetDepfileTemplate() const; - - // Path output writer that doesn't do any escaping or quoting. It does, - // however, convert slashes. Used for - // computing intermediate strings. - PathOutput path_output_no_escaping_; - - DISALLOW_COPY_AND_ASSIGN(NinjaScriptTargetWriter); -}; - -#endif // TOOLS_GN_NINJA_SCRIPT_TARGET_WRITER_H_ diff --git a/tools/gn/ninja_script_target_writer_unittest.cc b/tools/gn/ninja_script_target_writer_unittest.cc deleted file mode 100644 index 6319dff..0000000 --- a/tools/gn/ninja_script_target_writer_unittest.cc +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include - -#include "testing/gtest/include/gtest/gtest.h" -#include "tools/gn/file_template.h" -#include "tools/gn/ninja_script_target_writer.h" -#include "tools/gn/test_with_scope.h" - -TEST(NinjaScriptTargetWriter, WriteOutputFilesForBuildLine) { - TestWithScope setup; - setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); - Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); - - target.script_values().outputs().push_back( - SourceFile("//out/Debug/gen/a b{{source_name_part}}.h")); - target.script_values().outputs().push_back( - SourceFile("//out/Debug/gen/{{source_name_part}}.cc")); - - std::ostringstream out; - NinjaScriptTargetWriter writer(&target, setup.toolchain(), out); - - FileTemplate output_template = writer.GetOutputTemplate(); - - SourceFile source("//foo/bar.in"); - std::vector output_files; - writer.WriteOutputFilesForBuildLine(output_template, source, &output_files); - - std::string out_str = out.str(); -#if defined(OS_WIN) - std::replace(out_str.begin(), out_str.end(), '\\', '/'); -#endif - EXPECT_EQ(" gen/a$ bbar.h gen/bar.cc", out_str); -} - -TEST(NinjaScriptTargetWriter, WriteOutputFilesForBuildLineWithDepfile) { - TestWithScope setup; - setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); - Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); - - target.script_values().set_depfile( - SourceFile("//out/Debug/gen/{{source_name_part}}.d")); - target.script_values().outputs().push_back( - SourceFile("//out/Debug/gen/{{source_name_part}}.h")); - target.script_values().outputs().push_back( - SourceFile("//out/Debug/gen/{{source_name_part}}.cc")); - - std::ostringstream out; - NinjaScriptTargetWriter writer(&target, setup.toolchain(), out); - - FileTemplate output_template = writer.GetOutputTemplate(); - - SourceFile source("//foo/bar.in"); - std::vector output_files; - writer.WriteOutputFilesForBuildLine(output_template, source, &output_files); - - std::string out_str = out.str(); -#if defined(OS_WIN) - std::replace(out_str.begin(), out_str.end(), '\\', '/'); -#endif - EXPECT_EQ(" gen/bar.d gen/bar.h gen/bar.cc", out_str); -} - -TEST(NinjaScriptTargetWriter, WriteArgsSubstitutions) { - TestWithScope setup; - setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); - Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); - - std::ostringstream out; - NinjaScriptTargetWriter writer(&target, setup.toolchain(), out); - - std::vector args; - args.push_back("-i"); - args.push_back("{{source}}"); - args.push_back("--out=foo bar{{source_name_part}}.o"); - FileTemplate args_template(args); - - writer.WriteArgsSubstitutions(SourceFile("//foo/b ar.in"), args_template); - - std::string out_str = out.str(); -#if defined(OS_WIN) - std::replace(out_str.begin(), out_str.end(), '\\', '/'); -#endif - EXPECT_EQ(" source = ../../foo/b$ ar.in\n source_name_part = b$ ar\n", - out_str); -} - -// Tests the "run script over multiple source files" mode. -TEST(NinjaScriptTargetWriter, InvokeOverSources) { - TestWithScope setup; - setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); - Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); - target.set_output_type(Target::CUSTOM); - - target.sources().push_back(SourceFile("//foo/input1.txt")); - target.sources().push_back(SourceFile("//foo/input2.txt")); - - target.script_values().set_script(SourceFile("//foo/script.py")); - - target.script_values().args().push_back("-i"); - target.script_values().args().push_back("{{source}}"); - target.script_values().args().push_back( - "--out=foo bar{{source_name_part}}.o"); - - target.script_values().outputs().push_back( - SourceFile("//out/Debug/{{source_name_part}}.out")); - - target.source_prereqs().push_back(SourceFile("//foo/included.txt")); - - // Posix. - { - setup.settings()->set_target_os(Settings::LINUX); - setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL( - "/usr/bin/python"))); - - std::ostringstream out; - NinjaScriptTargetWriter writer(&target, setup.toolchain(), out); - writer.Run(); - - const char expected_linux[] = - "rule __foo_bar___rule\n" - " command = /usr/bin/python ../../foo/script.py -i ${source} " - "\"--out=foo$ bar${source_name_part}.o\"\n" - " description = CUSTOM //foo:bar()\n" - " restat = 1\n" - "\n" - "build input1.out: __foo_bar___rule ../../foo/input1.txt | " - "../../foo/included.txt\n" - " source = ../../foo/input1.txt\n" - " source_name_part = input1\n" - "build input2.out: __foo_bar___rule ../../foo/input2.txt | " - "../../foo/included.txt\n" - " source = ../../foo/input2.txt\n" - " source_name_part = input2\n" - "\n" - "build obj/foo/bar.stamp: stamp input1.out input2.out\n"; - - std::string out_str = out.str(); -#if defined(OS_WIN) - std::replace(out_str.begin(), out_str.end(), '\\', '/'); -#endif - EXPECT_EQ(expected_linux, out_str); - } - - // Windows. - { - // Note: we use forward slashes here so that the output will be the same on - // Linux and Windows. - setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL( - "C:/python/python.exe"))); - setup.settings()->set_target_os(Settings::WIN); - - std::ostringstream out; - NinjaScriptTargetWriter writer(&target, setup.toolchain(), out); - writer.Run(); - - // TODO(brettw) I think we'll need to worry about backslashes here - // depending if we're on actual Windows or Linux pretending to be Windows. - const char expected_win[] = - "rule __foo_bar___rule\n" - " command = C:/python/python.exe gyp-win-tool action-wrapper " - "environment.x86 __foo_bar___rule.$unique_name.rsp\n" - " description = CUSTOM //foo:bar()\n" - " restat = 1\n" - " rspfile = __foo_bar___rule.$unique_name.rsp\n" - " rspfile_content = C:/python/python.exe ../../foo/script.py -i " - "${source} \"--out=foo$ bar${source_name_part}.o\"\n" - "\n" - "build input1.out: __foo_bar___rule ../../foo/input1.txt | " - "../../foo/included.txt\n" - " unique_name = 0\n" - " source = ../../foo/input1.txt\n" - " source_name_part = input1\n" - "build input2.out: __foo_bar___rule ../../foo/input2.txt | " - "../../foo/included.txt\n" - " unique_name = 1\n" - " source = ../../foo/input2.txt\n" - " source_name_part = input2\n" - "\n" - "build obj/foo/bar.stamp: stamp input1.out input2.out\n"; - std::string out_str = out.str(); -#if defined(OS_WIN) - std::replace(out_str.begin(), out_str.end(), '\\', '/'); -#endif - EXPECT_EQ(expected_win, out_str); - } -} - -// Tests the "run script over multiple source files" mode, with a depfile. -TEST(NinjaScriptTargetWriter, InvokeOverSourcesWithDepfile) { - TestWithScope setup; - setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); - Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); - target.set_output_type(Target::CUSTOM); - - target.sources().push_back(SourceFile("//foo/input1.txt")); - target.sources().push_back(SourceFile("//foo/input2.txt")); - - target.script_values().set_script(SourceFile("//foo/script.py")); - target.script_values().set_depfile( - SourceFile("//out/Debug/gen/{{source_name_part}}.d")); - - target.script_values().args().push_back("-i"); - target.script_values().args().push_back("{{source}}"); - target.script_values().args().push_back( - "--out=foo bar{{source_name_part}}.o"); - - target.script_values().outputs().push_back( - SourceFile("//out/Debug/{{source_name_part}}.out")); - - target.source_prereqs().push_back(SourceFile("//foo/included.txt")); - - // Posix. - { - setup.settings()->set_target_os(Settings::LINUX); - setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL( - "/usr/bin/python"))); - - std::ostringstream out; - NinjaScriptTargetWriter writer(&target, setup.toolchain(), out); - writer.Run(); - - const char expected_linux[] = - "rule __foo_bar___rule\n" - " command = /usr/bin/python ../../foo/script.py -i ${source} " - "\"--out=foo$ bar${source_name_part}.o\"\n" - " description = CUSTOM //foo:bar()\n" - " restat = 1\n" - "\n" - "build gen/input1.d input1.out: __foo_bar___rule ../../foo/input1.txt" - " | ../../foo/included.txt\n" - " source = ../../foo/input1.txt\n" - " source_name_part = input1\n" - " depfile = gen/input1.d\n" - "build gen/input2.d input2.out: __foo_bar___rule ../../foo/input2.txt" - " | ../../foo/included.txt\n" - " source = ../../foo/input2.txt\n" - " source_name_part = input2\n" - " depfile = gen/input2.d\n" - "\n" - "build obj/foo/bar.stamp: stamp input1.out input2.out\n"; - - std::string out_str = out.str(); -#if defined(OS_WIN) - std::replace(out_str.begin(), out_str.end(), '\\', '/'); -#endif - EXPECT_EQ(expected_linux, out_str); - } - - // Windows. - { - // Note: we use forward slashes here so that the output will be the same on - // Linux and Windows. - setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL( - "C:/python/python.exe"))); - setup.settings()->set_target_os(Settings::WIN); - - std::ostringstream out; - NinjaScriptTargetWriter writer(&target, setup.toolchain(), out); - writer.Run(); - - // TODO(brettw) I think we'll need to worry about backslashes here - // depending if we're on actual Windows or Linux pretending to be Windows. - const char expected_win[] = - "rule __foo_bar___rule\n" - " command = C:/python/python.exe gyp-win-tool action-wrapper " - "environment.x86 __foo_bar___rule.$unique_name.rsp\n" - " description = CUSTOM //foo:bar()\n" - " restat = 1\n" - " rspfile = __foo_bar___rule.$unique_name.rsp\n" - " rspfile_content = C:/python/python.exe ../../foo/script.py -i " - "${source} \"--out=foo$ bar${source_name_part}.o\"\n" - "\n" - "build gen/input1.d input1.out: __foo_bar___rule ../../foo/input1.txt" - " | ../../foo/included.txt\n" - " unique_name = 0\n" - " source = ../../foo/input1.txt\n" - " source_name_part = input1\n" - " depfile = gen/input1.d\n" - "build gen/input2.d input2.out: __foo_bar___rule ../../foo/input2.txt" - " | ../../foo/included.txt\n" - " unique_name = 1\n" - " source = ../../foo/input2.txt\n" - " source_name_part = input2\n" - " depfile = gen/input2.d\n" - "\n" - "build obj/foo/bar.stamp: stamp input1.out input2.out\n"; - std::string out_str = out.str(); -#if defined(OS_WIN) - std::replace(out_str.begin(), out_str.end(), '\\', '/'); -#endif - EXPECT_EQ(expected_win, out_str); - } -} diff --git a/tools/gn/ninja_target_writer.cc b/tools/gn/ninja_target_writer.cc index adb1108..905f91f 100644 --- a/tools/gn/ninja_target_writer.cc +++ b/tools/gn/ninja_target_writer.cc @@ -10,10 +10,10 @@ #include "base/file_util.h" #include "tools/gn/err.h" #include "tools/gn/file_template.h" +#include "tools/gn/ninja_action_target_writer.h" #include "tools/gn/ninja_binary_target_writer.h" #include "tools/gn/ninja_copy_target_writer.h" #include "tools/gn/ninja_group_target_writer.h" -#include "tools/gn/ninja_script_target_writer.h" #include "tools/gn/scheduler.h" #include "tools/gn/string_utils.h" #include "tools/gn/target.h" @@ -61,8 +61,9 @@ void NinjaTargetWriter::RunAndWriteFile(const Target* target, if (target->output_type() == Target::COPY_FILES) { NinjaCopyTargetWriter writer(target, toolchain, file); writer.Run(); - } else if (target->output_type() == Target::CUSTOM) { - NinjaScriptTargetWriter writer(target, toolchain, file); + } else if (target->output_type() == Target::ACTION || + target->output_type() == Target::ACTION_FOREACH) { + NinjaActionTargetWriter writer(target, toolchain, file); writer.Run(); } else if (target->output_type() == Target::GROUP) { NinjaGroupTargetWriter writer(target, toolchain, file); @@ -110,7 +111,7 @@ std::string NinjaTargetWriter::GetSourcesImplicitDeps() const { } FileTemplate NinjaTargetWriter::GetOutputTemplate() const { - const Target::FileList& outputs = target_->script_values().outputs(); + const Target::FileList& outputs = target_->action_values().outputs(); std::vector output_template_args; for (size_t i = 0; i < outputs.size(); i++) { // All outputs should be in the output dir. diff --git a/tools/gn/script_target_generator.cc b/tools/gn/script_target_generator.cc deleted file mode 100644 index 8f414f4..0000000 --- a/tools/gn/script_target_generator.cc +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "tools/gn/script_target_generator.h" - -#include "tools/gn/build_settings.h" -#include "tools/gn/err.h" -#include "tools/gn/filesystem_utils.h" -#include "tools/gn/parse_tree.h" -#include "tools/gn/scope.h" -#include "tools/gn/value.h" -#include "tools/gn/value_extractors.h" -#include "tools/gn/variables.h" - -ScriptTargetGenerator::ScriptTargetGenerator( - Target* target, - Scope* scope, - const FunctionCallNode* function_call, - Err* err) - : TargetGenerator(target, scope, function_call, err) { -} - -ScriptTargetGenerator::~ScriptTargetGenerator() { -} - -void ScriptTargetGenerator::DoRun() { - target_->set_output_type(Target::CUSTOM); - - FillExternal(); - if (err_->has_error()) - return; - - FillSources(); - if (err_->has_error()) - return; - - FillSourcePrereqs(); - if (err_->has_error()) - return; - - FillScript(); - if (err_->has_error()) - return; - - FillScriptArgs(); - if (err_->has_error()) - return; - - FillOutputs(); - if (err_->has_error()) - return; - - FillDepfile(); - if (err_->has_error()) - return; - - // Script outputs don't depend on the current toolchain so we can skip adding - // that dependency. -} - -void ScriptTargetGenerator::FillScript() { - // If this gets called, the target type requires a script, so error out - // if it doesn't have one. - const Value* value = scope_->GetValue(variables::kScript, true); - if (!value) { - *err_ = Err(function_call_, "This target type requires a \"script\"."); - return; - } - if (!value->VerifyTypeIs(Value::STRING, err_)) - return; - - target_->script_values().set_script( - scope_->GetSourceDir().ResolveRelativeFile(value->string_value())); -} - -void ScriptTargetGenerator::FillScriptArgs() { - const Value* value = scope_->GetValue(variables::kArgs, true); - if (!value) - return; - - std::vector args; - if (!ExtractListOfStringValues(*value, &args, err_)) - return; - target_->script_values().swap_in_args(&args); -} - -void ScriptTargetGenerator::FillDepfile() { - const Value* value = scope_->GetValue(variables::kDepfile, true); - if (!value) - return; - target_->script_values().set_depfile( - scope_->settings()->build_settings()->build_dir().ResolveRelativeFile( - value->string_value())); -} diff --git a/tools/gn/script_target_generator.h b/tools/gn/script_target_generator.h deleted file mode 100644 index 4d79e51..0000000 --- a/tools/gn/script_target_generator.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef TOOLS_GN_SCRIPT_TARGET_GENERATOR_H_ -#define TOOLS_GN_SCRIPT_TARGET_GENERATOR_H_ - -#include "base/compiler_specific.h" -#include "tools/gn/target_generator.h" - -// Populates a Target with the values from a custom script rule. -class ScriptTargetGenerator : public TargetGenerator { - public: - ScriptTargetGenerator(Target* target, - Scope* scope, - const FunctionCallNode* function_call, - Err* err); - virtual ~ScriptTargetGenerator(); - - protected: - virtual void DoRun() OVERRIDE; - - private: - void FillScript(); - void FillScriptArgs(); - void FillDepfile(); - - DISALLOW_COPY_AND_ASSIGN(ScriptTargetGenerator); -}; - -#endif // TOOLS_GN_SCRIPT_TARGET_GENERATOR_H_ diff --git a/tools/gn/script_values.cc b/tools/gn/script_values.cc deleted file mode 100644 index c5d7989..0000000 --- a/tools/gn/script_values.cc +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "tools/gn/script_values.h" - -ScriptValues::ScriptValues() { -} - -ScriptValues::~ScriptValues() { -} diff --git a/tools/gn/script_values.h b/tools/gn/script_values.h deleted file mode 100644 index 50219ef..0000000 --- a/tools/gn/script_values.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef TOOLS_GN_SCRIPT_VALUES_H_ -#define TOOLS_GN_SCRIPT_VALUES_H_ - -#include -#include - -#include "base/basictypes.h" -#include "tools/gn/source_file.h" - -// Holds the values (outputs, args, script name, etc.) for a script-based -// target. -class ScriptValues { - public: - ScriptValues(); - ~ScriptValues(); - - // Filename of the script to execute. - const SourceFile& script() const { return script_; } - void set_script(const SourceFile& s) { script_ = s; } - - // Arguments to the script. - std::vector& args() { return args_; } - const std::vector& args() const { return args_; } - void swap_in_args(std::vector* a) { args_.swap(*a); } - - // Files created by the script. - std::vector& outputs() { return outputs_; } - const std::vector& outputs() const { return outputs_; } - void swap_in_outputs(std::vector* op) { outputs_.swap(*op); } - - // Depfile generated by the script. - const SourceFile& depfile() const { return depfile_; } - bool has_depfile() const { return !depfile_.is_null(); } - void set_depfile(const SourceFile& depfile) { depfile_ = depfile; } - - private: - SourceFile script_; - std::vector args_; - std::vector outputs_; - SourceFile depfile_; - - DISALLOW_COPY_AND_ASSIGN(ScriptValues); -}; - -#endif // TOOLS_GN_SCRIPT_VALUES_H_ diff --git a/tools/gn/target.cc b/tools/gn/target.cc index 635c50e..56b140f 100644 --- a/tools/gn/target.cc +++ b/tools/gn/target.cc @@ -76,8 +76,10 @@ const char* Target::GetStringForOutputType(OutputType type) { return "Static library"; case COPY_FILES: return "Copy"; - case CUSTOM: - return "Custom"; + case ACTION: + return "Action"; + case ACTION_FOREACH: + return "ActionForEach"; default: return ""; } diff --git a/tools/gn/target.h b/tools/gn/target.h index 4501494..2cf0409 100644 --- a/tools/gn/target.h +++ b/tools/gn/target.h @@ -14,11 +14,11 @@ #include "base/logging.h" #include "base/strings/string_piece.h" #include "base/synchronization/lock.h" +#include "tools/gn/action_values.h" #include "tools/gn/config_values.h" #include "tools/gn/item.h" #include "tools/gn/label_ptr.h" #include "tools/gn/ordered_set.h" -#include "tools/gn/script_values.h" #include "tools/gn/source_file.h" class InputFile; @@ -35,7 +35,8 @@ class Target : public Item { STATIC_LIBRARY, SOURCE_SET, COPY_FILES, - CUSTOM, + ACTION, + ACTION_FOREACH, }; typedef std::vector FileList; typedef std::vector StringVector; @@ -131,8 +132,8 @@ class Target : public Item { ConfigValues& config_values() { return config_values_; } const ConfigValues& config_values() const { return config_values_; } - ScriptValues& script_values() { return script_values_; } - const ScriptValues& script_values() const { return script_values_; } + ActionValues& action_values() { return action_values_; } + const ActionValues& action_values() const { return action_values_; } const OrderedSet& all_lib_dirs() const { return all_lib_dirs_; } const OrderedSet& all_libs() const { return all_libs_; } @@ -187,7 +188,7 @@ class Target : public Item { OrderedSet all_libs_; ConfigValues config_values_; // Used for all binary targets. - ScriptValues script_values_; // Used for script (CUSTOM) targets. + ActionValues action_values_; // Used for action[_foreach] targets. SourceFile gyp_file_; diff --git a/tools/gn/target_generator.cc b/tools/gn/target_generator.cc index 81c4394..2fb178a 100644 --- a/tools/gn/target_generator.cc +++ b/tools/gn/target_generator.cc @@ -4,6 +4,7 @@ #include "tools/gn/target_generator.h" +#include "tools/gn/action_target_generator.h" #include "tools/gn/binary_target_generator.h" #include "tools/gn/build_settings.h" #include "tools/gn/config.h" @@ -15,7 +16,6 @@ #include "tools/gn/parse_tree.h" #include "tools/gn/scheduler.h" #include "tools/gn/scope.h" -#include "tools/gn/script_target_generator.h" #include "tools/gn/token.h" #include "tools/gn/value.h" #include "tools/gn/value_extractors.h" @@ -75,8 +75,13 @@ void TargetGenerator::GenerateTarget(Scope* scope, if (output_type == functions::kCopy) { CopyTargetGenerator generator(target.get(), scope, function_call, err); generator.Run(); - } else if (output_type == functions::kCustom) { - ScriptTargetGenerator generator(target.get(), scope, function_call, err); + } else if (output_type == functions::kAction) { + ActionTargetGenerator generator(target.get(), scope, function_call, + Target::ACTION, err); + generator.Run(); + } else if (output_type == functions::kActionForEach) { + ActionTargetGenerator generator(target.get(), scope, function_call, + Target::ACTION_FOREACH, err); generator.Run(); } else if (output_type == functions::kExecutable) { BinaryTargetGenerator generator(target.get(), scope, function_call, @@ -215,7 +220,7 @@ void TargetGenerator::FillOutputs() { outputs[i].value(), value->list_value()[i], err_)) return; } - target_->script_values().outputs().swap(outputs); + target_->action_values().outputs().swap(outputs); } void TargetGenerator::FillGenericConfigs(const char* var_name, diff --git a/tools/gn/variables.cc b/tools/gn/variables.cc index b05e818..8fac840 100644 --- a/tools/gn/variables.cc +++ b/tools/gn/variables.cc @@ -181,7 +181,7 @@ const char kRootOutDir_Help[] = "\n" "Example:\n" "\n" - " custom(\"myscript\") {\n" + " action(\"myscript\") {\n" " # Pass the output dir to the script.\n" " args = [ \"-o\", rebase_path(root_out_dir, root_build_dir) ]\n" " }\n"; @@ -206,7 +206,7 @@ const char kTargetGenDir_Help[] = "\n" "Example:\n" "\n" - " custom(\"myscript\") {\n" + " action(\"myscript\") {\n" " # Pass the generated output dir to the script.\n" " args = [ \"-o\", rebase_path(target_gen_dir, root_build_dir) ]" "\n" @@ -232,7 +232,7 @@ const char kTargetOutDir_Help[] = "\n" "Example:\n" "\n" - " custom(\"myscript\") {\n" + " action(\"myscript\") {\n" " # Pass the output dir to the script.\n" " args = [ \"-o\", rebase_path(target_out_dir, root_build_dir) ]" "\n" @@ -263,15 +263,15 @@ const char kAllDependentConfigs_Help[] = const char kArgs[] = "args"; const char kArgs_HelpShort[] = - "args: [string list] Arguments passed to a custom script."; + "args: [string list] Arguments passed to an action."; const char kArgs_Help[] = - "args: Arguments passed to a custom script.\n" + "args: Arguments passed to an action.\n" "\n" - " For custom script targets, args is the list of arguments to pass\n" - " to the script. Typically you would use source expansion (see\n" + " For action and action_foreach targets, args is the list of arguments\n" + " to pass to the script. Typically you would use source expansion (see\n" " \"gn help source_expansion\") to insert the source file names.\n" "\n" - " See also \"gn help custom\".\n"; + " See also \"gn help action\" and \"gn help action_foreach\".\n"; const char kCflags[] = "cflags"; const char kCflags_HelpShort[] = @@ -398,25 +398,25 @@ const char kDefines_Help[] = const char kDepfile[] = "depfile"; const char kDepfile_HelpShort[] = - "depfile: [string] File name for input dependencies for custom targets."; + "depfile: [string] File name for input dependencies for actions."; const char kDepfile_Help[] = - "depfile: [string] File name for input dependencies for custom targets.\n" + "depfile: [string] File name for input dependencies for actions.\n" "\n" - " If nonempty, this string specifies that the current \"custom\" target\n" - " will generate the given \".d\" file containing the dependencies of the\n" - " input. Empty or unset means that the script doesn't generate the\n" - " files.\n" + " If nonempty, this string specifies that the current action or\n" + " action_foreach target will generate the given \".d\" file containing\n" + " the dependencies of the input. Empty or unset means that the script\n" + " doesn't generate the files.\n" "\n" " The .d file should go in the target output directory. If you have more\n" " than one source file that the script is being run over, you can use\n" - " the output file expansions described in \"gn help custom\" to name the\n" - " .d file according to the input." + " the output file expansions described in \"gn help action_foreach\" to\n" + " name the .d file according to the input." "\n" " The format is that of a Makefile, and all of the paths should be\n" " relative to the root build directory.\n" "\n" "Example:\n" - " custom(\"myscript_target\") {\n" + " action_foreach(\"myscript_target\") {\n" " script = \"myscript.py\"\n" " sources = [ ... ]\n" "\n" @@ -438,9 +438,9 @@ const char kDeps_Help[] = "\n" " Specifies dependencies of a target. Shared and dynamic libraries will\n" " be linked into the current target. Other target types that can't be\n" - " linked (like custom scripts and groups) listed in \"deps\" will be\n" - " treated as \"datadeps\". Likewise, if the current target isn't\n" - " linkable, then all deps will be treated as \"datadeps\".\n" + " linked (like actions and groups) listed in \"deps\" will be treated\n" + " as \"datadeps\". Likewise, if the current target isn't linkable, then\n" + " all deps will be treated as \"datadeps\".\n" "\n" " See also \"datadeps\".\n"; @@ -578,7 +578,7 @@ const char kHardDep_Help[] = " ...\n" " }\n" "\n" - " custom(\"myresource\") {\n" + " action(\"myresource\") {\n" " hard_dep = true\n" " script = \"my_generator.py\"\n" " outputs = \"$target_gen_dir/myresource.h\"\n" @@ -708,27 +708,28 @@ const char kOutputName_Help[] = const char kOutputs[] = "outputs"; const char kOutputs_HelpShort[] = - "outputs: [file list] Output files for custom script and copy targets."; + "outputs: [file list] Output files for actions and copy targets."; const char kOutputs_Help[] = - "outputs: Output files for custom script and copy targets.\n" + "outputs: Output files for actions and copy targets.\n" "\n" - " Outputs is valid for \"copy\" and \"custom\" target types and\n" - " indicates the resulting files. The values may contain source\n" - " expansions to generate the output names from the sources (see\n" + " Outputs is valid for \"copy\", \"action\", and \"action_foreach\"\n" + " target types and indicates the resulting files. The values may contain\n" + " source expansions to generate the output names from the sources (see\n" " \"gn help source_expansion\").\n" "\n" " For copy targets, the outputs is the destination for the copied\n" - " file(s). For custom script targets, the outputs should be the list of\n" - " files generated by the script.\n"; + " file(s). For actions, the outputs should be the list of files\n" + " generated by the script.\n"; const char kScript[] = "script"; const char kScript_HelpShort[] = - "script: [file name] Script file for custom script targets."; + "script: [file name] Script file for actions."; const char kScript_Help[] = - "script: Script file for custom script targets.\n" + "script: Script file for actions.\n" "\n" " An absolute or buildfile-relative file name of a Python script to run\n" - " for a custom script target (see \"gn help custom\").\n"; + " for a action and action_foreach targets (see \"gn help action\" and\n" + " \"gn help action_foreach\").\n"; const char kSourcePrereqs[] = "source_prereqs"; const char kSourcePrereqs_HelpShort[] = @@ -757,7 +758,7 @@ const char kSourcePrereqs_Help[] = " the normal include file dependency management will handle them more\n" " efficiently anyway.\n" "\n" - " For custom script targets that don't generate \".d\" files, the\n" + " For action targets that don't generate \".d\" files, the\n" " \"source_prereqs\" section is how you can list known compile-time\n" " dependencies your script may have.\n" "\n" @@ -772,7 +773,7 @@ const char kSourcePrereqs_Help[] = " source_prereqs = [ \"$root_gen_dir/something/generated_data.h\" ]\n" " }\n" "\n" - " custom(\"myscript\") {\n" + " action(\"myscript\") {\n" " script = \"domything.py\"\n" " source_prereqs = [ \"input.data\" ]\n" " }\n"; -- cgit v1.1