| // 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) { | |
| OutputString(output_file.value()); | |
| OutputString("\n"); | |
| } | |
| return 0; | |
| } | |
| } // namespace commands |