普通文本  |  390行  |  13.14 KB

// Copyright (c) 2013 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 <algorithm>
#include <set>
#include <sstream>

#include "base/command_line.h"
#include "tools/gn/commands.h"
#include "tools/gn/config.h"
#include "tools/gn/config_values_extractors.h"
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/item.h"
#include "tools/gn/label.h"
#include "tools/gn/setup.h"
#include "tools/gn/standard_out.h"
#include "tools/gn/target.h"

namespace commands {

namespace {

// Prints the given directory in a nice way for the user to view.
std::string FormatSourceDir(const SourceDir& dir) {
#if defined(OS_WIN)
  // On Windows we fix up system absolute paths to look like native ones.
  // Internally, they'll look like "/C:\foo\bar/"
  if (dir.is_system_absolute()) {
    std::string buf = dir.value();
    if (buf.size() > 3 && buf[2] == ':') {
      buf.erase(buf.begin());  // Erase beginning slash.
      ConvertPathToSystem(&buf);  // Convert to backslashes.
      return buf;
    }
  }
#endif
  return dir.value();
}

void RecursiveCollectChildDeps(const Target* target, std::set<Label>* result);

void RecursiveCollectDeps(const Target* target, std::set<Label>* result) {
  if (result->find(target->label()) != result->end())
    return;  // Already did this target.
  result->insert(target->label());

  RecursiveCollectChildDeps(target, result);
}

void RecursiveCollectChildDeps(const Target* target, std::set<Label>* result) {
  const LabelTargetVector& deps = target->deps();
  for (size_t i = 0; i < deps.size(); i++)
    RecursiveCollectDeps(deps[i].ptr, result);

  const LabelTargetVector& datadeps = target->datadeps();
  for (size_t i = 0; i < datadeps.size(); i++)
    RecursiveCollectDeps(datadeps[i].ptr, result);
}

// Prints dependencies of the given target (not the target itself).
void RecursivePrintDeps(const Target* target,
                        const Label& default_toolchain,
                        int indent_level) {
  LabelTargetVector sorted_deps = target->deps();
  const LabelTargetVector& datadeps = target->datadeps();
  sorted_deps.insert(sorted_deps.end(), datadeps.begin(), datadeps.end());
  std::sort(sorted_deps.begin(), sorted_deps.end(),
            LabelPtrLabelLess<Target>());

  std::string indent(indent_level * 2, ' ');
  for (size_t i = 0; i < sorted_deps.size(); i++) {
    OutputString(indent +
        sorted_deps[i].label.GetUserVisibleName(default_toolchain) + "\n");
    RecursivePrintDeps(sorted_deps[i].ptr, default_toolchain, indent_level + 1);
  }
}

void PrintDeps(const Target* target, bool display_header) {
  const CommandLine* cmdline = CommandLine::ForCurrentProcess();
  Label toolchain_label = target->label().GetToolchainLabel();

  // Tree mode is separate.
  if (cmdline->HasSwitch("tree")) {
    if (display_header)
      OutputString("\nDependency tree:\n");
    RecursivePrintDeps(target, toolchain_label, 1);
    return;
  }

  // Collect the deps to display.
  std::vector<Label> deps;
  if (cmdline->HasSwitch("all")) {
    if (display_header)
      OutputString("\nAll recursive dependencies:\n");

    std::set<Label> all_deps;
    RecursiveCollectChildDeps(target, &all_deps);
    for (std::set<Label>::iterator i = all_deps.begin();
         i != all_deps.end(); ++i)
      deps.push_back(*i);
  } else {
    if (display_header) {
      OutputString("\nDirect dependencies "
                   "(try also \"--all\" and \"--tree\"):\n");
    }

    const LabelTargetVector& target_deps = target->deps();
    for (size_t i = 0; i < target_deps.size(); i++)
      deps.push_back(target_deps[i].label);

    const LabelTargetVector& target_datadeps = target->datadeps();
    for (size_t i = 0; i < target_datadeps.size(); i++)
      deps.push_back(target_datadeps[i].label);
  }

  std::sort(deps.begin(), deps.end());
  for (size_t i = 0; i < deps.size(); i++)
    OutputString("  " + deps[i].GetUserVisibleName(toolchain_label) + "\n");
}

// libs and lib_dirs are special in that they're inherited. We don't currently
// implement a blame feature for this since the bottom-up inheritance makes
// this difficult.
void PrintLibDirs(const Target* target, bool display_header) {
  const OrderedSet<SourceDir>& lib_dirs = target->all_lib_dirs();
  if (lib_dirs.empty())
    return;

  if (display_header)
    OutputString("\nlib_dirs\n");

  for (size_t i = 0; i < lib_dirs.size(); i++)
    OutputString("    " + FormatSourceDir(lib_dirs[i]) + "\n");
}

void PrintLibs(const Target* target, bool display_header) {
  const OrderedSet<std::string>& libs = target->all_libs();
  if (libs.empty())
    return;

  if (display_header)
    OutputString("\nlibs\n");

  for (size_t i = 0; i < libs.size(); i++)
    OutputString("    " + libs[i] + "\n");
}

void PrintConfigs(const Target* target, bool display_header) {
  // Configs (don't sort since the order determines how things are processed).
  if (display_header)
    OutputString("\nConfigs (in order applying):\n");

  Label toolchain_label = target->label().GetToolchainLabel();
  const LabelConfigVector& configs = target->configs();
  for (size_t i = 0; i < configs.size(); i++) {
    OutputString("  " +
        configs[i].label.GetUserVisibleName(toolchain_label) + "\n");
  }
}

void PrintSources(const Target* target, bool display_header) {
  if (display_header)
    OutputString("\nSources:\n");

  Target::FileList sources = target->sources();
  std::sort(sources.begin(), sources.end());
  for (size_t i = 0; i < sources.size(); i++)
    OutputString("  " + sources[i].value() + "\n");
}

// Attribute the origin for attributing from where a target came from. Does
// nothing if the input is null or it does not have a location.
void OutputSourceOfDep(const ParseNode* origin, std::ostream& out) {
  if (!origin)
    return;
  Location location = origin->GetRange().begin();
  out << "       (Added by " + location.file()->name().value() << ":"
      << location.line_number() << ")\n";
}

// Templatized writer for writing out different config value types.
template<typename T> struct DescValueWriter {};
template<> struct DescValueWriter<std::string> {
  void operator()(const std::string& str, std::ostream& out) const {
    out << "    " << str << "\n";
  }
};
template<> struct DescValueWriter<SourceFile> {
  void operator()(const SourceFile& file, std::ostream& out) const {
    out << "    " << file.value() << "\n";
  }
};
template<> struct DescValueWriter<SourceDir> {
  void operator()(const SourceDir& dir, std::ostream& out) const {
    out << "    " << FormatSourceDir(dir) << "\n";
  }
};

// Writes a given config value type to the string, optionally with attribution.
// This should match RecursiveTargetConfigToStream in the order it traverses.
template<typename T> void OutputRecursiveTargetConfig(
    const Target* target,
    const char* header_name,
    const std::vector<T>& (ConfigValues::* getter)() const) {
  bool display_blame = CommandLine::ForCurrentProcess()->HasSwitch("blame");

  DescValueWriter<T> writer;
  std::ostringstream out;

  for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) {
    if ((iter.cur().*getter)().empty())
      continue;

    // Optional blame sub-head.
    if (display_blame) {
      const Config* config = iter.GetCurrentConfig();
      if (config) {
        // Source of this value is a config.
        out << "  From " << config->label().GetUserVisibleName(false) << "\n";
        OutputSourceOfDep(iter.origin(), out);
      } else {
        // Source of this value is the target itself.
        out << "  From " << target->label().GetUserVisibleName(false) << "\n";
      }
    }

    // Actual values.
    ConfigValuesToStream(iter.cur(), getter, writer, out);
  }

  std::string out_str = out.str();
  if (!out_str.empty()) {
    OutputString("\n" + std::string(header_name) + "\n");
    OutputString(out_str);
  }
}

}  // namespace

// desc ------------------------------------------------------------------------

const char kDesc[] = "desc";
const char kDesc_HelpShort[] =
    "desc: Show lots of insightful information about a target.";
const char kDesc_Help[] =
    "gn desc <target label> [<what to show>] [--blame] [--all | --tree]\n"
    "  Displays information about a given labeled target.\n"
    "\n"
    "Possibilities for <what to show>:\n"
    "  (If unspecified an overall summary will be displayed.)\n"
    "\n"
    "  sources\n"
    "      Source files.\n"
    "\n"
    "  configs\n"
    "      Shows configs applied to the given target, sorted in the order\n"
    "      they're specified. This includes both configs specified in the\n"
    "      \"configs\" variable, as well as configs pushed onto this target\n"
    "      via dependencies specifying \"all\" or \"direct\" dependent\n"
    "      configs.\n"
    "\n"
    "  deps [--all | --tree]\n"
    "      Show immediate (or, when \"--all\" or \"--tree\" is specified,\n"
    "      recursive) dependencies of the given target. \"--tree\" shows them\n"
    "      in a tree format.  Otherwise, they will be sorted alphabetically.\n"
    "      Both \"deps\" and \"datadeps\" will be included.\n"
    "\n"
    "  defines       [--blame]\n"
    "  include_dirs  [--blame]\n"
    "  cflags        [--blame]\n"
    "  cflags_cc     [--blame]\n"
    "  cflags_cxx    [--blame]\n"
    "  ldflags       [--blame]\n"
    "  lib_dirs\n"
    "  libs\n"
    "      Shows the given values taken from the target and all configs\n"
    "      applying. See \"--blame\" below.\n"
    "\n"
    "  --blame\n"
    "      Used with any value specified by a config, this will name\n"
    "      the config that specified the value. This doesn't currently work\n"
    "      for libs and lib_dirs because those are inherited and are more\n"
    "      complicated to figure out the blame (patches welcome).\n"
    "\n"
    "Note:\n"
    "  This command will show the full name of directories and source files,\n"
    "  but when directories and source paths are written to the build file,\n"
    "  they will be adjusted to be relative to the build directory. So the\n"
    "  values for paths displayed by this command won't match (but should\n"
    "  mean the same thing.\n"
    "\n"
    "Examples:\n"
    "  gn desc //base:base\n"
    "      Summarizes the given target.\n"
    "\n"
    "  gn desc :base_unittests deps --tree\n"
    "      Shows a dependency tree of the \"base_unittests\" project in\n"
    "      the current directory.\n"
    "\n"
    "  gn desc //base defines --blame\n"
    "      Shows defines set for the //base:base target, annotated by where\n"
    "      each one was set from.\n";

#define OUTPUT_CONFIG_VALUE(name, type) \
    OutputRecursiveTargetConfig<type>(target, #name, &ConfigValues::name);

int RunDesc(const std::vector<std::string>& args) {
  if (args.size() != 1 && args.size() != 2) {
    Err(Location(), "You're holding it wrong.",
        "Usage: \"gn desc <target_name> <what to display>\"").PrintToStdout();
    return 1;
  }

  const Target* target = GetTargetForDesc(args);
  if (!target)
    return 1;

#define CONFIG_VALUE_HANDLER(name, type) \
    } else if (what == #name) { OUTPUT_CONFIG_VALUE(name, type)

  if (args.size() == 2) {
    // User specified one thing to display.
    const std::string& what = args[1];
    if (what == "configs") {
      PrintConfigs(target, false);
    } else if (what == "sources") {
      PrintSources(target, false);
    } else if (what == "deps") {
      PrintDeps(target, false);
    } else if (what == "lib_dirs") {
      PrintLibDirs(target, false);
    } else if (what == "libs") {
      PrintLibs(target, false);

    CONFIG_VALUE_HANDLER(defines, std::string)
    CONFIG_VALUE_HANDLER(include_dirs, SourceDir)
    CONFIG_VALUE_HANDLER(cflags, std::string)
    CONFIG_VALUE_HANDLER(cflags_c, std::string)
    CONFIG_VALUE_HANDLER(cflags_cc, std::string)
    CONFIG_VALUE_HANDLER(cflags_objc, std::string)
    CONFIG_VALUE_HANDLER(cflags_objcc, std::string)
    CONFIG_VALUE_HANDLER(ldflags, std::string)

    } else {
      OutputString("Don't know how to display \"" + what + "\".\n");
      return 1;
    }

#undef CONFIG_VALUE_HANDLER
    return 0;
  }

  // Display summary.

  // Generally we only want to display toolchains on labels when the toolchain
  // is different than the default one for this target (which we always print
  // in the header).
  Label target_toolchain = target->label().GetToolchainLabel();

  // Header.
  OutputString("Target: ", DECORATION_YELLOW);
  OutputString(target->label().GetUserVisibleName(false) + "\n");
  OutputString("Type: ", DECORATION_YELLOW);
  OutputString(std::string(
      Target::GetStringForOutputType(target->output_type())) + "\n");
  OutputString("Toolchain: ", DECORATION_YELLOW);
  OutputString(target_toolchain.GetUserVisibleName(false) + "\n");

  PrintSources(target, true);
  PrintConfigs(target, true);

  OUTPUT_CONFIG_VALUE(defines, std::string)
  OUTPUT_CONFIG_VALUE(include_dirs, SourceDir)
  OUTPUT_CONFIG_VALUE(cflags, std::string)
  OUTPUT_CONFIG_VALUE(cflags_c, std::string)
  OUTPUT_CONFIG_VALUE(cflags_cc, std::string)
  OUTPUT_CONFIG_VALUE(cflags_objc, std::string)
  OUTPUT_CONFIG_VALUE(cflags_objcc, std::string)
  OUTPUT_CONFIG_VALUE(ldflags, std::string)
  PrintLibs(target, true);
  PrintLibDirs(target, true);

  PrintDeps(target, true);

  return 0;
}

}  // namespace commands