Supervised user import: Listen for profile creation/deletion
[chromium-blink-merge.git] / tools / gn / setup.cc
blobd8d525ad7b34b522316a00baf207b27b53bdc0c5
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>
10 #include <sstream>
12 #include "base/bind.h"
13 #include "base/command_line.h"
14 #include "base/files/file_path.h"
15 #include "base/files/file_util.h"
16 #include "base/process/launch.h"
17 #include "base/strings/string_split.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "build/build_config.h"
21 #include "tools/gn/commands.h"
22 #include "tools/gn/filesystem_utils.h"
23 #include "tools/gn/input_file.h"
24 #include "tools/gn/parse_tree.h"
25 #include "tools/gn/parser.h"
26 #include "tools/gn/source_dir.h"
27 #include "tools/gn/source_file.h"
28 #include "tools/gn/standard_out.h"
29 #include "tools/gn/switches.h"
30 #include "tools/gn/tokenizer.h"
31 #include "tools/gn/trace.h"
32 #include "tools/gn/value.h"
34 #if defined(OS_WIN)
35 #include <windows.h>
36 #endif
38 extern const char kDotfile_Help[] =
39 ".gn file\n"
40 "\n"
41 " When gn starts, it will search the current directory and parent\n"
42 " directories for a file called \".gn\". This indicates the source root.\n"
43 " You can override this detection by using the --root command-line\n"
44 " argument\n"
45 "\n"
46 " The .gn file in the source root will be executed. The syntax is the\n"
47 " same as a buildfile, but with very limited build setup-specific\n"
48 " meaning.\n"
49 "\n"
50 " If you specify --root, by default GN will look for the file .gn in\n"
51 " that directory. If you want to specify a different file, you can\n"
52 " additionally pass --dotfile:\n"
53 "\n"
54 " gn gen out/Debug --root=/home/build --dotfile=/home/my_gn_file.gn\n"
55 "\n"
56 "Variables\n"
57 "\n"
58 " buildconfig [required]\n"
59 " Label of the build config file. This file will be used to set up\n"
60 " the build file execution environment for each toolchain.\n"
61 "\n"
62 " check_targets [optional]\n"
63 " A list of labels and label patterns that should be checked when\n"
64 " running \"gn check\" or \"gn gen --check\". If unspecified, all\n"
65 " targets will be checked. If it is the empty list, no targets will\n"
66 " be checked.\n"
67 "\n"
68 " The format of this list is identical to that of \"visibility\"\n"
69 " so see \"gn help visibility\" for examples.\n"
70 "\n"
71 " exec_script_whitelist [optional]\n"
72 " A list of .gn/.gni files (not labels) that have permission to call\n"
73 " the exec_script function. If this list is defined, calls to\n"
74 " exec_script will be checked against this list and GN will fail if\n"
75 " the current file isn't in the list.\n"
76 "\n"
77 " This is to allow the use of exec_script to be restricted since\n"
78 " is easy to use inappropriately. Wildcards are not supported.\n"
79 " Files in the secondary_source tree (if defined) should be\n"
80 " referenced by ignoring the secondary tree and naming them as if\n"
81 " they are in the main tree.\n"
82 "\n"
83 " If unspecified, the ability to call exec_script is unrestricted.\n"
84 "\n"
85 " Example:\n"
86 " exec_script_whitelist = [\n"
87 " \"//base/BUILD.gn\",\n"
88 " \"//build/my_config.gni\",\n"
89 " ]\n"
90 "\n"
91 " root [optional]\n"
92 " Label of the root build target. The GN build will start by loading\n"
93 " the build file containing this target name. This defaults to\n"
94 " \"//:\" which will cause the file //BUILD.gn to be loaded.\n"
95 "\n"
96 " secondary_source [optional]\n"
97 " Label of an alternate directory tree to find input files. When\n"
98 " searching for a BUILD.gn file (or the build config file discussed\n"
99 " above), the file will first be looked for in the source root.\n"
100 " If it's not found, the secondary source root will be checked\n"
101 " (which would contain a parallel directory hierarchy).\n"
102 "\n"
103 " This behavior is intended to be used when BUILD.gn files can't be\n"
104 " checked in to certain source directories for whatever reason.\n"
105 "\n"
106 " The secondary source root must be inside the main source tree.\n"
107 "\n"
108 "Example .gn file contents\n"
109 "\n"
110 " buildconfig = \"//build/config/BUILDCONFIG.gn\"\n"
111 "\n"
112 " check_targets = [\n"
113 " \"//doom_melon/*\", # Check everything in this subtree.\n"
114 " \"//tools:mind_controlling_ant\", # Check this specific target.\n"
115 " ]\n"
116 "\n"
117 " root = \"//:root\"\n"
118 "\n"
119 " secondary_source = \"//build/config/temporary_buildfiles/\"\n";
121 namespace {
123 const base::FilePath::CharType kGnFile[] = FILE_PATH_LITERAL(".gn");
125 base::FilePath FindDotFile(const base::FilePath& current_dir) {
126 base::FilePath try_this_file = current_dir.Append(kGnFile);
127 if (base::PathExists(try_this_file))
128 return try_this_file;
130 base::FilePath with_no_slash = current_dir.StripTrailingSeparators();
131 base::FilePath up_one_dir = with_no_slash.DirName();
132 if (up_one_dir == current_dir)
133 return base::FilePath(); // Got to the top.
135 return FindDotFile(up_one_dir);
138 // Called on any thread. Post the item to the builder on the main thread.
139 void ItemDefinedCallback(base::MessageLoop* main_loop,
140 scoped_refptr<Builder> builder,
141 scoped_ptr<Item> item) {
142 DCHECK(item);
143 main_loop->PostTask(FROM_HERE, base::Bind(&Builder::ItemDefined, builder,
144 base::Passed(&item)));
147 void DecrementWorkCount() {
148 g_scheduler->DecrementWorkCount();
151 } // namespace
153 const char Setup::kBuildArgFileName[] = "args.gn";
155 Setup::Setup()
156 : build_settings_(),
157 loader_(new LoaderImpl(&build_settings_)),
158 builder_(new Builder(loader_.get())),
159 root_build_file_("//BUILD.gn"),
160 check_for_bad_items_(true),
161 check_for_unused_overrides_(true),
162 check_public_headers_(false),
163 dotfile_settings_(&build_settings_, std::string()),
164 dotfile_scope_(&dotfile_settings_),
165 fill_arguments_(true) {
166 dotfile_settings_.set_toolchain_label(Label());
167 build_settings_.set_item_defined_callback(
168 base::Bind(&ItemDefinedCallback, scheduler_.main_loop(), builder_));
170 loader_->set_complete_callback(base::Bind(&DecrementWorkCount));
171 // The scheduler's main loop wasn't created when the Loader was created, so
172 // we need to set it now.
173 loader_->set_main_loop(scheduler_.main_loop());
176 Setup::~Setup() {
179 bool Setup::DoSetup(const std::string& build_dir, bool force_create) {
180 base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
182 scheduler_.set_verbose_logging(cmdline->HasSwitch(switches::kVerbose));
183 if (cmdline->HasSwitch(switches::kTime) ||
184 cmdline->HasSwitch(switches::kTracelog))
185 EnableTracing();
187 ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "DoSetup");
189 if (!FillSourceDir(*cmdline))
190 return false;
191 if (!RunConfigFile())
192 return false;
193 if (!FillOtherConfig(*cmdline))
194 return false;
196 // Must be after FillSourceDir to resolve.
197 if (!FillBuildDir(build_dir, !force_create))
198 return false;
200 // Check for unused variables in the .gn file.
201 Err err;
202 if (!dotfile_scope_.CheckForUnusedVars(&err)) {
203 err.PrintToStdout();
204 return false;
207 if (fill_arguments_) {
208 if (!FillArguments(*cmdline))
209 return false;
211 FillPythonPath();
213 return true;
216 bool Setup::Run() {
217 RunPreMessageLoop();
218 if (!scheduler_.Run())
219 return false;
220 return RunPostMessageLoop();
223 SourceFile Setup::GetBuildArgFile() const {
224 return SourceFile(build_settings_.build_dir().value() + kBuildArgFileName);
227 void Setup::RunPreMessageLoop() {
228 // Load the root build file.
229 loader_->Load(root_build_file_, LocationRange(), Label());
231 // Will be decremented with the loader is drained.
232 g_scheduler->IncrementWorkCount();
235 bool Setup::RunPostMessageLoop() {
236 Err err;
237 if (check_for_bad_items_) {
238 if (!builder_->CheckForBadItems(&err)) {
239 err.PrintToStdout();
240 return false;
244 if (check_for_unused_overrides_) {
245 if (!build_settings_.build_args().VerifyAllOverridesUsed(&err)) {
246 // TODO(brettw) implement a system of warnings. Until we have a better
247 // system, print the error but don't return failure.
248 err.PrintToStdout();
249 return true;
253 if (check_public_headers_) {
254 std::vector<const Target*> all_targets = builder_->GetAllResolvedTargets();
255 std::vector<const Target*> to_check;
256 if (check_patterns()) {
257 commands::FilterTargetsByPatterns(all_targets, *check_patterns(),
258 &to_check);
259 } else {
260 to_check = all_targets;
263 if (!commands::CheckPublicHeaders(&build_settings_, all_targets,
264 to_check, false)) {
265 return false;
269 // Write out tracing and timing if requested.
270 const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
271 if (cmdline->HasSwitch(switches::kTime))
272 PrintLongHelp(SummarizeTraces());
273 if (cmdline->HasSwitch(switches::kTracelog))
274 SaveTraces(cmdline->GetSwitchValuePath(switches::kTracelog));
276 return true;
279 bool Setup::FillArguments(const base::CommandLine& cmdline) {
280 // Use the args on the command line if specified, and save them. Do this even
281 // if the list is empty (this means clear any defaults).
282 if (cmdline.HasSwitch(switches::kArgs)) {
283 if (!FillArgsFromCommandLine(cmdline.GetSwitchValueASCII(switches::kArgs)))
284 return false;
285 SaveArgsToFile();
286 return true;
289 // No command line args given, use the arguments from the build dir (if any).
290 return FillArgsFromFile();
293 bool Setup::FillArgsFromCommandLine(const std::string& args) {
294 args_input_file_.reset(new InputFile(SourceFile()));
295 args_input_file_->SetContents(args);
296 args_input_file_->set_friendly_name("the command-line \"--args\"");
297 return FillArgsFromArgsInputFile();
300 bool Setup::FillArgsFromFile() {
301 ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Load args file");
303 SourceFile build_arg_source_file = GetBuildArgFile();
304 base::FilePath build_arg_file =
305 build_settings_.GetFullPath(build_arg_source_file);
307 std::string contents;
308 if (!base::ReadFileToString(build_arg_file, &contents))
309 return true; // File doesn't exist, continue with default args.
311 // Add a dependency on the build arguments file. If this changes, we want
312 // to re-generate the build.
313 g_scheduler->AddGenDependency(build_arg_file);
315 if (contents.empty())
316 return true; // Empty file, do nothing.
318 args_input_file_.reset(new InputFile(build_arg_source_file));
319 args_input_file_->SetContents(contents);
320 args_input_file_->set_friendly_name(
321 "build arg file (use \"gn args <out_dir>\" to edit)");
323 setup_trace.Done(); // Only want to count the load as part of the trace.
324 return FillArgsFromArgsInputFile();
327 bool Setup::FillArgsFromArgsInputFile() {
328 ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Parse args");
330 Err err;
331 args_tokens_ = Tokenizer::Tokenize(args_input_file_.get(), &err);
332 if (err.has_error()) {
333 err.PrintToStdout();
334 return false;
337 args_root_ = Parser::Parse(args_tokens_, &err);
338 if (err.has_error()) {
339 err.PrintToStdout();
340 return false;
343 Scope arg_scope(&dotfile_settings_);
344 args_root_->Execute(&arg_scope, &err);
345 if (err.has_error()) {
346 err.PrintToStdout();
347 return false;
350 // Save the result of the command args.
351 Scope::KeyValueMap overrides;
352 arg_scope.GetCurrentScopeValues(&overrides);
353 build_settings_.build_args().AddArgOverrides(overrides);
354 return true;
357 bool Setup::SaveArgsToFile() {
358 ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Save args file");
360 std::ostringstream stream;
361 for (const auto& pair : build_settings_.build_args().GetAllOverrides()) {
362 stream << pair.first.as_string() << " = " << pair.second.ToString(true);
363 stream << std::endl;
366 // For the first run, the build output dir might not be created yet, so do
367 // that so we can write a file into it. Ignore errors, we'll catch the error
368 // when we try to write a file to it below.
369 base::FilePath build_arg_file =
370 build_settings_.GetFullPath(GetBuildArgFile());
371 base::CreateDirectory(build_arg_file.DirName());
373 std::string contents = stream.str();
374 #if defined(OS_WIN)
375 // Use Windows lineendings for this file since it will often open in
376 // Notepad which can't handle Unix ones.
377 ReplaceSubstringsAfterOffset(&contents, 0, "\n", "\r\n");
378 #endif
379 if (base::WriteFile(build_arg_file, contents.c_str(),
380 static_cast<int>(contents.size())) == -1) {
381 Err(Location(), "Args file could not be written.",
382 "The file is \"" + FilePathToUTF8(build_arg_file) +
383 "\"").PrintToStdout();
384 return false;
387 // Add a dependency on the build arguments file. If this changes, we want
388 // to re-generate the build.
389 g_scheduler->AddGenDependency(build_arg_file);
391 return true;
394 bool Setup::FillSourceDir(const base::CommandLine& cmdline) {
395 // Find the .gn file.
396 base::FilePath root_path;
398 // Prefer the command line args to the config file.
399 base::FilePath relative_root_path =
400 cmdline.GetSwitchValuePath(switches::kRoot);
401 if (!relative_root_path.empty()) {
402 root_path = base::MakeAbsoluteFilePath(relative_root_path);
403 if (root_path.empty()) {
404 Err(Location(), "Root source path not found.",
405 "The path \"" + FilePathToUTF8(relative_root_path) +
406 "\" doesn't exist.").PrintToStdout();
407 return false;
410 // When --root is specified, an alternate --dotfile can also be set.
411 // --dotfile should be a real file path and not a "//foo" source-relative
412 // path.
413 base::FilePath dot_file_path =
414 cmdline.GetSwitchValuePath(switches::kDotfile);
415 if (dot_file_path.empty()) {
416 dotfile_name_ = root_path.Append(kGnFile);
417 } else {
418 dotfile_name_ = base::MakeAbsoluteFilePath(dot_file_path);
419 if (dotfile_name_.empty()) {
420 Err(Location(), "Could not load dotfile.",
421 "The file \"" + FilePathToUTF8(dot_file_path) +
422 "\" cound't be loaded.").PrintToStdout();
423 return false;
426 } else {
427 // In the default case, look for a dotfile and that also tells us where the
428 // source root is.
429 base::FilePath cur_dir;
430 base::GetCurrentDirectory(&cur_dir);
431 dotfile_name_ = FindDotFile(cur_dir);
432 if (dotfile_name_.empty()) {
433 Err(Location(), "Can't find source root.",
434 "I could not find a \".gn\" file in the current directory or any "
435 "parent,\nand the --root command-line argument was not specified.")
436 .PrintToStdout();
437 return false;
439 root_path = dotfile_name_.DirName();
442 if (scheduler_.verbose_logging())
443 scheduler_.Log("Using source root", FilePathToUTF8(root_path));
444 build_settings_.SetRootPath(root_path);
446 return true;
449 bool Setup::FillBuildDir(const std::string& build_dir, bool require_exists) {
450 SourceDir resolved =
451 SourceDirForCurrentDirectory(build_settings_.root_path()).
452 ResolveRelativeDir(build_dir, build_settings_.root_path_utf8());
453 if (resolved.is_null()) {
454 Err(Location(), "Couldn't resolve build directory.",
455 "The build directory supplied (\"" + build_dir + "\") was not valid.").
456 PrintToStdout();
457 return false;
460 if (scheduler_.verbose_logging())
461 scheduler_.Log("Using build dir", resolved.value());
463 if (require_exists) {
464 base::FilePath build_dir_path = build_settings_.GetFullPath(resolved);
465 if (!base::PathExists(build_dir_path.Append(
466 FILE_PATH_LITERAL("build.ninja")))) {
467 Err(Location(), "Not a build directory.",
468 "This command requires an existing build directory. I interpreted "
469 "your input\n\"" + build_dir + "\" as:\n " +
470 FilePathToUTF8(build_dir_path) +
471 "\nwhich doesn't seem to contain a previously-generated build.")
472 .PrintToStdout();
473 return false;
477 build_settings_.SetBuildDir(resolved);
478 return true;
481 void Setup::FillPythonPath() {
482 // Trace this since it tends to be a bit slow on Windows.
483 ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Fill Python Path");
484 #if defined(OS_WIN)
485 // Find Python on the path so we can use the absolute path in the build.
486 const base::char16 kGetPython[] =
487 L"cmd.exe /c python -c \"import sys; print sys.executable\"";
488 std::string python_path;
489 if (base::GetAppOutput(kGetPython, &python_path)) {
490 base::TrimWhitespaceASCII(python_path, base::TRIM_ALL, &python_path);
491 if (scheduler_.verbose_logging())
492 scheduler_.Log("Found python", python_path);
493 } else {
494 scheduler_.Log("WARNING", "Could not find python on path, using "
495 "just \"python.exe\"");
496 python_path = "python.exe";
498 build_settings_.set_python_path(base::FilePath(base::UTF8ToUTF16(python_path))
499 .NormalizePathSeparatorsTo('/'));
500 #else
501 build_settings_.set_python_path(base::FilePath("python"));
502 #endif
505 bool Setup::RunConfigFile() {
506 if (scheduler_.verbose_logging())
507 scheduler_.Log("Got dotfile", FilePathToUTF8(dotfile_name_));
509 dotfile_input_file_.reset(new InputFile(SourceFile("//.gn")));
510 if (!dotfile_input_file_->Load(dotfile_name_)) {
511 Err(Location(), "Could not load dotfile.",
512 "The file \"" + FilePathToUTF8(dotfile_name_) + "\" cound't be loaded")
513 .PrintToStdout();
514 return false;
517 Err err;
518 dotfile_tokens_ = Tokenizer::Tokenize(dotfile_input_file_.get(), &err);
519 if (err.has_error()) {
520 err.PrintToStdout();
521 return false;
524 dotfile_root_ = Parser::Parse(dotfile_tokens_, &err);
525 if (err.has_error()) {
526 err.PrintToStdout();
527 return false;
530 dotfile_root_->Execute(&dotfile_scope_, &err);
531 if (err.has_error()) {
532 err.PrintToStdout();
533 return false;
536 return true;
539 bool Setup::FillOtherConfig(const base::CommandLine& cmdline) {
540 Err err;
541 SourceDir current_dir("//");
543 // Secondary source path, read from the config file if present.
544 // Read from the config file if present.
545 const Value* secondary_value =
546 dotfile_scope_.GetValue("secondary_source", true);
547 if (secondary_value) {
548 if (!secondary_value->VerifyTypeIs(Value::STRING, &err)) {
549 err.PrintToStdout();
550 return false;
552 build_settings_.SetSecondarySourcePath(
553 SourceDir(secondary_value->string_value()));
556 // Root build file.
557 const Value* root_value = dotfile_scope_.GetValue("root", true);
558 if (root_value) {
559 if (!root_value->VerifyTypeIs(Value::STRING, &err)) {
560 err.PrintToStdout();
561 return false;
564 Label root_target_label =
565 Label::Resolve(current_dir, Label(), *root_value, &err);
566 if (err.has_error()) {
567 err.PrintToStdout();
568 return false;
571 root_build_file_ = Loader::BuildFileForLabel(root_target_label);
574 // Build config file.
575 const Value* build_config_value =
576 dotfile_scope_.GetValue("buildconfig", true);
577 if (!build_config_value) {
578 Err(Location(), "No build config file.",
579 "Your .gn file (\"" + FilePathToUTF8(dotfile_name_) + "\")\n"
580 "didn't specify a \"buildconfig\" value.").PrintToStdout();
581 return false;
582 } else if (!build_config_value->VerifyTypeIs(Value::STRING, &err)) {
583 err.PrintToStdout();
584 return false;
586 build_settings_.set_build_config_file(
587 SourceFile(build_config_value->string_value()));
589 // Targets to check.
590 const Value* check_targets_value =
591 dotfile_scope_.GetValue("check_targets", true);
592 if (check_targets_value) {
593 // Fill the list of targets to check.
594 if (!check_targets_value->VerifyTypeIs(Value::LIST, &err)) {
595 err.PrintToStdout();
596 return false;
599 check_patterns_.reset(new std::vector<LabelPattern>);
600 for (const auto& item : check_targets_value->list_value()) {
601 check_patterns_->push_back(
602 LabelPattern::GetPattern(current_dir, item, &err));
603 if (err.has_error()) {
604 err.PrintToStdout();
605 return false;
610 // Fill exec_script_whitelist.
611 const Value* exec_script_whitelist_value =
612 dotfile_scope_.GetValue("exec_script_whitelist", true);
613 if (exec_script_whitelist_value) {
614 // Fill the list of targets to check.
615 if (!exec_script_whitelist_value->VerifyTypeIs(Value::LIST, &err)) {
616 err.PrintToStdout();
617 return false;
619 scoped_ptr<std::set<SourceFile>> whitelist(new std::set<SourceFile>);
620 for (const auto& item : exec_script_whitelist_value->list_value()) {
621 if (!item.VerifyTypeIs(Value::STRING, &err)) {
622 err.PrintToStdout();
623 return false;
625 whitelist->insert(current_dir.ResolveRelativeFile(item.string_value()));
627 build_settings_.set_exec_script_whitelist(whitelist.Pass());
630 return true;