// Copyright 2020 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 #include #include "base/command_line.h" #include "base/strings/stringprintf.h" #include "gn/commands.h" #include "gn/setup.h" #include "gn/standard_out.h" namespace commands { const char kOutputs[] = "outputs"; const char kOutputs_HelpShort[] = "outputs: Which files a source/target make."; const char kOutputs_Help[] = R"(gn outputs Lists the output files corresponding to the given target(s) or file name(s). There can be multiple outputs because there can be more than one output generated by a build step, and there can be more than one toolchain matched. You can also list multiple inputs which will generate a union of all the outputs from those inputs. - The input target/file names are relative to the current directory. - The output file names are relative to the root build directory. This command is useful for finding a ninja command that will build only a portion of the build. Target outputs If the parameter is a target name that includes a toolchain, it will match only that target in that toolchain. If no toolchain is specified, it will match all targets with that name in any toolchain. The result will be the outputs specified by that target which could be a library, executable, output of an action, a stamp file, etc. File outputs If the parameter is a file name it will compute the output for that compile step for all targets in all toolchains that contain that file as a source file. If the source is not compiled (e.g. a header or text file), the command will produce no output. If the source is listed as an "input" to a binary target or action will resolve to that target's outputs. Example gn outputs out/debug some/directory:some_target Find the outputs of a given target. gn outputs out/debug src/project/my_file.cc | xargs ninja -C out/debug Compiles just the given source file in all toolchains it's referenced in. git diff --name-only | xargs gn outputs out/x64 | xargs ninja -C out/x64 Compiles all files changed in git. )"; int RunOutputs(const std::vector& args) { if (args.size() < 2) { Err(Location(), "Expected a build dir and one or more input files or targets.\n" "Usage: \"gn outputs *\"") .PrintToStdout(); return 1; } // Deliberately leaked to avoid expensive process teardown. Setup* setup = new Setup; if (!setup->DoSetup(args[0], false)) return 1; if (!setup->Run()) return 1; std::vector inputs(args.begin() + 1, args.end()); UniqueVector target_matches; UniqueVector config_matches; UniqueVector toolchain_matches; UniqueVector file_matches; if (!ResolveFromCommandLineInput(setup, inputs, false, &target_matches, &config_matches, &toolchain_matches, &file_matches)) return 1; // We only care about targets and files. if (target_matches.empty() && file_matches.empty()) { Err(Location(), "The input matched no targets or files.").PrintToStdout(); return 1; } // Resulting outputs. std::vector outputs; // Files. This must go first because it may add to the "targets" list. std::vector all_targets = setup->builder().GetAllResolvedTargets(); for (const SourceFile& file : file_matches) { std::vector targets; GetTargetsContainingFile(setup, all_targets, file, false, &targets); if (targets.empty()) { Err(Location(), base::StringPrintf("No targets reference the file '%s'.", file.value().c_str())) .PrintToStdout(); return 1; } // There can be more than one target that references this file, evaluate the // output name in all of them. for (const TargetContainingFile& pair : targets) { if (pair.second == HowTargetContainsFile::kInputs) { // Inputs maps to the target itself. This will be evaluated below. target_matches.push_back(pair.first); } else if (pair.second == HowTargetContainsFile::kSources) { // Source file, check it. const char* computed_tool = nullptr; std::vector file_outputs; pair.first->GetOutputFilesForSource(file, &computed_tool, &file_outputs); outputs.insert(outputs.end(), file_outputs.begin(), file_outputs.end()); } } } // Targets. for (const Target* target : target_matches) { std::vector output_files; Err err; if (!target->GetOutputsAsSourceFiles(LocationRange(), true, &output_files, &err)) { err.PrintToStdout(); return 1; } // Convert to OutputFiles. for (const SourceFile& file : output_files) outputs.emplace_back(&setup->build_settings(), file); } // Print. for (const OutputFile& output_file : outputs) printf("%s\n", output_file.value().c_str()); return 0; } } // namespace commands