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