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#include <sstream>
11
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"
32
33#if defined(OS_WIN)
34#include <windows.h>
35#endif
36
37extern const char kDotfile_Help[] =
38    ".gn file\n"
39    "\n"
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"
43    "  argument\n"
44    "\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"
47    "  meaning.\n"
48    "\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"
52    "\n"
53    "    gn gen out/Debug --root=/home/build --dotfile=/home/my_gn_file.gn\n"
54    "\n"
55    "Variables\n"
56    "\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"
60    "\n"
61    "  root [optional]\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"
65    "\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"
72    "\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"
75    "\n"
76    "      The secondary source root must be inside the main source tree.\n"
77    "\n"
78    "Example .gn file contents\n"
79    "\n"
80    "  buildconfig = \"//build/config/BUILDCONFIG.gn\"\n"
81    "\n"
82    "  root = \"//:root\"\n"
83    "\n"
84    "  secondary_source = \"//build/config/temporary_buildfiles/\"\n";
85
86namespace {
87
88// More logging.
89const char kSwitchVerbose[] = "v";
90
91// Set build args.
92const char kSwitchArgs[] = "args";
93
94// Set root dir.
95const char kSwitchRoot[] = "root";
96
97// Set dotfile name.
98const char kSwitchDotfile[] = "dotfile";
99
100// Enable timing.
101const char kTimeSwitch[] = "time";
102
103const char kTracelogSwitch[] = "tracelog";
104
105const base::FilePath::CharType kGnFile[] = FILE_PATH_LITERAL(".gn");
106
107base::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;
111
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.
116
117  return FindDotFile(up_one_dir);
118}
119
120// Called on any thread. Post the item to the builder on the main thread.
121void ItemDefinedCallback(base::MessageLoop* main_loop,
122                         scoped_refptr<Builder> builder,
123                         scoped_ptr<Item> item) {
124  DCHECK(item);
125  main_loop->PostTask(FROM_HERE, base::Bind(&Builder::ItemDefined, builder,
126                                            base::Passed(&item)));
127}
128
129void DecrementWorkCount() {
130  g_scheduler->DecrementWorkCount();
131}
132
133}  // namespace
134
135// CommonSetup -----------------------------------------------------------------
136
137const char CommonSetup::kBuildArgFileName[] = "args.gn";
138
139CommonSetup::CommonSetup()
140    : build_settings_(),
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));
148}
149
150CommonSetup::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));
159}
160
161CommonSetup::~CommonSetup() {
162}
163
164void CommonSetup::RunPreMessageLoop() {
165  // Load the root build file.
166  loader_->Load(root_build_file_, LocationRange(), Label());
167
168  // Will be decremented with the loader is drained.
169  g_scheduler->IncrementWorkCount();
170}
171
172bool CommonSetup::RunPostMessageLoop() {
173  Err err;
174  if (check_for_bad_items_) {
175    if (!builder_->CheckForBadItems(&err)) {
176      err.PrintToStdout();
177      return false;
178    }
179  }
180
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.
185      err.PrintToStdout();
186      return true;
187    }
188  }
189
190  if (check_public_headers_) {
191    if (!commands::CheckPublicHeaders(&build_settings_,
192                                      builder_->GetAllResolvedTargets(),
193                                      std::vector<const Target*>(),
194                                      false)) {
195      return false;
196    }
197  }
198
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));
205
206  return true;
207}
208
209// Setup -----------------------------------------------------------------------
210
211Setup::Setup()
212    : CommonSetup(),
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_));
219
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());
223}
224
225Setup::~Setup() {
226}
227
228bool Setup::DoSetup(const std::string& build_dir, bool force_create) {
229  CommandLine* cmdline = CommandLine::ForCurrentProcess();
230
231  scheduler_.set_verbose_logging(cmdline->HasSwitch(kSwitchVerbose));
232  if (cmdline->HasSwitch(kTimeSwitch) ||
233      cmdline->HasSwitch(kTracelogSwitch))
234    EnableTracing();
235
236  ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "DoSetup");
237
238  if (!FillSourceDir(*cmdline))
239    return false;
240  if (!RunConfigFile())
241    return false;
242  if (!FillOtherConfig(*cmdline))
243    return false;
244
245  // Must be after FillSourceDir to resolve.
246  if (!FillBuildDir(build_dir, !force_create))
247    return false;
248
249  if (fill_arguments_) {
250    if (!FillArguments(*cmdline))
251      return false;
252  }
253  FillPythonPath();
254
255  return true;
256}
257
258bool Setup::Run() {
259  RunPreMessageLoop();
260  if (!scheduler_.Run())
261    return false;
262  return RunPostMessageLoop();
263}
264
265Scheduler* Setup::GetScheduler() {
266  return &scheduler_;
267}
268
269SourceFile Setup::GetBuildArgFile() const {
270  return SourceFile(build_settings_.build_dir().value() + kBuildArgFileName);
271}
272
273bool 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)))
278      return false;
279    SaveArgsToFile();
280    return true;
281  }
282
283  // No command line args given, use the arguments from the build dir (if any).
284  return FillArgsFromFile();
285}
286
287bool 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();
292}
293
294bool Setup::FillArgsFromFile() {
295  ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Load args file");
296
297  SourceFile build_arg_source_file = GetBuildArgFile();
298  base::FilePath build_arg_file =
299      build_settings_.GetFullPath(build_arg_source_file);
300
301  std::string contents;
302  if (!base::ReadFileToString(build_arg_file, &contents))
303    return true;  // File doesn't exist, continue with default args.
304
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);
308
309  if (contents.empty())
310    return true;  // Empty file, do nothing.
311
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)");
316
317  setup_trace.Done();  // Only want to count the load as part of the trace.
318  return FillArgsFromArgsInputFile();
319}
320
321bool Setup::FillArgsFromArgsInputFile() {
322  ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Parse args");
323
324  Err err;
325  args_tokens_ = Tokenizer::Tokenize(args_input_file_.get(), &err);
326  if (err.has_error()) {
327    err.PrintToStdout();
328    return false;
329  }
330
331  args_root_ = Parser::Parse(args_tokens_, &err);
332  if (err.has_error()) {
333    err.PrintToStdout();
334    return false;
335  }
336
337  Scope arg_scope(&empty_settings_);
338  args_root_->AsBlock()->ExecuteBlockInScope(&arg_scope, &err);
339  if (err.has_error()) {
340    err.PrintToStdout();
341    return false;
342  }
343
344  // Save the result of the command args.
345  Scope::KeyValueMap overrides;
346  arg_scope.GetCurrentScopeValues(&overrides);
347  build_settings_.build_args().AddArgOverrides(overrides);
348  return true;
349}
350
351bool Setup::SaveArgsToFile() {
352  ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Save args file");
353
354  Scope::KeyValueMap args = build_settings_.build_args().GetAllOverrides();
355
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);
360    stream << std::endl;
361  }
362
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());
369
370  std::string contents = stream.str();
371#if defined(OS_WIN)
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");
375#endif
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();
381    return false;
382  }
383
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);
387
388  return true;
389}
390
391bool Setup::FillSourceDir(const CommandLine& cmdline) {
392  // Find the .gn file.
393  base::FilePath root_path;
394
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();
403      return false;
404    }
405
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
408    // path.
409    base::FilePath dot_file_path = cmdline.GetSwitchValuePath(kSwitchDotfile);
410    if (dot_file_path.empty()) {
411      dotfile_name_ = root_path.Append(kGnFile);
412    } else {
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();
418        return false;
419      }
420    }
421  } else {
422    // In the default case, look for a dotfile and that also tells us where the
423    // source root is.
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.")
431          .PrintToStdout();
432      return false;
433    }
434    root_path = dotfile_name_.DirName();
435  }
436
437  if (scheduler_.verbose_logging())
438    scheduler_.Log("Using source root", FilePathToUTF8(root_path));
439  build_settings_.SetRootPath(root_path);
440
441  return true;
442}
443
444bool Setup::FillBuildDir(const std::string& build_dir, bool require_exists) {
445  SourceDir resolved =
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.").
451        PrintToStdout();
452    return false;
453  }
454
455  if (scheduler_.verbose_logging())
456    scheduler_.Log("Using build dir", resolved.value());
457
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.")
467          .PrintToStdout();
468      return false;
469    }
470  }
471
472  build_settings_.SetBuildDir(resolved);
473  return true;
474}
475
476void 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");
479#if defined(OS_WIN)
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);
488  } else {
489    scheduler_.Log("WARNING", "Could not find python on path, using "
490        "just \"python.exe\"");
491    python_path = "python.exe";
492  }
493  build_settings_.set_python_path(base::FilePath(base::UTF8ToUTF16(python_path))
494                                      .NormalizePathSeparatorsTo('/'));
495#else
496  build_settings_.set_python_path(base::FilePath("python"));
497#endif
498}
499
500bool Setup::RunConfigFile() {
501  if (scheduler_.verbose_logging())
502    scheduler_.Log("Got dotfile", FilePathToUTF8(dotfile_name_));
503
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")
508        .PrintToStdout();
509    return false;
510  }
511
512  Err err;
513  dotfile_tokens_ = Tokenizer::Tokenize(dotfile_input_file_.get(), &err);
514  if (err.has_error()) {
515    err.PrintToStdout();
516    return false;
517  }
518
519  dotfile_root_ = Parser::Parse(dotfile_tokens_, &err);
520  if (err.has_error()) {
521    err.PrintToStdout();
522    return false;
523  }
524
525  dotfile_root_->AsBlock()->ExecuteBlockInScope(&dotfile_scope_, &err);
526  if (err.has_error()) {
527    err.PrintToStdout();
528    return false;
529  }
530
531  return true;
532}
533
534bool Setup::FillOtherConfig(const CommandLine& cmdline) {
535  Err err;
536
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)) {
543      err.PrintToStdout();
544      return false;
545    }
546    build_settings_.SetSecondarySourcePath(
547        SourceDir(secondary_value->string_value()));
548  }
549
550  // Root build file.
551  const Value* root_value = dotfile_scope_.GetValue("root", true);
552  if (root_value) {
553    if (!root_value->VerifyTypeIs(Value::STRING, &err)) {
554      err.PrintToStdout();
555      return false;
556    }
557
558    Label root_target_label =
559        Label::Resolve(SourceDir("//"), Label(), *root_value, &err);
560    if (err.has_error()) {
561      err.PrintToStdout();
562      return false;
563    }
564
565    root_build_file_ = Loader::BuildFileForLabel(root_target_label);
566  }
567
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();
575    return false;
576  } else if (!build_config_value->VerifyTypeIs(Value::STRING, &err)) {
577    err.PrintToStdout();
578    return false;
579  }
580  build_settings_.set_build_config_file(
581      SourceFile(build_config_value->string_value()));
582
583  return true;
584}
585
586// DependentSetup --------------------------------------------------------------
587
588DependentSetup::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_));
593}
594
595DependentSetup::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_));
600}
601
602DependentSetup::~DependentSetup() {
603}
604
605Scheduler* DependentSetup::GetScheduler() {
606  return scheduler_;
607}
608
609void DependentSetup::RunPreMessageLoop() {
610  CommonSetup::RunPreMessageLoop();
611}
612
613bool DependentSetup::RunPostMessageLoop() {
614  return CommonSetup::RunPostMessageLoop();
615}
616
617