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/target_generator.h" 6 7 #include "base/files/file_path.h" 8 #include "base/logging.h" 9 #include "tools/gn/build_settings.h" 10 #include "tools/gn/config.h" 11 #include "tools/gn/config_values_generator.h" 12 #include "tools/gn/err.h" 13 #include "tools/gn/filesystem_utils.h" 14 #include "tools/gn/functions.h" 15 #include "tools/gn/input_file.h" 16 #include "tools/gn/item_node.h" 17 #include "tools/gn/ninja_target_writer.h" 18 #include "tools/gn/parse_tree.h" 19 #include "tools/gn/scheduler.h" 20 #include "tools/gn/scope.h" 21 #include "tools/gn/target_manager.h" 22 #include "tools/gn/token.h" 23 #include "tools/gn/value.h" 24 #include "tools/gn/value_extractors.h" 25 #include "tools/gn/variables.h" 26 27 namespace { 28 29 bool TypeHasConfigs(Target::OutputType type) { 30 return type == Target::EXECUTABLE || 31 type == Target::SHARED_LIBRARY || 32 type == Target::STATIC_LIBRARY || 33 type == Target::LOADABLE_MODULE; 34 } 35 36 bool TypeHasConfigValues(Target::OutputType type) { 37 return type == Target::EXECUTABLE || 38 type == Target::SHARED_LIBRARY || 39 type == Target::STATIC_LIBRARY || 40 type == Target::LOADABLE_MODULE; 41 } 42 43 bool TypeHasSources(Target::OutputType type) { 44 return type != Target::NONE; 45 } 46 47 bool TypeHasData(Target::OutputType type) { 48 return type != Target::NONE; 49 } 50 51 bool TypeHasDestDir(Target::OutputType type) { 52 return type == Target::COPY_FILES; 53 } 54 55 bool TypeHasOutputs(Target::OutputType type) { 56 return type == Target::CUSTOM; 57 } 58 59 } // namespace 60 61 TargetGenerator::TargetGenerator(Target* target, 62 Scope* scope, 63 const Token& function_token, 64 const std::vector<Value>& args, 65 const std::string& output_type, 66 Err* err) 67 : target_(target), 68 scope_(scope), 69 function_token_(function_token), 70 args_(args), 71 output_type_(output_type), 72 err_(err), 73 input_directory_(function_token.location().file()->dir()) { 74 } 75 76 TargetGenerator::~TargetGenerator() { 77 } 78 79 void TargetGenerator::Run() { 80 // Output type. 81 Target::OutputType output_type = GetOutputType(); 82 target_->set_output_type(output_type); 83 if (err_->has_error()) 84 return; 85 86 if (TypeHasConfigs(output_type)) { 87 FillConfigs(); 88 FillAllDependentConfigs(); 89 FillDirectDependentConfigs(); 90 } 91 if (TypeHasSources(output_type)) 92 FillSources(); 93 if (TypeHasData(output_type)) 94 FillData(); 95 if (output_type == Target::CUSTOM) { 96 FillScript(); 97 FillScriptArgs(); 98 } 99 if (TypeHasOutputs(output_type)) 100 FillOutputs(); 101 FillDependencies(); // All types have dependencies. 102 FillDataDependencies(); // All types have dependencies. 103 104 if (TypeHasConfigValues(output_type)) { 105 ConfigValuesGenerator gen(&target_->config_values(), scope_, 106 function_token_, input_directory_, err_); 107 gen.Run(); 108 if (err_->has_error()) 109 return; 110 } 111 112 if (TypeHasDestDir(output_type)) 113 FillDestDir(); 114 115 // Set the toolchain as a dependency of the target. 116 // TODO(brettw) currently we lock separately for each config, dep, and 117 // toolchain we add which is bad! Do this in one lock. 118 { 119 ItemTree* tree = &GetBuildSettings()->item_tree(); 120 base::AutoLock lock(tree->lock()); 121 ItemNode* tc_node = 122 tree->GetExistingNodeLocked(ToolchainLabelForScope(scope_)); 123 if (!tree->GetExistingNodeLocked(target_->label())->AddDependency( 124 GetBuildSettings(), function_token_.range(), tc_node, err_)) 125 return; 126 } 127 128 target_->SetGenerated(&function_token_); 129 GetBuildSettings()->target_manager().TargetGenerationComplete( 130 target_->label(), err_); 131 } 132 133 // static 134 void TargetGenerator::GenerateTarget(Scope* scope, 135 const Token& function_token, 136 const std::vector<Value>& args, 137 const std::string& output_type, 138 Err* err) { 139 // Name is the argument to the function. 140 if (args.size() != 1u || args[0].type() != Value::STRING) { 141 *err = Err(function_token, 142 "Target generator requires one string argument.", 143 "Otherwise I'm not sure what to call this target."); 144 return; 145 } 146 147 // The location of the target is the directory name with no slash at the end. 148 // FIXME(brettw) validate name. 149 const Label& toolchain_label = ToolchainLabelForScope(scope); 150 Label label(function_token.location().file()->dir(), 151 args[0].string_value(), 152 toolchain_label.dir(), toolchain_label.name()); 153 154 if (g_scheduler->verbose_logging()) 155 g_scheduler->Log("Generating target", label.GetUserVisibleName(true)); 156 157 Target* t = scope->settings()->build_settings()->target_manager().GetTarget( 158 label, function_token.range(), NULL, err); 159 if (err->has_error()) 160 return; 161 162 TargetGenerator gen(t, scope, function_token, args, output_type, err); 163 gen.Run(); 164 } 165 166 Target::OutputType TargetGenerator::GetOutputType() const { 167 if (output_type_ == functions::kGroup) 168 return Target::NONE; 169 if (output_type_ == functions::kExecutable) 170 return Target::EXECUTABLE; 171 if (output_type_ == functions::kSharedLibrary) 172 return Target::SHARED_LIBRARY; 173 if (output_type_ == functions::kStaticLibrary) 174 return Target::STATIC_LIBRARY; 175 // TODO(brettw) what does loadable module mean? 176 //if (output_type_ == ???) 177 // return Target::LOADABLE_MODULE; 178 if (output_type_ == functions::kCopy) 179 return Target::COPY_FILES; 180 if (output_type_ == functions::kCustom) 181 return Target::CUSTOM; 182 183 *err_ = Err(function_token_, "Not a known output type", 184 "I am very confused."); 185 return Target::NONE; 186 } 187 188 void TargetGenerator::FillGenericConfigs( 189 const char* var_name, 190 void (Target::*setter)(std::vector<const Config*>*)) { 191 const Value* value = scope_->GetValue(var_name, true); 192 if (!value) 193 return; 194 195 std::vector<Label> labels; 196 if (!ExtractListOfLabels(*value, input_directory_, 197 ToolchainLabelForScope(scope_), &labels, err_)) 198 return; 199 200 std::vector<const Config*> dest_configs; 201 dest_configs.resize(labels.size()); 202 for (size_t i = 0; i < labels.size(); i++) { 203 dest_configs[i] = Config::GetConfig( 204 scope_->settings(), 205 value->list_value()[i].origin()->GetRange(), 206 labels[i], target_, err_); 207 if (err_->has_error()) 208 return; 209 } 210 (target_->*setter)(&dest_configs); 211 } 212 213 void TargetGenerator::FillGenericDeps( 214 const char* var_name, 215 void (Target::*setter)(std::vector<const Target*>*)) { 216 const Value* value = scope_->GetValue(var_name, true); 217 if (!value) 218 return; 219 220 std::vector<Label> labels; 221 if (!ExtractListOfLabels(*value, input_directory_, 222 ToolchainLabelForScope(scope_), &labels, err_)) 223 return; 224 225 std::vector<const Target*> dest_deps; 226 dest_deps.resize(labels.size()); 227 for (size_t i = 0; i < labels.size(); i++) { 228 dest_deps[i] = GetBuildSettings()->target_manager().GetTarget( 229 labels[i], value->list_value()[i].origin()->GetRange(), target_, err_); 230 if (err_->has_error()) 231 return; 232 } 233 234 (target_->*setter)(&dest_deps); 235 } 236 237 void TargetGenerator::FillConfigs() { 238 FillGenericConfigs(variables::kConfigs, &Target::swap_in_configs); 239 } 240 241 void TargetGenerator::FillAllDependentConfigs() { 242 FillGenericConfigs(variables::kAllDependentConfigs, 243 &Target::swap_in_all_dependent_configs); 244 } 245 246 void TargetGenerator::FillDirectDependentConfigs() { 247 FillGenericConfigs(variables::kDirectDependentConfigs, 248 &Target::swap_in_direct_dependent_configs); 249 } 250 251 void TargetGenerator::FillSources() { 252 const Value* value = scope_->GetValue(variables::kSources, true); 253 if (!value) 254 return; 255 256 Target::FileList dest_sources; 257 if (!ExtractListOfRelativeFiles(*value, input_directory_, &dest_sources, 258 err_)) 259 return; 260 target_->swap_in_sources(&dest_sources); 261 } 262 263 void TargetGenerator::FillData() { 264 // TODO(brettW) hook this up to the constant when we have cleaned up 265 // how data files are used. 266 const Value* value = scope_->GetValue("data", true); 267 if (!value) 268 return; 269 270 Target::FileList dest_data; 271 if (!ExtractListOfRelativeFiles(*value, input_directory_, &dest_data, 272 err_)) 273 return; 274 target_->swap_in_data(&dest_data); 275 } 276 277 void TargetGenerator::FillDependencies() { 278 FillGenericDeps(variables::kDeps, &Target::swap_in_deps); 279 } 280 281 void TargetGenerator::FillDataDependencies() { 282 FillGenericDeps(variables::kDatadeps, &Target::swap_in_datadeps); 283 } 284 285 void TargetGenerator::FillDestDir() { 286 // Destdir is required for all targets that use it. 287 const Value* value = scope_->GetValue("destdir", true); 288 if (!value) { 289 *err_ = Err(function_token_, "This target type requires a \"destdir\"."); 290 return; 291 } 292 if (!value->VerifyTypeIs(Value::STRING, err_)) 293 return; 294 295 if (!EnsureStringIsInOutputDir( 296 GetBuildSettings()->build_dir(), 297 value->string_value(), *value, err_)) 298 return; 299 target_->set_destdir(SourceDir(value->string_value())); 300 } 301 302 void TargetGenerator::FillScript() { 303 // If this gets called, the target type requires a script, so error out 304 // if it doesn't have one. 305 const Value* value = scope_->GetValue("script", true); 306 if (!value) { 307 *err_ = Err(function_token_, "This target type requires a \"script\"."); 308 return; 309 } 310 if (!value->VerifyTypeIs(Value::STRING, err_)) 311 return; 312 313 target_->set_script( 314 input_directory_.ResolveRelativeFile(value->string_value())); 315 } 316 317 void TargetGenerator::FillScriptArgs() { 318 const Value* value = scope_->GetValue("args", true); 319 if (!value) 320 return; 321 322 std::vector<std::string> args; 323 if (!ExtractListOfStringValues(*value, &args, err_)) 324 return; 325 target_->swap_in_script_args(&args); 326 } 327 328 void TargetGenerator::FillOutputs() { 329 const Value* value = scope_->GetValue("outputs", true); 330 if (!value) 331 return; 332 333 Target::FileList outputs; 334 if (!ExtractListOfRelativeFiles(*value, input_directory_, &outputs, err_)) 335 return; 336 337 // Validate that outputs are in the output dir. 338 CHECK(outputs.size() == value->list_value().size()); 339 for (size_t i = 0; i < outputs.size(); i++) { 340 if (!EnsureStringIsInOutputDir( 341 GetBuildSettings()->build_dir(), 342 outputs[i].value(), value->list_value()[i], err_)) 343 return; 344 } 345 target_->swap_in_outputs(&outputs); 346 } 347 348 const BuildSettings* TargetGenerator::GetBuildSettings() const { 349 return scope_->settings()->build_settings(); 350 } 351