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/file_util.h" 15 #include "base/files/file_path.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/filesystem_utils.h" 22 #include "tools/gn/header_checker.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 37 extern 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 fill 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 86 namespace { 87 88 // More logging. 89 const char kSwitchVerbose[] = "v"; 90 91 // Set build args. 92 const char kSwitchArgs[] = "args"; 93 94 // Set root dir. 95 const char kSwitchRoot[] = "root"; 96 97 // Set dotfile name. 98 const char kSwitchDotfile[] = "dotfile"; 99 100 // Enable timing. 101 const char kTimeSwitch[] = "time"; 102 103 const char kTracelogSwitch[] = "tracelog"; 104 105 const base::FilePath::CharType kGnFile[] = FILE_PATH_LITERAL(".gn"); 106 107 base::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. 121 void 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 129 void DecrementWorkCount() { 130 g_scheduler->DecrementWorkCount(); 131 } 132 133 } // namespace 134 135 // CommonSetup ----------------------------------------------------------------- 136 137 const char CommonSetup::kBuildArgFileName[] = "args.gn"; 138 139 CommonSetup::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 150 CommonSetup::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 161 CommonSetup::~CommonSetup() { 162 } 163 164 void 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 172 bool 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 std::vector<const Target*> targets = builder_->GetAllResolvedTargets(); 192 scoped_refptr<HeaderChecker> header_checker( 193 new HeaderChecker(&build_settings_, targets)); 194 195 std::vector<Err> header_errors; 196 header_checker->Run(&header_errors); 197 for (size_t i = 0; i < header_errors.size(); i++) { 198 if (i > 0) 199 OutputString("___________________\n", DECORATION_YELLOW); 200 header_errors[i].PrintToStdout(); 201 } 202 if (!header_errors.empty()) 203 return false; 204 } 205 206 // Write out tracing and timing if requested. 207 const CommandLine* cmdline = CommandLine::ForCurrentProcess(); 208 if (cmdline->HasSwitch(kTimeSwitch)) 209 PrintLongHelp(SummarizeTraces()); 210 if (cmdline->HasSwitch(kTracelogSwitch)) 211 SaveTraces(cmdline->GetSwitchValuePath(kTracelogSwitch)); 212 213 return true; 214 } 215 216 // Setup ----------------------------------------------------------------------- 217 218 Setup::Setup() 219 : CommonSetup(), 220 empty_settings_(&empty_build_settings_, std::string()), 221 dotfile_scope_(&empty_settings_), 222 fill_arguments_(true) { 223 empty_settings_.set_toolchain_label(Label()); 224 build_settings_.set_item_defined_callback( 225 base::Bind(&ItemDefinedCallback, scheduler_.main_loop(), builder_)); 226 227 // The scheduler's main loop wasn't created when the Loader was created, so 228 // we need to set it now. 229 loader_->set_main_loop(scheduler_.main_loop()); 230 } 231 232 Setup::~Setup() { 233 } 234 235 bool Setup::DoSetup(const std::string& build_dir) { 236 CommandLine* cmdline = CommandLine::ForCurrentProcess(); 237 238 scheduler_.set_verbose_logging(cmdline->HasSwitch(kSwitchVerbose)); 239 if (cmdline->HasSwitch(kTimeSwitch) || 240 cmdline->HasSwitch(kTracelogSwitch)) 241 EnableTracing(); 242 243 ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "DoSetup"); 244 245 if (!FillSourceDir(*cmdline)) 246 return false; 247 if (!RunConfigFile()) 248 return false; 249 if (!FillOtherConfig(*cmdline)) 250 return false; 251 if (!FillBuildDir(build_dir)) // Must be after FillSourceDir to resolve. 252 return false; 253 if (fill_arguments_) { 254 if (!FillArguments(*cmdline)) 255 return false; 256 } 257 FillPythonPath(); 258 259 return true; 260 } 261 262 bool Setup::Run() { 263 RunPreMessageLoop(); 264 if (!scheduler_.Run()) 265 return false; 266 return RunPostMessageLoop(); 267 } 268 269 Scheduler* Setup::GetScheduler() { 270 return &scheduler_; 271 } 272 273 SourceFile Setup::GetBuildArgFile() const { 274 return SourceFile(build_settings_.build_dir().value() + kBuildArgFileName); 275 } 276 277 bool Setup::FillArguments(const CommandLine& cmdline) { 278 // Use the args on the command line if specified, and save them. Do this even 279 // if the list is empty (this means clear any defaults). 280 if (cmdline.HasSwitch(kSwitchArgs)) { 281 if (!FillArgsFromCommandLine(cmdline.GetSwitchValueASCII(kSwitchArgs))) 282 return false; 283 SaveArgsToFile(); 284 return true; 285 } 286 287 // No command line args given, use the arguments from the build dir (if any). 288 return FillArgsFromFile(); 289 } 290 291 bool Setup::FillArgsFromCommandLine(const std::string& args) { 292 args_input_file_.reset(new InputFile(SourceFile())); 293 args_input_file_->SetContents(args); 294 args_input_file_->set_friendly_name("the command-line \"--args\""); 295 return FillArgsFromArgsInputFile(); 296 } 297 298 bool Setup::FillArgsFromFile() { 299 ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Load args file"); 300 301 SourceFile build_arg_source_file = GetBuildArgFile(); 302 base::FilePath build_arg_file = 303 build_settings_.GetFullPath(build_arg_source_file); 304 305 std::string contents; 306 if (!base::ReadFileToString(build_arg_file, &contents)) 307 return true; // File doesn't exist, continue with default args. 308 309 // Add a dependency on the build arguments file. If this changes, we want 310 // to re-generate the build. 311 g_scheduler->AddGenDependency(build_arg_file); 312 313 if (contents.empty()) 314 return true; // Empty file, do nothing. 315 316 args_input_file_.reset(new InputFile(build_arg_source_file)); 317 args_input_file_->SetContents(contents); 318 args_input_file_->set_friendly_name( 319 "build arg file (use \"gn args <out_dir>\" to edit)"); 320 321 setup_trace.Done(); // Only want to count the load as part of the trace. 322 return FillArgsFromArgsInputFile(); 323 } 324 325 bool Setup::FillArgsFromArgsInputFile() { 326 ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Parse args"); 327 328 Err err; 329 args_tokens_ = Tokenizer::Tokenize(args_input_file_.get(), &err); 330 if (err.has_error()) { 331 err.PrintToStdout(); 332 return false; 333 } 334 335 args_root_ = Parser::Parse(args_tokens_, &err); 336 if (err.has_error()) { 337 err.PrintToStdout(); 338 return false; 339 } 340 341 Scope arg_scope(&empty_settings_); 342 args_root_->AsBlock()->ExecuteBlockInScope(&arg_scope, &err); 343 if (err.has_error()) { 344 err.PrintToStdout(); 345 return false; 346 } 347 348 // Save the result of the command args. 349 Scope::KeyValueMap overrides; 350 arg_scope.GetCurrentScopeValues(&overrides); 351 build_settings_.build_args().AddArgOverrides(overrides); 352 return true; 353 } 354 355 bool Setup::SaveArgsToFile() { 356 ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Save args file"); 357 358 Scope::KeyValueMap args = build_settings_.build_args().GetAllOverrides(); 359 360 std::ostringstream stream; 361 for (Scope::KeyValueMap::const_iterator i = args.begin(); 362 i != args.end(); ++i) { 363 stream << i->first.as_string() << " = " << i->second.ToString(true); 364 stream << std::endl; 365 } 366 367 // For the first run, the build output dir might not be created yet, so do 368 // that so we can write a file into it. Ignore errors, we'll catch the error 369 // when we try to write a file to it below. 370 base::FilePath build_arg_file = 371 build_settings_.GetFullPath(GetBuildArgFile()); 372 base::CreateDirectory(build_arg_file.DirName()); 373 374 std::string contents = stream.str(); 375 #if defined(OS_WIN) 376 // Use Windows lineendings for this file since it will often open in 377 // Notepad which can't handle Unix ones. 378 ReplaceSubstringsAfterOffset(&contents, 0, "\n", "\r\n"); 379 #endif 380 if (base::WriteFile(build_arg_file, contents.c_str(), 381 static_cast<int>(contents.size())) == -1) { 382 Err(Location(), "Args file could not be written.", 383 "The file is \"" + FilePathToUTF8(build_arg_file) + 384 "\"").PrintToStdout(); 385 return false; 386 } 387 388 // Add a dependency on the build arguments file. If this changes, we want 389 // to re-generate the build. 390 g_scheduler->AddGenDependency(build_arg_file); 391 392 return true; 393 } 394 395 bool Setup::FillSourceDir(const CommandLine& cmdline) { 396 // Find the .gn file. 397 base::FilePath root_path; 398 399 // Prefer the command line args to the config file. 400 base::FilePath relative_root_path = cmdline.GetSwitchValuePath(kSwitchRoot); 401 if (!relative_root_path.empty()) { 402 root_path = base::MakeAbsoluteFilePath(relative_root_path); 403 if (root_path.empty()) { 404 Err(Location(), "Root source path not found.", 405 "The path \"" + FilePathToUTF8(relative_root_path) + 406 "\" doesn't exist.").PrintToStdout(); 407 return false; 408 } 409 410 // When --root is specified, an alternate --dotfile can also be set. 411 // --dotfile should be a real file path and not a "//foo" source-relative 412 // path. 413 base::FilePath dot_file_path = cmdline.GetSwitchValuePath(kSwitchDotfile); 414 if (dot_file_path.empty()) { 415 dotfile_name_ = root_path.Append(kGnFile); 416 } else { 417 dotfile_name_ = base::MakeAbsoluteFilePath(dot_file_path); 418 if (dotfile_name_.empty()) { 419 Err(Location(), "Could not load dotfile.", 420 "The file \"" + FilePathToUTF8(dot_file_path) + 421 "\" cound't be loaded.").PrintToStdout(); 422 return false; 423 } 424 } 425 } else { 426 // In the default case, look for a dotfile and that also tells us where the 427 // source root is. 428 base::FilePath cur_dir; 429 base::GetCurrentDirectory(&cur_dir); 430 dotfile_name_ = FindDotFile(cur_dir); 431 if (dotfile_name_.empty()) { 432 Err(Location(), "Can't find source root.", 433 "I could not find a \".gn\" file in the current directory or any " 434 "parent,\nand the --root command-line argument was not specified.") 435 .PrintToStdout(); 436 return false; 437 } 438 root_path = dotfile_name_.DirName(); 439 } 440 441 if (scheduler_.verbose_logging()) 442 scheduler_.Log("Using source root", FilePathToUTF8(root_path)); 443 build_settings_.SetRootPath(root_path); 444 445 return true; 446 } 447 448 bool Setup::FillBuildDir(const std::string& build_dir) { 449 SourceDir resolved = 450 SourceDirForCurrentDirectory(build_settings_.root_path()). 451 ResolveRelativeDir(build_dir); 452 if (resolved.is_null()) { 453 Err(Location(), "Couldn't resolve build directory.", 454 "The build directory supplied (\"" + build_dir + "\") was not valid."). 455 PrintToStdout(); 456 return false; 457 } 458 459 if (scheduler_.verbose_logging()) 460 scheduler_.Log("Using build dir", resolved.value()); 461 build_settings_.SetBuildDir(resolved); 462 return true; 463 } 464 465 void Setup::FillPythonPath() { 466 // Trace this since it tends to be a bit slow on Windows. 467 ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Fill Python Path"); 468 #if defined(OS_WIN) 469 // Find Python on the path so we can use the absolute path in the build. 470 const base::char16 kGetPython[] = 471 L"cmd.exe /c python -c \"import sys; print sys.executable\""; 472 std::string python_path; 473 if (base::GetAppOutput(kGetPython, &python_path)) { 474 base::TrimWhitespaceASCII(python_path, base::TRIM_ALL, &python_path); 475 if (scheduler_.verbose_logging()) 476 scheduler_.Log("Found python", python_path); 477 } else { 478 scheduler_.Log("WARNING", "Could not find python on path, using " 479 "just \"python.exe\""); 480 python_path = "python.exe"; 481 } 482 build_settings_.set_python_path(base::FilePath(base::UTF8ToUTF16(python_path)) 483 .NormalizePathSeparatorsTo('/')); 484 #else 485 build_settings_.set_python_path(base::FilePath("python")); 486 #endif 487 } 488 489 bool Setup::RunConfigFile() { 490 if (scheduler_.verbose_logging()) 491 scheduler_.Log("Got dotfile", FilePathToUTF8(dotfile_name_)); 492 493 dotfile_input_file_.reset(new InputFile(SourceFile("//.gn"))); 494 if (!dotfile_input_file_->Load(dotfile_name_)) { 495 Err(Location(), "Could not load dotfile.", 496 "The file \"" + FilePathToUTF8(dotfile_name_) + "\" cound't be loaded") 497 .PrintToStdout(); 498 return false; 499 } 500 501 Err err; 502 dotfile_tokens_ = Tokenizer::Tokenize(dotfile_input_file_.get(), &err); 503 if (err.has_error()) { 504 err.PrintToStdout(); 505 return false; 506 } 507 508 dotfile_root_ = Parser::Parse(dotfile_tokens_, &err); 509 if (err.has_error()) { 510 err.PrintToStdout(); 511 return false; 512 } 513 514 dotfile_root_->AsBlock()->ExecuteBlockInScope(&dotfile_scope_, &err); 515 if (err.has_error()) { 516 err.PrintToStdout(); 517 return false; 518 } 519 520 return true; 521 } 522 523 bool Setup::FillOtherConfig(const CommandLine& cmdline) { 524 Err err; 525 526 // Secondary source path, read from the config file if present. 527 // Read from the config file if present. 528 const Value* secondary_value = 529 dotfile_scope_.GetValue("secondary_source", true); 530 if (secondary_value) { 531 if (!secondary_value->VerifyTypeIs(Value::STRING, &err)) { 532 err.PrintToStdout(); 533 return false; 534 } 535 build_settings_.SetSecondarySourcePath( 536 SourceDir(secondary_value->string_value())); 537 } 538 539 // Root build file. 540 const Value* root_value = dotfile_scope_.GetValue("root", true); 541 if (root_value) { 542 if (!root_value->VerifyTypeIs(Value::STRING, &err)) { 543 err.PrintToStdout(); 544 return false; 545 } 546 547 Label root_target_label = 548 Label::Resolve(SourceDir("//"), Label(), *root_value, &err); 549 if (err.has_error()) { 550 err.PrintToStdout(); 551 return false; 552 } 553 554 root_build_file_ = Loader::BuildFileForLabel(root_target_label); 555 } 556 557 // Build config file. 558 const Value* build_config_value = 559 dotfile_scope_.GetValue("buildconfig", true); 560 if (!build_config_value) { 561 Err(Location(), "No build config file.", 562 "Your .gn file (\"" + FilePathToUTF8(dotfile_name_) + "\")\n" 563 "didn't specify a \"buildconfig\" value.").PrintToStdout(); 564 return false; 565 } else if (!build_config_value->VerifyTypeIs(Value::STRING, &err)) { 566 err.PrintToStdout(); 567 return false; 568 } 569 build_settings_.set_build_config_file( 570 SourceFile(build_config_value->string_value())); 571 572 return true; 573 } 574 575 // DependentSetup -------------------------------------------------------------- 576 577 DependentSetup::DependentSetup(Setup* derive_from) 578 : CommonSetup(*derive_from), 579 scheduler_(derive_from->GetScheduler()) { 580 build_settings_.set_item_defined_callback( 581 base::Bind(&ItemDefinedCallback, scheduler_->main_loop(), builder_)); 582 } 583 584 DependentSetup::DependentSetup(DependentSetup* derive_from) 585 : CommonSetup(*derive_from), 586 scheduler_(derive_from->GetScheduler()) { 587 build_settings_.set_item_defined_callback( 588 base::Bind(&ItemDefinedCallback, scheduler_->main_loop(), builder_)); 589 } 590 591 DependentSetup::~DependentSetup() { 592 } 593 594 Scheduler* DependentSetup::GetScheduler() { 595 return scheduler_; 596 } 597 598 void DependentSetup::RunPreMessageLoop() { 599 CommonSetup::RunPreMessageLoop(); 600 } 601 602 bool DependentSetup::RunPostMessageLoop() { 603 return CommonSetup::RunPostMessageLoop(); 604 } 605 606