Home | History | Annotate | Download | only in gn
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "tools/gn/action_target_generator.h"
      6 
      7 #include "tools/gn/build_settings.h"
      8 #include "tools/gn/err.h"
      9 #include "tools/gn/filesystem_utils.h"
     10 #include "tools/gn/parse_tree.h"
     11 #include "tools/gn/scope.h"
     12 #include "tools/gn/value.h"
     13 #include "tools/gn/value_extractors.h"
     14 #include "tools/gn/variables.h"
     15 
     16 namespace {
     17 
     18 // Returns true if the list of files looks like it might have a {{ }} pattern
     19 // in it. Used for error checking.
     20 bool FileListHasPattern(const Target::FileList& files) {
     21   for (size_t i = 0; i < files.size(); i++) {
     22     if (files[i].value().find("{{") != std::string::npos &&
     23         files[i].value().find("}}") != std::string::npos)
     24       return true;
     25   }
     26   return false;
     27 }
     28 
     29 }  // namespace
     30 
     31 ActionTargetGenerator::ActionTargetGenerator(
     32     Target* target,
     33     Scope* scope,
     34     const FunctionCallNode* function_call,
     35     Target::OutputType type,
     36     Err* err)
     37     : TargetGenerator(target, scope, function_call, err),
     38       output_type_(type) {
     39 }
     40 
     41 ActionTargetGenerator::~ActionTargetGenerator() {
     42 }
     43 
     44 void ActionTargetGenerator::DoRun() {
     45   target_->set_output_type(output_type_);
     46 
     47   FillSources();
     48   if (err_->has_error())
     49     return;
     50   if (output_type_ == Target::ACTION_FOREACH && target_->sources().empty()) {
     51     // Foreach rules must always have some sources to have an effect.
     52     *err_ = Err(function_call_, "action_foreach target has no sources.",
     53         "If you don't specify any sources, there is nothing to run your\n"
     54         "script over.");
     55     return;
     56   }
     57 
     58   FillInputs();
     59   if (err_->has_error())
     60     return;
     61 
     62   FillScript();
     63   if (err_->has_error())
     64     return;
     65 
     66   FillScriptArgs();
     67   if (err_->has_error())
     68     return;
     69 
     70   FillOutputs();
     71   if (err_->has_error())
     72     return;
     73 
     74   FillDepfile();
     75   if (err_->has_error())
     76     return;
     77 
     78   CheckOutputs();
     79   if (err_->has_error())
     80     return;
     81 
     82   // Action outputs don't depend on the current toolchain so we can skip adding
     83   // that dependency.
     84 }
     85 
     86 void ActionTargetGenerator::FillScript() {
     87   // If this gets called, the target type requires a script, so error out
     88   // if it doesn't have one.
     89   const Value* value = scope_->GetValue(variables::kScript, true);
     90   if (!value) {
     91     *err_ = Err(function_call_, "This target type requires a \"script\".");
     92     return;
     93   }
     94   if (!value->VerifyTypeIs(Value::STRING, err_))
     95     return;
     96 
     97   SourceFile script_file =
     98       scope_->GetSourceDir().ResolveRelativeFile(value->string_value());
     99   if (script_file.value().empty()) {
    100     *err_ = Err(*value, "script name is empty");
    101     return;
    102   }
    103   target_->action_values().set_script(script_file);
    104 }
    105 
    106 void ActionTargetGenerator::FillScriptArgs() {
    107   const Value* value = scope_->GetValue(variables::kArgs, true);
    108   if (!value)
    109     return;
    110 
    111   std::vector<std::string> args;
    112   if (!ExtractListOfStringValues(*value, &args, err_))
    113     return;
    114   target_->action_values().swap_in_args(&args);
    115 }
    116 
    117 void ActionTargetGenerator::FillDepfile() {
    118   const Value* value = scope_->GetValue(variables::kDepfile, true);
    119   if (!value)
    120     return;
    121   target_->action_values().set_depfile(
    122       scope_->settings()->build_settings()->build_dir().ResolveRelativeFile(
    123           value->string_value()));
    124 }
    125 
    126 void ActionTargetGenerator::CheckOutputs() {
    127   const Target::FileList& outputs = target_->action_values().outputs();
    128   if (outputs.empty()) {
    129     *err_ = Err(function_call_, "Action has no outputs.",
    130         "If you have no outputs, the build system can not tell when your\n"
    131         "script needs to be run.");
    132     return;
    133   }
    134 
    135   if (output_type_ == Target::ACTION) {
    136     // Make sure the outputs for an action have no patterns in them.
    137     if (FileListHasPattern(outputs)) {
    138       *err_ = Err(function_call_, "Action has patterns in the output.",
    139           "An action target should have the outputs completely specified. If\n"
    140           "you want to provide a mapping from source to output, use an\n"
    141           "\"action_foreach\" target.");
    142       return;
    143     }
    144   } else if (output_type_ == Target::ACTION_FOREACH) {
    145     // A foreach target should always have a pattern in the outputs.
    146     if (!FileListHasPattern(outputs)) {
    147       *err_ = Err(function_call_,
    148           "action_foreach should have a pattern in the output.",
    149           "An action_foreach target should have a source expansion pattern in\n"
    150           "it to map source file to unique output file name. Otherwise, the\n"
    151           "build system can't determine when your script needs to be run.");
    152       return;
    153     }
    154   }
    155 }
    156