brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 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 "tools/gn/runtime_deps.h" |
| 6 | |
brettw | 295e0720 | 2015-07-21 23:31:11 | [diff] [blame] | 7 | #include <map> |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 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 "tools/gn/build_settings.h" |
| 15 | #include "tools/gn/builder.h" |
| 16 | #include "tools/gn/deps_iterator.h" |
| 17 | #include "tools/gn/filesystem_utils.h" |
| 18 | #include "tools/gn/loader.h" |
| 19 | #include "tools/gn/output_file.h" |
agrieve | 0f8ad42 | 2016-03-31 22:00:07 | [diff] [blame] | 20 | #include "tools/gn/scheduler.h" |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 21 | #include "tools/gn/settings.h" |
| 22 | #include "tools/gn/switches.h" |
| 23 | #include "tools/gn/target.h" |
| 24 | #include "tools/gn/trace.h" |
| 25 | |
| 26 | namespace { |
| 27 | |
| 28 | using RuntimeDepsVector = std::vector<std::pair<OutputFile, const Target*>>; |
| 29 | |
| 30 | // Adds the given file to the deps list if it hasn't already been listed in |
| 31 | // the found_files list. Updates the list. |
| 32 | void AddIfNew(const OutputFile& output_file, |
| 33 | const Target* source, |
| 34 | RuntimeDepsVector* deps, |
| 35 | std::set<OutputFile>* found_file) { |
| 36 | if (found_file->find(output_file) != found_file->end()) |
| 37 | return; // Already there. |
| 38 | deps->push_back(std::make_pair(output_file, source)); |
| 39 | } |
| 40 | |
brettw | e903c0f | 2015-06-03 22:40:17 | [diff] [blame] | 41 | // Automatically converts a string that looks like a source to an OutputFile. |
| 42 | void AddIfNew(const std::string& str, |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 43 | const Target* source, |
| 44 | RuntimeDepsVector* deps, |
| 45 | std::set<OutputFile>* found_file) { |
brettw | e903c0f | 2015-06-03 22:40:17 | [diff] [blame] | 46 | OutputFile output_file(RebasePath( |
| 47 | str, |
| 48 | source->settings()->build_settings()->build_dir(), |
| 49 | source->settings()->build_settings()->root_path_utf8())); |
| 50 | AddIfNew(output_file, source, deps, found_file); |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 51 | } |
| 52 | |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 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 |
brettw | 295e0720 | 2015-07-21 23:31:11 | [diff] [blame] | 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. |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 59 | void RecursiveCollectRuntimeDeps(const Target* target, |
| 60 | bool is_target_data_dep, |
| 61 | RuntimeDepsVector* deps, |
brettw | 295e0720 | 2015-07-21 23:31:11 | [diff] [blame] | 62 | std::map<const Target*, bool>* seen_targets, |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 63 | std::set<OutputFile>* found_files) { |
brettw | 295e0720 | 2015-07-21 23:31:11 | [diff] [blame] | 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; |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 76 | |
andybons | 027840d | 2015-10-14 18:49:30 | [diff] [blame] | 77 | // Add the main output file for executables, shared libraries, and |
| 78 | // loadable modules. |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 79 | if (target->output_type() == Target::EXECUTABLE || |
andybons | 027840d | 2015-10-14 18:49:30 | [diff] [blame] | 80 | target->output_type() == Target::LOADABLE_MODULE || |
brettw | 15e0e74f | 2016-07-26 18:00:24 | [diff] [blame] | 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 | } |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 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 && |
| 92 | (target->output_type() == Target::ACTION || |
| 93 | target->output_type() == Target::ACTION_FOREACH || |
| 94 | target->output_type() == Target::COPY_FILES)) { |
| 95 | std::vector<SourceFile> outputs; |
| 96 | target->action_values().GetOutputsAsSourceFiles(target, &outputs); |
| 97 | for (const auto& output_file : outputs) |
brettw | e903c0f | 2015-06-03 22:40:17 | [diff] [blame] | 98 | AddIfNew(output_file.value(), target, deps, found_files); |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 99 | } |
| 100 | |
rsesek | be4da4cd | 2016-05-31 20:10:49 | [diff] [blame] | 101 | // Data dependencies. |
| 102 | for (const auto& dep_pair : target->data_deps()) { |
| 103 | RecursiveCollectRuntimeDeps(dep_pair.ptr, true, |
| 104 | deps, seen_targets, found_files); |
| 105 | } |
| 106 | |
rsesek | 903e2b8 | 2016-05-19 19:43:32 | [diff] [blame] | 107 | // Do not recurse into bundle targets. A bundle's dependencies should be |
| 108 | // copied into the bundle itself for run-time access. |
| 109 | if (target->output_type() == Target::CREATE_BUNDLE) { |
| 110 | SourceDir bundle_root_dir = |
| 111 | target->bundle_data().GetBundleRootDirOutputAsDir(target->settings()); |
| 112 | AddIfNew(bundle_root_dir.value(), target, deps, found_files); |
| 113 | return; |
| 114 | } |
| 115 | |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 116 | // Non-data dependencies (both public and private). |
| 117 | for (const auto& dep_pair : target->GetDeps(Target::DEPS_LINKED)) { |
| 118 | if (dep_pair.ptr->output_type() == Target::EXECUTABLE) |
| 119 | continue; // Skip executables that aren't data deps. |
Greg Kraynov | 655a12bc | 2017-08-15 18:26:21 | [diff] [blame] | 120 | if (dep_pair.ptr->output_type() == Target::SHARED_LIBRARY && |
| 121 | (target->output_type() == Target::ACTION || |
| 122 | target->output_type() == Target::ACTION_FOREACH)) { |
| 123 | // Skip shared libraries that action depends on, |
| 124 | // unless it were listed in data deps. |
| 125 | continue; |
| 126 | } |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 127 | RecursiveCollectRuntimeDeps(dep_pair.ptr, false, |
| 128 | deps, seen_targets, found_files); |
| 129 | } |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 130 | } |
| 131 | |
agrieve | 0f8ad42 | 2016-03-31 22:00:07 | [diff] [blame] | 132 | bool CollectRuntimeDepsFromFlag(const Builder& builder, |
| 133 | RuntimeDepsVector* files_to_write, |
| 134 | Err* err) { |
| 135 | std::string deps_target_list_file = |
| 136 | base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |
| 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 : |
| 157 | base::SplitString(list_contents, "\n", base::TRIM_WHITESPACE, |
| 158 | base::SPLIT_WANT_ALL)) { |
| 159 | if (line.empty()) |
| 160 | continue; |
| 161 | Label label = Label::Resolve(root_dir, default_toolchain_label, |
| 162 | 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 = Err(Location(), "The label \"" + label.GetUserVisibleName(true) + |
| 170 | "\" isn't a target.", |
| 171 | "When reading the line:\n " + line + "\n" |
| 172 | "from the --" + switches::kRuntimeDepsListFile + "=" + |
| 173 | deps_target_list_file); |
| 174 | return false; |
| 175 | } |
| 176 | |
brettw | 15e0e74f | 2016-07-26 18:00:24 | [diff] [blame] | 177 | OutputFile output_file; |
| 178 | const char extension[] = ".runtime_deps"; |
| 179 | if (target->output_type() == Target::SHARED_LIBRARY || |
| 180 | target->output_type() == Target::LOADABLE_MODULE) { |
| 181 | // Force the first output for shared-library-type linker outputs since |
| 182 | // the dependency output files might not be the main output. |
| 183 | CHECK(!target->computed_outputs().empty()); |
| 184 | output_file = |
| 185 | OutputFile(target->computed_outputs()[0].value() + extension); |
| 186 | } else { |
| 187 | output_file = |
| 188 | OutputFile(target->dependency_output_file().value() + extension); |
| 189 | } |
agrieve | 0f8ad42 | 2016-03-31 22:00:07 | [diff] [blame] | 190 | files_to_write->push_back(std::make_pair(output_file, target)); |
| 191 | } |
| 192 | return true; |
| 193 | } |
| 194 | |
| 195 | bool WriteRuntimeDepsFile(const OutputFile& output_file, |
| 196 | const Target* target, |
| 197 | Err* err) { |
| 198 | SourceFile output_as_source = |
| 199 | output_file.AsSourceFile(target->settings()->build_settings()); |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 200 | base::FilePath data_deps_file = |
agrieve | 0f8ad42 | 2016-03-31 22:00:07 | [diff] [blame] | 201 | target->settings()->build_settings()->GetFullPath(output_as_source); |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 202 | |
| 203 | std::stringstream contents; |
| 204 | for (const auto& pair : ComputeRuntimeDeps(target)) |
| 205 | contents << pair.first.value() << std::endl; |
| 206 | |
agrieve | 0f8ad42 | 2016-03-31 22:00:07 | [diff] [blame] | 207 | ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, output_as_source.value()); |
| 208 | return WriteFileIfChanged(data_deps_file, contents.str(), err); |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 209 | } |
| 210 | |
| 211 | } // namespace |
| 212 | |
| 213 | const char kRuntimeDeps_Help[] = |
brettw | 085a9b2 | 2016-11-08 19:10:03 | [diff] [blame] | 214 | R"(Runtime dependencies |
| 215 | |
| 216 | Runtime dependencies of a target are exposed via the "runtime_deps" category |
| 217 | of "gn desc" (see "gn help desc") or they can be written at build generation |
| 218 | time via write_runtime_deps(), or --runtime-deps-list-file (see "gn help |
| 219 | --runtime-deps-list-file"). |
| 220 | |
| 221 | To a first approximation, the runtime dependencies of a target are the set of |
| 222 | "data" files, data directories, and the shared libraries from all transitive |
| 223 | dependencies. Executables, shared libraries, and loadable modules are |
| 224 | considered runtime dependencies of themselves. |
| 225 | |
| 226 | Executables |
| 227 | |
| 228 | Executable targets and those executable targets' transitive dependencies are |
| 229 | not considered unless that executable is listed in "data_deps". Otherwise, GN |
| 230 | assumes that the executable (and everything it requires) is a build-time |
| 231 | dependency only. |
| 232 | |
| 233 | Actions and copies |
| 234 | |
| 235 | Action and copy targets that are listed as "data_deps" will have all of their |
| 236 | outputs and data files considered as runtime dependencies. Action and copy |
| 237 | targets that are "deps" or "public_deps" will have only their data files |
| 238 | considered as runtime dependencies. These targets can list an output file in |
| 239 | both the "outputs" and "data" lists to force an output file as a runtime |
| 240 | dependency in all cases. |
| 241 | |
| 242 | The different rules for deps and data_deps are to express build-time (deps) |
| 243 | vs. run-time (data_deps) outputs. If GN counted all build-time copy steps as |
| 244 | data dependencies, there would be a lot of extra stuff, and if GN counted all |
| 245 | run-time dependencies as regular deps, the build's parallelism would be |
| 246 | unnecessarily constrained. |
| 247 | |
| 248 | This rule can sometimes lead to unintuitive results. For example, given the |
| 249 | three targets: |
| 250 | A --[data_deps]--> B --[deps]--> ACTION |
| 251 | GN would say that A does not have runtime deps on the result of the ACTION, |
| 252 | which is often correct. But the purpose of the B target might be to collect |
| 253 | many actions into one logic unit, and the "data"-ness of A's dependency is |
| 254 | lost. Solutions: |
| 255 | |
| 256 | - List the outputs of the action in it's data section (if the results of |
| 257 | that action are always runtime files). |
| 258 | - Have B list the action in data_deps (if the outputs of the actions are |
| 259 | always runtime files). |
| 260 | - Have B list the action in both deps and data deps (if the outputs might be |
| 261 | used in both contexts and you don't care about unnecessary entries in the |
| 262 | list of files required at runtime). |
| 263 | - Split B into run-time and build-time versions with the appropriate "deps" |
| 264 | for each. |
| 265 | |
| 266 | Static libraries and source sets |
| 267 | |
| 268 | The results of static_library or source_set targets are not considered |
| 269 | runtime dependencies since these are assumed to be intermediate targets only. |
| 270 | If you need to list a static library as a runtime dependency, you can |
| 271 | manually compute the .a/.lib file name for the current platform and list it |
| 272 | in the "data" list of a target (possibly on the static library target |
| 273 | itself). |
| 274 | |
| 275 | Multiple outputs |
| 276 | |
| 277 | Linker tools can specify which of their outputs should be considered when |
| 278 | computing the runtime deps by setting runtime_outputs. If this is unset on |
| 279 | the tool, the default will be the first output only. |
| 280 | )"; |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 281 | |
| 282 | RuntimeDepsVector ComputeRuntimeDeps(const Target* target) { |
| 283 | RuntimeDepsVector result; |
brettw | 295e0720 | 2015-07-21 23:31:11 | [diff] [blame] | 284 | std::map<const Target*, bool> seen_targets; |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 285 | std::set<OutputFile> found_files; |
| 286 | |
| 287 | // The initial target is not considered a data dependency so that actions's |
| 288 | // outputs (if the current target is an action) are not automatically |
| 289 | // considered data deps. |
| 290 | RecursiveCollectRuntimeDeps(target, false, |
| 291 | &result, &seen_targets, &found_files); |
| 292 | return result; |
| 293 | } |
| 294 | |
| 295 | bool WriteRuntimeDepsFilesIfNecessary(const Builder& builder, Err* err) { |
agrieve | 0f8ad42 | 2016-03-31 22:00:07 | [diff] [blame] | 296 | RuntimeDepsVector files_to_write; |
| 297 | if (!CollectRuntimeDepsFromFlag(builder, &files_to_write, err)) |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 298 | return false; |
agrieve | 0f8ad42 | 2016-03-31 22:00:07 | [diff] [blame] | 299 | |
| 300 | // Files scheduled by write_runtime_deps. |
| 301 | for (const Target* target : g_scheduler->GetWriteRuntimeDepsTargets()) { |
| 302 | files_to_write.push_back( |
| 303 | std::make_pair(target->write_runtime_deps_output(), target)); |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 304 | } |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 305 | |
agrieve | 0f8ad42 | 2016-03-31 22:00:07 | [diff] [blame] | 306 | for (const auto& entry : files_to_write) { |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 307 | // Currently this writes all runtime deps files sequentially. We generally |
| 308 | // expect few of these. We can run this on the worker pool if it looks |
| 309 | // like it's talking a long time. |
agrieve | 0f8ad42 | 2016-03-31 22:00:07 | [diff] [blame] | 310 | if (!WriteRuntimeDepsFile(entry.first, entry.second, err)) |
| 311 | return false; |
brettw | 0db9185 | 2015-05-14 23:40:20 | [diff] [blame] | 312 | } |
| 313 | return true; |
| 314 | } |