// 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/commands.h" #include #include #include "base/command_line.h" #include "base/environment.h" #include "base/files/file_util.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "gn/builder.h" #include "gn/config_values_extractors.h" #include "gn/filesystem_utils.h" #include "gn/item.h" #include "gn/label.h" #include "gn/label_pattern.h" #include "gn/ninja_build_writer.h" #include "gn/setup.h" #include "gn/standard_out.h" #include "gn/switches.h" #include "gn/target.h" #include "util/atomic_write.h" #include "util/build_config.h" namespace commands { namespace { // Like above but the input string can be a pattern that matches multiple // targets. If the input does not parse as a pattern, prints and error and // returns false. If the pattern is valid, fills the vector (which might be // empty if there are no matches) and returns true. // // If default_toolchain_only is true, a pattern with an unspecified toolchain // will match the default toolchain only. If true, all toolchains will be // matched. bool ResolveTargetsFromCommandLinePattern(Setup* setup, const std::string& label_pattern, bool default_toolchain_only, std::vector* matches) { Value pattern_value(nullptr, label_pattern); Err err; LabelPattern pattern = LabelPattern::GetPattern( SourceDirForCurrentDirectory(setup->build_settings().root_path()), setup->build_settings().root_path_utf8(), pattern_value, &err); if (err.has_error()) { err.PrintToStdout(); return false; } if (default_toolchain_only) { // By default a pattern with an empty toolchain will match all toolchains. // If the caller wants to default to the main toolchain only, set it // explicitly. if (pattern.toolchain().is_null()) { // No explicit toolchain set. pattern.set_toolchain(setup->loader()->default_toolchain_label()); } } std::vector pattern_vector; pattern_vector.push_back(pattern); FilterTargetsByPatterns(setup->builder().GetAllResolvedTargets(), pattern_vector, matches); return true; } // If there's an error, it will be printed and false will be returned. bool ResolveStringFromCommandLineInput( Setup* setup, const SourceDir& current_dir, const std::string& input, bool default_toolchain_only, UniqueVector* target_matches, UniqueVector* config_matches, UniqueVector* toolchain_matches, UniqueVector* file_matches) { if (LabelPattern::HasWildcard(input)) { // For now, only match patterns against targets. It might be nice in the // future to allow the user to specify which types of things they want to // match, but it should probably only match targets by default. std::vector target_match_vector; if (!ResolveTargetsFromCommandLinePattern( setup, input, default_toolchain_only, &target_match_vector)) return false; for (const Target* target : target_match_vector) target_matches->push_back(target); return true; } // Try to figure out what this thing is. Err err; Label label = Label::Resolve( current_dir, setup->build_settings().root_path_utf8(), setup->loader()->default_toolchain_label(), Value(nullptr, input), &err); if (err.has_error()) { // Not a valid label, assume this must be a file. err = Err(); file_matches->push_back(current_dir.ResolveRelativeFile( Value(nullptr, input), &err, setup->build_settings().root_path_utf8())); if (err.has_error()) { err.PrintToStdout(); return false; } return true; } const Item* item = setup->builder().GetItem(label); if (item) { if (const Config* as_config = item->AsConfig()) config_matches->push_back(as_config); else if (const Target* as_target = item->AsTarget()) target_matches->push_back(as_target); else if (const Toolchain* as_toolchain = item->AsToolchain()) toolchain_matches->push_back(as_toolchain); } else { // Not an item, assume this must be a file. file_matches->push_back(current_dir.ResolveRelativeFile( Value(nullptr, input), &err, setup->build_settings().root_path_utf8())); if (err.has_error()) { err.PrintToStdout(); return false; } } return true; } // Retrieves the target printing mode based on the command line flags for the // current process. Returns true on success. On error, prints a message to the // console and returns false. bool GetTargetPrintingMode(CommandSwitches::TargetPrintMode* mode) { *mode = CommandSwitches::Get().target_print_mode(); return true; } // Returns the target type filter based on the command line flags for the // current process. Returns true on success. On error, prints a message to the // console and returns false. // // Target::UNKNOWN will be set if there is no filter. Target::ACTION_FOREACH // will never be returned. Code applying the filters should apply Target::ACTION // to both ACTION and ACTION_FOREACH. bool GetTargetTypeFilter(Target::OutputType* type) { *type = CommandSwitches::Get().target_type(); return true; } // Applies any testonly filtering specified on the command line to the given // target set. On failure, prints an error and returns false. bool ApplyTestonlyFilter(std::vector* targets) { CommandSwitches::TestonlyMode testonly_mode = CommandSwitches::Get().testonly_mode(); if (targets->empty() || testonly_mode == CommandSwitches::TESTONLY_NONE) return true; bool testonly = (testonly_mode == CommandSwitches::TESTONLY_TRUE); // Filter into a copy of the vector, then replace the output. std::vector result; result.reserve(targets->size()); for (const Target* target : *targets) { if (target->testonly() == testonly) result.push_back(target); } *targets = std::move(result); return true; } // Applies any target type filtering specified on the command line to the given // target set. On failure, prints an error and returns false. bool ApplyTypeFilter(std::vector* targets) { Target::OutputType type = Target::UNKNOWN; if (!GetTargetTypeFilter(&type)) return false; if (targets->empty() || type == Target::UNKNOWN) return true; // Nothing to filter out. // Filter into a copy of the vector, then replace the output. std::vector result; result.reserve(targets->size()); for (const Target* target : *targets) { // Make "action" also apply to ACTION_FOREACH. if (target->output_type() == type || (type == Target::ACTION && target->output_type() == Target::ACTION_FOREACH)) result.push_back(target); } *targets = std::move(result); return true; } // Returns the file path of the BUILD.gn file generating this item. base::FilePath BuildFileForItem(const Item* item) { // Find the only BUILD.gn file listed in build_dependency_files() for // this Item. This may not exist if the item is defined in BUILDCONFIG.gn // instead, so account for this too. const SourceFile* buildconfig_gn = nullptr; const SourceFile* build_gn = nullptr; for (const SourceFile& build_file : item->build_dependency_files()) { const std::string& name = build_file.GetName(); if (name == "BUILDCONFIG.gn") { buildconfig_gn = &build_file; } else if (name == "BUILD.gn") { build_gn = &build_file; break; } } if (!build_gn) build_gn = buildconfig_gn; CHECK(build_gn) << "No BUILD.gn or BUILDCONFIG.gn file defining " << item->label().GetUserVisibleName(true); return build_gn->Resolve(item->settings()->build_settings()->root_path()); } void PrintTargetsAsBuildfiles(const std::vector& targets, base::ListValue* out) { // Output the set of unique source files. std::set unique_files; for (const Target* target : targets) unique_files.insert(FilePathToUTF8(BuildFileForItem(target))); for (const std::string& file : unique_files) { out->AppendString(file); } } void PrintTargetsAsLabels(const std::vector& targets, base::ListValue* out) { // Putting the labels into a set automatically sorts them for us. std::set