1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <stddef.h>
6
7 #include <algorithm>
8
9 #include "base/command_line.h"
10 #include "base/strings/stringprintf.h"
11 #include "gn/commands.h"
12 #include "gn/setup.h"
13 #include "gn/standard_out.h"
14
15 namespace commands {
16
17 const char kOutputs[] = "outputs";
18 const char kOutputs_HelpShort[] = "outputs: Which files a source/target make.";
19 const char kOutputs_Help[] =
20 R"(gn outputs <out_dir> <list of target or file names...>
21
22 Lists the output files corresponding to the given target(s) or file name(s).
23 There can be multiple outputs because there can be more than one output
24 generated by a build step, and there can be more than one toolchain matched.
25 You can also list multiple inputs which will generate a union of all the
26 outputs from those inputs.
27
28 - The input target/file names are relative to the current directory.
29
30 - The output file names are relative to the root build directory.
31
32 This command is useful for finding a ninja command that will build only a
33 portion of the build.
34
35 Target outputs
36
37 If the parameter is a target name that includes a toolchain, it will match
38 only that target in that toolchain. If no toolchain is specified, it will
39 match all targets with that name in any toolchain.
40
41 The result will be the outputs specified by that target which could be a
42 library, executable, output of an action, a stamp file, etc.
43
44 File outputs
45
46 If the parameter is a file name it will compute the output for that compile
47 step for all targets in all toolchains that contain that file as a source
48 file.
49
50 If the source is not compiled (e.g. a header or text file), the command will
51 produce no output.
52
53 If the source is listed as an "input" to a binary target or action will
54 resolve to that target's outputs.
55
56 Example
57
58 gn outputs out/debug some/directory:some_target
59 Find the outputs of a given target.
60
61 gn outputs out/debug src/project/my_file.cc | xargs ninja -C out/debug
62 Compiles just the given source file in all toolchains it's referenced in.
63
64 git diff --name-only | xargs gn outputs out/x64 | xargs ninja -C out/x64
65 Compiles all files changed in git.
66 )";
67
RunOutputs(const std::vector<std::string> & args)68 int RunOutputs(const std::vector<std::string>& args) {
69 if (args.size() < 2) {
70 Err(Location(),
71 "Expected a build dir and one or more input files or targets.\n"
72 "Usage: \"gn outputs <out_dir> <target-or-file>*\"")
73 .PrintToStdout();
74 return 1;
75 }
76
77 // Deliberately leaked to avoid expensive process teardown.
78 Setup* setup = new Setup;
79 if (!setup->DoSetup(args[0], false))
80 return 1;
81 if (!setup->Run())
82 return 1;
83
84 std::vector<std::string> inputs(args.begin() + 1, args.end());
85
86 UniqueVector<const Target*> target_matches;
87 UniqueVector<const Config*> config_matches;
88 UniqueVector<const Toolchain*> toolchain_matches;
89 UniqueVector<SourceFile> file_matches;
90 if (!ResolveFromCommandLineInput(setup, inputs, false, &target_matches,
91 &config_matches, &toolchain_matches,
92 &file_matches))
93 return 1;
94
95 // We only care about targets and files.
96 if (target_matches.empty() && file_matches.empty()) {
97 Err(Location(), "The input matched no targets or files.").PrintToStdout();
98 return 1;
99 }
100
101 // Resulting outputs.
102 std::vector<OutputFile> outputs;
103
104 // Files. This must go first because it may add to the "targets" list.
105 std::vector<const Target*> all_targets =
106 setup->builder().GetAllResolvedTargets();
107 for (const SourceFile& file : file_matches) {
108 std::vector<TargetContainingFile> targets;
109 GetTargetsContainingFile(setup, all_targets, file, false, &targets);
110 if (targets.empty()) {
111 Err(Location(), base::StringPrintf("No targets reference the file '%s'.",
112 file.value().c_str()))
113 .PrintToStdout();
114 return 1;
115 }
116
117 // There can be more than one target that references this file, evaluate the
118 // output name in all of them.
119 for (const TargetContainingFile& pair : targets) {
120 if (pair.second == HowTargetContainsFile::kInputs) {
121 // Inputs maps to the target itself. This will be evaluated below.
122 target_matches.push_back(pair.first);
123 } else if (pair.second == HowTargetContainsFile::kSources) {
124 // Source file, check it.
125 const char* computed_tool = nullptr;
126 std::vector<OutputFile> file_outputs;
127 pair.first->GetOutputFilesForSource(file, &computed_tool,
128 &file_outputs);
129 outputs.insert(outputs.end(), file_outputs.begin(), file_outputs.end());
130 }
131 }
132 }
133
134 // Targets.
135 for (const Target* target : target_matches) {
136 std::vector<SourceFile> output_files;
137 Err err;
138 if (!target->GetOutputsAsSourceFiles(LocationRange(), true, &output_files,
139 &err)) {
140 err.PrintToStdout();
141 return 1;
142 }
143
144 // Convert to OutputFiles.
145 for (const SourceFile& file : output_files)
146 outputs.emplace_back(&setup->build_settings(), file);
147 }
148
149 // Print.
150 for (const OutputFile& output_file : outputs)
151 printf("%s\n", output_file.value().c_str());
152 return 0;
153 }
154
155 } // namespace commands
156