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