Replace command buffer FlushSync with WaitForTokenInRange and WaitForGetOffsetInRange
[chromium-blink-merge.git] / tools / gn / setup.cc
blob759b40a4d64f600d89d5ac21ae0206a18f3eddcc
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "tools/gn/setup.h"
7 #include <stdlib.h>
9 #include <algorithm>
11 #include "base/bind.h"
12 #include "base/command_line.h"
13 #include "base/file_util.h"
14 #include "base/files/file_path.h"
15 #include "base/process/launch.h"
16 #include "base/strings/string_split.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "build/build_config.h"
20 #include "tools/gn/filesystem_utils.h"
21 #include "tools/gn/input_file.h"
22 #include "tools/gn/parse_tree.h"
23 #include "tools/gn/parser.h"
24 #include "tools/gn/source_dir.h"
25 #include "tools/gn/source_file.h"
26 #include "tools/gn/standard_out.h"
27 #include "tools/gn/tokenizer.h"
28 #include "tools/gn/trace.h"
29 #include "tools/gn/value.h"
31 #if defined(OS_WIN)
32 #include <windows.h>
33 #endif
35 extern const char kDotfile_Help[] =
36 ".gn file\n"
37 "\n"
38 " When gn starts, it will search the current directory and parent\n"
39 " directories for a file called \".gn\". This indicates the source root.\n"
40 " You can override this detection by using the --root command-line\n"
41 " argument\n"
42 "\n"
43 " The .gn file in the source root will be executed. The syntax is the\n"
44 " same as a buildfile, but with very limited build setup-specific\n"
45 " meaning.\n"
46 "\n"
47 "Variables\n"
48 "\n"
49 " buildconfig [required]\n"
50 " Label of the build config file. This file will be used to setup\n"
51 " the build file execution environment for each toolchain.\n"
52 "\n"
53 " secondary_source [optional]\n"
54 " Label of an alternate directory tree to find input files. When\n"
55 " searching for a BUILD.gn file (or the build config file discussed\n"
56 " above), the file fill first be looked for in the source root.\n"
57 " If it's not found, the secondary source root will be checked\n"
58 " (which would contain a parallel directory hierarchy).\n"
59 "\n"
60 " This behavior is intended to be used when BUILD.gn files can't be\n"
61 " checked in to certain source directories for whatever reason.\n"
62 "\n"
63 " The secondary source root must be inside the main source tree.\n"
64 "\n"
65 "Example .gn file contents\n"
66 "\n"
67 " buildconfig = \"//build/config/BUILDCONFIG.gn\"\n"
68 "\n"
69 " secondary_source = \"//build/config/temporary_buildfiles/\"\n";
71 namespace {
73 // More logging.
74 const char kSwitchVerbose[] = "v";
76 // Set build args.
77 const char kSwitchArgs[] = "args";
79 // Set root dir.
80 const char kSwitchRoot[] = "root";
82 // Enable timing.
83 const char kTimeSwitch[] = "time";
85 const char kTracelogSwitch[] = "tracelog";
87 const char kSecondarySource[] = "secondary";
89 const base::FilePath::CharType kGnFile[] = FILE_PATH_LITERAL(".gn");
91 base::FilePath FindDotFile(const base::FilePath& current_dir) {
92 base::FilePath try_this_file = current_dir.Append(kGnFile);
93 if (base::PathExists(try_this_file))
94 return try_this_file;
96 base::FilePath with_no_slash = current_dir.StripTrailingSeparators();
97 base::FilePath up_one_dir = with_no_slash.DirName();
98 if (up_one_dir == current_dir)
99 return base::FilePath(); // Got to the top.
101 return FindDotFile(up_one_dir);
104 // Called on any thread. Post the item to the builder on the main thread.
105 void ItemDefinedCallback(base::MessageLoop* main_loop,
106 scoped_refptr<Builder> builder,
107 scoped_ptr<Item> item) {
108 DCHECK(item);
109 main_loop->PostTask(FROM_HERE, base::Bind(&Builder::ItemDefined, builder,
110 base::Passed(&item)));
113 void DecrementWorkCount() {
114 g_scheduler->DecrementWorkCount();
117 } // namespace
119 // CommonSetup -----------------------------------------------------------------
121 CommonSetup::CommonSetup()
122 : build_settings_(),
123 loader_(new LoaderImpl(&build_settings_)),
124 builder_(new Builder(loader_.get())),
125 check_for_bad_items_(true),
126 check_for_unused_overrides_(true) {
127 loader_->set_complete_callback(base::Bind(&DecrementWorkCount));
130 CommonSetup::CommonSetup(const CommonSetup& other)
131 : build_settings_(other.build_settings_),
132 loader_(new LoaderImpl(&build_settings_)),
133 builder_(new Builder(loader_.get())),
134 check_for_bad_items_(other.check_for_bad_items_),
135 check_for_unused_overrides_(other.check_for_unused_overrides_) {
136 loader_->set_complete_callback(base::Bind(&DecrementWorkCount));
139 CommonSetup::~CommonSetup() {
142 void CommonSetup::RunPreMessageLoop() {
143 // Load the root build file.
144 loader_->Load(SourceFile("//BUILD.gn"), Label());
146 // Will be decremented with the loader is drained.
147 g_scheduler->IncrementWorkCount();
150 bool CommonSetup::RunPostMessageLoop() {
151 Err err;
152 if (check_for_bad_items_) {
153 if (!builder_->CheckForBadItems(&err)) {
154 err.PrintToStdout();
155 return false;
159 if (check_for_unused_overrides_) {
160 if (!build_settings_.build_args().VerifyAllOverridesUsed(&err)) {
161 // TODO(brettw) implement a system of warnings. Until we have a better
162 // system, print the error but don't return failure.
163 err.PrintToStdout();
164 return true;
168 // Write out tracing and timing if requested.
169 const CommandLine* cmdline = CommandLine::ForCurrentProcess();
170 if (cmdline->HasSwitch(kTimeSwitch))
171 PrintLongHelp(SummarizeTraces());
172 if (cmdline->HasSwitch(kTracelogSwitch))
173 SaveTraces(cmdline->GetSwitchValuePath(kTracelogSwitch));
175 return true;
178 // Setup -----------------------------------------------------------------------
180 Setup::Setup()
181 : CommonSetup(),
182 empty_settings_(&empty_build_settings_, std::string()),
183 dotfile_scope_(&empty_settings_) {
184 empty_settings_.set_toolchain_label(Label());
185 build_settings_.set_item_defined_callback(
186 base::Bind(&ItemDefinedCallback, scheduler_.main_loop(), builder_));
188 // The scheduler's main loop wasn't created when the Loader was created, so
189 // we need to set it now.
190 loader_->set_main_loop(scheduler_.main_loop());
193 Setup::~Setup() {
196 bool Setup::DoSetup(const std::string& build_dir) {
197 CommandLine* cmdline = CommandLine::ForCurrentProcess();
199 scheduler_.set_verbose_logging(cmdline->HasSwitch(kSwitchVerbose));
200 if (cmdline->HasSwitch(kTimeSwitch) ||
201 cmdline->HasSwitch(kTracelogSwitch))
202 EnableTracing();
204 if (!FillArguments(*cmdline))
205 return false;
206 if (!FillSourceDir(*cmdline))
207 return false;
208 if (!RunConfigFile())
209 return false;
210 if (!FillOtherConfig(*cmdline))
211 return false;
212 if (!FillBuildDir(build_dir)) // Must be after FillSourceDir to resolve.
213 return false;
214 FillPythonPath();
216 return true;
219 bool Setup::Run() {
220 RunPreMessageLoop();
221 if (!scheduler_.Run())
222 return false;
223 return RunPostMessageLoop();
226 Scheduler* Setup::GetScheduler() {
227 return &scheduler_;
230 bool Setup::FillArguments(const CommandLine& cmdline) {
231 std::string args = cmdline.GetSwitchValueASCII(kSwitchArgs);
232 if (args.empty())
233 return true; // Nothing to set.
235 args_input_file_.reset(new InputFile(SourceFile()));
236 args_input_file_->SetContents(args);
237 args_input_file_->set_friendly_name("the command-line \"--args\" settings");
239 Err err;
240 args_tokens_ = Tokenizer::Tokenize(args_input_file_.get(), &err);
241 if (err.has_error()) {
242 err.PrintToStdout();
243 return false;
246 args_root_ = Parser::Parse(args_tokens_, &err);
247 if (err.has_error()) {
248 err.PrintToStdout();
249 return false;
252 Scope arg_scope(&empty_settings_);
253 args_root_->AsBlock()->ExecuteBlockInScope(&arg_scope, &err);
254 if (err.has_error()) {
255 err.PrintToStdout();
256 return false;
259 // Save the result of the command args.
260 Scope::KeyValueMap overrides;
261 arg_scope.GetCurrentScopeValues(&overrides);
262 build_settings_.build_args().AddArgOverrides(overrides);
263 return true;
266 bool Setup::FillSourceDir(const CommandLine& cmdline) {
267 // Find the .gn file.
268 base::FilePath root_path;
270 // Prefer the command line args to the config file.
271 base::FilePath relative_root_path = cmdline.GetSwitchValuePath(kSwitchRoot);
272 if (!relative_root_path.empty()) {
273 root_path = base::MakeAbsoluteFilePath(relative_root_path);
274 dotfile_name_ = root_path.Append(kGnFile);
275 } else {
276 base::FilePath cur_dir;
277 base::GetCurrentDirectory(&cur_dir);
278 dotfile_name_ = FindDotFile(cur_dir);
279 if (dotfile_name_.empty()) {
280 Err(Location(), "Can't find source root.",
281 "I could not find a \".gn\" file in the current directory or any "
282 "parent,\nand the --root command-line argument was not specified.")
283 .PrintToStdout();
284 return false;
286 root_path = dotfile_name_.DirName();
289 if (scheduler_.verbose_logging())
290 scheduler_.Log("Using source root", FilePathToUTF8(root_path));
291 build_settings_.SetRootPath(root_path);
293 return true;
296 bool Setup::FillBuildDir(const std::string& build_dir) {
297 std::string normalized_build_dir = PathToSystem(build_dir);
299 SourceDir resolved =
300 SourceDirForCurrentDirectory(build_settings_.root_path()).
301 ResolveRelativeDir(normalized_build_dir);
302 if (resolved.is_null()) {
303 Err(Location(), "Couldn't resolve build directory.",
304 "The build directory supplied (\"" + build_dir + "\") was not valid.").
305 PrintToStdout();
306 return false;
309 if (scheduler_.verbose_logging())
310 scheduler_.Log("Using build dir", resolved.value());
311 build_settings_.SetBuildDir(resolved);
312 return true;
315 void Setup::FillPythonPath() {
316 #if defined(OS_WIN)
317 // Find Python on the path so we can use the absolute path in the build.
318 const base::char16 kGetPython[] =
319 L"cmd.exe /c python -c \"import sys; print sys.executable\"";
320 std::string python_path;
321 if (base::GetAppOutput(kGetPython, &python_path)) {
322 base::TrimWhitespaceASCII(python_path, base::TRIM_ALL, &python_path);
323 if (scheduler_.verbose_logging())
324 scheduler_.Log("Found python", python_path);
325 } else {
326 scheduler_.Log("WARNING", "Could not find python on path, using "
327 "just \"python.exe\"");
328 python_path = "python.exe";
330 build_settings_.set_python_path(
331 base::FilePath(base::UTF8ToUTF16(python_path)));
332 #else
333 build_settings_.set_python_path(base::FilePath("python"));
334 #endif
337 bool Setup::RunConfigFile() {
338 if (scheduler_.verbose_logging())
339 scheduler_.Log("Got dotfile", FilePathToUTF8(dotfile_name_));
341 dotfile_input_file_.reset(new InputFile(SourceFile("//.gn")));
342 if (!dotfile_input_file_->Load(dotfile_name_)) {
343 Err(Location(), "Could not load dotfile.",
344 "The file \"" + FilePathToUTF8(dotfile_name_) + "\" cound't be loaded")
345 .PrintToStdout();
346 return false;
349 Err err;
350 dotfile_tokens_ = Tokenizer::Tokenize(dotfile_input_file_.get(), &err);
351 if (err.has_error()) {
352 err.PrintToStdout();
353 return false;
356 dotfile_root_ = Parser::Parse(dotfile_tokens_, &err);
357 if (err.has_error()) {
358 err.PrintToStdout();
359 return false;
362 dotfile_root_->AsBlock()->ExecuteBlockInScope(&dotfile_scope_, &err);
363 if (err.has_error()) {
364 err.PrintToStdout();
365 return false;
368 return true;
371 bool Setup::FillOtherConfig(const CommandLine& cmdline) {
372 Err err;
374 // Secondary source path.
375 SourceDir secondary_source;
376 if (cmdline.HasSwitch(kSecondarySource)) {
377 // Prefer the command line over the config file.
378 secondary_source =
379 SourceDir(cmdline.GetSwitchValueASCII(kSecondarySource));
380 } else {
381 // Read from the config file if present.
382 const Value* secondary_value =
383 dotfile_scope_.GetValue("secondary_source", true);
384 if (secondary_value) {
385 if (!secondary_value->VerifyTypeIs(Value::STRING, &err)) {
386 err.PrintToStdout();
387 return false;
389 build_settings_.SetSecondarySourcePath(
390 SourceDir(secondary_value->string_value()));
394 // Build config file.
395 const Value* build_config_value =
396 dotfile_scope_.GetValue("buildconfig", true);
397 if (!build_config_value) {
398 Err(Location(), "No build config file.",
399 "Your .gn file (\"" + FilePathToUTF8(dotfile_name_) + "\")\n"
400 "didn't specify a \"buildconfig\" value.").PrintToStdout();
401 return false;
402 } else if (!build_config_value->VerifyTypeIs(Value::STRING, &err)) {
403 err.PrintToStdout();
404 return false;
406 build_settings_.set_build_config_file(
407 SourceFile(build_config_value->string_value()));
409 return true;
412 // DependentSetup --------------------------------------------------------------
414 DependentSetup::DependentSetup(Setup* derive_from)
415 : CommonSetup(*derive_from),
416 scheduler_(derive_from->GetScheduler()) {
417 build_settings_.set_item_defined_callback(
418 base::Bind(&ItemDefinedCallback, scheduler_->main_loop(), builder_));
421 DependentSetup::DependentSetup(DependentSetup* derive_from)
422 : CommonSetup(*derive_from),
423 scheduler_(derive_from->GetScheduler()) {
424 build_settings_.set_item_defined_callback(
425 base::Bind(&ItemDefinedCallback, scheduler_->main_loop(), builder_));
428 DependentSetup::~DependentSetup() {
431 Scheduler* DependentSetup::GetScheduler() {
432 return scheduler_;
435 void DependentSetup::RunPreMessageLoop() {
436 CommonSetup::RunPreMessageLoop();
439 bool DependentSetup::RunPostMessageLoop() {
440 return CommonSetup::RunPostMessageLoop();