| 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);
|
| }
|
|
|