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"
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/tokenizer.h"
30 #include "tools/gn/trace.h"
31 #include "tools/gn/value.h"
37 extern const char kDotfile_Help
[] =
40 " When gn starts, it will search the current directory and parent\n"
41 " directories for a file called \".gn\". This indicates the source root.\n"
42 " You can override this detection by using the --root command-line\n"
45 " The .gn file in the source root will be executed. The syntax is the\n"
46 " same as a buildfile, but with very limited build setup-specific\n"
49 " If you specify --root, by default GN will look for the file .gn in\n"
50 " that directory. If you want to specify a different file, you can\n"
51 " additionally pass --dotfile:\n"
53 " gn gen out/Debug --root=/home/build --dotfile=/home/my_gn_file.gn\n"
57 " buildconfig [required]\n"
58 " Label of the build config file. This file will be used to set up\n"
59 " the build file execution environment for each toolchain.\n"
62 " Label of the root build target. The GN build will start by loading\n"
63 " the build file containing this target name. This defaults to\n"
64 " \"//:\" which will cause the file //BUILD.gn to be loaded.\n"
66 " secondary_source [optional]\n"
67 " Label of an alternate directory tree to find input files. When\n"
68 " searching for a BUILD.gn file (or the build config file discussed\n"
69 " above), the file will first be looked for in the source root.\n"
70 " If it's not found, the secondary source root will be checked\n"
71 " (which would contain a parallel directory hierarchy).\n"
73 " This behavior is intended to be used when BUILD.gn files can't be\n"
74 " checked in to certain source directories for whatever reason.\n"
76 " The secondary source root must be inside the main source tree.\n"
78 "Example .gn file contents\n"
80 " buildconfig = \"//build/config/BUILDCONFIG.gn\"\n"
82 " root = \"//:root\"\n"
84 " secondary_source = \"//build/config/temporary_buildfiles/\"\n";
89 const char kSwitchVerbose
[] = "v";
92 const char kSwitchArgs
[] = "args";
95 const char kSwitchRoot
[] = "root";
98 const char kSwitchDotfile
[] = "dotfile";
101 const char kTimeSwitch
[] = "time";
103 const char kTracelogSwitch
[] = "tracelog";
105 const base::FilePath::CharType kGnFile
[] = FILE_PATH_LITERAL(".gn");
107 base::FilePath
FindDotFile(const base::FilePath
& current_dir
) {
108 base::FilePath try_this_file
= current_dir
.Append(kGnFile
);
109 if (base::PathExists(try_this_file
))
110 return try_this_file
;
112 base::FilePath with_no_slash
= current_dir
.StripTrailingSeparators();
113 base::FilePath up_one_dir
= with_no_slash
.DirName();
114 if (up_one_dir
== current_dir
)
115 return base::FilePath(); // Got to the top.
117 return FindDotFile(up_one_dir
);
120 // Called on any thread. Post the item to the builder on the main thread.
121 void ItemDefinedCallback(base::MessageLoop
* main_loop
,
122 scoped_refptr
<Builder
> builder
,
123 scoped_ptr
<Item
> item
) {
125 main_loop
->PostTask(FROM_HERE
, base::Bind(&Builder::ItemDefined
, builder
,
126 base::Passed(&item
)));
129 void DecrementWorkCount() {
130 g_scheduler
->DecrementWorkCount();
135 // CommonSetup -----------------------------------------------------------------
137 const char CommonSetup::kBuildArgFileName
[] = "args.gn";
139 CommonSetup::CommonSetup()
141 loader_(new LoaderImpl(&build_settings_
)),
142 builder_(new Builder(loader_
.get())),
143 root_build_file_("//BUILD.gn"),
144 check_for_bad_items_(true),
145 check_for_unused_overrides_(true),
146 check_public_headers_(false) {
147 loader_
->set_complete_callback(base::Bind(&DecrementWorkCount
));
150 CommonSetup::CommonSetup(const CommonSetup
& other
)
151 : build_settings_(other
.build_settings_
),
152 loader_(new LoaderImpl(&build_settings_
)),
153 builder_(new Builder(loader_
.get())),
154 root_build_file_(other
.root_build_file_
),
155 check_for_bad_items_(other
.check_for_bad_items_
),
156 check_for_unused_overrides_(other
.check_for_unused_overrides_
),
157 check_public_headers_(other
.check_public_headers_
) {
158 loader_
->set_complete_callback(base::Bind(&DecrementWorkCount
));
161 CommonSetup::~CommonSetup() {
164 void CommonSetup::RunPreMessageLoop() {
165 // Load the root build file.
166 loader_
->Load(root_build_file_
, LocationRange(), Label());
168 // Will be decremented with the loader is drained.
169 g_scheduler
->IncrementWorkCount();
172 bool CommonSetup::RunPostMessageLoop() {
174 if (check_for_bad_items_
) {
175 if (!builder_
->CheckForBadItems(&err
)) {
181 if (check_for_unused_overrides_
) {
182 if (!build_settings_
.build_args().VerifyAllOverridesUsed(&err
)) {
183 // TODO(brettw) implement a system of warnings. Until we have a better
184 // system, print the error but don't return failure.
190 if (check_public_headers_
) {
191 if (!commands::CheckPublicHeaders(&build_settings_
,
192 builder_
->GetAllResolvedTargets(),
193 std::vector
<const Target
*>(),
199 // Write out tracing and timing if requested.
200 const CommandLine
* cmdline
= CommandLine::ForCurrentProcess();
201 if (cmdline
->HasSwitch(kTimeSwitch
))
202 PrintLongHelp(SummarizeTraces());
203 if (cmdline
->HasSwitch(kTracelogSwitch
))
204 SaveTraces(cmdline
->GetSwitchValuePath(kTracelogSwitch
));
209 // Setup -----------------------------------------------------------------------
213 empty_settings_(&empty_build_settings_
, std::string()),
214 dotfile_scope_(&empty_settings_
),
215 fill_arguments_(true) {
216 empty_settings_
.set_toolchain_label(Label());
217 build_settings_
.set_item_defined_callback(
218 base::Bind(&ItemDefinedCallback
, scheduler_
.main_loop(), builder_
));
220 // The scheduler's main loop wasn't created when the Loader was created, so
221 // we need to set it now.
222 loader_
->set_main_loop(scheduler_
.main_loop());
228 bool Setup::DoSetup(const std::string
& build_dir
, bool force_create
) {
229 CommandLine
* cmdline
= CommandLine::ForCurrentProcess();
231 scheduler_
.set_verbose_logging(cmdline
->HasSwitch(kSwitchVerbose
));
232 if (cmdline
->HasSwitch(kTimeSwitch
) ||
233 cmdline
->HasSwitch(kTracelogSwitch
))
236 ScopedTrace
setup_trace(TraceItem::TRACE_SETUP
, "DoSetup");
238 if (!FillSourceDir(*cmdline
))
240 if (!RunConfigFile())
242 if (!FillOtherConfig(*cmdline
))
245 // Must be after FillSourceDir to resolve.
246 if (!FillBuildDir(build_dir
, !force_create
))
249 if (fill_arguments_
) {
250 if (!FillArguments(*cmdline
))
260 if (!scheduler_
.Run())
262 return RunPostMessageLoop();
265 Scheduler
* Setup::GetScheduler() {
269 SourceFile
Setup::GetBuildArgFile() const {
270 return SourceFile(build_settings_
.build_dir().value() + kBuildArgFileName
);
273 bool Setup::FillArguments(const CommandLine
& cmdline
) {
274 // Use the args on the command line if specified, and save them. Do this even
275 // if the list is empty (this means clear any defaults).
276 if (cmdline
.HasSwitch(kSwitchArgs
)) {
277 if (!FillArgsFromCommandLine(cmdline
.GetSwitchValueASCII(kSwitchArgs
)))
283 // No command line args given, use the arguments from the build dir (if any).
284 return FillArgsFromFile();
287 bool Setup::FillArgsFromCommandLine(const std::string
& args
) {
288 args_input_file_
.reset(new InputFile(SourceFile()));
289 args_input_file_
->SetContents(args
);
290 args_input_file_
->set_friendly_name("the command-line \"--args\"");
291 return FillArgsFromArgsInputFile();
294 bool Setup::FillArgsFromFile() {
295 ScopedTrace
setup_trace(TraceItem::TRACE_SETUP
, "Load args file");
297 SourceFile build_arg_source_file
= GetBuildArgFile();
298 base::FilePath build_arg_file
=
299 build_settings_
.GetFullPath(build_arg_source_file
);
301 std::string contents
;
302 if (!base::ReadFileToString(build_arg_file
, &contents
))
303 return true; // File doesn't exist, continue with default args.
305 // Add a dependency on the build arguments file. If this changes, we want
306 // to re-generate the build.
307 g_scheduler
->AddGenDependency(build_arg_file
);
309 if (contents
.empty())
310 return true; // Empty file, do nothing.
312 args_input_file_
.reset(new InputFile(build_arg_source_file
));
313 args_input_file_
->SetContents(contents
);
314 args_input_file_
->set_friendly_name(
315 "build arg file (use \"gn args <out_dir>\" to edit)");
317 setup_trace
.Done(); // Only want to count the load as part of the trace.
318 return FillArgsFromArgsInputFile();
321 bool Setup::FillArgsFromArgsInputFile() {
322 ScopedTrace
setup_trace(TraceItem::TRACE_SETUP
, "Parse args");
325 args_tokens_
= Tokenizer::Tokenize(args_input_file_
.get(), &err
);
326 if (err
.has_error()) {
331 args_root_
= Parser::Parse(args_tokens_
, &err
);
332 if (err
.has_error()) {
337 Scope
arg_scope(&empty_settings_
);
338 args_root_
->AsBlock()->ExecuteBlockInScope(&arg_scope
, &err
);
339 if (err
.has_error()) {
344 // Save the result of the command args.
345 Scope::KeyValueMap overrides
;
346 arg_scope
.GetCurrentScopeValues(&overrides
);
347 build_settings_
.build_args().AddArgOverrides(overrides
);
351 bool Setup::SaveArgsToFile() {
352 ScopedTrace
setup_trace(TraceItem::TRACE_SETUP
, "Save args file");
354 Scope::KeyValueMap args
= build_settings_
.build_args().GetAllOverrides();
356 std::ostringstream stream
;
357 for (Scope::KeyValueMap::const_iterator i
= args
.begin();
358 i
!= args
.end(); ++i
) {
359 stream
<< i
->first
.as_string() << " = " << i
->second
.ToString(true);
363 // For the first run, the build output dir might not be created yet, so do
364 // that so we can write a file into it. Ignore errors, we'll catch the error
365 // when we try to write a file to it below.
366 base::FilePath build_arg_file
=
367 build_settings_
.GetFullPath(GetBuildArgFile());
368 base::CreateDirectory(build_arg_file
.DirName());
370 std::string contents
= stream
.str();
372 // Use Windows lineendings for this file since it will often open in
373 // Notepad which can't handle Unix ones.
374 ReplaceSubstringsAfterOffset(&contents
, 0, "\n", "\r\n");
376 if (base::WriteFile(build_arg_file
, contents
.c_str(),
377 static_cast<int>(contents
.size())) == -1) {
378 Err(Location(), "Args file could not be written.",
379 "The file is \"" + FilePathToUTF8(build_arg_file
) +
380 "\"").PrintToStdout();
384 // Add a dependency on the build arguments file. If this changes, we want
385 // to re-generate the build.
386 g_scheduler
->AddGenDependency(build_arg_file
);
391 bool Setup::FillSourceDir(const CommandLine
& cmdline
) {
392 // Find the .gn file.
393 base::FilePath root_path
;
395 // Prefer the command line args to the config file.
396 base::FilePath relative_root_path
= cmdline
.GetSwitchValuePath(kSwitchRoot
);
397 if (!relative_root_path
.empty()) {
398 root_path
= base::MakeAbsoluteFilePath(relative_root_path
);
399 if (root_path
.empty()) {
400 Err(Location(), "Root source path not found.",
401 "The path \"" + FilePathToUTF8(relative_root_path
) +
402 "\" doesn't exist.").PrintToStdout();
406 // When --root is specified, an alternate --dotfile can also be set.
407 // --dotfile should be a real file path and not a "//foo" source-relative
409 base::FilePath dot_file_path
= cmdline
.GetSwitchValuePath(kSwitchDotfile
);
410 if (dot_file_path
.empty()) {
411 dotfile_name_
= root_path
.Append(kGnFile
);
413 dotfile_name_
= base::MakeAbsoluteFilePath(dot_file_path
);
414 if (dotfile_name_
.empty()) {
415 Err(Location(), "Could not load dotfile.",
416 "The file \"" + FilePathToUTF8(dot_file_path
) +
417 "\" cound't be loaded.").PrintToStdout();
422 // In the default case, look for a dotfile and that also tells us where the
424 base::FilePath cur_dir
;
425 base::GetCurrentDirectory(&cur_dir
);
426 dotfile_name_
= FindDotFile(cur_dir
);
427 if (dotfile_name_
.empty()) {
428 Err(Location(), "Can't find source root.",
429 "I could not find a \".gn\" file in the current directory or any "
430 "parent,\nand the --root command-line argument was not specified.")
434 root_path
= dotfile_name_
.DirName();
437 if (scheduler_
.verbose_logging())
438 scheduler_
.Log("Using source root", FilePathToUTF8(root_path
));
439 build_settings_
.SetRootPath(root_path
);
444 bool Setup::FillBuildDir(const std::string
& build_dir
, bool require_exists
) {
446 SourceDirForCurrentDirectory(build_settings_
.root_path()).
447 ResolveRelativeDir(build_dir
);
448 if (resolved
.is_null()) {
449 Err(Location(), "Couldn't resolve build directory.",
450 "The build directory supplied (\"" + build_dir
+ "\") was not valid.").
455 if (scheduler_
.verbose_logging())
456 scheduler_
.Log("Using build dir", resolved
.value());
458 if (require_exists
) {
459 base::FilePath build_dir_path
= build_settings_
.GetFullPath(resolved
);
460 if (!base::PathExists(build_dir_path
.Append(
461 FILE_PATH_LITERAL("build.ninja")))) {
462 Err(Location(), "Not a build directory.",
463 "This command requires an existing build directory. I interpreted "
464 "your input\n\"" + build_dir
+ "\" as:\n " +
465 FilePathToUTF8(build_dir_path
) +
466 "\nwhich doesn't seem to contain a previously-generated build.")
472 build_settings_
.SetBuildDir(resolved
);
476 void Setup::FillPythonPath() {
477 // Trace this since it tends to be a bit slow on Windows.
478 ScopedTrace
setup_trace(TraceItem::TRACE_SETUP
, "Fill Python Path");
480 // Find Python on the path so we can use the absolute path in the build.
481 const base::char16 kGetPython
[] =
482 L
"cmd.exe /c python -c \"import sys; print sys.executable\"";
483 std::string python_path
;
484 if (base::GetAppOutput(kGetPython
, &python_path
)) {
485 base::TrimWhitespaceASCII(python_path
, base::TRIM_ALL
, &python_path
);
486 if (scheduler_
.verbose_logging())
487 scheduler_
.Log("Found python", python_path
);
489 scheduler_
.Log("WARNING", "Could not find python on path, using "
490 "just \"python.exe\"");
491 python_path
= "python.exe";
493 build_settings_
.set_python_path(base::FilePath(base::UTF8ToUTF16(python_path
))
494 .NormalizePathSeparatorsTo('/'));
496 build_settings_
.set_python_path(base::FilePath("python"));
500 bool Setup::RunConfigFile() {
501 if (scheduler_
.verbose_logging())
502 scheduler_
.Log("Got dotfile", FilePathToUTF8(dotfile_name_
));
504 dotfile_input_file_
.reset(new InputFile(SourceFile("//.gn")));
505 if (!dotfile_input_file_
->Load(dotfile_name_
)) {
506 Err(Location(), "Could not load dotfile.",
507 "The file \"" + FilePathToUTF8(dotfile_name_
) + "\" cound't be loaded")
513 dotfile_tokens_
= Tokenizer::Tokenize(dotfile_input_file_
.get(), &err
);
514 if (err
.has_error()) {
519 dotfile_root_
= Parser::Parse(dotfile_tokens_
, &err
);
520 if (err
.has_error()) {
525 dotfile_root_
->AsBlock()->ExecuteBlockInScope(&dotfile_scope_
, &err
);
526 if (err
.has_error()) {
534 bool Setup::FillOtherConfig(const CommandLine
& cmdline
) {
537 // Secondary source path, read from the config file if present.
538 // Read from the config file if present.
539 const Value
* secondary_value
=
540 dotfile_scope_
.GetValue("secondary_source", true);
541 if (secondary_value
) {
542 if (!secondary_value
->VerifyTypeIs(Value::STRING
, &err
)) {
546 build_settings_
.SetSecondarySourcePath(
547 SourceDir(secondary_value
->string_value()));
551 const Value
* root_value
= dotfile_scope_
.GetValue("root", true);
553 if (!root_value
->VerifyTypeIs(Value::STRING
, &err
)) {
558 Label root_target_label
=
559 Label::Resolve(SourceDir("//"), Label(), *root_value
, &err
);
560 if (err
.has_error()) {
565 root_build_file_
= Loader::BuildFileForLabel(root_target_label
);
568 // Build config file.
569 const Value
* build_config_value
=
570 dotfile_scope_
.GetValue("buildconfig", true);
571 if (!build_config_value
) {
572 Err(Location(), "No build config file.",
573 "Your .gn file (\"" + FilePathToUTF8(dotfile_name_
) + "\")\n"
574 "didn't specify a \"buildconfig\" value.").PrintToStdout();
576 } else if (!build_config_value
->VerifyTypeIs(Value::STRING
, &err
)) {
580 build_settings_
.set_build_config_file(
581 SourceFile(build_config_value
->string_value()));
586 // DependentSetup --------------------------------------------------------------
588 DependentSetup::DependentSetup(Setup
* derive_from
)
589 : CommonSetup(*derive_from
),
590 scheduler_(derive_from
->GetScheduler()) {
591 build_settings_
.set_item_defined_callback(
592 base::Bind(&ItemDefinedCallback
, scheduler_
->main_loop(), builder_
));
595 DependentSetup::DependentSetup(DependentSetup
* derive_from
)
596 : CommonSetup(*derive_from
),
597 scheduler_(derive_from
->GetScheduler()) {
598 build_settings_
.set_item_defined_callback(
599 base::Bind(&ItemDefinedCallback
, scheduler_
->main_loop(), builder_
));
602 DependentSetup::~DependentSetup() {
605 Scheduler
* DependentSetup::GetScheduler() {
609 void DependentSetup::RunPreMessageLoop() {
610 CommonSetup::RunPreMessageLoop();
613 bool DependentSetup::RunPostMessageLoop() {
614 return CommonSetup::RunPostMessageLoop();