Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "tools/gn/target.h" | 5 #include "tools/gn/target.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/strings/string_util.h" | 8 #include "base/strings/string_util.h" |
| 9 #include "base/strings/stringprintf.h" | 9 #include "base/strings/stringprintf.h" |
| 10 #include "tools/gn/config_values_extractors.h" | 10 #include "tools/gn/config_values_extractors.h" |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 53 from->label().GetUserVisibleName(false) + | 53 from->label().GetUserVisibleName(false) + |
| 54 "\n" | 54 "\n" |
| 55 "which is a complete static library can't depend on\n" + | 55 "which is a complete static library can't depend on\n" + |
| 56 to->label().GetUserVisibleName(false) + | 56 to->label().GetUserVisibleName(false) + |
| 57 "\n" | 57 "\n" |
| 58 "which is a static library.\n" | 58 "which is a static library.\n" |
| 59 "\n" | 59 "\n" |
| 60 "Use source sets for intermediate targets instead."); | 60 "Use source sets for intermediate targets instead."); |
| 61 } | 61 } |
| 62 | 62 |
| 63 // Set check_private_deps to true for the first invocation since a target | |
| 64 // can see all of its dependencies. For recursive invocations this will be set | |
| 65 // to false to follow only public dependency paths. | |
| 66 // | |
| 67 // Pass a pointer to an empty set for the first invocation. This will be used | |
| 68 // to avoid duplicate checking. | |
| 69 bool EnsureFileIsGeneratedByDependency(const Target* target, | |
| 70 const OutputFile& file, | |
| 71 bool check_private_deps, | |
| 72 std::set<const Target*>* seen_targets) { | |
| 73 if (seen_targets->find(target) != seen_targets->end()) | |
| 74 return false; // Already checked this one and it's not found. | |
| 75 seen_targets->insert(target); | |
| 76 | |
| 77 // Assume that we have relatively few generated inputs so brute-force | |
| 78 // searching here is OK. If this becomes a bottleneck, consider storing | |
| 79 // computed_outputs as a hash set. | |
| 80 for (const OutputFile& cur : target->computed_outputs()) { | |
|
scottmg
2015/05/14 23:04:04
(this could be std::find, but whatever you prefer)
brettw
2015/05/14 23:21:03
In this case the find code is silly looking becaus
| |
| 81 if (file == cur) | |
| 82 return true; | |
| 83 } | |
| 84 | |
| 85 // Check all public dependencies (don't do data ones since those are | |
| 86 // runtime-only). | |
| 87 for (const auto& pair : target->public_deps()) { | |
| 88 if (EnsureFileIsGeneratedByDependency(pair.ptr, file, false, | |
| 89 seen_targets)) | |
| 90 return true; // Found a path. | |
| 91 } | |
| 92 | |
| 93 // Only check private deps if requested. | |
| 94 if (check_private_deps) { | |
| 95 for (const auto& pair : target->private_deps()) { | |
| 96 if (EnsureFileIsGeneratedByDependency(pair.ptr, file, false, | |
| 97 seen_targets)) | |
| 98 return true; // Found a path. | |
| 99 } | |
| 100 } | |
| 101 return false; | |
| 102 } | |
| 103 | |
| 63 } // namespace | 104 } // namespace |
| 64 | 105 |
| 65 Target::Target(const Settings* settings, const Label& label) | 106 Target::Target(const Settings* settings, const Label& label) |
| 66 : Item(settings, label), | 107 : Item(settings, label), |
| 67 output_type_(UNKNOWN), | 108 output_type_(UNKNOWN), |
| 68 all_headers_public_(true), | 109 all_headers_public_(true), |
| 69 check_includes_(true), | 110 check_includes_(true), |
| 70 complete_static_lib_(false), | 111 complete_static_lib_(false), |
| 71 testonly_(false), | 112 testonly_(false), |
| 72 toolchain_(nullptr) { | 113 toolchain_(nullptr) { |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 125 all_lib_dirs_.append(cur.lib_dirs().begin(), cur.lib_dirs().end()); | 166 all_lib_dirs_.append(cur.lib_dirs().begin(), cur.lib_dirs().end()); |
| 126 all_libs_.append(cur.libs().begin(), cur.libs().end()); | 167 all_libs_.append(cur.libs().begin(), cur.libs().end()); |
| 127 } | 168 } |
| 128 | 169 |
| 129 PullDependentTargets(); | 170 PullDependentTargets(); |
| 130 PullForwardedDependentConfigs(); | 171 PullForwardedDependentConfigs(); |
| 131 PullRecursiveHardDeps(); | 172 PullRecursiveHardDeps(); |
| 132 | 173 |
| 133 FillOutputFiles(); | 174 FillOutputFiles(); |
| 134 | 175 |
| 135 if (!CheckVisibility(err)) | 176 if (settings()->build_settings()->check_for_bad_items()) { |
| 136 return false; | 177 if (!CheckVisibility(err)) |
| 137 if (!CheckTestonly(err)) | 178 return false; |
| 138 return false; | 179 if (!CheckTestonly(err)) |
| 139 if (!CheckNoNestedStaticLibs(err)) | 180 return false; |
| 140 return false; | 181 if (!CheckNoNestedStaticLibs(err)) |
| 182 return false; | |
| 183 if (!CheckSourcesGenerated(err)) | |
| 184 return false; | |
| 185 } | |
| 141 | 186 |
| 142 return true; | 187 return true; |
| 143 } | 188 } |
| 144 | 189 |
| 145 bool Target::IsLinkable() const { | 190 bool Target::IsLinkable() const { |
| 146 return output_type_ == STATIC_LIBRARY || output_type_ == SHARED_LIBRARY; | 191 return output_type_ == STATIC_LIBRARY || output_type_ == SHARED_LIBRARY; |
| 147 } | 192 } |
| 148 | 193 |
| 149 bool Target::IsFinal() const { | 194 bool Target::IsFinal() const { |
| 150 return output_type_ == EXECUTABLE || output_type_ == SHARED_LIBRARY || | 195 return output_type_ == EXECUTABLE || output_type_ == SHARED_LIBRARY || |
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 297 // when Android uses a better STL. | 342 // when Android uses a better STL. |
| 298 for (std::set<const Target*>::const_iterator cur = | 343 for (std::set<const Target*>::const_iterator cur = |
| 299 pair.ptr->recursive_hard_deps().begin(); | 344 pair.ptr->recursive_hard_deps().begin(); |
| 300 cur != pair.ptr->recursive_hard_deps().end(); ++cur) | 345 cur != pair.ptr->recursive_hard_deps().end(); ++cur) |
| 301 recursive_hard_deps_.insert(*cur); | 346 recursive_hard_deps_.insert(*cur); |
| 302 } | 347 } |
| 303 } | 348 } |
| 304 | 349 |
| 305 void Target::FillOutputFiles() { | 350 void Target::FillOutputFiles() { |
| 306 const Tool* tool = toolchain_->GetToolForTargetFinalOutput(this); | 351 const Tool* tool = toolchain_->GetToolForTargetFinalOutput(this); |
| 352 bool check_tool_outputs = false; | |
| 307 switch (output_type_) { | 353 switch (output_type_) { |
| 308 case GROUP: | 354 case GROUP: |
| 309 case SOURCE_SET: | 355 case SOURCE_SET: |
| 310 case COPY_FILES: | 356 case COPY_FILES: |
| 311 case ACTION: | 357 case ACTION: |
| 312 case ACTION_FOREACH: { | 358 case ACTION_FOREACH: { |
| 313 // These don't get linked to and use stamps which should be the first | 359 // These don't get linked to and use stamps which should be the first |
| 314 // entry in the outputs. These stamps are named | 360 // entry in the outputs. These stamps are named |
| 315 // "<target_out_dir>/<targetname>.stamp". | 361 // "<target_out_dir>/<targetname>.stamp". |
| 316 dependency_output_file_ = GetTargetOutputDirAsOutputFile(this); | 362 dependency_output_file_ = GetTargetOutputDirAsOutputFile(this); |
| 317 dependency_output_file_.value().append(GetComputedOutputName(true)); | 363 dependency_output_file_.value().append(GetComputedOutputName(true)); |
| 318 dependency_output_file_.value().append(".stamp"); | 364 dependency_output_file_.value().append(".stamp"); |
| 319 break; | 365 break; |
| 320 } | 366 } |
| 321 case EXECUTABLE: | 367 case EXECUTABLE: |
| 322 // Executables don't get linked to, but the first output is used for | 368 // Executables don't get linked to, but the first output is used for |
| 323 // dependency management. | 369 // dependency management. |
| 324 CHECK_GE(tool->outputs().list().size(), 1u); | 370 CHECK_GE(tool->outputs().list().size(), 1u); |
| 371 check_tool_outputs = true; | |
| 325 dependency_output_file_ = | 372 dependency_output_file_ = |
| 326 SubstitutionWriter::ApplyPatternToLinkerAsOutputFile( | 373 SubstitutionWriter::ApplyPatternToLinkerAsOutputFile( |
| 327 this, tool, tool->outputs().list()[0]); | 374 this, tool, tool->outputs().list()[0]); |
| 328 break; | 375 break; |
| 329 case STATIC_LIBRARY: | 376 case STATIC_LIBRARY: |
| 330 // Static libraries both have dependencies and linking going off of the | 377 // Static libraries both have dependencies and linking going off of the |
| 331 // first output. | 378 // first output. |
| 332 CHECK(tool->outputs().list().size() >= 1); | 379 CHECK(tool->outputs().list().size() >= 1); |
| 380 check_tool_outputs = true; | |
| 333 link_output_file_ = dependency_output_file_ = | 381 link_output_file_ = dependency_output_file_ = |
| 334 SubstitutionWriter::ApplyPatternToLinkerAsOutputFile( | 382 SubstitutionWriter::ApplyPatternToLinkerAsOutputFile( |
| 335 this, tool, tool->outputs().list()[0]); | 383 this, tool, tool->outputs().list()[0]); |
| 336 break; | 384 break; |
| 337 case SHARED_LIBRARY: | 385 case SHARED_LIBRARY: |
| 338 CHECK(tool->outputs().list().size() >= 1); | 386 CHECK(tool->outputs().list().size() >= 1); |
| 387 check_tool_outputs = true; | |
| 339 if (tool->link_output().empty() && tool->depend_output().empty()) { | 388 if (tool->link_output().empty() && tool->depend_output().empty()) { |
| 340 // Default behavior, use the first output file for both. | 389 // Default behavior, use the first output file for both. |
| 341 link_output_file_ = dependency_output_file_ = | 390 link_output_file_ = dependency_output_file_ = |
| 342 SubstitutionWriter::ApplyPatternToLinkerAsOutputFile( | 391 SubstitutionWriter::ApplyPatternToLinkerAsOutputFile( |
| 343 this, tool, tool->outputs().list()[0]); | 392 this, tool, tool->outputs().list()[0]); |
| 344 } else { | 393 } else { |
| 345 // Use the tool-specified ones. | 394 // Use the tool-specified ones. |
| 346 if (!tool->link_output().empty()) { | 395 if (!tool->link_output().empty()) { |
| 347 link_output_file_ = | 396 link_output_file_ = |
| 348 SubstitutionWriter::ApplyPatternToLinkerAsOutputFile( | 397 SubstitutionWriter::ApplyPatternToLinkerAsOutputFile( |
| 349 this, tool, tool->link_output()); | 398 this, tool, tool->link_output()); |
| 350 } | 399 } |
| 351 if (!tool->depend_output().empty()) { | 400 if (!tool->depend_output().empty()) { |
| 352 dependency_output_file_ = | 401 dependency_output_file_ = |
| 353 SubstitutionWriter::ApplyPatternToLinkerAsOutputFile( | 402 SubstitutionWriter::ApplyPatternToLinkerAsOutputFile( |
| 354 this, tool, tool->depend_output()); | 403 this, tool, tool->depend_output()); |
| 355 } | 404 } |
| 356 } | 405 } |
| 357 break; | 406 break; |
| 358 case UNKNOWN: | 407 case UNKNOWN: |
| 359 default: | 408 default: |
| 360 NOTREACHED(); | 409 NOTREACHED(); |
| 361 } | 410 } |
| 411 | |
| 412 // Count all outputs from this tool as something generated by this target. | |
| 413 if (check_tool_outputs) { | |
| 414 SubstitutionWriter::ApplyListToLinkerAsOutputFile( | |
| 415 this, tool, tool->outputs(), &computed_outputs_); | |
| 416 } | |
| 417 | |
| 418 // Also count anything the target has declared to be an output. | |
| 419 std::vector<SourceFile> outputs_as_sources; | |
| 420 action_values_.GetOutputsAsSourceFiles(this, &outputs_as_sources); | |
| 421 for (const SourceFile& out : outputs_as_sources) | |
| 422 computed_outputs_.push_back(OutputFile(settings()->build_settings(), out)); | |
| 362 } | 423 } |
| 363 | 424 |
| 364 bool Target::CheckVisibility(Err* err) const { | 425 bool Target::CheckVisibility(Err* err) const { |
| 365 for (const auto& pair : GetDeps(DEPS_ALL)) { | 426 for (const auto& pair : GetDeps(DEPS_ALL)) { |
| 366 if (!Visibility::CheckItemVisibility(this, pair.ptr, err)) | 427 if (!Visibility::CheckItemVisibility(this, pair.ptr, err)) |
| 367 return false; | 428 return false; |
| 368 } | 429 } |
| 369 return true; | 430 return true; |
| 370 } | 431 } |
| 371 | 432 |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 402 | 463 |
| 403 // Verify no inherited libraries are static libraries. | 464 // Verify no inherited libraries are static libraries. |
| 404 for (const auto& lib : inherited_libraries().GetOrdered()) { | 465 for (const auto& lib : inherited_libraries().GetOrdered()) { |
| 405 if (lib->output_type() == Target::STATIC_LIBRARY) { | 466 if (lib->output_type() == Target::STATIC_LIBRARY) { |
| 406 *err = MakeStaticLibDepsError(this, lib); | 467 *err = MakeStaticLibDepsError(this, lib); |
| 407 return false; | 468 return false; |
| 408 } | 469 } |
| 409 } | 470 } |
| 410 return true; | 471 return true; |
| 411 } | 472 } |
| 473 | |
| 474 bool Target::CheckSourcesGenerated(Err* err) const { | |
| 475 // Checks that any inputs or sources to this target that are in the build | |
| 476 // directory are generated by a target that this one transitively depends on | |
| 477 // in some way. We already guarantee that all generated files are written | |
| 478 // to the build dir. | |
| 479 for (const SourceFile& file : sources_) { | |
| 480 if (!CheckSourceGenerated(file, err)) | |
| 481 return false; | |
| 482 } | |
| 483 for (const SourceFile& file : inputs_) { | |
| 484 if (!CheckSourceGenerated(file, err)) | |
| 485 return false; | |
| 486 } | |
| 487 return true; | |
| 488 } | |
| 489 | |
| 490 bool Target::CheckSourceGenerated(const SourceFile& source, Err* err) const { | |
| 491 if (!IsStringInOutputDir(settings()->build_settings()->build_dir(), | |
| 492 source.value())) | |
| 493 return true; // Not in output dir, this is OK. | |
| 494 | |
| 495 OutputFile out_file(settings()->build_settings(), source); | |
| 496 std::set<const Target*> seen_targets; | |
| 497 if (!EnsureFileIsGeneratedByDependency(this, out_file, true, &seen_targets)) { | |
| 498 *err = Err(defined_from(), | |
| 499 "Target has an input not generated by a dependency.", | |
| 500 "The source or input file\n " + source.value() + "\n" | |
| 501 "on the target\n " + label().GetUserVisibleName(false) + "\n" | |
| 502 "is in the build directory but was not specified as an output by\n" | |
| 503 "any of this target's dependencies or transitive public dependencies.\n" | |
| 504 "\n" | |
| 505 "If you have generated inputs, there needs to be a dependency path\n" | |
| 506 "between the two targets in addition to just listing the files.\n" | |
| 507 "For indirect dependencies, the intermediate ones must be\n" | |
| 508 "public_deps. data_deps don't count since they're only runtime\n" | |
| 509 "dependencies.\n" | |
| 510 "\n" | |
| 511 "Start with:\n" | |
| 512 " gn refs " + | |
| 513 DirectoryWithNoLastSlash(settings()->build_settings()->build_dir()) + | |
| 514 " " + source.value() + "\n" | |
| 515 "which will print all targets that reference the file EITHER as an\n" | |
| 516 "input or an output, and go from there."); | |
| 517 return false; | |
| 518 } | |
| 519 return true; | |
| 520 } | |
| OLD | NEW |