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 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.")
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
) {
298 SourceDirForCurrentDirectory(build_settings_
.root_path()).
299 ResolveRelativeDir(build_dir
);
300 if (resolved
.is_null()) {
301 Err(Location(), "Couldn't resolve build directory.",
302 "The build directory supplied (\"" + build_dir
+ "\") was not valid.").
307 if (scheduler_
.verbose_logging())
308 scheduler_
.Log("Using build dir", resolved
.value());
309 build_settings_
.SetBuildDir(resolved
);
313 void Setup::FillPythonPath() {
315 // Find Python on the path so we can use the absolute path in the build.
316 const base::char16 kGetPython
[] =
317 L
"cmd.exe /c python -c \"import sys; print sys.executable\"";
318 std::string python_path
;
319 if (base::GetAppOutput(kGetPython
, &python_path
)) {
320 base::TrimWhitespaceASCII(python_path
, base::TRIM_ALL
, &python_path
);
321 if (scheduler_
.verbose_logging())
322 scheduler_
.Log("Found python", python_path
);
324 scheduler_
.Log("WARNING", "Could not find python on path, using "
325 "just \"python.exe\"");
326 python_path
= "python.exe";
328 build_settings_
.set_python_path(base::FilePath(base::UTF8ToUTF16(python_path
))
329 .NormalizePathSeparatorsTo('/'));
331 build_settings_
.set_python_path(base::FilePath("python"));
335 bool Setup::RunConfigFile() {
336 if (scheduler_
.verbose_logging())
337 scheduler_
.Log("Got dotfile", FilePathToUTF8(dotfile_name_
));
339 dotfile_input_file_
.reset(new InputFile(SourceFile("//.gn")));
340 if (!dotfile_input_file_
->Load(dotfile_name_
)) {
341 Err(Location(), "Could not load dotfile.",
342 "The file \"" + FilePathToUTF8(dotfile_name_
) + "\" cound't be loaded")
348 dotfile_tokens_
= Tokenizer::Tokenize(dotfile_input_file_
.get(), &err
);
349 if (err
.has_error()) {
354 dotfile_root_
= Parser::Parse(dotfile_tokens_
, &err
);
355 if (err
.has_error()) {
360 dotfile_root_
->AsBlock()->ExecuteBlockInScope(&dotfile_scope_
, &err
);
361 if (err
.has_error()) {
369 bool Setup::FillOtherConfig(const CommandLine
& cmdline
) {
372 // Secondary source path.
373 SourceDir secondary_source
;
374 if (cmdline
.HasSwitch(kSecondarySource
)) {
375 // Prefer the command line over the config file.
377 SourceDir(cmdline
.GetSwitchValueASCII(kSecondarySource
));
379 // Read from the config file if present.
380 const Value
* secondary_value
=
381 dotfile_scope_
.GetValue("secondary_source", true);
382 if (secondary_value
) {
383 if (!secondary_value
->VerifyTypeIs(Value::STRING
, &err
)) {
387 build_settings_
.SetSecondarySourcePath(
388 SourceDir(secondary_value
->string_value()));
392 // Build config file.
393 const Value
* build_config_value
=
394 dotfile_scope_
.GetValue("buildconfig", true);
395 if (!build_config_value
) {
396 Err(Location(), "No build config file.",
397 "Your .gn file (\"" + FilePathToUTF8(dotfile_name_
) + "\")\n"
398 "didn't specify a \"buildconfig\" value.").PrintToStdout();
400 } else if (!build_config_value
->VerifyTypeIs(Value::STRING
, &err
)) {
404 build_settings_
.set_build_config_file(
405 SourceFile(build_config_value
->string_value()));
410 // DependentSetup --------------------------------------------------------------
412 DependentSetup::DependentSetup(Setup
* derive_from
)
413 : CommonSetup(*derive_from
),
414 scheduler_(derive_from
->GetScheduler()) {
415 build_settings_
.set_item_defined_callback(
416 base::Bind(&ItemDefinedCallback
, scheduler_
->main_loop(), builder_
));
419 DependentSetup::DependentSetup(DependentSetup
* derive_from
)
420 : CommonSetup(*derive_from
),
421 scheduler_(derive_from
->GetScheduler()) {
422 build_settings_
.set_item_defined_callback(
423 base::Bind(&ItemDefinedCallback
, scheduler_
->main_loop(), builder_
));
426 DependentSetup::~DependentSetup() {
429 Scheduler
* DependentSetup::GetScheduler() {
433 void DependentSetup::RunPreMessageLoop() {
434 CommonSetup::RunPreMessageLoop();
437 bool DependentSetup::RunPostMessageLoop() {
438 return CommonSetup::RunPostMessageLoop();