Revert 264226 "Reduce dependency of TiclInvalidationService on P..."
[chromium-blink-merge.git] / tools / gn / setup.cc
blobd956c0f94ac0339c45941fa0dce63a40dbdfaf96
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/header_checker.h"
22 #include "tools/gn/input_file.h"
23 #include "tools/gn/parse_tree.h"
24 #include "tools/gn/parser.h"
25 #include "tools/gn/source_dir.h"
26 #include "tools/gn/source_file.h"
27 #include "tools/gn/standard_out.h"
28 #include "tools/gn/tokenizer.h"
29 #include "tools/gn/trace.h"
30 #include "tools/gn/value.h"
32 #if defined(OS_WIN)
33 #include <windows.h>
34 #endif
36 extern const char kDotfile_Help[] =
37 ".gn file\n"
38 "\n"
39 " When gn starts, it will search the current directory and parent\n"
40 " directories for a file called \".gn\". This indicates the source root.\n"
41 " You can override this detection by using the --root command-line\n"
42 " argument\n"
43 "\n"
44 " The .gn file in the source root will be executed. The syntax is the\n"
45 " same as a buildfile, but with very limited build setup-specific\n"
46 " meaning.\n"
47 "\n"
48 " If you specify --root, by default GN will look for the file .gn in\n"
49 " that directory. If you want to specify a different file, you can\n"
50 " additionally pass --dotfile:\n"
51 "\n"
52 " gn gen out/Debug --root=/home/build --dotfile=/home/my_gn_file.gn\n"
53 "\n"
54 "Variables\n"
55 "\n"
56 " buildconfig [required]\n"
57 " Label of the build config file. This file will be used to set up\n"
58 " the build file execution environment for each toolchain.\n"
59 "\n"
60 " root [optional]\n"
61 " Label of the root build target. The GN build will start by loading\n"
62 " the build file containing this target name. This defaults to\n"
63 " \"//:\" which will cause the file //BUILD.gn to be loaded.\n"
64 "\n"
65 " secondary_source [optional]\n"
66 " Label of an alternate directory tree to find input files. When\n"
67 " searching for a BUILD.gn file (or the build config file discussed\n"
68 " above), the file fill first be looked for in the source root.\n"
69 " If it's not found, the secondary source root will be checked\n"
70 " (which would contain a parallel directory hierarchy).\n"
71 "\n"
72 " This behavior is intended to be used when BUILD.gn files can't be\n"
73 " checked in to certain source directories for whatever reason.\n"
74 "\n"
75 " The secondary source root must be inside the main source tree.\n"
76 "\n"
77 "Example .gn file contents\n"
78 "\n"
79 " buildconfig = \"//build/config/BUILDCONFIG.gn\"\n"
80 "\n"
81 " root = \"//:root\"\n"
82 "\n"
83 " secondary_source = \"//build/config/temporary_buildfiles/\"\n";
85 namespace {
87 // More logging.
88 const char kSwitchVerbose[] = "v";
90 // Set build args.
91 const char kSwitchArgs[] = "args";
93 // Set root dir.
94 const char kSwitchRoot[] = "root";
96 // Set dotfile name.
97 const char kSwitchDotfile[] = "dotfile";
99 // Enable timing.
100 const char kTimeSwitch[] = "time";
102 const char kTracelogSwitch[] = "tracelog";
104 const base::FilePath::CharType kGnFile[] = FILE_PATH_LITERAL(".gn");
106 base::FilePath FindDotFile(const base::FilePath& current_dir) {
107 base::FilePath try_this_file = current_dir.Append(kGnFile);
108 if (base::PathExists(try_this_file))
109 return try_this_file;
111 base::FilePath with_no_slash = current_dir.StripTrailingSeparators();
112 base::FilePath up_one_dir = with_no_slash.DirName();
113 if (up_one_dir == current_dir)
114 return base::FilePath(); // Got to the top.
116 return FindDotFile(up_one_dir);
119 // Called on any thread. Post the item to the builder on the main thread.
120 void ItemDefinedCallback(base::MessageLoop* main_loop,
121 scoped_refptr<Builder> builder,
122 scoped_ptr<Item> item) {
123 DCHECK(item);
124 main_loop->PostTask(FROM_HERE, base::Bind(&Builder::ItemDefined, builder,
125 base::Passed(&item)));
128 void DecrementWorkCount() {
129 g_scheduler->DecrementWorkCount();
132 } // namespace
134 // CommonSetup -----------------------------------------------------------------
136 CommonSetup::CommonSetup()
137 : build_settings_(),
138 loader_(new LoaderImpl(&build_settings_)),
139 builder_(new Builder(loader_.get())),
140 root_build_file_("//BUILD.gn"),
141 check_for_bad_items_(true),
142 check_for_unused_overrides_(true),
143 check_public_headers_(false) {
144 loader_->set_complete_callback(base::Bind(&DecrementWorkCount));
147 CommonSetup::CommonSetup(const CommonSetup& other)
148 : build_settings_(other.build_settings_),
149 loader_(new LoaderImpl(&build_settings_)),
150 builder_(new Builder(loader_.get())),
151 root_build_file_(other.root_build_file_),
152 check_for_bad_items_(other.check_for_bad_items_),
153 check_for_unused_overrides_(other.check_for_unused_overrides_),
154 check_public_headers_(other.check_public_headers_) {
155 loader_->set_complete_callback(base::Bind(&DecrementWorkCount));
158 CommonSetup::~CommonSetup() {
161 void CommonSetup::RunPreMessageLoop() {
162 // Load the root build file.
163 loader_->Load(root_build_file_, Label());
165 // Will be decremented with the loader is drained.
166 g_scheduler->IncrementWorkCount();
169 bool CommonSetup::RunPostMessageLoop() {
170 Err err;
171 if (check_for_bad_items_) {
172 if (!builder_->CheckForBadItems(&err)) {
173 err.PrintToStdout();
174 return false;
178 if (check_for_unused_overrides_) {
179 if (!build_settings_.build_args().VerifyAllOverridesUsed(&err)) {
180 // TODO(brettw) implement a system of warnings. Until we have a better
181 // system, print the error but don't return failure.
182 err.PrintToStdout();
183 return true;
187 if (check_public_headers_) {
188 std::vector<const Target*> targets = builder_->GetAllResolvedTargets();
189 scoped_refptr<HeaderChecker> header_checker(
190 new HeaderChecker(&build_settings_, targets));
192 std::vector<Err> header_errors;
193 header_checker->Run(&header_errors);
194 for (size_t i = 0; i < header_errors.size(); i++) {
195 if (i > 0)
196 OutputString("___________________\n", DECORATION_YELLOW);
197 header_errors[i].PrintToStdout();
199 if (!header_errors.empty())
200 return false;
203 // Write out tracing and timing if requested.
204 const CommandLine* cmdline = CommandLine::ForCurrentProcess();
205 if (cmdline->HasSwitch(kTimeSwitch))
206 PrintLongHelp(SummarizeTraces());
207 if (cmdline->HasSwitch(kTracelogSwitch))
208 SaveTraces(cmdline->GetSwitchValuePath(kTracelogSwitch));
210 return true;
213 // Setup -----------------------------------------------------------------------
215 Setup::Setup()
216 : CommonSetup(),
217 empty_settings_(&empty_build_settings_, std::string()),
218 dotfile_scope_(&empty_settings_) {
219 empty_settings_.set_toolchain_label(Label());
220 build_settings_.set_item_defined_callback(
221 base::Bind(&ItemDefinedCallback, scheduler_.main_loop(), builder_));
223 // The scheduler's main loop wasn't created when the Loader was created, so
224 // we need to set it now.
225 loader_->set_main_loop(scheduler_.main_loop());
228 Setup::~Setup() {
231 bool Setup::DoSetup(const std::string& build_dir) {
232 CommandLine* cmdline = CommandLine::ForCurrentProcess();
234 scheduler_.set_verbose_logging(cmdline->HasSwitch(kSwitchVerbose));
235 if (cmdline->HasSwitch(kTimeSwitch) ||
236 cmdline->HasSwitch(kTracelogSwitch))
237 EnableTracing();
239 if (!FillArguments(*cmdline))
240 return false;
241 if (!FillSourceDir(*cmdline))
242 return false;
243 if (!RunConfigFile())
244 return false;
245 if (!FillOtherConfig(*cmdline))
246 return false;
247 if (!FillBuildDir(build_dir)) // Must be after FillSourceDir to resolve.
248 return false;
249 FillPythonPath();
251 return true;
254 bool Setup::Run() {
255 RunPreMessageLoop();
256 if (!scheduler_.Run())
257 return false;
258 return RunPostMessageLoop();
261 Scheduler* Setup::GetScheduler() {
262 return &scheduler_;
265 bool Setup::FillArguments(const CommandLine& cmdline) {
266 std::string args = cmdline.GetSwitchValueASCII(kSwitchArgs);
267 if (args.empty())
268 return true; // Nothing to set.
270 args_input_file_.reset(new InputFile(SourceFile()));
271 args_input_file_->SetContents(args);
272 args_input_file_->set_friendly_name("the command-line \"--args\" settings");
274 Err err;
275 args_tokens_ = Tokenizer::Tokenize(args_input_file_.get(), &err);
276 if (err.has_error()) {
277 err.PrintToStdout();
278 return false;
281 args_root_ = Parser::Parse(args_tokens_, &err);
282 if (err.has_error()) {
283 err.PrintToStdout();
284 return false;
287 Scope arg_scope(&empty_settings_);
288 args_root_->AsBlock()->ExecuteBlockInScope(&arg_scope, &err);
289 if (err.has_error()) {
290 err.PrintToStdout();
291 return false;
294 // Save the result of the command args.
295 Scope::KeyValueMap overrides;
296 arg_scope.GetCurrentScopeValues(&overrides);
297 build_settings_.build_args().AddArgOverrides(overrides);
298 return true;
301 bool Setup::FillSourceDir(const CommandLine& cmdline) {
302 // Find the .gn file.
303 base::FilePath root_path;
305 // Prefer the command line args to the config file.
306 base::FilePath relative_root_path = cmdline.GetSwitchValuePath(kSwitchRoot);
307 if (!relative_root_path.empty()) {
308 root_path = base::MakeAbsoluteFilePath(relative_root_path);
309 if (root_path.empty()) {
310 Err(Location(), "Root source path not found.",
311 "The path \"" + FilePathToUTF8(relative_root_path) +
312 "\" doesn't exist.").PrintToStdout();
313 return false;
316 // When --root is specified, an alternate --dotfile can also be set.
317 // --dotfile should be a real file path and not a "//foo" source-relative
318 // path.
319 base::FilePath dot_file_path = cmdline.GetSwitchValuePath(kSwitchDotfile);
320 if (dot_file_path.empty()) {
321 dotfile_name_ = root_path.Append(kGnFile);
322 } else {
323 dotfile_name_ = base::MakeAbsoluteFilePath(dot_file_path);
324 if (dotfile_name_.empty()) {
325 Err(Location(), "Could not load dotfile.",
326 "The file \"" + FilePathToUTF8(dot_file_path) +
327 "\" cound't be loaded.").PrintToStdout();
328 return false;
331 } else {
332 // In the default case, look for a dotfile and that also tells us where the
333 // source root is.
334 base::FilePath cur_dir;
335 base::GetCurrentDirectory(&cur_dir);
336 dotfile_name_ = FindDotFile(cur_dir);
337 if (dotfile_name_.empty()) {
338 Err(Location(), "Can't find source root.",
339 "I could not find a \".gn\" file in the current directory or any "
340 "parent,\nand the --root command-line argument was not specified.")
341 .PrintToStdout();
342 return false;
344 root_path = dotfile_name_.DirName();
347 if (scheduler_.verbose_logging())
348 scheduler_.Log("Using source root", FilePathToUTF8(root_path));
349 build_settings_.SetRootPath(root_path);
351 return true;
354 bool Setup::FillBuildDir(const std::string& build_dir) {
355 SourceDir resolved =
356 SourceDirForCurrentDirectory(build_settings_.root_path()).
357 ResolveRelativeDir(build_dir);
358 if (resolved.is_null()) {
359 Err(Location(), "Couldn't resolve build directory.",
360 "The build directory supplied (\"" + build_dir + "\") was not valid.").
361 PrintToStdout();
362 return false;
365 if (scheduler_.verbose_logging())
366 scheduler_.Log("Using build dir", resolved.value());
367 build_settings_.SetBuildDir(resolved);
368 return true;
371 void Setup::FillPythonPath() {
372 #if defined(OS_WIN)
373 // Find Python on the path so we can use the absolute path in the build.
374 const base::char16 kGetPython[] =
375 L"cmd.exe /c python -c \"import sys; print sys.executable\"";
376 std::string python_path;
377 if (base::GetAppOutput(kGetPython, &python_path)) {
378 base::TrimWhitespaceASCII(python_path, base::TRIM_ALL, &python_path);
379 if (scheduler_.verbose_logging())
380 scheduler_.Log("Found python", python_path);
381 } else {
382 scheduler_.Log("WARNING", "Could not find python on path, using "
383 "just \"python.exe\"");
384 python_path = "python.exe";
386 build_settings_.set_python_path(base::FilePath(base::UTF8ToUTF16(python_path))
387 .NormalizePathSeparatorsTo('/'));
388 #else
389 build_settings_.set_python_path(base::FilePath("python"));
390 #endif
393 bool Setup::RunConfigFile() {
394 if (scheduler_.verbose_logging())
395 scheduler_.Log("Got dotfile", FilePathToUTF8(dotfile_name_));
397 dotfile_input_file_.reset(new InputFile(SourceFile("//.gn")));
398 if (!dotfile_input_file_->Load(dotfile_name_)) {
399 Err(Location(), "Could not load dotfile.",
400 "The file \"" + FilePathToUTF8(dotfile_name_) + "\" cound't be loaded")
401 .PrintToStdout();
402 return false;
405 Err err;
406 dotfile_tokens_ = Tokenizer::Tokenize(dotfile_input_file_.get(), &err);
407 if (err.has_error()) {
408 err.PrintToStdout();
409 return false;
412 dotfile_root_ = Parser::Parse(dotfile_tokens_, &err);
413 if (err.has_error()) {
414 err.PrintToStdout();
415 return false;
418 dotfile_root_->AsBlock()->ExecuteBlockInScope(&dotfile_scope_, &err);
419 if (err.has_error()) {
420 err.PrintToStdout();
421 return false;
424 return true;
427 bool Setup::FillOtherConfig(const CommandLine& cmdline) {
428 Err err;
430 // Secondary source path, read from the config file if present.
431 // Read from the config file if present.
432 const Value* secondary_value =
433 dotfile_scope_.GetValue("secondary_source", true);
434 if (secondary_value) {
435 if (!secondary_value->VerifyTypeIs(Value::STRING, &err)) {
436 err.PrintToStdout();
437 return false;
439 build_settings_.SetSecondarySourcePath(
440 SourceDir(secondary_value->string_value()));
443 // Root build file.
444 const Value* root_value = dotfile_scope_.GetValue("root", true);
445 if (root_value) {
446 if (!root_value->VerifyTypeIs(Value::STRING, &err)) {
447 err.PrintToStdout();
448 return false;
451 Label root_target_label =
452 Label::Resolve(SourceDir("//"), Label(), *root_value, &err);
453 if (err.has_error()) {
454 err.PrintToStdout();
455 return false;
458 root_build_file_ = Loader::BuildFileForLabel(root_target_label);
461 // Build config file.
462 const Value* build_config_value =
463 dotfile_scope_.GetValue("buildconfig", true);
464 if (!build_config_value) {
465 Err(Location(), "No build config file.",
466 "Your .gn file (\"" + FilePathToUTF8(dotfile_name_) + "\")\n"
467 "didn't specify a \"buildconfig\" value.").PrintToStdout();
468 return false;
469 } else if (!build_config_value->VerifyTypeIs(Value::STRING, &err)) {
470 err.PrintToStdout();
471 return false;
473 build_settings_.set_build_config_file(
474 SourceFile(build_config_value->string_value()));
476 return true;
479 // DependentSetup --------------------------------------------------------------
481 DependentSetup::DependentSetup(Setup* derive_from)
482 : CommonSetup(*derive_from),
483 scheduler_(derive_from->GetScheduler()) {
484 build_settings_.set_item_defined_callback(
485 base::Bind(&ItemDefinedCallback, scheduler_->main_loop(), builder_));
488 DependentSetup::DependentSetup(DependentSetup* derive_from)
489 : CommonSetup(*derive_from),
490 scheduler_(derive_from->GetScheduler()) {
491 build_settings_.set_item_defined_callback(
492 base::Bind(&ItemDefinedCallback, scheduler_->main_loop(), builder_));
495 DependentSetup::~DependentSetup() {
498 Scheduler* DependentSetup::GetScheduler() {
499 return scheduler_;
502 void DependentSetup::RunPreMessageLoop() {
503 CommonSetup::RunPreMessageLoop();
506 bool DependentSetup::RunPostMessageLoop() {
507 return CommonSetup::RunPostMessageLoop();