Chromium Code Reviews| Index: tools/gn/command_gyp.cc |
| diff --git a/tools/gn/command_gyp.cc b/tools/gn/command_gyp.cc |
| index bc5ab5dba068f1ca23a1ab926ad32965332d3aa0..8bcad4660ea92e6a03c61ea4a934e07028510a7f 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 baclskash escaping and quoting. |
|
scottmg
2013/10/15 19:58:21
baclskash -> backslash
|
| +// 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 eescape sequences: \\, \", \<space>. |
|
scottmg
2013/10/15 19:58:21
eescape -> escape
|
| + 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()) |
|
scottmg
2013/10/15 19:58:21
might have to augment with the other silly ways ch
brettw
2013/10/15 21:18:06
I'll make a note
|
| + 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); |
| } |