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"
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"
35 extern const char kDotfile_Help
[] =
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"
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"
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"
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"
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"
63 " The secondary source root must be inside the main source tree.\n"
65 "Example .gn file contents\n"
67 " buildconfig = \"//build/config/BUILDCONFIG.gn\"\n"
69 " secondary_source = \"//build/config/temporary_buildfiles/\"\n";
74 const char kSwitchVerbose
[] = "v";
77 const char kSwitchArgs
[] = "args";
80 const char kSwitchRoot
[] = "root";
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
))
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
) {
109 main_loop
->PostTask(FROM_HERE
, base::Bind(&Builder::ItemDefined
, builder
,
110 base::Passed(&item
)));
113 void DecrementWorkCount() {
114 g_scheduler
->DecrementWorkCount();
119 // CommonSetup -----------------------------------------------------------------
121 CommonSetup::CommonSetup()
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() {
152 if (check_for_bad_items_
) {
153 if (!builder_
->CheckForBadItems(&err
)) {
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.
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
));
178 // Setup -----------------------------------------------------------------------
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());
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
))
204 if (!FillArguments(*cmdline
))
206 if (!FillSourceDir(*cmdline
))
208 if (!RunConfigFile())
210 if (!FillOtherConfig(*cmdline
))
212 if (!FillBuildDir(build_dir
)) // Must be after FillSourceDir to resolve.
221 if (!scheduler_
.Run())
223 return RunPostMessageLoop();
226 Scheduler
* Setup::GetScheduler() {
230 bool Setup::FillArguments(const CommandLine
& cmdline
) {
231 std::string args
= cmdline
.GetSwitchValueASCII(kSwitchArgs
);
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");
240 args_tokens_
= Tokenizer::Tokenize(args_input_file_
.get(), &err
);
241 if (err
.has_error()) {
246 args_root_
= Parser::Parse(args_tokens_
, &err
);
247 if (err
.has_error()) {
252 Scope
arg_scope(&empty_settings_
);
253 args_root_
->AsBlock()->ExecuteBlockInScope(&arg_scope
, &err
);
254 if (err
.has_error()) {
259 // Save the result of the command args.
260 Scope::KeyValueMap overrides
;
261 arg_scope
.GetCurrentScopeValues(&overrides
);
262 build_settings_
.build_args().AddArgOverrides(overrides
);
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
);
276 base::FilePath cur_dir
;
277 file_util::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.")
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
);
296 bool Setup::FillBuildDir(const std::string
& build_dir
) {
297 std::string normalized_build_dir
= PathToSystem(build_dir
);
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.").
309 if (scheduler_
.verbose_logging())
310 scheduler_
.Log("Using build dir", resolved
.value());
311 build_settings_
.SetBuildDir(resolved
);
315 void Setup::FillPythonPath() {
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 TrimWhitespaceASCII(python_path
, TRIM_ALL
, &python_path
);
323 if (scheduler_
.verbose_logging())
324 scheduler_
.Log("Found python", python_path
);
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
)));
333 build_settings_
.set_python_path(base::FilePath("python"));
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")
350 dotfile_tokens_
= Tokenizer::Tokenize(dotfile_input_file_
.get(), &err
);
351 if (err
.has_error()) {
356 dotfile_root_
= Parser::Parse(dotfile_tokens_
, &err
);
357 if (err
.has_error()) {
362 dotfile_root_
->AsBlock()->ExecuteBlockInScope(&dotfile_scope_
, &err
);
363 if (err
.has_error()) {
371 bool Setup::FillOtherConfig(const CommandLine
& cmdline
) {
374 // Secondary source path.
375 SourceDir secondary_source
;
376 if (cmdline
.HasSwitch(kSecondarySource
)) {
377 // Prefer the command line over the config file.
379 SourceDir(cmdline
.GetSwitchValueASCII(kSecondarySource
));
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
)) {
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();
402 } else if (!build_config_value
->VerifyTypeIs(Value::STRING
, &err
)) {
406 build_settings_
.set_build_config_file(
407 SourceFile(build_config_value
->string_value()));
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() {
435 void DependentSetup::RunPreMessageLoop() {
436 CommonSetup::RunPreMessageLoop();
439 bool DependentSetup::RunPostMessageLoop() {
440 return CommonSetup::RunPostMessageLoop();