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 // Set build output directory.
88 const char kSwitchBuildOutput[] = "output";
90 const char kSecondarySource[] = "secondary";
92 const base::FilePath::CharType kGnFile[] = FILE_PATH_LITERAL(".gn");
94 base::FilePath FindDotFile(const base::FilePath& current_dir) {
95 base::FilePath try_this_file = current_dir.Append(kGnFile);
96 if (base::PathExists(try_this_file))
99 base::FilePath with_no_slash = current_dir.StripTrailingSeparators();
100 base::FilePath up_one_dir = with_no_slash.DirName();
101 if (up_one_dir == current_dir)
102 return base::FilePath(); // Got to the top.
104 return FindDotFile(up_one_dir);
107 // Called on any thread. Post the item to the builder on the main thread.
108 void ItemDefinedCallback(base::MessageLoop* main_loop,
109 scoped_refptr<Builder> builder,
110 scoped_ptr<Item> item) {
112 main_loop->PostTask(FROM_HERE, base::Bind(&Builder::ItemDefined, builder,
113 base::Passed(&item)));
116 void DecrementWorkCount() {
117 g_scheduler->DecrementWorkCount();
122 // CommonSetup -----------------------------------------------------------------
124 CommonSetup::CommonSetup()
126 loader_(new LoaderImpl(&build_settings_)),
127 builder_(new Builder(loader_.get())),
128 check_for_bad_items_(true),
129 check_for_unused_overrides_(true) {
130 loader_->set_complete_callback(base::Bind(&DecrementWorkCount));
133 CommonSetup::CommonSetup(const CommonSetup& other)
134 : build_settings_(other.build_settings_),
135 loader_(new LoaderImpl(&build_settings_)),
136 builder_(new Builder(loader_.get())),
137 check_for_bad_items_(other.check_for_bad_items_),
138 check_for_unused_overrides_(other.check_for_unused_overrides_) {
139 loader_->set_complete_callback(base::Bind(&DecrementWorkCount));
142 CommonSetup::~CommonSetup() {
145 void CommonSetup::RunPreMessageLoop() {
146 // Load the root build file.
147 loader_->Load(SourceFile("//BUILD.gn"), Label());
149 // Will be decremented with the loader is drained.
150 g_scheduler->IncrementWorkCount();
153 bool CommonSetup::RunPostMessageLoop() {
155 if (check_for_bad_items_) {
156 if (!builder_->CheckForBadItems(&err)) {
162 if (check_for_unused_overrides_) {
163 if (!build_settings_.build_args().VerifyAllOverridesUsed(&err)) {
164 // TODO(brettw) implement a system of warnings. Until we have a better
165 // system, print the error but don't return failure.
171 // Write out tracing and timing if requested.
172 const CommandLine* cmdline = CommandLine::ForCurrentProcess();
173 if (cmdline->HasSwitch(kTimeSwitch))
174 PrintLongHelp(SummarizeTraces());
175 if (cmdline->HasSwitch(kTracelogSwitch))
176 SaveTraces(cmdline->GetSwitchValuePath(kTracelogSwitch));
181 // Setup -----------------------------------------------------------------------
185 empty_settings_(&empty_build_settings_, std::string()),
186 dotfile_scope_(&empty_settings_) {
187 empty_settings_.set_toolchain_label(Label());
188 build_settings_.set_item_defined_callback(
189 base::Bind(&ItemDefinedCallback, scheduler_.main_loop(), builder_));
191 // The scheduler's main loop wasn't created when the Loader was created, so
192 // we need to set it now.
193 loader_->set_main_loop(scheduler_.main_loop());
199 bool Setup::DoSetup() {
200 CommandLine* cmdline = CommandLine::ForCurrentProcess();
202 scheduler_.set_verbose_logging(cmdline->HasSwitch(kSwitchVerbose));
203 if (cmdline->HasSwitch(kTimeSwitch) ||
204 cmdline->HasSwitch(kTracelogSwitch))
207 if (!FillArguments(*cmdline))
209 if (!FillSourceDir(*cmdline))
211 if (!RunConfigFile())
213 if (!FillOtherConfig(*cmdline))
217 base::FilePath build_path = cmdline->GetSwitchValuePath(kSwitchBuildOutput);
218 if (!build_path.empty()) {
219 // We accept either repo paths "//out/Debug" or raw source-root-relative
220 // paths "out/Debug".
221 std::string build_path_8 = FilePathToUTF8(build_path);
222 if (build_path_8.compare(0, 2, "//") != 0)
223 build_path_8.insert(0, "//");
225 // Canonicalize to forward slashes on Windows.
226 std::replace(build_path_8.begin(), build_path_8.end(), '\\', '/');
228 build_settings_.SetBuildDir(SourceDir(build_path_8));
230 // Default output dir.
231 build_settings_.SetBuildDir(SourceDir("//out/Default/"));
239 if (!scheduler_.Run())
241 return RunPostMessageLoop();
244 Scheduler* Setup::GetScheduler() {
248 bool Setup::FillArguments(const CommandLine& cmdline) {
249 std::string args = cmdline.GetSwitchValueASCII(kSwitchArgs);
251 return true; // Nothing to set.
253 args_input_file_.reset(new InputFile(SourceFile()));
254 args_input_file_->SetContents(args);
255 args_input_file_->set_friendly_name("the command-line \"--args\" settings");
258 args_tokens_ = Tokenizer::Tokenize(args_input_file_.get(), &err);
259 if (err.has_error()) {
264 args_root_ = Parser::Parse(args_tokens_, &err);
265 if (err.has_error()) {
270 Scope arg_scope(&empty_settings_);
271 args_root_->AsBlock()->ExecuteBlockInScope(&arg_scope, &err);
272 if (err.has_error()) {
277 // Save the result of the command args.
278 Scope::KeyValueMap overrides;
279 arg_scope.GetCurrentScopeValues(&overrides);
280 build_settings_.build_args().AddArgOverrides(overrides);
284 bool Setup::FillSourceDir(const CommandLine& cmdline) {
285 // Find the .gn file.
286 base::FilePath root_path;
288 // Prefer the command line args to the config file.
289 base::FilePath relative_root_path = cmdline.GetSwitchValuePath(kSwitchRoot);
290 if (!relative_root_path.empty()) {
291 root_path = base::MakeAbsoluteFilePath(relative_root_path);
292 dotfile_name_ = root_path.Append(kGnFile);
294 base::FilePath cur_dir;
295 file_util::GetCurrentDirectory(&cur_dir);
296 dotfile_name_ = FindDotFile(cur_dir);
297 if (dotfile_name_.empty()) {
298 Err(Location(), "Can't find source root.",
299 "I could not find a \".gn\" file in the current directory or any "
300 "parent,\nand the --root command-line argument was not specified.")
304 root_path = dotfile_name_.DirName();
307 if (scheduler_.verbose_logging())
308 scheduler_.Log("Using source root", FilePathToUTF8(root_path));
309 build_settings_.SetRootPath(root_path);
314 void Setup::FillPythonPath() {
316 // Find Python on the path so we can use the absolute path in the build.
317 const base::char16 kGetPython[] =
318 L"cmd.exe /c python -c \"import sys; print sys.executable\"";
319 std::string python_path;
320 if (base::GetAppOutput(kGetPython, &python_path)) {
321 TrimWhitespaceASCII(python_path, TRIM_ALL, &python_path);
322 if (scheduler_.verbose_logging())
323 scheduler_.Log("Found python", python_path);
325 scheduler_.Log("WARNING", "Could not find python on path, using "
326 "just \"python.exe\"");
327 python_path = "python.exe";
329 build_settings_.set_python_path(
330 base::FilePath(base::UTF8ToUTF16(python_path)));
332 build_settings_.set_python_path(base::FilePath("python"));
336 bool Setup::RunConfigFile() {
337 if (scheduler_.verbose_logging())
338 scheduler_.Log("Got dotfile", FilePathToUTF8(dotfile_name_));
340 dotfile_input_file_.reset(new InputFile(SourceFile("//.gn")));
341 if (!dotfile_input_file_->Load(dotfile_name_)) {
342 Err(Location(), "Could not load dotfile.",
343 "The file \"" + FilePathToUTF8(dotfile_name_) + "\" cound't be loaded")
349 dotfile_tokens_ = Tokenizer::Tokenize(dotfile_input_file_.get(), &err);
350 if (err.has_error()) {
355 dotfile_root_ = Parser::Parse(dotfile_tokens_, &err);
356 if (err.has_error()) {
361 dotfile_root_->AsBlock()->ExecuteBlockInScope(&dotfile_scope_, &err);
362 if (err.has_error()) {
370 bool Setup::FillOtherConfig(const CommandLine& cmdline) {
373 // Secondary source path.
374 SourceDir secondary_source;
375 if (cmdline.HasSwitch(kSecondarySource)) {
376 // Prefer the command line over the config file.
378 SourceDir(cmdline.GetSwitchValueASCII(kSecondarySource));
380 // Read from the config file if present.
381 const Value* secondary_value =
382 dotfile_scope_.GetValue("secondary_source", true);
383 if (secondary_value) {
384 if (!secondary_value->VerifyTypeIs(Value::STRING, &err)) {
388 build_settings_.SetSecondarySourcePath(
389 SourceDir(secondary_value->string_value()));
393 // Build config file.
394 const Value* build_config_value =
395 dotfile_scope_.GetValue("buildconfig", true);
396 if (!build_config_value) {
397 Err(Location(), "No build config file.",
398 "Your .gn file (\"" + FilePathToUTF8(dotfile_name_) + "\")\n"
399 "didn't specify a \"buildconfig\" value.").PrintToStdout();
401 } else if (!build_config_value->VerifyTypeIs(Value::STRING, &err)) {
405 build_settings_.set_build_config_file(
406 SourceFile(build_config_value->string_value()));
411 // DependentSetup --------------------------------------------------------------
413 DependentSetup::DependentSetup(Setup* derive_from)
414 : CommonSetup(*derive_from),
415 scheduler_(derive_from->GetScheduler()) {
416 build_settings_.set_item_defined_callback(
417 base::Bind(&ItemDefinedCallback, scheduler_->main_loop(), builder_));
420 DependentSetup::DependentSetup(DependentSetup* derive_from)
421 : CommonSetup(*derive_from),
422 scheduler_(derive_from->GetScheduler()) {
423 build_settings_.set_item_defined_callback(
424 base::Bind(&ItemDefinedCallback, scheduler_->main_loop(), builder_));
427 DependentSetup::~DependentSetup() {
430 Scheduler* DependentSetup::GetScheduler() {
434 void DependentSetup::RunPreMessageLoop() {
435 CommonSetup::RunPreMessageLoop();
438 bool DependentSetup::RunPostMessageLoop() {
439 return CommonSetup::RunPostMessageLoop();