Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / tools / gn / setup.cc
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.
4
5 #include "tools/gn/setup.h"
6
7 #include <stdlib.h>
8
9 #include <algorithm>
10
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"
30
31 #if defined(OS_WIN)
32 #include <windows.h>
33 #endif
34
35 extern const char kDotfile_Help[] =
36     ".gn file\n"
37     "\n"
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"
41     "  argument\n"
42     "\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"
45     "  meaning.\n"
46     "\n"
47     "Variables\n"
48     "\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"
52     "\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"
59     "\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"
62     "\n"
63     "      The secondary source root must be inside the main source tree.\n"
64     "\n"
65     "Example .gn file contents\n"
66     "\n"
67     "  buildconfig = \"//build/config/BUILDCONFIG.gn\"\n"
68     "\n"
69     "  secondary_source = \"//build/config/temporary_buildfiles/\"\n";
70
71 namespace {
72
73 // More logging.
74 const char kSwitchVerbose[] = "v";
75
76 // Set build args.
77 const char kSwitchArgs[] = "args";
78
79 // Set root dir.
80 const char kSwitchRoot[] = "root";
81
82 // Enable timing.
83 const char kTimeSwitch[] = "time";
84
85 const char kTracelogSwitch[] = "tracelog";
86
87 // Set build output directory.
88 const char kSwitchBuildOutput[] = "output";
89
90 const char kSecondarySource[] = "secondary";
91
92 const base::FilePath::CharType kGnFile[] = FILE_PATH_LITERAL(".gn");
93
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))
97     return try_this_file;
98
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.
103
104   return FindDotFile(up_one_dir);
105 }
106
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) {
111   DCHECK(item);
112   main_loop->PostTask(FROM_HERE, base::Bind(&Builder::ItemDefined, builder,
113                                             base::Passed(&item)));
114 }
115
116 void DecrementWorkCount() {
117   g_scheduler->DecrementWorkCount();
118 }
119
120 }  // namespace
121
122 // CommonSetup -----------------------------------------------------------------
123
124 CommonSetup::CommonSetup()
125     : build_settings_(),
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));
131 }
132
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));
140 }
141
142 CommonSetup::~CommonSetup() {
143 }
144
145 void CommonSetup::RunPreMessageLoop() {
146   // Load the root build file.
147   loader_->Load(SourceFile("//BUILD.gn"), Label());
148
149   // Will be decremented with the loader is drained.
150   g_scheduler->IncrementWorkCount();
151 }
152
153 bool CommonSetup::RunPostMessageLoop() {
154   Err err;
155   if (check_for_bad_items_) {
156     if (!builder_->CheckForBadItems(&err)) {
157       err.PrintToStdout();
158       return false;
159     }
160   }
161
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.
166       err.PrintToStdout();
167       return true;
168     }
169   }
170
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));
177
178   return true;
179 }
180
181 // Setup -----------------------------------------------------------------------
182
183 Setup::Setup()
184     : CommonSetup(),
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_));
190
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());
194 }
195
196 Setup::~Setup() {
197 }
198
199 bool Setup::DoSetup() {
200   CommandLine* cmdline = CommandLine::ForCurrentProcess();
201
202   scheduler_.set_verbose_logging(cmdline->HasSwitch(kSwitchVerbose));
203   if (cmdline->HasSwitch(kTimeSwitch) ||
204       cmdline->HasSwitch(kTracelogSwitch))
205     EnableTracing();
206
207   if (!FillArguments(*cmdline))
208     return false;
209   if (!FillSourceDir(*cmdline))
210     return false;
211   if (!RunConfigFile())
212     return false;
213   if (!FillOtherConfig(*cmdline))
214     return false;
215   FillPythonPath();
216
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, "//");
224 #if defined(OS_WIN)
225     // Canonicalize to forward slashes on Windows.
226     std::replace(build_path_8.begin(), build_path_8.end(), '\\', '/');
227 #endif
228     build_settings_.SetBuildDir(SourceDir(build_path_8));
229   } else {
230     // Default output dir.
231     build_settings_.SetBuildDir(SourceDir("//out/Default/"));
232   }
233
234   return true;
235 }
236
237 bool Setup::Run() {
238   RunPreMessageLoop();
239   if (!scheduler_.Run())
240     return false;
241   return RunPostMessageLoop();
242 }
243
244 Scheduler* Setup::GetScheduler() {
245   return &scheduler_;
246 }
247
248 bool Setup::FillArguments(const CommandLine& cmdline) {
249   std::string args = cmdline.GetSwitchValueASCII(kSwitchArgs);
250   if (args.empty())
251     return true;  // Nothing to set.
252
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");
256
257   Err err;
258   args_tokens_ = Tokenizer::Tokenize(args_input_file_.get(), &err);
259   if (err.has_error()) {
260     err.PrintToStdout();
261     return false;
262   }
263
264   args_root_ = Parser::Parse(args_tokens_, &err);
265   if (err.has_error()) {
266     err.PrintToStdout();
267     return false;
268   }
269
270   Scope arg_scope(&empty_settings_);
271   args_root_->AsBlock()->ExecuteBlockInScope(&arg_scope, &err);
272   if (err.has_error()) {
273     err.PrintToStdout();
274     return false;
275   }
276
277   // Save the result of the command args.
278   Scope::KeyValueMap overrides;
279   arg_scope.GetCurrentScopeValues(&overrides);
280   build_settings_.build_args().AddArgOverrides(overrides);
281   return true;
282 }
283
284 bool Setup::FillSourceDir(const CommandLine& cmdline) {
285   // Find the .gn file.
286   base::FilePath root_path;
287
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);
293   } else {
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.")
301           .PrintToStdout();
302       return false;
303     }
304     root_path = dotfile_name_.DirName();
305   }
306
307   if (scheduler_.verbose_logging())
308     scheduler_.Log("Using source root", FilePathToUTF8(root_path));
309   build_settings_.SetRootPath(root_path);
310
311   return true;
312 }
313
314 void Setup::FillPythonPath() {
315 #if defined(OS_WIN)
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);
324   } else {
325     scheduler_.Log("WARNING", "Could not find python on path, using "
326         "just \"python.exe\"");
327     python_path = "python.exe";
328   }
329   build_settings_.set_python_path(
330       base::FilePath(base::UTF8ToUTF16(python_path)));
331 #else
332   build_settings_.set_python_path(base::FilePath("python"));
333 #endif
334 }
335
336 bool Setup::RunConfigFile() {
337   if (scheduler_.verbose_logging())
338     scheduler_.Log("Got dotfile", FilePathToUTF8(dotfile_name_));
339
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")
344         .PrintToStdout();
345     return false;
346   }
347
348   Err err;
349   dotfile_tokens_ = Tokenizer::Tokenize(dotfile_input_file_.get(), &err);
350   if (err.has_error()) {
351     err.PrintToStdout();
352     return false;
353   }
354
355   dotfile_root_ = Parser::Parse(dotfile_tokens_, &err);
356   if (err.has_error()) {
357     err.PrintToStdout();
358     return false;
359   }
360
361   dotfile_root_->AsBlock()->ExecuteBlockInScope(&dotfile_scope_, &err);
362   if (err.has_error()) {
363     err.PrintToStdout();
364     return false;
365   }
366
367   return true;
368 }
369
370 bool Setup::FillOtherConfig(const CommandLine& cmdline) {
371   Err err;
372
373   // Secondary source path.
374   SourceDir secondary_source;
375   if (cmdline.HasSwitch(kSecondarySource)) {
376     // Prefer the command line over the config file.
377     secondary_source =
378         SourceDir(cmdline.GetSwitchValueASCII(kSecondarySource));
379   } else {
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)) {
385         err.PrintToStdout();
386         return false;
387       }
388       build_settings_.SetSecondarySourcePath(
389           SourceDir(secondary_value->string_value()));
390     }
391   }
392
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();
400     return false;
401   } else if (!build_config_value->VerifyTypeIs(Value::STRING, &err)) {
402     err.PrintToStdout();
403     return false;
404   }
405   build_settings_.set_build_config_file(
406       SourceFile(build_config_value->string_value()));
407
408   return true;
409 }
410
411 // DependentSetup --------------------------------------------------------------
412
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_));
418 }
419
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_));
425 }
426
427 DependentSetup::~DependentSetup() {
428 }
429
430 Scheduler* DependentSetup::GetScheduler() {
431   return scheduler_;
432 }
433
434 void DependentSetup::RunPreMessageLoop() {
435   CommonSetup::RunPreMessageLoop();
436 }
437
438 bool DependentSetup::RunPostMessageLoop() {
439   return CommonSetup::RunPostMessageLoop();
440 }
441