| // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "tools/gn/target_generator.h" | |
| #include <stddef.h> | |
| #include <memory> | |
| #include <utility> | |
| #include "tools/gn/action_target_generator.h" | |
| #include "tools/gn/binary_target_generator.h" | |
| #include "tools/gn/build_settings.h" | |
| #include "tools/gn/bundle_data_target_generator.h" | |
| #include "tools/gn/config.h" | |
| #include "tools/gn/copy_target_generator.h" | |
| #include "tools/gn/create_bundle_target_generator.h" | |
| #include "tools/gn/err.h" | |
| #include "tools/gn/filesystem_utils.h" | |
| #include "tools/gn/functions.h" | |
| #include "tools/gn/generated_file_target_generator.h" | |
| #include "tools/gn/group_target_generator.h" | |
| #include "tools/gn/metadata.h" | |
| #include "tools/gn/parse_tree.h" | |
| #include "tools/gn/scheduler.h" | |
| #include "tools/gn/scope.h" | |
| #include "tools/gn/token.h" | |
| #include "tools/gn/value.h" | |
| #include "tools/gn/value_extractors.h" | |
| #include "tools/gn/variables.h" | |
| TargetGenerator::TargetGenerator(Target* target, | |
| Scope* scope, | |
| const FunctionCallNode* function_call, | |
| Err* err) | |
| : target_(target), | |
| scope_(scope), | |
| function_call_(function_call), | |
| err_(err) {} | |
| TargetGenerator::~TargetGenerator() = default; | |
| void TargetGenerator::Run() { | |
| // All target types use these. | |
| if (!FillDependentConfigs()) | |
| return; | |
| if (!FillData()) | |
| return; | |
| if (!FillDependencies()) | |
| return; | |
| if (!FillMetadata()) | |
| return; | |
| if (!FillTestonly()) | |
| return; | |
| if (!FillAssertNoDeps()) | |
| return; | |
| if (!Visibility::FillItemVisibility(target_, scope_, err_)) | |
| return; | |
| if (!FillWriteRuntimeDeps()) | |
| return; | |
| // Do type-specific generation. | |
| DoRun(); | |
| } | |
| // static | |
| void TargetGenerator::GenerateTarget(Scope* scope, | |
| const FunctionCallNode* function_call, | |
| const std::vector<Value>& args, | |
| const std::string& output_type, | |
| Err* err) { | |
| // Name is the argument to the function. | |
| if (args.size() != 1u || args[0].type() != Value::STRING) { | |
| *err = Err(function_call, "Target generator requires one string argument.", | |
| "Otherwise I'm not sure what to call this target."); | |
| return; | |
| } | |
| // The location of the target is the directory name with no slash at the end. | |
| // FIXME(brettw) validate name. | |
| const Label& toolchain_label = ToolchainLabelForScope(scope); | |
| Label label(scope->GetSourceDir(), args[0].string_value(), | |
| toolchain_label.dir(), toolchain_label.name()); | |
| if (g_scheduler->verbose_logging()) | |
| g_scheduler->Log("Defining target", label.GetUserVisibleName(true)); | |
| std::unique_ptr<Target> target = std::make_unique<Target>( | |
| scope->settings(), label, scope->build_dependency_files()); | |
| target->set_defined_from(function_call); | |
| // Create and call out to the proper generator. | |
| if (output_type == functions::kBundleData) { | |
| BundleDataTargetGenerator generator(target.get(), scope, function_call, | |
| err); | |
| generator.Run(); | |
| } else if (output_type == functions::kCreateBundle) { | |
| CreateBundleTargetGenerator generator(target.get(), scope, function_call, | |
| err); | |
| generator.Run(); | |
| } else if (output_type == functions::kCopy) { | |
| CopyTargetGenerator generator(target.get(), scope, function_call, err); | |
| generator.Run(); | |
| } else if (output_type == functions::kAction) { | |
| ActionTargetGenerator generator(target.get(), scope, function_call, | |
| Target::ACTION, err); | |
| generator.Run(); | |
| } else if (output_type == functions::kActionForEach) { | |
| ActionTargetGenerator generator(target.get(), scope, function_call, | |
| Target::ACTION_FOREACH, err); | |
| generator.Run(); | |
| } else if (output_type == functions::kExecutable) { | |
| BinaryTargetGenerator generator(target.get(), scope, function_call, | |
| Target::EXECUTABLE, err); | |
| generator.Run(); | |
| } else if (output_type == functions::kGroup) { | |
| GroupTargetGenerator generator(target.get(), scope, function_call, err); | |
| generator.Run(); | |
| } else if (output_type == functions::kLoadableModule) { | |
| BinaryTargetGenerator generator(target.get(), scope, function_call, | |
| Target::LOADABLE_MODULE, err); | |
| generator.Run(); | |
| } else if (output_type == functions::kSharedLibrary) { | |
| BinaryTargetGenerator generator(target.get(), scope, function_call, | |
| Target::SHARED_LIBRARY, err); | |
| generator.Run(); | |
| } else if (output_type == functions::kSourceSet) { | |
| BinaryTargetGenerator generator(target.get(), scope, function_call, | |
| Target::SOURCE_SET, err); | |
| generator.Run(); | |
| } else if (output_type == functions::kStaticLibrary) { | |
| BinaryTargetGenerator generator(target.get(), scope, function_call, | |
| Target::STATIC_LIBRARY, err); | |
| generator.Run(); | |
| } else if (output_type == functions::kGeneratedFile) { | |
| GeneratedFileTargetGenerator generator(target.get(), scope, function_call, | |
| Target::GENERATED_FILE, err); | |
| generator.Run(); | |
| } else if (output_type == functions::kRustLibrary) { | |
| BinaryTargetGenerator generator(target.get(), scope, function_call, | |
| Target::RUST_LIBRARY, err); | |
| generator.Run(); | |
| } else { | |
| *err = Err(function_call, "Not a known target type", | |
| "I am very confused by the target type \"" + output_type + "\""); | |
| } | |
| if (err->has_error()) | |
| return; | |
| // Save this target for the file. | |
| Scope::ItemVector* collector = scope->GetItemCollector(); | |
| if (!collector) { | |
| *err = Err(function_call, "Can't define a target in this context."); | |
| return; | |
| } | |
| collector->push_back(std::move(target)); | |
| } | |
| const BuildSettings* TargetGenerator::GetBuildSettings() const { | |
| return scope_->settings()->build_settings(); | |
| } | |
| bool TargetGenerator::FillSources() { | |
| const Value* value = scope_->GetValue(variables::kSources, true); | |
| if (!value) | |
| return true; | |
| Target::FileList dest_sources; | |
| if (!ExtractListOfRelativeFiles(scope_->settings()->build_settings(), *value, | |
| scope_->GetSourceDir(), &dest_sources, err_)) | |
| return false; | |
| target_->sources() = std::move(dest_sources); | |
| return true; | |
| } | |
| bool TargetGenerator::FillPublic() { | |
| const Value* value = scope_->GetValue(variables::kPublic, true); | |
| if (!value) | |
| return true; | |
| // If the public headers are defined, don't default to public. | |
| target_->set_all_headers_public(false); | |
| Target::FileList dest_public; | |
| if (!ExtractListOfRelativeFiles(scope_->settings()->build_settings(), *value, | |
| scope_->GetSourceDir(), &dest_public, err_)) | |
| return false; | |
| target_->public_headers() = std::move(dest_public); | |
| return true; | |
| } | |
| bool TargetGenerator::FillConfigs() { | |
| return FillGenericConfigs(variables::kConfigs, &target_->configs()); | |
| } | |
| bool TargetGenerator::FillDependentConfigs() { | |
| if (!FillGenericConfigs(variables::kAllDependentConfigs, | |
| &target_->all_dependent_configs())) | |
| return false; | |
| if (!FillGenericConfigs(variables::kPublicConfigs, | |
| &target_->public_configs())) | |
| return false; | |
| return true; | |
| } | |
| bool TargetGenerator::FillData() { | |
| const Value* value = scope_->GetValue(variables::kData, true); | |
| if (!value) | |
| return true; | |
| if (!value->VerifyTypeIs(Value::LIST, err_)) | |
| return false; | |
| const std::vector<Value>& input_list = value->list_value(); | |
| std::vector<std::string>& output_list = target_->data(); | |
| output_list.reserve(input_list.size()); | |
| const SourceDir& dir = scope_->GetSourceDir(); | |
| const std::string& root_path = | |
| scope_->settings()->build_settings()->root_path_utf8(); | |
| for (size_t i = 0; i < input_list.size(); i++) { | |
| const Value& input = input_list[i]; | |
| if (!input.VerifyTypeIs(Value::STRING, err_)) | |
| return false; | |
| const std::string input_str = input.string_value(); | |
| // Treat each input as either a file or a directory, depending on the | |
| // last character. | |
| bool as_dir = !input_str.empty() && input_str[input_str.size() - 1] == '/'; | |
| std::string resolved = | |
| dir.ResolveRelativeAs(!as_dir, input, err_, root_path, &input_str); | |
| if (err_->has_error()) | |
| return false; | |
| output_list.push_back(resolved); | |
| } | |
| return true; | |
| } | |
| bool TargetGenerator::FillDependencies() { | |
| if (!FillGenericDeps(variables::kDeps, &target_->private_deps())) | |
| return false; | |
| if (!FillGenericDeps(variables::kPublicDeps, &target_->public_deps())) | |
| return false; | |
| if (!FillGenericDeps(variables::kDataDeps, &target_->data_deps())) | |
| return false; | |
| // "data_deps" was previously named "datadeps". For backwards-compat, read | |
| // the old one if no "data_deps" were specified. | |
| if (!scope_->GetValue(variables::kDataDeps, false)) { | |
| if (!FillGenericDeps("datadeps", &target_->data_deps())) | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool TargetGenerator::FillMetadata() { | |
| // Need to get a mutable value to mark all values in the scope as used. This | |
| // cannot be done on a const Scope. | |
| Value* value = scope_->GetMutableValue(variables::kMetadata, | |
| Scope::SEARCH_CURRENT, true); | |
| if (!value) | |
| return true; | |
| if (!value->VerifyTypeIs(Value::SCOPE, err_)) | |
| return false; | |
| Scope* scope_value = value->scope_value(); | |
| scope_value->GetCurrentScopeValues(&target_->metadata().contents()); | |
| scope_value->MarkAllUsed(); | |
| // Metadata values should always hold lists of Values, such that they can be | |
| // collected and concatenated. Any additional specific type verification is | |
| // done at walk time. | |
| for (const auto& iter : target_->metadata().contents()) { | |
| if (!iter.second.VerifyTypeIs(Value::LIST, err_)) | |
| return false; | |
| } | |
| target_->metadata().set_source_dir(scope_->GetSourceDir()); | |
| target_->metadata().set_origin(value->origin()); | |
| return true; | |
| } | |
| bool TargetGenerator::FillTestonly() { | |
| const Value* value = scope_->GetValue(variables::kTestonly, true); | |
| if (value) { | |
| if (!value->VerifyTypeIs(Value::BOOLEAN, err_)) | |
| return false; | |
| target_->set_testonly(value->boolean_value()); | |
| } | |
| return true; | |
| } | |
| bool TargetGenerator::FillAssertNoDeps() { | |
| const Value* value = scope_->GetValue(variables::kAssertNoDeps, true); | |
| if (value) { | |
| return ExtractListOfLabelPatterns(*value, scope_->GetSourceDir(), | |
| &target_->assert_no_deps(), err_); | |
| } | |
| return true; | |
| } | |
| bool TargetGenerator::FillOutputs(bool allow_substitutions) { | |
| const Value* value = scope_->GetValue(variables::kOutputs, true); | |
| if (!value) | |
| return true; | |
| SubstitutionList& outputs = target_->action_values().outputs(); | |
| if (!outputs.Parse(*value, err_)) | |
| return false; | |
| if (!allow_substitutions) { | |
| // Verify no substitutions were actually used. | |
| if (!outputs.required_types().empty()) { | |
| *err_ = | |
| Err(*value, "Source expansions not allowed here.", | |
| "The outputs of this target used source {{expansions}} but this " | |
| "target type\ndoesn't support them. Just express the outputs " | |
| "literally."); | |
| return false; | |
| } | |
| } | |
| // Check the substitutions used are valid for this purpose. | |
| if (!EnsureValidSubstitutions(outputs.required_types(), | |
| &IsValidSourceSubstitution, value->origin(), | |
| err_)) | |
| return false; | |
| // Validate that outputs are in the output dir. | |
| CHECK(outputs.list().size() == value->list_value().size()); | |
| for (size_t i = 0; i < outputs.list().size(); i++) { | |
| if (!EnsureSubstitutionIsInOutputDir(outputs.list()[i], | |
| value->list_value()[i])) | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool TargetGenerator::FillCheckIncludes() { | |
| const Value* value = scope_->GetValue(variables::kCheckIncludes, true); | |
| if (!value) | |
| return true; | |
| if (!value->VerifyTypeIs(Value::BOOLEAN, err_)) | |
| return false; | |
| target_->set_check_includes(value->boolean_value()); | |
| return true; | |
| } | |
| bool TargetGenerator::FillOutputExtension() { | |
| const Value* value = scope_->GetValue(variables::kOutputExtension, true); | |
| if (!value) | |
| return true; | |
| if (!value->VerifyTypeIs(Value::STRING, err_)) | |
| return false; | |
| target_->set_output_extension(value->string_value()); | |
| return true; | |
| } | |
| bool TargetGenerator::EnsureSubstitutionIsInOutputDir( | |
| const SubstitutionPattern& pattern, | |
| const Value& original_value) { | |
| if (pattern.ranges().empty()) { | |
| // Pattern is empty, error out (this prevents weirdness below). | |
| *err_ = Err(original_value, "This has an empty value in it."); | |
| return false; | |
| } | |
| if (pattern.ranges()[0].type == &SubstitutionLiteral) { | |
| // If the first thing is a literal, it must start with the output dir. | |
| if (!EnsureStringIsInOutputDir(GetBuildSettings()->build_dir(), | |
| pattern.ranges()[0].literal, | |
| original_value.origin(), err_)) | |
| return false; | |
| } else { | |
| // Otherwise, the first subrange must be a pattern that expands to | |
| // something in the output directory. | |
| if (!SubstitutionIsInOutputDir(pattern.ranges()[0].type)) { | |
| *err_ = | |
| Err(original_value, "File is not inside output directory.", | |
| "The given file should be in the output directory. Normally you\n" | |
| "would specify\n\"$target_out_dir/foo\" or " | |
| "\"{{source_gen_dir}}/foo\"."); | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| bool TargetGenerator::FillGenericConfigs(const char* var_name, | |
| UniqueVector<LabelConfigPair>* dest) { | |
| const Value* value = scope_->GetValue(var_name, true); | |
| if (value) { | |
| ExtractListOfUniqueLabels(*value, scope_->GetSourceDir(), | |
| ToolchainLabelForScope(scope_), dest, err_); | |
| } | |
| return !err_->has_error(); | |
| } | |
| bool TargetGenerator::FillGenericDeps(const char* var_name, | |
| LabelTargetVector* dest) { | |
| const Value* value = scope_->GetValue(var_name, true); | |
| if (value) { | |
| ExtractListOfLabels(*value, scope_->GetSourceDir(), | |
| ToolchainLabelForScope(scope_), dest, err_); | |
| } | |
| return !err_->has_error(); | |
| } | |
| bool TargetGenerator::FillWriteRuntimeDeps() { | |
| const Value* value = scope_->GetValue(variables::kWriteRuntimeDeps, true); | |
| if (!value) | |
| return true; | |
| // Compute the file name and make sure it's in the output dir. | |
| SourceFile source_file = scope_->GetSourceDir().ResolveRelativeFile( | |
| *value, err_, GetBuildSettings()->root_path_utf8()); | |
| if (err_->has_error()) | |
| return false; | |
| if (!EnsureStringIsInOutputDir(GetBuildSettings()->build_dir(), | |
| source_file.value(), value->origin(), err_)) | |
| return false; | |
| OutputFile output_file(GetBuildSettings(), source_file); | |
| target_->set_write_runtime_deps_output(output_file); | |
| return true; | |
| } |