|  | // 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 "gn/target_generator.h" | 
|  |  | 
|  | #include <stddef.h> | 
|  |  | 
|  | #include <memory> | 
|  | #include <utility> | 
|  |  | 
|  | #include "gn/action_target_generator.h" | 
|  | #include "gn/binary_target_generator.h" | 
|  | #include "gn/build_settings.h" | 
|  | #include "gn/bundle_data_target_generator.h" | 
|  | #include "gn/config.h" | 
|  | #include "gn/copy_target_generator.h" | 
|  | #include "gn/create_bundle_target_generator.h" | 
|  | #include "gn/err.h" | 
|  | #include "gn/filesystem_utils.h" | 
|  | #include "gn/functions.h" | 
|  | #include "gn/generated_file_target_generator.h" | 
|  | #include "gn/group_target_generator.h" | 
|  | #include "gn/metadata.h" | 
|  | #include "gn/parse_tree.h" | 
|  | #include "gn/scheduler.h" | 
|  | #include "gn/scope.h" | 
|  | #include "gn/token.h" | 
|  | #include "gn/value.h" | 
|  | #include "gn/value_extractors.h" | 
|  | #include "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 if (output_type == functions::kRustProcMacro) { | 
|  | BinaryTargetGenerator generator(target.get(), scope, function_call, | 
|  | Target::RUST_PROC_MACRO, 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(scope_->settings()->build_settings(), | 
|  | *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(scope_->settings()->build_settings(), *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(scope_->settings()->build_settings(), *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; | 
|  | } |