OLD | NEW |
(Empty) | |
| 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 |
| 7 #include <set> |
| 8 #include <sstream> |
| 9 |
| 10 #include "base/command_line.h" |
| 11 #include "base/files/file_util.h" |
| 12 #include "base/strings/string_split.h" |
| 13 #include "tools/gn/build_settings.h" |
| 14 #include "tools/gn/builder.h" |
| 15 #include "tools/gn/deps_iterator.h" |
| 16 #include "tools/gn/filesystem_utils.h" |
| 17 #include "tools/gn/loader.h" |
| 18 #include "tools/gn/output_file.h" |
| 19 #include "tools/gn/settings.h" |
| 20 #include "tools/gn/switches.h" |
| 21 #include "tools/gn/target.h" |
| 22 #include "tools/gn/trace.h" |
| 23 |
| 24 namespace { |
| 25 |
| 26 using RuntimeDepsVector = std::vector<std::pair<OutputFile, const Target*>>; |
| 27 |
| 28 // Adds the given file to the deps list if it hasn't already been listed in |
| 29 // the found_files list. Updates the list. |
| 30 void AddIfNew(const OutputFile& output_file, |
| 31 const Target* source, |
| 32 RuntimeDepsVector* deps, |
| 33 std::set<OutputFile>* found_file) { |
| 34 if (found_file->find(output_file) != found_file->end()) |
| 35 return; // Already there. |
| 36 deps->push_back(std::make_pair(output_file, source)); |
| 37 } |
| 38 |
| 39 // Automatically converts a SourceFile to an OutputFile. |
| 40 void AddIfNew(const SourceFile& source_file, |
| 41 const Target* source, |
| 42 RuntimeDepsVector* deps, |
| 43 std::set<OutputFile>* found_file) { |
| 44 AddIfNew(OutputFile(source->settings()->build_settings(), source_file), |
| 45 source, deps, found_file); |
| 46 } |
| 47 |
| 48 // Returns the output file that the runtime deps considers for the given |
| 49 // targets. This is weird only for shared libraries. |
| 50 const OutputFile& GetMainOutput(const Target* target) { |
| 51 if (target->output_type() == Target::SHARED_LIBRARY) |
| 52 return target->link_output_file(); |
| 53 return target->dependency_output_file(); |
| 54 } |
| 55 |
| 56 // To avoid duplicate traversals of targets, or duplicating output files that |
| 57 // might be listed by more than one target, the set of targets and output files |
| 58 // that have been found so far is passed. |
| 59 void RecursiveCollectRuntimeDeps(const Target* target, |
| 60 bool is_target_data_dep, |
| 61 RuntimeDepsVector* deps, |
| 62 std::set<const Target*>* seen_targets, |
| 63 std::set<OutputFile>* found_files) { |
| 64 if (seen_targets->find(target) != seen_targets->end()) |
| 65 return; // Already checked. |
| 66 seen_targets->insert(target); |
| 67 |
| 68 // Add the main output file for executables and shared libraries. |
| 69 if (target->output_type() == Target::EXECUTABLE || |
| 70 target->output_type() == Target::SHARED_LIBRARY) |
| 71 AddIfNew(GetMainOutput(target), target, deps, found_files); |
| 72 |
| 73 // Add all data files. |
| 74 for (const auto& file : target->data()) |
| 75 AddIfNew(file, target, deps, found_files); |
| 76 |
| 77 // Actions/copy have all outputs considered when the're a data dep. |
| 78 if (is_target_data_dep && |
| 79 (target->output_type() == Target::ACTION || |
| 80 target->output_type() == Target::ACTION_FOREACH || |
| 81 target->output_type() == Target::COPY_FILES)) { |
| 82 std::vector<SourceFile> outputs; |
| 83 target->action_values().GetOutputsAsSourceFiles(target, &outputs); |
| 84 for (const auto& output_file : outputs) |
| 85 AddIfNew(output_file, target, deps, found_files); |
| 86 } |
| 87 |
| 88 // Non-data dependencies (both public and private). |
| 89 for (const auto& dep_pair : target->GetDeps(Target::DEPS_LINKED)) { |
| 90 if (dep_pair.ptr->output_type() == Target::EXECUTABLE) |
| 91 continue; // Skip executables that aren't data deps. |
| 92 RecursiveCollectRuntimeDeps(dep_pair.ptr, false, |
| 93 deps, seen_targets, found_files); |
| 94 } |
| 95 |
| 96 // Data dependencies. |
| 97 for (const auto& dep_pair : target->data_deps()) { |
| 98 RecursiveCollectRuntimeDeps(dep_pair.ptr, true, |
| 99 deps, seen_targets, found_files); |
| 100 } |
| 101 } |
| 102 |
| 103 bool WriteRuntimeDepsFile(const Target* target) { |
| 104 SourceFile target_output_as_source = |
| 105 GetMainOutput(target).AsSourceFile(target->settings()->build_settings()); |
| 106 std::string data_deps_file_as_str = target_output_as_source.value(); |
| 107 data_deps_file_as_str.append(".runtime_deps"); |
| 108 base::FilePath data_deps_file = |
| 109 target->settings()->build_settings()->GetFullPath( |
| 110 SourceFile(SourceFile::SwapIn(), &data_deps_file_as_str)); |
| 111 |
| 112 std::stringstream contents; |
| 113 for (const auto& pair : ComputeRuntimeDeps(target)) |
| 114 contents << pair.first.value() << std::endl; |
| 115 |
| 116 ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, data_deps_file_as_str); |
| 117 base::CreateDirectory(data_deps_file.DirName()); |
| 118 |
| 119 std::string contents_str = contents.str(); |
| 120 return base::WriteFile(data_deps_file, contents_str.c_str(), |
| 121 static_cast<int>(contents_str.size())) > -1; |
| 122 } |
| 123 |
| 124 } // namespace |
| 125 |
| 126 const char kRuntimeDeps_Help[] = |
| 127 "Runtime dependencies\n" |
| 128 "\n" |
| 129 " Runtime dependencies of a target are exposed via the \"runtime_deps\"\n" |
| 130 " category of \"gn desc\" (see \"gn help desc\") or they can be written\n" |
| 131 " at build generation time via \"--runtime-deps-list-file\"\n" |
| 132 " (see \"gn help --runtime-deps-list-file\").\n" |
| 133 "\n" |
| 134 " To a first approximation, the runtime dependencies of a target are\n" |
| 135 " the set of \"data\" files and the shared libraries from all transitive\n" |
| 136 " dependencies. Executables and shared libraries are considered runtime\n" |
| 137 " dependencies of themselves.\n" |
| 138 "\n" |
| 139 "Details\n" |
| 140 "\n" |
| 141 " Executable targets and those executable targets' transitive\n" |
| 142 " dependencies are not considered unless that executable is listed in\n" |
| 143 " \"data_deps\". Otherwise, GN assumes that the executable (and\n" |
| 144 " everything it requires) is a build-time dependency only.\n" |
| 145 "\n" |
| 146 " Action and copy targets that are listed as \"data_deps\" will have all\n" |
| 147 " of their outputs and data files considered as runtime dependencies.\n" |
| 148 " Action and copy targets that are \"deps\" or \"public_deps\" will have\n" |
| 149 " only their data files considered as runtime dependencies. These\n" |
| 150 " targets can list an output file in both the \"outputs\" and \"data\"\n" |
| 151 " lists to force an output file as a runtime dependency in all cases.\n" |
| 152 "\n" |
| 153 " The results of static_library or source_set targets are not considered\n" |
| 154 " runtime dependencies since these are assumed to be intermediate\n" |
| 155 " targets only. If you need to list a static library as a runtime\n" |
| 156 " dependency, you can manually compute the .a/.lib file name for the\n" |
| 157 " current platform and list it in the \"data\" list of a target\n" |
| 158 " (possibly on the static library target itself).\n" |
| 159 "\n" |
| 160 " When a tool produces more than one output, only the first output\n" |
| 161 " is considered. For example, a shared library target may produce a\n" |
| 162 " .dll and a .lib file on Windows. Only the .dll file will be considered\n" |
| 163 " a runtime dependency.\n"; |
| 164 |
| 165 RuntimeDepsVector ComputeRuntimeDeps(const Target* target) { |
| 166 RuntimeDepsVector result; |
| 167 std::set<const Target*> seen_targets; |
| 168 std::set<OutputFile> found_files; |
| 169 |
| 170 // The initial target is not considered a data dependency so that actions's |
| 171 // outputs (if the current target is an action) are not automatically |
| 172 // considered data deps. |
| 173 RecursiveCollectRuntimeDeps(target, false, |
| 174 &result, &seen_targets, &found_files); |
| 175 return result; |
| 176 } |
| 177 |
| 178 bool WriteRuntimeDepsFilesIfNecessary(const Builder& builder, Err* err) { |
| 179 std::string deps_target_list_file = |
| 180 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |
| 181 switches::kRuntimeDepsListFile); |
| 182 if (deps_target_list_file.empty()) |
| 183 return true; // Nothing to do. |
| 184 |
| 185 std::string list_contents; |
| 186 ScopedTrace load_trace(TraceItem::TRACE_FILE_LOAD, deps_target_list_file); |
| 187 if (!base::ReadFileToString(UTF8ToFilePath(deps_target_list_file), |
| 188 &list_contents)) { |
| 189 *err = Err(Location(), |
| 190 std::string("File for --") + switches::kRuntimeDepsListFile + |
| 191 " doesn't exist.", |
| 192 "The file given was \"" + deps_target_list_file + "\""); |
| 193 return false; |
| 194 } |
| 195 load_trace.Done(); |
| 196 |
| 197 std::vector<std::string> lines; |
| 198 base::SplitString(list_contents, '\n', &lines); |
| 199 |
| 200 SourceDir root_dir("//"); |
| 201 Label default_toolchain_label = builder.loader()->GetDefaultToolchain(); |
| 202 for (const auto& line : lines) { |
| 203 if (line.empty()) |
| 204 continue; |
| 205 Label label = Label::Resolve(root_dir, default_toolchain_label, |
| 206 Value(nullptr, line), err); |
| 207 if (err->has_error()) |
| 208 return false; |
| 209 |
| 210 const Item* item = builder.GetItem(label); |
| 211 const Target* target = item ? item->AsTarget() : nullptr; |
| 212 if (!target) { |
| 213 *err = Err(Location(), "The label \"" + label.GetUserVisibleName(true) + |
| 214 "\" isn't a target.", |
| 215 "When reading the line:\n " + line + "\n" |
| 216 "from the --" + switches::kRuntimeDepsListFile + "=" + |
| 217 deps_target_list_file); |
| 218 return false; |
| 219 } |
| 220 |
| 221 // Currently this writes all runtime deps files sequentially. We generally |
| 222 // expect few of these. We can run this on the worker pool if it looks |
| 223 // like it's talking a long time. |
| 224 WriteRuntimeDepsFile(target); |
| 225 } |
| 226 return true; |
| 227 } |
OLD | NEW |