|  | // 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 <stddef.h> | 
|  |  | 
|  | #include <algorithm> | 
|  |  | 
|  | #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 <out_dir> <list of target or file names...> | 
|  |  | 
|  | 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<std::string>& args) { | 
|  | if (args.size() < 2) { | 
|  | Err(Location(), | 
|  | "Expected a build dir and one or more input files or targets.\n" | 
|  | "Usage: \"gn outputs <out_dir> <target-or-file>*\"") | 
|  | .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<std::string> inputs(args.begin() + 1, args.end()); | 
|  |  | 
|  | UniqueVector<const Target*> target_matches; | 
|  | UniqueVector<const Config*> config_matches; | 
|  | UniqueVector<const Toolchain*> toolchain_matches; | 
|  | UniqueVector<SourceFile> 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<OutputFile> outputs; | 
|  |  | 
|  | // Files. This must go first because it may add to the "targets" list. | 
|  | std::vector<const Target*> all_targets = | 
|  | setup->builder().GetAllResolvedTargets(); | 
|  | for (const SourceFile& file : file_matches) { | 
|  | std::vector<TargetContainingFile> 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<OutputFile> 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<SourceFile> 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 |