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