|  | // 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 <string> | 
|  |  | 
|  | #include "base/at_exit.h" | 
|  | #include "base/command_line.h" | 
|  | #include "base/message_loop/message_loop.h" | 
|  | #include "base/strings/string_number_conversions.h" | 
|  | #include "base/strings/utf_string_conversions.h" | 
|  | #include "base/sys_info.h" | 
|  | #include "base/task_scheduler/task_scheduler.h" | 
|  | #include "build_config.h" | 
|  | #include "tools/gn/commands.h" | 
|  | #include "tools/gn/err.h" | 
|  | #include "tools/gn/location.h" | 
|  | #include "tools/gn/standard_out.h" | 
|  | #include "tools/gn/switches.h" | 
|  |  | 
|  | // Only the GN-generated build makes this header for now. | 
|  | // TODO(brettw) consider adding this if we need it in GYP. | 
|  | #if defined(GN_BUILD) | 
|  | #include "tools/gn/last_commit_position.h" | 
|  | #else | 
|  | #define LAST_COMMIT_POSITION "UNKNOWN" | 
|  | #endif | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | std::vector<std::string> GetArgs(const base::CommandLine& cmdline) { | 
|  | base::CommandLine::StringVector in_args = cmdline.GetArgs(); | 
|  | #if defined(OS_WIN) | 
|  | std::vector<std::string> out_args; | 
|  | for (const auto& arg : in_args) | 
|  | out_args.push_back(base::WideToUTF8(arg)); | 
|  | return out_args; | 
|  | #else | 
|  | return in_args; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | int GetThreadCount() { | 
|  | std::string thread_count = | 
|  | base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | 
|  | switches::kThreads); | 
|  |  | 
|  | // See if an override was specified on the command line. | 
|  | int result; | 
|  | if (!thread_count.empty() && base::StringToInt(thread_count, &result) && | 
|  | result >= 1) { | 
|  | return result; | 
|  | } | 
|  |  | 
|  | // Base the default number of worker threads on number of cores in the | 
|  | // system. When building large projects, the speed can be limited by how fast | 
|  | // the main thread can dispatch work and connect the dependency graph. If | 
|  | // there are too many worker threads, the main thread can be starved and it | 
|  | // will run slower overall. | 
|  | // | 
|  | // One less worker thread than the number of physical CPUs seems to be a | 
|  | // good value, both theoretically and experimentally. But always use at | 
|  | // least some workers to prevent us from being too sensitive to I/O latency | 
|  | // on low-end systems. | 
|  | // | 
|  | // The minimum thread count is based on measuring the optimal threads for the | 
|  | // Chrome build on a several-year-old 4-core MacBook. | 
|  | // Almost all CPUs now are hyperthreaded. | 
|  | int num_cores = base::SysInfo::NumberOfProcessors() / 2; | 
|  | return std::max(num_cores - 1, 8); | 
|  | } | 
|  |  | 
|  | void StartTaskScheduler() { | 
|  | constexpr base::TimeDelta kSuggestedReclaimTime = | 
|  | base::TimeDelta::FromSeconds(30); | 
|  |  | 
|  | constexpr int kBackgroundMaxThreads = 1; | 
|  | constexpr int kBackgroundBlockingMaxThreads = 2; | 
|  | const int kForegroundMaxThreads = | 
|  | std::max(1, base::SysInfo::NumberOfProcessors()); | 
|  | const int foreground_blocking_max_threads = GetThreadCount(); | 
|  |  | 
|  | base::TaskScheduler::Create("gn"); | 
|  | base::TaskScheduler::GetInstance()->Start( | 
|  | {{kBackgroundMaxThreads, kSuggestedReclaimTime}, | 
|  | {kBackgroundBlockingMaxThreads, kSuggestedReclaimTime}, | 
|  | {kForegroundMaxThreads, kSuggestedReclaimTime}, | 
|  | {foreground_blocking_max_threads, kSuggestedReclaimTime}}); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | int main(int argc, char** argv) { | 
|  | base::AtExitManager at_exit; | 
|  | #if defined(OS_WIN) | 
|  | base::CommandLine::set_slash_is_not_a_switch(); | 
|  | #endif | 
|  | base::CommandLine::Init(argc, argv); | 
|  |  | 
|  | const base::CommandLine& cmdline = *base::CommandLine::ForCurrentProcess(); | 
|  | std::vector<std::string> args = GetArgs(cmdline); | 
|  |  | 
|  | std::string command; | 
|  | if (cmdline.HasSwitch("help") || cmdline.HasSwitch("h")) { | 
|  | // Make "-h" and "--help" default to help command. | 
|  | command = commands::kHelp; | 
|  | } else if (cmdline.HasSwitch(switches::kVersion)) { | 
|  | // Make "--version" print the version and exit. | 
|  | OutputString(std::string(LAST_COMMIT_POSITION) + "\n"); | 
|  | exit(0); | 
|  | } else if (args.empty()) { | 
|  | // No command, print error and exit. | 
|  | Err(Location(), "No command specified.", | 
|  | "Most commonly you want \"gn gen <out_dir>\" to make a build dir.\n" | 
|  | "Or try \"gn help\" for more commands.").PrintToStdout(); | 
|  | return 1; | 
|  | } else { | 
|  | command = args[0]; | 
|  | args.erase(args.begin()); | 
|  | } | 
|  |  | 
|  | const commands::CommandInfoMap& command_map = commands::GetCommands(); | 
|  | commands::CommandInfoMap::const_iterator found_command = | 
|  | command_map.find(command); | 
|  |  | 
|  | int retval; | 
|  | if (found_command != command_map.end()) { | 
|  | base::MessageLoop message_loop; | 
|  | StartTaskScheduler(); | 
|  | retval = found_command->second.runner(args); | 
|  | base::TaskScheduler::GetInstance()->Shutdown(); | 
|  | } else { | 
|  | Err(Location(), "Command \"" + command + "\" unknown.").PrintToStdout(); | 
|  | OutputString( | 
|  | "Available commands (type \"gn help <command>\" for more details):\n"); | 
|  | for (const auto& cmd : commands::GetCommands()) | 
|  | PrintShortHelp(cmd.second.help_short); | 
|  |  | 
|  | retval = 1; | 
|  | } | 
|  |  | 
|  | exit(retval);  // Don't free memory, it can be really slow! | 
|  | } |