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 "base/command_line.h"
      8 #include "base/file_util.h"
      9 #include "base/files/file_path.h"
     10 #include "tools/gn/filesystem_utils.h"
     11 #include "tools/gn/input_file.h"
     12 #include "tools/gn/parse_tree.h"
     13 #include "tools/gn/parser.h"
     14 #include "tools/gn/source_dir.h"
     15 #include "tools/gn/source_file.h"
     16 #include "tools/gn/tokenizer.h"
     17 #include "tools/gn/value.h"
     18 
     19 extern const char kDotfile_Help[] =
     20     ".gn file\n"
     21     "\n"
     22     "  When gn starts, it will search the current directory and parent\n"
     23     "  directories for a file called \".gn\". This indicates the source root.\n"
     24     "  You can override this detection by using the --root command-line\n"
     25     "  argument\n"
     26     "\n"
     27     "  The .gn file in the source root will be executed. The syntax is the\n"
     28     "  same as a buildfile, but with very limited build setup-specific\n"
     29     "  meaning.\n"
     30     "\n"
     31     "Variables:\n"
     32     "  buildconfig [required]\n"
     33     "      Label of the build config file. This file will be used to setup\n"
     34     "      the build file execution environment for each toolchain.\n"
     35     "\n"
     36     "  secondary_source [optional]\n"
     37     "      Label of an alternate directory tree to find input files. When\n"
     38     "      searching for a BUILD.gn file (or the build config file discussed\n"
     39     "      above), the file fill first be looked for in the source root.\n"
     40     "      If it's not found, the secondary source root will be checked\n"
     41     "      (which would contain a parallel directory hierarchy).\n"
     42     "\n"
     43     "      This behavior is intended to be used when BUILD.gn files can't be\n"
     44     "      checked in to certain source directories for whaever reason.\n"
     45     "\n"
     46     "      The secondary source root must be inside the main source tree.\n"
     47     "\n"
     48     "Example .gn file contents:\n"
     49     "\n"
     50     "  buildconfig = \"//build/config/BUILDCONFIG.gn\"\n"
     51     "\n"
     52     "  secondary_source = \"//build/config/temporary_buildfiles/\"\n";
     53 
     54 namespace {
     55 
     56 // More logging.
     57 const char kSwitchVerbose[] = "v";
     58 
     59 const char kSwitchRoot[] = "root";
     60 const char kSecondarySource[] = "secondary";
     61 
     62 const base::FilePath::CharType kGnFile[] = FILE_PATH_LITERAL(".gn");
     63 
     64 base::FilePath FindDotFile(const base::FilePath& current_dir) {
     65   base::FilePath try_this_file = current_dir.Append(kGnFile);
     66   if (base::PathExists(try_this_file))
     67     return try_this_file;
     68 
     69   base::FilePath with_no_slash = current_dir.StripTrailingSeparators();
     70   base::FilePath up_one_dir = with_no_slash.DirName();
     71   if (up_one_dir == current_dir)
     72     return base::FilePath();  // Got to the top.
     73 
     74   return FindDotFile(up_one_dir);
     75 }
     76 
     77 }  // namespace
     78 
     79 Setup::Setup()
     80     : dotfile_toolchain_(Label()),
     81       dotfile_settings_(&dotfile_build_settings_, &dotfile_toolchain_,
     82                         std::string()),
     83       dotfile_scope_(&dotfile_settings_) {
     84 }
     85 
     86 Setup::~Setup() {
     87 }
     88 
     89 bool Setup::DoSetup() {
     90   CommandLine* cmdline = CommandLine::ForCurrentProcess();
     91 
     92   scheduler_.set_verbose_logging(cmdline->HasSwitch(kSwitchVerbose));
     93 
     94   if (!FillSourceDir(*cmdline))
     95     return false;
     96   if (!RunConfigFile())
     97     return false;
     98   if (!FillOtherConfig(*cmdline))
     99     return false;
    100 
    101   // FIXME(brettw) get python path!
    102 #if defined(OS_WIN)
    103   build_settings_.set_python_path(
    104       base::FilePath(FILE_PATH_LITERAL("cmd.exe /c python")));
    105 #else
    106   build_settings_.set_python_path(base::FilePath(FILE_PATH_LITERAL("python")));
    107 #endif
    108 
    109   build_settings_.SetBuildDir(SourceDir("//out/gn/"));
    110 
    111   return true;
    112 }
    113 
    114 bool Setup::Run() {
    115   // Load the root build file and start runnung.
    116   build_settings_.toolchain_manager().StartLoadingUnlocked(
    117       SourceFile("//BUILD.gn"));
    118   if (!scheduler_.Run())
    119     return false;
    120 
    121   Err err = build_settings_.item_tree().CheckForBadItems();
    122   if (err.has_error()) {
    123     err.PrintToStdout();
    124     return false;
    125   }
    126   return true;
    127 }
    128 
    129 bool Setup::FillSourceDir(const CommandLine& cmdline) {
    130   // Find the .gn file.
    131   base::FilePath root_path;
    132 
    133   // Prefer the command line args to the config file.
    134   base::FilePath relative_root_path = cmdline.GetSwitchValuePath(kSwitchRoot);
    135   if (!relative_root_path.empty()) {
    136     root_path = base::MakeAbsoluteFilePath(relative_root_path);
    137     dotfile_name_ = root_path.Append(kGnFile);
    138   } else {
    139     base::FilePath cur_dir;
    140     file_util::GetCurrentDirectory(&cur_dir);
    141     dotfile_name_ = FindDotFile(cur_dir);
    142     if (dotfile_name_.empty()) {
    143       Err(Location(), "Can't find source root.",
    144           "I could not find a \".gn\" file in the current directory or any "
    145           "parent,\nand the --root command-line argument was not specified.")
    146           .PrintToStdout();
    147       return false;
    148     }
    149     root_path = dotfile_name_.DirName();
    150   }
    151 
    152   if (scheduler_.verbose_logging())
    153     scheduler_.Log("Using source root", FilePathToUTF8(root_path));
    154   build_settings_.set_root_path(root_path);
    155 
    156   return true;
    157 }
    158 
    159 bool Setup::RunConfigFile() {
    160   if (scheduler_.verbose_logging())
    161     scheduler_.Log("Got dotfile", FilePathToUTF8(dotfile_name_));
    162 
    163   dotfile_input_file_.reset(new InputFile(SourceFile("//.gn")));
    164   if (!dotfile_input_file_->Load(dotfile_name_)) {
    165     Err(Location(), "Could not load dotfile.",
    166         "The file \"" + FilePathToUTF8(dotfile_name_) + "\" cound't be loaded")
    167         .PrintToStdout();
    168     return false;
    169   }
    170 
    171   Err err;
    172   dotfile_tokens_ = Tokenizer::Tokenize(dotfile_input_file_.get(), &err);
    173   if (err.has_error()) {
    174     err.PrintToStdout();
    175     return false;
    176   }
    177 
    178   dotfile_root_ = Parser::Parse(dotfile_tokens_, &err);
    179   if (err.has_error()) {
    180     err.PrintToStdout();
    181     return false;
    182   }
    183 
    184   dotfile_root_->AsBlock()->ExecuteBlockInScope(&dotfile_scope_, &err);
    185   if (err.has_error()) {
    186     err.PrintToStdout();
    187     return false;
    188   }
    189 
    190   return true;
    191 }
    192 
    193 bool Setup::FillOtherConfig(const CommandLine& cmdline) {
    194   Err err;
    195 
    196   // Secondary source path.
    197   SourceDir secondary_source;
    198   if (cmdline.HasSwitch(kSecondarySource)) {
    199     // Prefer the command line over the config file.
    200     secondary_source =
    201         SourceDir(cmdline.GetSwitchValueASCII(kSecondarySource));
    202   } else {
    203     // Read from the config file if present.
    204     const Value* secondary_value =
    205         dotfile_scope_.GetValue("secondary_source", true);
    206     if (secondary_value) {
    207       if (!secondary_value->VerifyTypeIs(Value::STRING, &err)) {
    208         err.PrintToStdout();
    209         return false;
    210       }
    211       build_settings_.SetSecondarySourcePath(
    212           SourceDir(secondary_value->string_value()));
    213     }
    214   }
    215 
    216   // Build config file.
    217   const Value* build_config_value =
    218       dotfile_scope_.GetValue("buildconfig", true);
    219   if (!build_config_value) {
    220     Err(Location(), "No build config file.",
    221         "Your .gn file (\"" + FilePathToUTF8(dotfile_name_) + "\")\n"
    222         "didn't specify a \"buildconfig\" value.").PrintToStdout();
    223     return false;
    224   } else if (!build_config_value->VerifyTypeIs(Value::STRING, &err)) {
    225     err.PrintToStdout();
    226     return false;
    227   }
    228   build_settings_.set_build_config_file(
    229       SourceFile(build_config_value->string_value()));
    230 
    231   return true;
    232 }
    233