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