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