Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Unified Diff: tools/gn/command_gyp.cc

Issue 26561005: GYP generator for GN (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/gn/build_settings.cc ('k') | tools/gn/escape.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/gn/command_gyp.cc
diff --git a/tools/gn/command_gyp.cc b/tools/gn/command_gyp.cc
index bc5ab5dba068f1ca23a1ab926ad32965332d3aa0..9c005136088a25f5fbd889e671c58d99064b2a35 100644
--- a/tools/gn/command_gyp.cc
+++ b/tools/gn/command_gyp.cc
@@ -2,357 +2,313 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <algorithm>
-#include <fstream>
+#include <iostream>
+#include <map>
+#include <utility>
+#include <vector>
-#include "base/atomicops.h"
-#include "base/bind.h"
#include "base/command_line.h"
-#include "base/file_util.h"
-#include "base/process/launch.h"
+#include "base/environment.h"
#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "tools/gn/build_settings.h"
#include "tools/gn/commands.h"
#include "tools/gn/err.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/ninja_target_writer.h"
-#include "tools/gn/ninja_writer.h"
-#include "tools/gn/output_file.h"
-#include "tools/gn/path_output.h"
+#include "tools/gn/gyp_helper.h"
+#include "tools/gn/gyp_target_writer.h"
+#include "tools/gn/item_node.h"
+#include "tools/gn/location.h"
#include "tools/gn/setup.h"
+#include "tools/gn/source_file.h"
#include "tools/gn/standard_out.h"
+#include "tools/gn/target.h"
namespace commands {
namespace {
-// Suppress output on success.
-const char kSwitchQuiet[] = "q";
-
-// Skip actually executing GYP. This is for when you're working on the GN
-// build and don't want to wait for GYP to regenerate. All GN files are
-// regenerated, but the GYP ones are not.
-const char kSwitchNoGyp[] = "no-gyp";
-
-// Where to have GYP write its outputs.
-const char kDirOut[] = "out.gn";
-
-// We'll do the GN build to here.
-const char kBuildSourceDir[] = "//out.gn/Debug/";
-
-// File that GYP will write dependency information to.
-const char kGypDepsSourceFileName[] = "//out.gn/gyp_deps.txt";
-
-void TargetResolvedCallback(base::subtle::Atomic32* write_counter,
- const Target* target) {
- base::subtle::NoBarrier_AtomicIncrement(write_counter, 1);
- NinjaTargetWriter::RunAndWriteFile(target);
-}
-
-bool SimpleNinjaParse(const std::string& data,
- std::set<std::string>* subninjas,
- size_t* first_subninja_offset) {
- const size_t kSubninjaPrefixLen = 10;
- const char kSubninjaPrefix[kSubninjaPrefixLen + 1] = "\nsubninja ";
-
- *first_subninja_offset = std::string::npos;
- size_t next_subninja = 0;
- while ((next_subninja = data.find(kSubninjaPrefix, next_subninja)) !=
- std::string::npos) {
- if (*first_subninja_offset == std::string::npos)
- *first_subninja_offset = next_subninja;
-
- size_t line_end = data.find('\n', next_subninja + 1);
- if (line_end == std::string::npos)
- return false;
-
- std::string filename = data.substr(
- next_subninja + kSubninjaPrefixLen,
- line_end - next_subninja - kSubninjaPrefixLen);
- TrimWhitespaceASCII(filename, TRIM_ALL, &filename);
-#if defined(OS_WIN)
- // We always want our array to use forward slashes.
- std::replace(filename.begin(), filename.end(), '\\', '/');
-#endif
- subninjas->insert(filename);
-
- next_subninja = line_end;
+typedef GypTargetWriter::TargetPair TargetPair;
+typedef std::map<Label, TargetPair> CorrelatedTargetsMap;
+typedef std::map<SourceFile, std::vector<TargetPair> > GroupedTargetsMap;
+typedef std::map<std::string, std::string> StringStringMap;
+
+// Groups targets sharing the same label between debug and release.
+void CorrelateTargets(const std::vector<const Target*>& debug_targets,
+ const std::vector<const Target*>& release_targets,
+ CorrelatedTargetsMap* correlated) {
+ for (size_t i = 0; i < debug_targets.size(); i++) {
+ const Target* target = debug_targets[i];
+ (*correlated)[target->label()].debug = target;
+ }
+ for (size_t i = 0; i < release_targets.size(); i++) {
+ const Target* target = release_targets[i];
+ (*correlated)[target->label()].release = target;
}
- return *first_subninja_offset != std::string::npos;
}
-bool FixupBuildNinja(const BuildSettings* build_settings,
- const base::FilePath& buildfile) {
- std::string contents;
- if (!base::ReadFileToString(buildfile, &contents)) {
- Err(Location(), "Could not load " + FilePathToUTF8(buildfile))
- .PrintToStdout();
+// Verifies that both debug and release variants match. They can differ only
+// by flags.
+bool EnsureTargetsMatch(const TargetPair& pair, Err* err) {
+ // Check that both debug and release made this target.
+ if (!pair.debug || !pair.release) {
+ const Target* non_null_one = pair.debug ? pair.debug : pair.release;
+ *err = Err(Location(), "The debug and release builds did not both generate "
+ "a target with the name\n" +
+ non_null_one->label().GetUserVisibleName(true));
return false;
}
- std::set<std::string> subninjas;
- size_t first_subninja_offset = 0;
- if (!SimpleNinjaParse(contents, &subninjas, &first_subninja_offset)) {
- Err(Location(), "Could not parse " + FilePathToUTF8(buildfile))
- .PrintToStdout();
+ // Check the flags that determine if and where we write the GYP file.
+ if (pair.debug->item_node()->should_generate() !=
+ pair.release->item_node()->should_generate() ||
+ pair.debug->external() != pair.release->external() ||
+ pair.debug->gyp_file() != pair.release->gyp_file()) {
+ *err = Err(Location(), "The metadata for the target\n" +
+ pair.debug->label().GetUserVisibleName(true) +
+ "\ndoesn't match between the debug and release builds.");
return false;
}
- // Write toolchain files.
- std::vector<const Settings*> all_settings;
- if (!NinjaWriter::RunAndWriteToolchainFiles(
- build_settings, subninjas, &all_settings))
+ // Check that the sources match.
+ if (pair.debug->sources().size() != pair.release->sources().size()) {
+ *err = Err(Location(), "The source file count for the target\n" +
+ pair.debug->label().GetUserVisibleName(true) +
+ "\ndoesn't have the same number of files between the debug and "
+ "release builds.");
return false;
+ }
+ for (size_t i = 0; i < pair.debug->sources().size(); i++) {
+ if (pair.debug->sources()[i] != pair.release->sources()[i]) {
+ *err = Err(Location(), "The debug and release version of the target \n" +
+ pair.debug->label().GetUserVisibleName(true) +
+ "\ndon't agree on the file\n" +
+ pair.debug->sources()[i].value());
+ return false;
+ }
+ }
- // Copy first part of buildfile to the output.
- std::ofstream file;
- file.open(FilePathToUTF8(buildfile).c_str(),
- std::ios_base::out | std::ios_base::binary);
- if (file.fail()) {
- Err(Location(), "Could not write " + FilePathToUTF8(buildfile))
- .PrintToStdout();
+ // Check that the deps match.
+ if (pair.debug->deps().size() != pair.release->deps().size()) {
+ *err = Err(Location(), "The source file count for the target\n" +
+ pair.debug->label().GetUserVisibleName(true) +
+ "\ndoesn't have the same number of deps between the debug and "
+ "release builds.");
return false;
}
- file.write(contents.data(), first_subninja_offset);
-
- // Add refs for our toolchains to the original build.ninja.
- NinjaHelper helper(build_settings);
- PathOutput path_output(build_settings->build_dir(), ESCAPE_NINJA, true);
- file << "\n# GN-added toolchain files.\n";
- for (size_t i = 0; i < all_settings.size(); i++) {
- file << "subninja ";
- path_output.WriteFile(file,
- helper.GetNinjaFileForToolchain(all_settings[i]));
- file << std::endl;
+ for (size_t i = 0; i < pair.debug->deps().size(); i++) {
+ if (pair.debug->deps()[i]->label() != pair.release->deps()[i]->label()) {
+ *err = Err(Location(), "The debug and release version of the target \n" +
+ pair.debug->label().GetUserVisibleName(true) +
+ "\ndon't agree on the dep\n" +
+ pair.debug->deps()[i]->label().GetUserVisibleName(true));
+ return false;
+ }
}
- file << "\n# GYP-written subninjas.";
- // Write remaining old subninjas from original file.
- file.write(&contents[first_subninja_offset],
- contents.size() - first_subninja_offset);
return true;
}
-bool RunGyp(const BuildSettings* build_settings) {
- if (!CommandLine::ForCurrentProcess()->HasSwitch(kSwitchQuiet))
- OutputString("Running GYP...\n");
-
- const base::FilePath& python_path = build_settings->python_path();
-
- // Construct the command line. Note that AppendArgPath and AppendSwitchPath
- // don't preserve the relative ordering, and we need the python file to be
- // first, so we have to convert switch values to strings before appending.
- //
- // Note that GYP will get confused if this path is quoted, so don't quote it
- // and hope that there are no spaces!
- CommandLine cmdline(python_path);
- cmdline.AppendArgPath(
- build_settings->GetFullPath(SourceFile("//build/gyp_chromium.py")));
-
- // Override the default output directory so we can coexist in parallel
- // with a normal Ninja GYP build.
- cmdline.AppendArg("-G");
- cmdline.AppendArg(std::string("output_dir=") + kDirOut);
-
- // Force the Ninja generator.
- cmdline.AppendArg("-f");
- cmdline.AppendArg("ninja");
-
- // Write deps for libraries so we can pick them up.
- cmdline.AppendArg("-G");
- cmdline.AppendArg("link_deps_file=" + FilePathToUTF8(
- build_settings->GetFullPath(SourceFile(kGypDepsSourceFileName))));
-
- std::string output;
- if (!base::GetAppOutput(cmdline, &output)) {
- Err(Location(), "GYP execution failed.", output).PrintToStdout();
- return false;
+// Python uses shlex.split, which we partially emulate here.
+//
+// Advances to the next "word" in a GYP_DEFINES entry. This is something
+// separated by whitespace or '='. We allow backslash escaping and quoting.
+// The return value will be the index into the array immediately following the
+// word, and the contents of the word will be placed into |*word|.
+size_t GetNextGypDefinesWord(const std::string& defines,
+ size_t cur,
+ std::string* word) {
+ size_t i = cur;
+ bool is_quoted = false;
+ if (cur < defines.size() && defines[cur] == '"') {
+ i++;
+ is_quoted = true;
}
- return true;
+
+ for (; i < defines.size(); i++) {
+ // Handle certain escape sequences: \\, \", \<space>.
+ if (defines[i] == '\\' && i < defines.size() - 1 &&
+ (defines[i + 1] == '\\' ||
+ defines[i + 1] == ' ' ||
+ defines[i + 1] == '=' ||
+ defines[i + 1] == '"')) {
+ i++;
+ word->push_back(defines[i]);
+ continue;
+ }
+ if (is_quoted && defines[i] == '"') {
+ // Got to the end of the quoted sequence.
+ return i + 1;
+ }
+ if (!is_quoted && (defines[i] == ' ' || defines[i] == '=')) {
+ return i;
+ }
+ word->push_back(defines[i]);
+ }
+ return i;
}
-} // namespace
+// Advances to the beginning of the next word, or the size of the string if
+// the end was encountered.
+size_t AdvanceToNextGypDefinesWord(const std::string& defines, size_t cur) {
+ while (cur < defines.size() && defines[cur] == ' ')
+ cur++;
+ return cur;
+}
-// Converts a GYP qualified target which looks like:
-// "/home/you/src/third_party/icu/icu.gyp:icui18n#target" to a GN label like
-// "//third_party/icu:icui18n". On failure returns an empty label and sets the
-// error.
-Label GypQualifiedTargetToLabel(const std::string& source_root_prefix,
- const base::StringPiece& target,
- Err* err) {
- // Prefix should end in canonical path separator.
- const char kSep = static_cast<char>(base::FilePath::kSeparators[0]);
- DCHECK(source_root_prefix[source_root_prefix.size() - 1] == kSep);
-
- if (!target.starts_with(source_root_prefix)) {
- *err = Err(Location(), "GYP deps parsing failed.",
- "The line was \"" + target.as_string() + "\" and it should have "
- "started with \"" + source_root_prefix + "\"");
- return Label();
- }
+// The GYP defines looks like:
+// component=shared_library
+// component=shared_library foo=1
+// component=shared_library foo=1 windows_sdk_dir="C:\Program Files\..."
+StringStringMap GetGypDefines() {
+ StringStringMap result;
- size_t begin = source_root_prefix.size();
- size_t colon = target.find(':', begin);
- if (colon == std::string::npos) {
- *err = Err(Location(), "Expected :", target.as_string());
- return Label();
- }
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ std::string defines;
+ if (!env->GetVar("GYP_DEFINES", &defines) || defines.empty())
+ return result;
- size_t octothorpe = target.find('#', colon);
- if (octothorpe == std::string::npos) {
- *err = Err(Location(), "Expected #", target.as_string());
- return Label();
- }
+ size_t cur = 0;
+ while (cur < defines.size()) {
+ std::string key;
+ cur = AdvanceToNextGypDefinesWord(defines, cur);
+ cur = GetNextGypDefinesWord(defines, cur, &key);
+
+ // The words should be separated by an equals.
+ cur = AdvanceToNextGypDefinesWord(defines, cur);
+ if (cur == defines.size())
+ break;
+ if (defines[cur] != '=')
+ continue;
+ cur++; // Skip over '='.
- // This will look like "third_party/icu/icu.gyp"
- base::StringPiece gyp_file = target.substr(begin, colon - begin);
+ std::string value;
+ cur = AdvanceToNextGypDefinesWord(defines, cur);
+ cur = GetNextGypDefinesWord(defines, cur, &value);
- // Strip the file name from the end to get "third_party/icu".
- size_t last_sep = gyp_file.find_last_of(kSep);
- if (last_sep == std::string::npos) {
- *err = Err(Location(), "Expected path separator.", target.as_string());
- return Label();
+ result[key] = value;
}
- base::StringPiece path = gyp_file.substr(0, last_sep);
- SourceDir dir("//" + path.as_string());
-
- base::StringPiece name = target.substr(colon + 1, octothorpe - colon - 1);
- return Label(dir, name);
+ return result;
}
-// Parses the link deps file, filling the given map. Returns true on sucess.
-// On failure fills the error and returns false.
-//
-// Example format for each line:
-// /home/you/src/third_party/icu/icu.gyp:icui18n#target lib/libi18n.so
-bool ParseLinkDepsFile(const BuildSettings* build_settings,
- const std::string& contents,
- BuildSettings::AdditionalLibsMap* deps,
- Err* err) {
- std::string source_root_prefix = FilePathToUTF8(build_settings->root_path());
- source_root_prefix.push_back(base::FilePath::kSeparators[0]);
+// Returns a set of args from known GYP define values.
+Scope::KeyValueMap GetArgsFromGypDefines() {
+ StringStringMap gyp_defines = GetGypDefines();
- size_t cur = 0;
- while (cur < contents.size()) {
- // The source file is everything up to the space.
- size_t space = contents.find(' ', cur);
- if (space == std::string::npos)
- break;
- Label source(GypQualifiedTargetToLabel(
- source_root_prefix,
- base::StringPiece(&contents[cur], space - cur),
- err));
- if (source.is_null())
- return false;
+ Scope::KeyValueMap result;
- // The library file is everything between the space and EOL.
- cur = space + 1;
- size_t eol = contents.find('\n', cur);
- if (eol == std::string::npos) {
- *err = Err(Location(), "Expected newline at end of link deps file.");
- return false;
- }
- OutputFile lib(contents.substr(cur, eol - cur));
+ if (gyp_defines["component"] == "shared_library") {
+ result["is_component_build"] = Value(NULL, true);
+ } else {
+ result["is_component_build"] = Value(NULL, false);
+ }
- deps->insert(std::make_pair(source, lib));
- cur = eol + 1;
+ // Windows SDK path. GYP and the GN build use the same name.
+ const char kWinSdkPath[] = "windows_sdk_path";
+ if (gyp_defines[kWinSdkPath].empty())
+ result[kWinSdkPath] = Value(NULL, gyp_defines[kWinSdkPath]);
+
+ return result;
+}
+
+// Returns the number of targets, number of GYP files.
+std::pair<int, int> WriteGypFiles(
+ const BuildSettings& debug_settings,
+ const BuildSettings& release_settings,
+ Err* err) {
+ // Group all targets by output GYP file name.
+ std::vector<const Target*> debug_targets;
+ std::vector<const Target*> release_targets;
+ debug_settings.target_manager().GetAllTargets(&debug_targets);
+ release_settings.target_manager().GetAllTargets(&release_targets);
+
+ // Match up the debug and release version of each target by label.
+ CorrelatedTargetsMap correlated;
+ CorrelateTargets(debug_targets, release_targets, &correlated);
+
+ GypHelper helper;
+ GroupedTargetsMap grouped_targets;
+ int target_count = 0;
+ for (CorrelatedTargetsMap::iterator i = correlated.begin();
+ i != correlated.end(); ++i) {
+ const TargetPair& pair = i->second;
+ if (!EnsureTargetsMatch(pair, err))
+ return std::make_pair(0, 0);
+
+ if (!pair.debug->item_node()->should_generate())
+ continue; // Skip non-generated ones.
+ if (pair.debug->external())
+ continue; // Skip external ones.
+ if (pair.debug->gyp_file().is_null())
+ continue; // Skip ones without GYP files.
+
+ target_count++;
+ grouped_targets[helper.GetGypFileForTarget(pair.debug, err)].push_back(
+ pair);
+ if (err->has_error())
+ return std::make_pair(0, 0);
}
- return true;
+
+ // Write each GYP file.
+ for (GroupedTargetsMap::iterator i = grouped_targets.begin();
+ i != grouped_targets.end(); ++i) {
+ GypTargetWriter::WriteFile(i->first, i->second, err);
+ if (err->has_error())
+ return std::make_pair(0, 0);
+ }
+
+ return std::make_pair(target_count,
+ static_cast<int>(grouped_targets.size()));
}
+} // namespace
+
+// Suppress output on success.
+const char kSwitchQuiet[] = "q";
+
const char kGyp[] = "gyp";
const char kGyp_HelpShort[] =
- "gyp: Run GYP and then GN.";
-const char kGyp_Help[] =
- "gyp: Run GYP and then GN.\n"
- "\n"
- " Generate a hybrid GYP/GN build where some targets are generated by\n"
- " each of the tools. As long as target names and locations match between\n"
- " the two tools, they can depend on each other.\n"
- "\n"
- " When GN is run in this mode, it will not write out any targets\n"
- " annotated with \"external = true\". Otherwise, GYP targets with the\n"
- " same name and location will be overwritten.\n"
- "\n"
- " References to the GN ninja files will be inserted into the\n"
- " GYP-generated build.ninja file.\n"
- "\n"
- "Option:\n"
- " --no-gyp\n"
- " Don't actually run GYP or modify build.ninja. This is used when\n"
- " working on the GN build when it is known that no GYP files have\n"
- " changed and you want it to run faster.\n";
-
-// Note: partially duplicated from command_gen.cc.
+ "gyp: Make GYP files from GN.";
+const char kGyp_Help[] = "Doooooom.\n";
+
int RunGyp(const std::vector<std::string>& args) {
const CommandLine* cmdline = CommandLine::ForCurrentProcess();
- bool no_gyp = cmdline->HasSwitch(kSwitchNoGyp);
-
- // Deliberately leaked to avoid expensive process teardown.
- Setup* setup = new Setup;
- if (!setup->DoSetup())
- return 1;
-
- setup->build_settings().SetBuildDir(SourceDir(kBuildSourceDir));
- setup->build_settings().set_using_external_generator(true);
-
- // Provide a way for buildfiles to know we're doing a GYP build.
- /*
- Scope::KeyValueMap variable_overrides;
- variable_overrides["is_gyp"] = Value(NULL, true);
- setup->build_settings().build_args().AddArgOverrides(variable_overrides);
- */
-
- base::FilePath link_deps_file =
- setup->build_settings().GetFullPath(SourceFile(kGypDepsSourceFileName));
- if (!no_gyp)
- base::DeleteFile(link_deps_file, false);
base::TimeTicks begin_time = base::TimeTicks::Now();
- if (!no_gyp) {
- if (!RunGyp(&setup->build_settings()))
- return 1;
- }
- base::TimeTicks end_gyp_time = base::TimeTicks::Now();
- // Read in the GYP link dependencies.
- std::string link_deps_contents;
- if (!base::ReadFileToString(link_deps_file, &link_deps_contents)) {
- Err(Location(), "Couldn't load link deps file.",
- FilePathToUTF8(link_deps_file)).PrintToStdout();
+ // Deliberately leaked to avoid expensive process teardown.
+ Setup* setup_debug = new Setup;
+ if (!setup_debug->DoSetup())
return 1;
- }
- Err err;
- if (!ParseLinkDepsFile(&setup->build_settings(),
- link_deps_contents,
- &setup->build_settings().external_link_deps(), &err)) {
- err.PrintToStdout();
+ const char kIsDebug[] = "is_debug";
+ setup_debug->build_settings().build_args().AddArgOverrides(
+ GetArgsFromGypDefines());
+ setup_debug->build_settings().build_args().AddArgOverride(
+ kIsDebug, Value(NULL, true));
+
+ // Make a release build based on the debug one. We use a new directory for
+ // the build output so that they don't stomp on each other.
+ DependentSetup* setup_release = new DependentSetup(*setup_debug);
+ setup_release->build_settings().build_args().AddArgOverride(
+ kIsDebug, Value(NULL, false));
+ setup_release->build_settings().SetBuildDir(
+ SourceDir(setup_release->build_settings().build_dir().value() +
+ "gn_release.tmp/"));
+
+ // Run both debug and release builds in parallel.
+ setup_release->RunPreMessageLoop();
+ if (!setup_debug->Run())
return 1;
- }
-
- if (!cmdline->HasSwitch(kSwitchQuiet))
- OutputString("Running GN...\n");
-
- // Cause the load to also generate the ninja files for each target. We wrap
- // the writing to maintain a counter.
- base::subtle::Atomic32 write_counter = 0;
- setup->build_settings().set_target_resolved_callback(
- base::Bind(&TargetResolvedCallback, &write_counter));
-
- // Do the actual load. This will also write out the target ninja files.
- if (!setup->Run())
+ if (!setup_release->RunPostMessageLoop())
return 1;
- // Integrate with the GYP build.
- if (!no_gyp) {
- base::FilePath ninja_buildfile(setup->build_settings().GetFullPath(
- SourceFile(setup->build_settings().build_dir().value() +
- "build.ninja")));
- if (!FixupBuildNinja(&setup->build_settings(), ninja_buildfile))
- return 1;
+ Err err;
+ std::pair<int, int> counts = WriteGypFiles(setup_debug->build_settings(),
+ setup_release->build_settings(),
+ &err);
+ if (err.has_error()) {
+ err.PrintToStdout();
+ return 1;
}
// Timing info.
@@ -361,15 +317,12 @@ int RunGyp(const std::vector<std::string>& args) {
OutputString("Done. ", DECORATION_GREEN);
std::string stats = "Wrote " +
- base::IntToString(static_cast<int>(write_counter)) +
- " targets from " +
+ base::IntToString(counts.first) + " targets to " +
+ base::IntToString(counts.second) + " GYP files read from " +
base::IntToString(
- setup->scheduler().input_file_manager()->GetInputFileCount()) +
- " files in " +
- base::IntToString((end_time - end_gyp_time).InMilliseconds()) + "ms " +
- "(GYP took " +
- base::IntToString((end_gyp_time - begin_time).InMilliseconds()) +
- "ms)\n";
+ setup_debug->scheduler().input_file_manager()->GetInputFileCount())
+ + " GN files in " +
+ base::IntToString((end_time - begin_time).InMilliseconds()) + "ms\n";
OutputString(stats);
}
« no previous file with comments | « tools/gn/build_settings.cc ('k') | tools/gn/escape.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698