1 // Copyright 2015 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 "gn/runtime_deps.h"
6
7 #include <map>
8 #include <set>
9 #include <sstream>
10
11 #include "base/command_line.h"
12 #include "base/files/file_util.h"
13 #include "base/strings/string_split.h"
14 #include "gn/build_settings.h"
15 #include "gn/builder.h"
16 #include "gn/deps_iterator.h"
17 #include "gn/filesystem_utils.h"
18 #include "gn/loader.h"
19 #include "gn/output_file.h"
20 #include "gn/scheduler.h"
21 #include "gn/settings.h"
22 #include "gn/string_output_buffer.h"
23 #include "gn/switches.h"
24 #include "gn/target.h"
25 #include "gn/trace.h"
26
27 namespace {
28
29 using RuntimeDepsVector = std::vector<std::pair<OutputFile, const Target*>>;
30
31 // Adds the given file to the deps list if it hasn't already been listed in
32 // the found_files list. Updates the list.
AddIfNew(const OutputFile & output_file,const Target * source,RuntimeDepsVector * deps,std::set<OutputFile> * found_file)33 void AddIfNew(const OutputFile& output_file,
34 const Target* source,
35 RuntimeDepsVector* deps,
36 std::set<OutputFile>* found_file) {
37 if (found_file->find(output_file) != found_file->end())
38 return; // Already there.
39 deps->push_back(std::make_pair(output_file, source));
40 }
41
42 // Automatically converts a string that looks like a source to an OutputFile.
AddIfNew(const std::string & str,const Target * source,RuntimeDepsVector * deps,std::set<OutputFile> * found_file)43 void AddIfNew(const std::string& str,
44 const Target* source,
45 RuntimeDepsVector* deps,
46 std::set<OutputFile>* found_file) {
47 OutputFile output_file(
48 RebasePath(str, source->settings()->build_settings()->build_dir(),
49 source->settings()->build_settings()->root_path_utf8()));
50 AddIfNew(output_file, source, deps, found_file);
51 }
52
53 // To avoid duplicate traversals of targets, or duplicating output files that
54 // might be listed by more than one target, the set of targets and output files
55 // that have been found so far is passed. The "value" of the seen_targets map
56 // is a boolean indicating if the seen dep was a data dep (true = data_dep).
57 // data deps add more stuff, so we will want to revisit a target if it's a
58 // data dependency and we've previously only seen it as a regular dep.
RecursiveCollectRuntimeDeps(const Target * target,bool is_target_data_dep,RuntimeDepsVector * deps,std::map<const Target *,bool> * seen_targets,std::set<OutputFile> * found_files)59 void RecursiveCollectRuntimeDeps(const Target* target,
60 bool is_target_data_dep,
61 RuntimeDepsVector* deps,
62 std::map<const Target*, bool>* seen_targets,
63 std::set<OutputFile>* found_files) {
64 const auto& found_seen_target = seen_targets->find(target);
65 if (found_seen_target != seen_targets->end()) {
66 // Already visited.
67 if (found_seen_target->second || !is_target_data_dep) {
68 // Already visited as a data dep, or the current dep is not a data
69 // dep so visiting again will be a no-op.
70 return;
71 }
72 // In the else case, the previously seen target was a regular dependency
73 // and we'll now process it as a data dependency.
74 }
75 (*seen_targets)[target] = is_target_data_dep;
76
77 // Add the main output file for executables, shared libraries, and
78 // loadable modules.
79 if (target->output_type() == Target::EXECUTABLE ||
80 target->output_type() == Target::LOADABLE_MODULE ||
81 target->output_type() == Target::SHARED_LIBRARY) {
82 for (const auto& runtime_output : target->runtime_outputs())
83 AddIfNew(runtime_output, target, deps, found_files);
84 }
85
86 // Add all data files.
87 for (const auto& file : target->data())
88 AddIfNew(file, target, deps, found_files);
89
90 // Actions/copy have all outputs considered when the're a data dep.
91 if (is_target_data_dep && (target->output_type() == Target::ACTION ||
92 target->output_type() == Target::ACTION_FOREACH ||
93 target->output_type() == Target::COPY_FILES)) {
94 std::vector<SourceFile> outputs;
95 target->action_values().GetOutputsAsSourceFiles(target, &outputs);
96 for (const auto& output_file : outputs)
97 AddIfNew(output_file.value(), target, deps, found_files);
98 }
99
100 // Data dependencies.
101 for (const auto& dep_pair : target->data_deps()) {
102 RecursiveCollectRuntimeDeps(dep_pair.ptr, true, deps, seen_targets,
103 found_files);
104 }
105
106 // Do not recurse into bundle targets. A bundle's dependencies should be
107 // copied into the bundle itself for run-time access.
108 if (target->output_type() == Target::CREATE_BUNDLE) {
109 SourceDir bundle_root_dir =
110 target->bundle_data().GetBundleRootDirOutputAsDir(target->settings());
111 AddIfNew(bundle_root_dir.value(), target, deps, found_files);
112 return;
113 }
114
115 // Non-data dependencies (both public and private).
116 for (const auto& dep_pair : target->GetDeps(Target::DEPS_LINKED)) {
117 if (dep_pair.ptr->output_type() == Target::EXECUTABLE)
118 continue; // Skip executables that aren't data deps.
119 if (dep_pair.ptr->output_type() == Target::SHARED_LIBRARY &&
120 (target->output_type() == Target::ACTION ||
121 target->output_type() == Target::ACTION_FOREACH)) {
122 // Skip shared libraries that action depends on,
123 // unless it were listed in data deps.
124 continue;
125 }
126 RecursiveCollectRuntimeDeps(dep_pair.ptr, false, deps, seen_targets,
127 found_files);
128 }
129 }
130
CollectRuntimeDepsFromFlag(const BuildSettings * build_settings,const Builder & builder,RuntimeDepsVector * files_to_write,Err * err)131 bool CollectRuntimeDepsFromFlag(const BuildSettings* build_settings,
132 const Builder& builder,
133 RuntimeDepsVector* files_to_write,
134 Err* err) {
135 std::string deps_target_list_file =
136 base::CommandLine::ForCurrentProcess()->GetSwitchValueString(
137 switches::kRuntimeDepsListFile);
138
139 if (deps_target_list_file.empty())
140 return true;
141
142 std::string list_contents;
143 ScopedTrace load_trace(TraceItem::TRACE_FILE_LOAD, deps_target_list_file);
144 if (!base::ReadFileToString(UTF8ToFilePath(deps_target_list_file),
145 &list_contents)) {
146 *err = Err(Location(),
147 std::string("File for --") + switches::kRuntimeDepsListFile +
148 " doesn't exist.",
149 "The file given was \"" + deps_target_list_file + "\"");
150 return false;
151 }
152 load_trace.Done();
153
154 SourceDir root_dir("//");
155 Label default_toolchain_label = builder.loader()->GetDefaultToolchain();
156 for (const auto& line : base::SplitString(
157 list_contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
158 if (line.empty())
159 continue;
160 Label label =
161 Label::Resolve(root_dir, build_settings->root_path_utf8(),
162 default_toolchain_label, Value(nullptr, line), err);
163 if (err->has_error())
164 return false;
165
166 const Item* item = builder.GetItem(label);
167 const Target* target = item ? item->AsTarget() : nullptr;
168 if (!target) {
169 *err =
170 Err(Location(),
171 "The label \"" + label.GetUserVisibleName(true) +
172 "\" isn't a target.",
173 "When reading the line:\n " + line +
174 "\n"
175 "from the --" +
176 switches::kRuntimeDepsListFile + "=" + deps_target_list_file);
177 return false;
178 }
179
180 OutputFile output_file;
181 const char extension[] = ".runtime_deps";
182 if (target->output_type() == Target::SHARED_LIBRARY ||
183 target->output_type() == Target::LOADABLE_MODULE) {
184 // Force the first output for shared-library-type linker outputs since
185 // the dependency output files might not be the main output.
186 CHECK(!target->computed_outputs().empty());
187 output_file =
188 OutputFile(target->computed_outputs()[0].value() + extension);
189 } else {
190 output_file =
191 OutputFile(target->dependency_output_file().value() + extension);
192 }
193 files_to_write->push_back(std::make_pair(output_file, target));
194 }
195 return true;
196 }
197
WriteRuntimeDepsFile(const OutputFile & output_file,const Target * target,Err * err)198 bool WriteRuntimeDepsFile(const OutputFile& output_file,
199 const Target* target,
200 Err* err) {
201 SourceFile output_as_source =
202 output_file.AsSourceFile(target->settings()->build_settings());
203 base::FilePath data_deps_file =
204 target->settings()->build_settings()->GetFullPath(output_as_source);
205
206 StringOutputBuffer storage;
207 std::ostream contents(&storage);
208 for (const auto& pair : ComputeRuntimeDeps(target))
209 contents << pair.first.value() << std::endl;
210
211 ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, output_as_source.value());
212 return storage.WriteToFileIfChanged(data_deps_file, err);
213 }
214
215 } // namespace
216
217 const char kRuntimeDeps_Help[] =
218 R"(Runtime dependencies
219
220 Runtime dependencies of a target are exposed via the "runtime_deps" category
221 of "gn desc" (see "gn help desc") or they can be written at build generation
222 time via write_runtime_deps(), or --runtime-deps-list-file (see "gn help
223 --runtime-deps-list-file").
224
225 To a first approximation, the runtime dependencies of a target are the set of
226 "data" files, data directories, and the shared libraries from all transitive
227 dependencies. Executables, shared libraries, and loadable modules are
228 considered runtime dependencies of themselves.
229
230 Executables
231
232 Executable targets and those executable targets' transitive dependencies are
233 not considered unless that executable is listed in "data_deps". Otherwise, GN
234 assumes that the executable (and everything it requires) is a build-time
235 dependency only.
236
237 Actions and copies
238
239 Action and copy targets that are listed as "data_deps" will have all of their
240 outputs and data files considered as runtime dependencies. Action and copy
241 targets that are "deps" or "public_deps" will have only their data files
242 considered as runtime dependencies. These targets can list an output file in
243 both the "outputs" and "data" lists to force an output file as a runtime
244 dependency in all cases.
245
246 The different rules for deps and data_deps are to express build-time (deps)
247 vs. run-time (data_deps) outputs. If GN counted all build-time copy steps as
248 data dependencies, there would be a lot of extra stuff, and if GN counted all
249 run-time dependencies as regular deps, the build's parallelism would be
250 unnecessarily constrained.
251
252 This rule can sometimes lead to unintuitive results. For example, given the
253 three targets:
254 A --[data_deps]--> B --[deps]--> ACTION
255 GN would say that A does not have runtime deps on the result of the ACTION,
256 which is often correct. But the purpose of the B target might be to collect
257 many actions into one logic unit, and the "data"-ness of A's dependency is
258 lost. Solutions:
259
260 - List the outputs of the action in its data section (if the results of
261 that action are always runtime files).
262 - Have B list the action in data_deps (if the outputs of the actions are
263 always runtime files).
264 - Have B list the action in both deps and data deps (if the outputs might be
265 used in both contexts and you don't care about unnecessary entries in the
266 list of files required at runtime).
267 - Split B into run-time and build-time versions with the appropriate "deps"
268 for each.
269
270 Static libraries and source sets
271
272 The results of static_library or source_set targets are not considered
273 runtime dependencies since these are assumed to be intermediate targets only.
274 If you need to list a static library as a runtime dependency, you can
275 manually compute the .a/.lib file name for the current platform and list it
276 in the "data" list of a target (possibly on the static library target
277 itself).
278
279 Multiple outputs
280
281 Linker tools can specify which of their outputs should be considered when
282 computing the runtime deps by setting runtime_outputs. If this is unset on
283 the tool, the default will be the first output only.
284 )";
285
ComputeRuntimeDeps(const Target * target)286 RuntimeDepsVector ComputeRuntimeDeps(const Target* target) {
287 RuntimeDepsVector result;
288 std::map<const Target*, bool> seen_targets;
289 std::set<OutputFile> found_files;
290
291 // The initial target is not considered a data dependency so that actions's
292 // outputs (if the current target is an action) are not automatically
293 // considered data deps.
294 RecursiveCollectRuntimeDeps(target, false, &result, &seen_targets,
295 &found_files);
296 return result;
297 }
298
WriteRuntimeDepsFilesIfNecessary(const BuildSettings * build_settings,const Builder & builder,Err * err)299 bool WriteRuntimeDepsFilesIfNecessary(const BuildSettings* build_settings,
300 const Builder& builder,
301 Err* err) {
302 RuntimeDepsVector files_to_write;
303 if (!CollectRuntimeDepsFromFlag(build_settings, builder, &files_to_write,
304 err))
305 return false;
306
307 // Files scheduled by write_runtime_deps.
308 for (const Target* target : g_scheduler->GetWriteRuntimeDepsTargets()) {
309 files_to_write.push_back(
310 std::make_pair(target->write_runtime_deps_output(), target));
311 }
312
313 for (const auto& entry : files_to_write) {
314 // Currently this writes all runtime deps files sequentially. We generally
315 // expect few of these. We can run this on the worker pool if it looks
316 // like it's talking a long time.
317 if (!WriteRuntimeDepsFile(entry.first, entry.second, err))
318 return false;
319 }
320 return true;
321 }
322