Home | History | Annotate | Download | only in gn
      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 whaever 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   loader_->set_complete_callback(base::Bind(&DecrementWorkCount));
    130 }
    131 
    132 CommonSetup::CommonSetup(const CommonSetup& other)
    133     : build_settings_(other.build_settings_),
    134       loader_(new LoaderImpl(&build_settings_)),
    135       builder_(new Builder(loader_.get())),
    136       check_for_bad_items_(other.check_for_bad_items_) {
    137   loader_->set_complete_callback(base::Bind(&DecrementWorkCount));
    138 }
    139 
    140 CommonSetup::~CommonSetup() {
    141 }
    142 
    143 void CommonSetup::RunPreMessageLoop() {
    144   // Load the root build file.
    145   loader_->Load(SourceFile("//BUILD.gn"), Label());
    146 
    147   // Will be decremented with the loader is drained.
    148   g_scheduler->IncrementWorkCount();
    149 }
    150 
    151 bool CommonSetup::RunPostMessageLoop() {
    152   Err err;
    153   if (check_for_bad_items_) {
    154     if (!builder_->CheckForBadItems(&err)) {
    155       err.PrintToStdout();
    156       return false;
    157     }
    158   }
    159 
    160   if (!build_settings_.build_args().VerifyAllOverridesUsed(&err)) {
    161     err.PrintToStdout();
    162     return false;
    163   }
    164 
    165   // Write out tracing and timing if requested.
    166   const CommandLine* cmdline = CommandLine::ForCurrentProcess();
    167   if (cmdline->HasSwitch(kTimeSwitch))
    168     PrintLongHelp(SummarizeTraces());
    169   if (cmdline->HasSwitch(kTracelogSwitch))
    170     SaveTraces(cmdline->GetSwitchValuePath(kTracelogSwitch));
    171 
    172   return true;
    173 }
    174 
    175 // Setup -----------------------------------------------------------------------
    176 
    177 Setup::Setup()
    178     : CommonSetup(),
    179       empty_settings_(&empty_build_settings_, std::string()),
    180       dotfile_scope_(&empty_settings_) {
    181   empty_settings_.set_toolchain_label(Label());
    182   build_settings_.set_item_defined_callback(
    183       base::Bind(&ItemDefinedCallback, scheduler_.main_loop(), builder_));
    184 
    185   // The scheduler's main loop wasn't created when the Loader was created, so
    186   // we need to set it now.
    187   loader_->set_main_loop(scheduler_.main_loop());
    188 }
    189 
    190 Setup::~Setup() {
    191 }
    192 
    193 bool Setup::DoSetup() {
    194   CommandLine* cmdline = CommandLine::ForCurrentProcess();
    195 
    196   scheduler_.set_verbose_logging(cmdline->HasSwitch(kSwitchVerbose));
    197   if (cmdline->HasSwitch(kTimeSwitch) ||
    198       cmdline->HasSwitch(kTracelogSwitch))
    199     EnableTracing();
    200 
    201   if (!FillArguments(*cmdline))
    202     return false;
    203   if (!FillSourceDir(*cmdline))
    204     return false;
    205   if (!RunConfigFile())
    206     return false;
    207   if (!FillOtherConfig(*cmdline))
    208     return false;
    209   FillPythonPath();
    210 
    211   base::FilePath build_path = cmdline->GetSwitchValuePath(kSwitchBuildOutput);
    212   if (!build_path.empty()) {
    213     // We accept either repo paths "//out/Debug" or raw source-root-relative
    214     // paths "out/Debug".
    215     std::string build_path_8 = FilePathToUTF8(build_path);
    216     if (build_path_8.compare(0, 2, "//") != 0)
    217       build_path_8.insert(0, "//");
    218 #if defined(OS_WIN)
    219     // Canonicalize to forward slashes on Windows.
    220     std::replace(build_path_8.begin(), build_path_8.end(), '\\', '/');
    221 #endif
    222     build_settings_.SetBuildDir(SourceDir(build_path_8));
    223   } else {
    224     // Default output dir.
    225     build_settings_.SetBuildDir(SourceDir("//out/Default/"));
    226   }
    227 
    228   return true;
    229 }
    230 
    231 bool Setup::Run() {
    232   RunPreMessageLoop();
    233   if (!scheduler_.Run())
    234     return false;
    235   return RunPostMessageLoop();
    236 }
    237 
    238 Scheduler* Setup::GetScheduler() {
    239   return &scheduler_;
    240 }
    241 
    242 bool Setup::FillArguments(const CommandLine& cmdline) {
    243   std::string args = cmdline.GetSwitchValueASCII(kSwitchArgs);
    244   if (args.empty())
    245     return true;  // Nothing to set.
    246 
    247   args_input_file_.reset(new InputFile(SourceFile()));
    248   args_input_file_->SetContents(args);
    249   args_input_file_->set_friendly_name("the command-line \"--args\" settings");
    250 
    251   Err err;
    252   args_tokens_ = Tokenizer::Tokenize(args_input_file_.get(), &err);
    253   if (err.has_error()) {
    254     err.PrintToStdout();
    255     return false;
    256   }
    257 
    258   args_root_ = Parser::Parse(args_tokens_, &err);
    259   if (err.has_error()) {
    260     err.PrintToStdout();
    261     return false;
    262   }
    263 
    264   Scope arg_scope(&empty_settings_);
    265   args_root_->AsBlock()->ExecuteBlockInScope(&arg_scope, &err);
    266   if (err.has_error()) {
    267     err.PrintToStdout();
    268     return false;
    269   }
    270 
    271   // Save the result of the command args.
    272   Scope::KeyValueMap overrides;
    273   arg_scope.GetCurrentScopeValues(&overrides);
    274   build_settings_.build_args().AddArgOverrides(overrides);
    275   return true;
    276 }
    277 
    278 bool Setup::FillSourceDir(const CommandLine& cmdline) {
    279   // Find the .gn file.
    280   base::FilePath root_path;
    281 
    282   // Prefer the command line args to the config file.
    283   base::FilePath relative_root_path = cmdline.GetSwitchValuePath(kSwitchRoot);
    284   if (!relative_root_path.empty()) {
    285     root_path = base::MakeAbsoluteFilePath(relative_root_path);
    286     dotfile_name_ = root_path.Append(kGnFile);
    287   } else {
    288     base::FilePath cur_dir;
    289     file_util::GetCurrentDirectory(&cur_dir);
    290     dotfile_name_ = FindDotFile(cur_dir);
    291     if (dotfile_name_.empty()) {
    292       Err(Location(), "Can't find source root.",
    293           "I could not find a \".gn\" file in the current directory or any "
    294           "parent,\nand the --root command-line argument was not specified.")
    295           .PrintToStdout();
    296       return false;
    297     }
    298     root_path = dotfile_name_.DirName();
    299   }
    300 
    301   if (scheduler_.verbose_logging())
    302     scheduler_.Log("Using source root", FilePathToUTF8(root_path));
    303   build_settings_.SetRootPath(root_path);
    304 
    305   return true;
    306 }
    307 
    308 void Setup::FillPythonPath() {
    309 #if defined(OS_WIN)
    310   // Find Python on the path so we can use the absolute path in the build.
    311   const base::char16 kGetPython[] =
    312       L"cmd.exe /c python -c \"import sys; print sys.executable\"";
    313   std::string python_path;
    314   if (base::GetAppOutput(kGetPython, &python_path)) {
    315     TrimWhitespaceASCII(python_path, TRIM_ALL, &python_path);
    316     if (scheduler_.verbose_logging())
    317       scheduler_.Log("Found python", python_path);
    318   } else {
    319     scheduler_.Log("WARNING", "Could not find python on path, using "
    320         "just \"python.exe\"");
    321     python_path = "python.exe";
    322   }
    323   build_settings_.set_python_path(base::FilePath(UTF8ToUTF16(python_path)));
    324 #else
    325   build_settings_.set_python_path(base::FilePath("python"));
    326 #endif
    327 }
    328 
    329 bool Setup::RunConfigFile() {
    330   if (scheduler_.verbose_logging())
    331     scheduler_.Log("Got dotfile", FilePathToUTF8(dotfile_name_));
    332 
    333   dotfile_input_file_.reset(new InputFile(SourceFile("//.gn")));
    334   if (!dotfile_input_file_->Load(dotfile_name_)) {
    335     Err(Location(), "Could not load dotfile.",
    336         "The file \"" + FilePathToUTF8(dotfile_name_) + "\" cound't be loaded")
    337         .PrintToStdout();
    338     return false;
    339   }
    340 
    341   Err err;
    342   dotfile_tokens_ = Tokenizer::Tokenize(dotfile_input_file_.get(), &err);
    343   if (err.has_error()) {
    344     err.PrintToStdout();
    345     return false;
    346   }
    347 
    348   dotfile_root_ = Parser::Parse(dotfile_tokens_, &err);
    349   if (err.has_error()) {
    350     err.PrintToStdout();
    351     return false;
    352   }
    353 
    354   dotfile_root_->AsBlock()->ExecuteBlockInScope(&dotfile_scope_, &err);
    355   if (err.has_error()) {
    356     err.PrintToStdout();
    357     return false;
    358   }
    359 
    360   return true;
    361 }
    362 
    363 bool Setup::FillOtherConfig(const CommandLine& cmdline) {
    364   Err err;
    365 
    366   // Secondary source path.
    367   SourceDir secondary_source;
    368   if (cmdline.HasSwitch(kSecondarySource)) {
    369     // Prefer the command line over the config file.
    370     secondary_source =
    371         SourceDir(cmdline.GetSwitchValueASCII(kSecondarySource));
    372   } else {
    373     // Read from the config file if present.
    374     const Value* secondary_value =
    375         dotfile_scope_.GetValue("secondary_source", true);
    376     if (secondary_value) {
    377       if (!secondary_value->VerifyTypeIs(Value::STRING, &err)) {
    378         err.PrintToStdout();
    379         return false;
    380       }
    381       build_settings_.SetSecondarySourcePath(
    382           SourceDir(secondary_value->string_value()));
    383     }
    384   }
    385 
    386   // Build config file.
    387   const Value* build_config_value =
    388       dotfile_scope_.GetValue("buildconfig", true);
    389   if (!build_config_value) {
    390     Err(Location(), "No build config file.",
    391         "Your .gn file (\"" + FilePathToUTF8(dotfile_name_) + "\")\n"
    392         "didn't specify a \"buildconfig\" value.").PrintToStdout();
    393     return false;
    394   } else if (!build_config_value->VerifyTypeIs(Value::STRING, &err)) {
    395     err.PrintToStdout();
    396     return false;
    397   }
    398   build_settings_.set_build_config_file(
    399       SourceFile(build_config_value->string_value()));
    400 
    401   return true;
    402 }
    403 
    404 // DependentSetup --------------------------------------------------------------
    405 
    406 DependentSetup::DependentSetup(Setup* derive_from)
    407     : CommonSetup(*derive_from),
    408       scheduler_(derive_from->GetScheduler()) {
    409   build_settings_.set_item_defined_callback(
    410       base::Bind(&ItemDefinedCallback, scheduler_->main_loop(), builder_));
    411 }
    412 
    413 DependentSetup::DependentSetup(DependentSetup* 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() {
    421 }
    422 
    423 Scheduler* DependentSetup::GetScheduler() {
    424   return scheduler_;
    425 }
    426 
    427 void DependentSetup::RunPreMessageLoop() {
    428   CommonSetup::RunPreMessageLoop();
    429 }
    430 
    431 bool DependentSetup::RunPostMessageLoop() {
    432   return CommonSetup::RunPostMessageLoop();
    433 }
    434 
    435