diff options
Diffstat (limited to 'tools/gn/runtime_deps.cc')
-rw-r--r-- | tools/gn/runtime_deps.cc | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/tools/gn/runtime_deps.cc b/tools/gn/runtime_deps.cc new file mode 100644 index 0000000..a8dfb0b --- /dev/null +++ b/tools/gn/runtime_deps.cc @@ -0,0 +1,227 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "tools/gn/runtime_deps.h" + +#include <set> +#include <sstream> + +#include "base/command_line.h" +#include "base/files/file_util.h" +#include "base/strings/string_split.h" +#include "tools/gn/build_settings.h" +#include "tools/gn/builder.h" +#include "tools/gn/deps_iterator.h" +#include "tools/gn/filesystem_utils.h" +#include "tools/gn/loader.h" +#include "tools/gn/output_file.h" +#include "tools/gn/settings.h" +#include "tools/gn/switches.h" +#include "tools/gn/target.h" +#include "tools/gn/trace.h" + +namespace { + +using RuntimeDepsVector = std::vector<std::pair<OutputFile, const Target*>>; + +// Adds the given file to the deps list if it hasn't already been listed in +// the found_files list. Updates the list. +void AddIfNew(const OutputFile& output_file, + const Target* source, + RuntimeDepsVector* deps, + std::set<OutputFile>* found_file) { + if (found_file->find(output_file) != found_file->end()) + return; // Already there. + deps->push_back(std::make_pair(output_file, source)); +} + +// Automatically converts a SourceFile to an OutputFile. +void AddIfNew(const SourceFile& source_file, + const Target* source, + RuntimeDepsVector* deps, + std::set<OutputFile>* found_file) { + AddIfNew(OutputFile(source->settings()->build_settings(), source_file), + source, deps, found_file); +} + +// Returns the output file that the runtime deps considers for the given +// targets. This is weird only for shared libraries. +const OutputFile& GetMainOutput(const Target* target) { + if (target->output_type() == Target::SHARED_LIBRARY) + return target->link_output_file(); + return target->dependency_output_file(); +} + +// To avoid duplicate traversals of targets, or duplicating output files that +// might be listed by more than one target, the set of targets and output files +// that have been found so far is passed. +void RecursiveCollectRuntimeDeps(const Target* target, + bool is_target_data_dep, + RuntimeDepsVector* deps, + std::set<const Target*>* seen_targets, + std::set<OutputFile>* found_files) { + if (seen_targets->find(target) != seen_targets->end()) + return; // Already checked. + seen_targets->insert(target); + + // Add the main output file for executables and shared libraries. + if (target->output_type() == Target::EXECUTABLE || + target->output_type() == Target::SHARED_LIBRARY) + AddIfNew(GetMainOutput(target), target, deps, found_files); + + // Add all data files. + for (const auto& file : target->data()) + AddIfNew(file, target, deps, found_files); + + // Actions/copy have all outputs considered when the're a data dep. + if (is_target_data_dep && + (target->output_type() == Target::ACTION || + target->output_type() == Target::ACTION_FOREACH || + target->output_type() == Target::COPY_FILES)) { + std::vector<SourceFile> outputs; + target->action_values().GetOutputsAsSourceFiles(target, &outputs); + for (const auto& output_file : outputs) + AddIfNew(output_file, target, deps, found_files); + } + + // Non-data dependencies (both public and private). + for (const auto& dep_pair : target->GetDeps(Target::DEPS_LINKED)) { + if (dep_pair.ptr->output_type() == Target::EXECUTABLE) + continue; // Skip executables that aren't data deps. + RecursiveCollectRuntimeDeps(dep_pair.ptr, false, + deps, seen_targets, found_files); + } + + // Data dependencies. + for (const auto& dep_pair : target->data_deps()) { + RecursiveCollectRuntimeDeps(dep_pair.ptr, true, + deps, seen_targets, found_files); + } +} + +bool WriteRuntimeDepsFile(const Target* target) { + SourceFile target_output_as_source = + GetMainOutput(target).AsSourceFile(target->settings()->build_settings()); + std::string data_deps_file_as_str = target_output_as_source.value(); + data_deps_file_as_str.append(".runtime_deps"); + base::FilePath data_deps_file = + target->settings()->build_settings()->GetFullPath( + SourceFile(SourceFile::SwapIn(), &data_deps_file_as_str)); + + std::stringstream contents; + for (const auto& pair : ComputeRuntimeDeps(target)) + contents << pair.first.value() << std::endl; + + ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, data_deps_file_as_str); + base::CreateDirectory(data_deps_file.DirName()); + + std::string contents_str = contents.str(); + return base::WriteFile(data_deps_file, contents_str.c_str(), + static_cast<int>(contents_str.size())) > -1; +} + +} // namespace + +const char kRuntimeDeps_Help[] = + "Runtime dependencies\n" + "\n" + " Runtime dependencies of a target are exposed via the \"runtime_deps\"\n" + " category of \"gn desc\" (see \"gn help desc\") or they can be written\n" + " at build generation time via \"--runtime-deps-list-file\"\n" + " (see \"gn help --runtime-deps-list-file\").\n" + "\n" + " To a first approximation, the runtime dependencies of a target are\n" + " the set of \"data\" files and the shared libraries from all transitive\n" + " dependencies. Executables and shared libraries are considered runtime\n" + " dependencies of themselves.\n" + "\n" + "Details\n" + "\n" + " Executable targets and those executable targets' transitive\n" + " dependencies are not considered unless that executable is listed in\n" + " \"data_deps\". Otherwise, GN assumes that the executable (and\n" + " everything it requires) is a build-time dependency only.\n" + "\n" + " Action and copy targets that are listed as \"data_deps\" will have all\n" + " of their outputs and data files considered as runtime dependencies.\n" + " Action and copy targets that are \"deps\" or \"public_deps\" will have\n" + " only their data files considered as runtime dependencies. These\n" + " targets can list an output file in both the \"outputs\" and \"data\"\n" + " lists to force an output file as a runtime dependency in all cases.\n" + "\n" + " The results of static_library or source_set targets are not considered\n" + " runtime dependencies since these are assumed to be intermediate\n" + " targets only. If you need to list a static library as a runtime\n" + " dependency, you can manually compute the .a/.lib file name for the\n" + " current platform and list it in the \"data\" list of a target\n" + " (possibly on the static library target itself).\n" + "\n" + " When a tool produces more than one output, only the first output\n" + " is considered. For example, a shared library target may produce a\n" + " .dll and a .lib file on Windows. Only the .dll file will be considered\n" + " a runtime dependency.\n"; + +RuntimeDepsVector ComputeRuntimeDeps(const Target* target) { + RuntimeDepsVector result; + std::set<const Target*> seen_targets; + std::set<OutputFile> found_files; + + // The initial target is not considered a data dependency so that actions's + // outputs (if the current target is an action) are not automatically + // considered data deps. + RecursiveCollectRuntimeDeps(target, false, + &result, &seen_targets, &found_files); + return result; +} + +bool WriteRuntimeDepsFilesIfNecessary(const Builder& builder, Err* err) { + std::string deps_target_list_file = + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kRuntimeDepsListFile); + if (deps_target_list_file.empty()) + return true; // Nothing to do. + + std::string list_contents; + ScopedTrace load_trace(TraceItem::TRACE_FILE_LOAD, deps_target_list_file); + if (!base::ReadFileToString(UTF8ToFilePath(deps_target_list_file), + &list_contents)) { + *err = Err(Location(), + std::string("File for --") + switches::kRuntimeDepsListFile + + " doesn't exist.", + "The file given was \"" + deps_target_list_file + "\""); + return false; + } + load_trace.Done(); + + std::vector<std::string> lines; + base::SplitString(list_contents, '\n', &lines); + + SourceDir root_dir("//"); + Label default_toolchain_label = builder.loader()->GetDefaultToolchain(); + for (const auto& line : lines) { + if (line.empty()) + continue; + Label label = Label::Resolve(root_dir, default_toolchain_label, + Value(nullptr, line), err); + if (err->has_error()) + return false; + + const Item* item = builder.GetItem(label); + const Target* target = item ? item->AsTarget() : nullptr; + if (!target) { + *err = Err(Location(), "The label \"" + label.GetUserVisibleName(true) + + "\" isn't a target.", + "When reading the line:\n " + line + "\n" + "from the --" + switches::kRuntimeDepsListFile + "=" + + deps_target_list_file); + return false; + } + + // Currently this writes all runtime deps files sequentially. We generally + // expect few of these. We can run this on the worker pool if it looks + // like it's talking a long time. + WriteRuntimeDepsFile(target); + } + return true; +} |