• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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