| OLD | NEW |
| (Empty) |
| 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/toolchain_manager.h" | |
| 6 | |
| 7 #include <set> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "build/build_config.h" | |
| 11 #include "tools/gn/err.h" | |
| 12 #include "tools/gn/item.h" | |
| 13 #include "tools/gn/item_node.h" | |
| 14 #include "tools/gn/item_tree.h" | |
| 15 #include "tools/gn/parse_tree.h" | |
| 16 #include "tools/gn/scheduler.h" | |
| 17 #include "tools/gn/scope.h" | |
| 18 #include "tools/gn/scope_per_file_provider.h" | |
| 19 #include "tools/gn/trace.h" | |
| 20 | |
| 21 // How toolchain loading works | |
| 22 // --------------------------- | |
| 23 // When we start loading a build, we'll load the build config file and that | |
| 24 // will call set_default_toolchain. We'll schedule a load of the file | |
| 25 // containing the default toolchain definition, and can do this in parallel | |
| 26 // with all other build files. Targets will have an implicit dependency on the | |
| 27 // toolchain so we won't write out any files until we've loaded the toolchain | |
| 28 // definition. | |
| 29 // | |
| 30 // When we see a reference to a target using a different toolchain, it gets | |
| 31 // more complicated. In this case, the toolchain definition contains arguments | |
| 32 // to pass into the build config file when it is invoked in the context of that | |
| 33 // toolchain. As a result, we have to actually see the definition of the | |
| 34 // toolchain before doing anything else. | |
| 35 // | |
| 36 // So when we see a reference to a non-default toolchain we do the following: | |
| 37 // | |
| 38 // 1. Schedule a load of the file containing the toolchain definition, if it | |
| 39 // isn't loaded already. | |
| 40 // 2. When the toolchain definition is loaded, we schedule a load of the | |
| 41 // build config file in the context of that toolchain. We'll use the | |
| 42 // arguments from the toolchain definition to execute it. | |
| 43 // 3. When the build config is set up, then we can load all of the individual | |
| 44 // buildfiles in the context of that config that we require. | |
| 45 | |
| 46 namespace { | |
| 47 | |
| 48 enum ToolchainState { | |
| 49 // Toolchain settings have not requested to be loaded. This means we | |
| 50 // haven't seen any targets that require this toolchain yet. This means that | |
| 51 // we have seen a toolchain definition, but no targets that use it. Not | |
| 52 // loading the settings automatically allows you to define a bunch of | |
| 53 // toolchains and potentially not use them without much overhead. | |
| 54 TOOLCHAIN_NOT_LOADED, | |
| 55 | |
| 56 // The toolchain definition for non-default toolchains has been scheduled | |
| 57 // to be loaded but has not completed. When this is done, we can load the | |
| 58 // settings file. This is needed to get the arguments out of the toolchain | |
| 59 // definition. This is skipped for the default toolchain which has no | |
| 60 // arguments (see summary above). | |
| 61 TOOLCHAIN_DEFINITION_LOADING, | |
| 62 | |
| 63 // The settings have been scheduled to be loaded but have not completed. | |
| 64 TOOLCHAIN_SETTINGS_LOADING, | |
| 65 | |
| 66 // The settings are done being loaded. | |
| 67 TOOLCHAIN_SETTINGS_LOADED | |
| 68 }; | |
| 69 | |
| 70 SourceFile DirToBuildFile(const SourceDir& dir) { | |
| 71 return SourceFile(dir.value() + "BUILD.gn"); | |
| 72 } | |
| 73 | |
| 74 } // namespace | |
| 75 | |
| 76 struct ToolchainManager::Info { | |
| 77 Info(const BuildSettings* build_settings, | |
| 78 const Label& toolchain_name, | |
| 79 const std::string& output_subdir_name) | |
| 80 : state(TOOLCHAIN_NOT_LOADED), | |
| 81 settings(build_settings, output_subdir_name), | |
| 82 toolchain(new Toolchain(&settings, toolchain_name)), | |
| 83 toolchain_set(false), | |
| 84 toolchain_file_loaded(false), | |
| 85 item_node(NULL) { | |
| 86 settings.set_toolchain_label(toolchain_name); | |
| 87 } | |
| 88 | |
| 89 ~Info() { | |
| 90 if (!item_node) // See toolchain definition for more. | |
| 91 delete toolchain; | |
| 92 } | |
| 93 | |
| 94 // Makes sure that an ItemNode is created for the toolchain, which lets | |
| 95 // targets depend on the (potentially future) loading of the toolchain. | |
| 96 // | |
| 97 // We can't always do this at the beginning since when doing the default | |
| 98 // build config, we don't know the toolchain name yet. We also need to go | |
| 99 // through some effort to avoid doing this inside the toolchain manager's | |
| 100 // lock (to avoid holding two locks at once). | |
| 101 void EnsureItemNode() { | |
| 102 if (!item_node) { | |
| 103 ItemTree& tree = settings.build_settings()->item_tree(); | |
| 104 item_node = new ItemNode(toolchain); | |
| 105 tree.AddNodeLocked(item_node); | |
| 106 } | |
| 107 } | |
| 108 | |
| 109 ToolchainState state; | |
| 110 | |
| 111 // The first place in the build that we saw a reference for this toolchain. | |
| 112 // This allows us to report errors if it can't be loaded and blame some | |
| 113 // reasonable place of the code. This will be empty for the default toolchain. | |
| 114 LocationRange specified_from; | |
| 115 | |
| 116 // When the state is TOOLCHAIN_SETTINGS_LOADED, the settings should be | |
| 117 // considered read-only and can be read without locking. Otherwise, they | |
| 118 // should not be accessed at all except to load them (which can therefore | |
| 119 // also be done outside of the lock). This works as long as the state flag | |
| 120 // is only ever read or written inside the lock. | |
| 121 Settings settings; | |
| 122 | |
| 123 // When we create an item node, this pointer will be owned by that node | |
| 124 // so it's lifetime is managed by the dependency graph. Before we've created | |
| 125 // the ItemNode, this class has to takre responsibility for this pointer. | |
| 126 Toolchain* toolchain; | |
| 127 bool toolchain_set; | |
| 128 LocationRange toolchain_definition_location; | |
| 129 | |
| 130 // Set when the file corresponding to the toolchain definition is loaded. | |
| 131 // This will normally be set right after "toolchain_set". However, if the | |
| 132 // toolchain definition is missing, the file might be marked loaded but the | |
| 133 // toolchain definition could still be unset. | |
| 134 bool toolchain_file_loaded; | |
| 135 | |
| 136 // While state == TOOLCHAIN_SETTINGS_LOADING || TOOLCHAIN_DEFINITION_LOADING, | |
| 137 // this will collect all scheduled invocations using this toolchain. They'll | |
| 138 // be issued once the settings file has been interpreted. | |
| 139 // | |
| 140 // The map maps the source file to "some" location it was invoked from (so | |
| 141 // we can give good error messages). It does NOT map to the root of the | |
| 142 // file to be invoked (the file still needs loading). This will be NULL | |
| 143 // for internally invoked files. | |
| 144 typedef std::map<SourceFile, LocationRange> ScheduledInvocationMap; | |
| 145 ScheduledInvocationMap scheduled_invocations; | |
| 146 | |
| 147 // Tracks all scheduled and executed invocations for this toolchain. This | |
| 148 // is used to avoid invoking a file more than once for a toolchain. | |
| 149 std::set<SourceFile> all_invocations; | |
| 150 | |
| 151 // Filled in by EnsureItemNode, see that for more. | |
| 152 ItemNode* item_node; | |
| 153 }; | |
| 154 | |
| 155 ToolchainManager::ToolchainManager(const BuildSettings* build_settings) | |
| 156 : build_settings_(build_settings) { | |
| 157 } | |
| 158 | |
| 159 ToolchainManager::~ToolchainManager() { | |
| 160 for (ToolchainMap::iterator i = toolchains_.begin(); | |
| 161 i != toolchains_.end(); ++i) | |
| 162 delete i->second; | |
| 163 toolchains_.clear(); | |
| 164 } | |
| 165 | |
| 166 void ToolchainManager::StartLoadingUnlocked(const SourceFile& build_file_name) { | |
| 167 // How the default build config works: Initially we don't have a toolchain | |
| 168 // name to call the settings for the default build config. So we create one | |
| 169 // with an empty toolchain name and execute the default build config file. | |
| 170 // When that's done, we'll go and fix up the name to the default build config | |
| 171 // that the script set. | |
| 172 base::AutoLock lock(GetLock()); | |
| 173 Err err; | |
| 174 Info* info = LoadNewToolchainLocked(LocationRange(), Label(), &err); | |
| 175 if (err.has_error()) | |
| 176 g_scheduler->FailWithError(err); | |
| 177 CHECK(info); | |
| 178 info->scheduled_invocations[build_file_name] = LocationRange(); | |
| 179 info->all_invocations.insert(build_file_name); | |
| 180 | |
| 181 if (!ScheduleBuildConfigLoadLocked(info, true, &err)) | |
| 182 g_scheduler->FailWithError(err); | |
| 183 } | |
| 184 | |
| 185 const Settings* ToolchainManager::GetSettingsForToolchainLocked( | |
| 186 const LocationRange& from_here, | |
| 187 const Label& toolchain_name, | |
| 188 Err* err) { | |
| 189 GetLock().AssertAcquired(); | |
| 190 ToolchainMap::iterator found = toolchains_.find(toolchain_name); | |
| 191 Info* info = NULL; | |
| 192 if (found == toolchains_.end()) { | |
| 193 info = LoadNewToolchainLocked(from_here, toolchain_name, err); | |
| 194 if (!info) | |
| 195 return NULL; | |
| 196 } else { | |
| 197 info = found->second; | |
| 198 } | |
| 199 info->EnsureItemNode(); | |
| 200 | |
| 201 return &info->settings; | |
| 202 } | |
| 203 | |
| 204 const Toolchain* ToolchainManager::GetToolchainDefinitionUnlocked( | |
| 205 const Label& toolchain_name) { | |
| 206 base::AutoLock lock(GetLock()); | |
| 207 ToolchainMap::iterator found = toolchains_.find(toolchain_name); | |
| 208 if (found == toolchains_.end() || !found->second->toolchain_set) | |
| 209 return NULL; | |
| 210 | |
| 211 // Since we don't allow defining a toolchain more than once, we know that | |
| 212 // once it's set it won't be mutated, so we can safely return this pointer | |
| 213 // for reading outside the lock. | |
| 214 return found->second->toolchain; | |
| 215 } | |
| 216 | |
| 217 bool ToolchainManager::SetDefaultToolchainUnlocked( | |
| 218 const Label& default_toolchain, | |
| 219 const LocationRange& defined_here, | |
| 220 Err* err) { | |
| 221 base::AutoLock lock(GetLock()); | |
| 222 if (!default_toolchain_.is_null()) { | |
| 223 *err = Err(defined_here, "Default toolchain already set."); | |
| 224 err->AppendSubErr(Err(default_toolchain_defined_here_, | |
| 225 "Previously defined here.", | |
| 226 "You can only set this once.")); | |
| 227 return false; | |
| 228 } | |
| 229 | |
| 230 if (default_toolchain.is_null()) { | |
| 231 *err = Err(defined_here, "Bad default toolchain name.", | |
| 232 "You can't set the default toolchain name to nothing."); | |
| 233 return false; | |
| 234 } | |
| 235 if (!default_toolchain.toolchain_dir().is_null() || | |
| 236 !default_toolchain.toolchain_name().empty()) { | |
| 237 *err = Err(defined_here, "Toolchain name has toolchain.", | |
| 238 "You can't specify a toolchain (inside the parens) for a toolchain " | |
| 239 "name. I got:\n" + default_toolchain.GetUserVisibleName(true)); | |
| 240 return false; | |
| 241 } | |
| 242 | |
| 243 default_toolchain_ = default_toolchain; | |
| 244 default_toolchain_defined_here_ = defined_here; | |
| 245 return true; | |
| 246 } | |
| 247 | |
| 248 Label ToolchainManager::GetDefaultToolchainUnlocked() const { | |
| 249 base::AutoLock lock(GetLock()); | |
| 250 return default_toolchain_; | |
| 251 } | |
| 252 | |
| 253 bool ToolchainManager::SetToolchainDefinitionLocked( | |
| 254 const Toolchain& tc, | |
| 255 const LocationRange& defined_from, | |
| 256 Err* err) { | |
| 257 GetLock().AssertAcquired(); | |
| 258 | |
| 259 ToolchainMap::iterator found = toolchains_.find(tc.label()); | |
| 260 Info* info = NULL; | |
| 261 if (found == toolchains_.end()) { | |
| 262 // New toolchain. | |
| 263 info = LoadNewToolchainLocked(defined_from, tc.label(), err); | |
| 264 if (!info) | |
| 265 return false; | |
| 266 } else { | |
| 267 // It's important to preserve the exact Toolchain object in our tree since | |
| 268 // it will be in the ItemTree and targets may have dependencies on it. | |
| 269 info = found->second; | |
| 270 } | |
| 271 | |
| 272 // The labels should match or else we're setting the wrong one! | |
| 273 CHECK(info->toolchain->label() == tc.label()); | |
| 274 | |
| 275 // Save the toolchain. We can just overwrite our definition. | |
| 276 *info->toolchain = tc; | |
| 277 | |
| 278 if (info->toolchain_set) { | |
| 279 *err = Err(defined_from, "Duplicate toolchain definition."); | |
| 280 err->AppendSubErr(Err( | |
| 281 info->toolchain_definition_location, | |
| 282 "Previously defined here.", | |
| 283 "A toolchain can only be defined once. One tricky way that this could\n" | |
| 284 "happen is if your definition is itself in a file that's interpreted\n" | |
| 285 "under different toolchains, which would result in multiple\n" | |
| 286 "definitions as the file is loaded multiple times. So be sure your\n" | |
| 287 "toolchain definitions are in files that either don't define any\n" | |
| 288 "targets (probably best) or at least don't contain targets executed\n" | |
| 289 "with more than one toolchain.")); | |
| 290 return false; | |
| 291 } | |
| 292 | |
| 293 info->EnsureItemNode(); | |
| 294 | |
| 295 info->toolchain_set = true; | |
| 296 info->toolchain_definition_location = defined_from; | |
| 297 return true; | |
| 298 } | |
| 299 | |
| 300 bool ToolchainManager::ScheduleInvocationLocked( | |
| 301 const LocationRange& specified_from, | |
| 302 const Label& toolchain_name, | |
| 303 const SourceDir& dir, | |
| 304 Err* err) { | |
| 305 GetLock().AssertAcquired(); | |
| 306 SourceFile build_file(DirToBuildFile(dir)); | |
| 307 | |
| 308 // If there's no specified toolchain name, use the default. | |
| 309 ToolchainMap::iterator found; | |
| 310 if (toolchain_name.is_null()) | |
| 311 found = toolchains_.find(default_toolchain_); | |
| 312 else | |
| 313 found = toolchains_.find(toolchain_name); | |
| 314 | |
| 315 Info* info = NULL; | |
| 316 if (found == toolchains_.end()) { | |
| 317 // New toolchain. | |
| 318 info = LoadNewToolchainLocked(specified_from, toolchain_name, err); | |
| 319 if (!info) | |
| 320 return false; | |
| 321 } else { | |
| 322 // Use existing one. | |
| 323 info = found->second; | |
| 324 if (info->all_invocations.find(build_file) != | |
| 325 info->all_invocations.end()) { | |
| 326 // We've already seen this source file for this toolchain, don't need | |
| 327 // to do anything. | |
| 328 return true; | |
| 329 } | |
| 330 } | |
| 331 | |
| 332 info->all_invocations.insert(build_file); | |
| 333 | |
| 334 switch (info->state) { | |
| 335 case TOOLCHAIN_NOT_LOADED: | |
| 336 // Toolchain needs to be loaded. Start loading it and push this buildfile | |
| 337 // on the wait queue. The actual toolchain build file will have been | |
| 338 // scheduled to be loaded by LoadNewToolchainLocked() above, so we just | |
| 339 // need to request that the build settings be loaded when that toolchain | |
| 340 // file is done executing (recall toolchain files are executed in the | |
| 341 // context of the default toolchain, which is why we need to do the extra | |
| 342 // Info lookup in the make_pair). | |
| 343 DCHECK(!default_toolchain_.is_null()); | |
| 344 pending_build_config_map_[ | |
| 345 std::make_pair(DirToBuildFile(toolchain_name.dir()), | |
| 346 toolchains_[default_toolchain_])] = | |
| 347 info; | |
| 348 info->scheduled_invocations[build_file] = specified_from; | |
| 349 | |
| 350 // Transition to the loading state. | |
| 351 info->specified_from = specified_from; | |
| 352 info->state = TOOLCHAIN_DEFINITION_LOADING; | |
| 353 return true; | |
| 354 | |
| 355 case TOOLCHAIN_DEFINITION_LOADING: | |
| 356 case TOOLCHAIN_SETTINGS_LOADING: | |
| 357 // Toolchain is in the process of loading, push this buildfile on the | |
| 358 // wait queue to run when the config is ready. | |
| 359 info->scheduled_invocations[build_file] = specified_from; | |
| 360 return true; | |
| 361 | |
| 362 case TOOLCHAIN_SETTINGS_LOADED: | |
| 363 // Everything is ready, just schedule the build file to load. | |
| 364 return ScheduleBackgroundInvoke(info, specified_from, build_file, err); | |
| 365 | |
| 366 default: | |
| 367 NOTREACHED(); | |
| 368 return false; | |
| 369 } | |
| 370 } | |
| 371 | |
| 372 // static | |
| 373 std::string ToolchainManager::ToolchainToOutputSubdir( | |
| 374 const Label& toolchain_name) { | |
| 375 // For now just assume the toolchain name is always a valid dir name. We may | |
| 376 // want to clean up the in the future. | |
| 377 return toolchain_name.name(); | |
| 378 } | |
| 379 | |
| 380 ToolchainManager::Info* ToolchainManager::LoadNewToolchainLocked( | |
| 381 const LocationRange& specified_from, | |
| 382 const Label& toolchain_name, | |
| 383 Err* err) { | |
| 384 GetLock().AssertAcquired(); | |
| 385 Info* info = new Info(build_settings_, | |
| 386 toolchain_name, | |
| 387 ToolchainToOutputSubdir(toolchain_name)); | |
| 388 | |
| 389 toolchains_[toolchain_name] = info; | |
| 390 | |
| 391 // Invoke the file containing the toolchain definition so that it gets | |
| 392 // defined. The default one (label is empty) will be done spearately. | |
| 393 if (!toolchain_name.is_null()) { | |
| 394 // The default toolchain should be specified whenever we're requesting | |
| 395 // another one. This is how we know under what context we should execute | |
| 396 // the invoke for the toolchain file. | |
| 397 CHECK(!default_toolchain_.is_null()); | |
| 398 ScheduleInvocationLocked(specified_from, default_toolchain_, | |
| 399 toolchain_name.dir(), err); | |
| 400 } | |
| 401 return info; | |
| 402 } | |
| 403 | |
| 404 void ToolchainManager::FixupDefaultToolchainLocked() { | |
| 405 // Now that we've run the default build config, we should know the | |
| 406 // default toolchain name. Fix up our reference. | |
| 407 // See Start() for more. | |
| 408 GetLock().AssertAcquired(); | |
| 409 if (default_toolchain_.is_null()) { | |
| 410 g_scheduler->FailWithError(Err(Location(), | |
| 411 "Default toolchain not set.", | |
| 412 "Your build config file \"" + | |
| 413 build_settings_->build_config_file().value() + | |
| 414 "\"\ndid not call set_default_toolchain(). This is needed so " | |
| 415 "I know how to actually\ncompile your code.")); | |
| 416 return; | |
| 417 } | |
| 418 | |
| 419 ToolchainMap::iterator old_default = toolchains_.find(Label()); | |
| 420 CHECK(old_default != toolchains_.end()); | |
| 421 Info* info = old_default->second; | |
| 422 toolchains_[default_toolchain_] = info; | |
| 423 toolchains_.erase(old_default); | |
| 424 | |
| 425 // Toolchain should not have been loaded in the build config file. | |
| 426 CHECK(!info->toolchain_set); | |
| 427 | |
| 428 // We need to set the toolchain label now that we know it. There's no way | |
| 429 // to set the label, but we can assign the toolchain to a new one. Loading | |
| 430 // the build config can not change the toolchain, so we won't be overwriting | |
| 431 // anything useful. | |
| 432 *info->toolchain = Toolchain(&info->settings, default_toolchain_); | |
| 433 info->settings.set_is_default(true); | |
| 434 info->settings.set_toolchain_label(default_toolchain_); | |
| 435 info->EnsureItemNode(); | |
| 436 | |
| 437 // The default toolchain is loaded in greedy mode so all targets we | |
| 438 // encounter are generated. Non-default toolchain settings stay in non-greedy | |
| 439 // so we only generate the minimally required set. | |
| 440 info->settings.set_greedy_target_generation(true); | |
| 441 | |
| 442 // Schedule a load of the toolchain build file. | |
| 443 Err err; | |
| 444 ScheduleInvocationLocked(LocationRange(), default_toolchain_, | |
| 445 default_toolchain_.dir(), &err); | |
| 446 if (err.has_error()) | |
| 447 g_scheduler->FailWithError(err); | |
| 448 } | |
| 449 | |
| 450 bool ToolchainManager::ScheduleBackgroundInvoke( | |
| 451 Info* info, | |
| 452 const LocationRange& specified_from, | |
| 453 const SourceFile& build_file, | |
| 454 Err* err) { | |
| 455 g_scheduler->IncrementWorkCount(); | |
| 456 if (!g_scheduler->input_file_manager()->AsyncLoadFile( | |
| 457 specified_from, build_settings_, build_file, | |
| 458 base::Bind(&ToolchainManager::BackgroundInvoke, | |
| 459 base::Unretained(this), info, build_file), | |
| 460 err)) { | |
| 461 g_scheduler->DecrementWorkCount(); | |
| 462 return false; | |
| 463 } | |
| 464 return true; | |
| 465 } | |
| 466 | |
| 467 bool ToolchainManager::ScheduleBuildConfigLoadLocked(Info* info, | |
| 468 bool is_default, | |
| 469 Err* err) { | |
| 470 GetLock().AssertAcquired(); | |
| 471 | |
| 472 g_scheduler->IncrementWorkCount(); | |
| 473 if (!g_scheduler->input_file_manager()->AsyncLoadFile( | |
| 474 info->specified_from, build_settings_, | |
| 475 build_settings_->build_config_file(), | |
| 476 base::Bind(&ToolchainManager::BackgroundLoadBuildConfig, | |
| 477 base::Unretained(this), info, is_default), | |
| 478 err)) { | |
| 479 g_scheduler->DecrementWorkCount(); | |
| 480 return false; | |
| 481 } | |
| 482 info->state = TOOLCHAIN_SETTINGS_LOADING; | |
| 483 return true; | |
| 484 } | |
| 485 | |
| 486 void ToolchainManager::BackgroundLoadBuildConfig(Info* info, | |
| 487 bool is_default, | |
| 488 const ParseNode* root) { | |
| 489 // Danger: No early returns without decrementing the work count. | |
| 490 if (root && !g_scheduler->is_failed()) { | |
| 491 // Nobody should be accessing settings at this point other than us since we | |
| 492 // haven't marked it loaded, so we can do it outside the lock. | |
| 493 Scope* base_config = info->settings.base_config(); | |
| 494 base_config->set_source_dir(SourceDir("//")); | |
| 495 | |
| 496 info->settings.build_settings()->build_args().SetupRootScope( | |
| 497 base_config, info->toolchain->args()); | |
| 498 | |
| 499 base_config->SetProcessingBuildConfig(); | |
| 500 if (is_default) | |
| 501 base_config->SetProcessingDefaultBuildConfig(); | |
| 502 | |
| 503 ScopedTrace trace(TraceItem::TRACE_FILE_EXECUTE, | |
| 504 info->settings.build_settings()->build_config_file().value()); | |
| 505 trace.SetToolchain(info->settings.toolchain_label()); | |
| 506 | |
| 507 const BlockNode* root_block = root->AsBlock(); | |
| 508 Err err; | |
| 509 root_block->ExecuteBlockInScope(base_config, &err); | |
| 510 | |
| 511 trace.Done(); | |
| 512 | |
| 513 base_config->ClearProcessingBuildConfig(); | |
| 514 if (is_default) | |
| 515 base_config->ClearProcessingDefaultBuildConfig(); | |
| 516 | |
| 517 if (err.has_error()) { | |
| 518 g_scheduler->FailWithError(err); | |
| 519 } else { | |
| 520 // Base config processing succeeded. | |
| 521 Info::ScheduledInvocationMap schedule_these; | |
| 522 { | |
| 523 base::AutoLock lock(GetLock()); | |
| 524 schedule_these.swap(info->scheduled_invocations); | |
| 525 info->state = TOOLCHAIN_SETTINGS_LOADED; | |
| 526 if (is_default) | |
| 527 FixupDefaultToolchainLocked(); | |
| 528 } | |
| 529 | |
| 530 // Schedule build files waiting on this settings. There can be many so we | |
| 531 // want to load them in parallel on the pool. | |
| 532 for (Info::ScheduledInvocationMap::iterator i = schedule_these.begin(); | |
| 533 i != schedule_these.end() && !g_scheduler->is_failed(); ++i) { | |
| 534 if (!ScheduleBackgroundInvoke(info, i->second, i->first, &err)) { | |
| 535 g_scheduler->FailWithError(err); | |
| 536 break; | |
| 537 } | |
| 538 } | |
| 539 } | |
| 540 } | |
| 541 g_scheduler->DecrementWorkCount(); | |
| 542 } | |
| 543 | |
| 544 void ToolchainManager::BackgroundInvoke(const Info* info, | |
| 545 const SourceFile& file_name, | |
| 546 const ParseNode* root) { | |
| 547 if (root && !g_scheduler->is_failed()) { | |
| 548 if (g_scheduler->verbose_logging()) { | |
| 549 g_scheduler->Log("Running", file_name.value() + " with toolchain " + | |
| 550 info->toolchain->label().GetUserVisibleName(false)); | |
| 551 } | |
| 552 | |
| 553 Scope our_scope(info->settings.base_config()); | |
| 554 ScopePerFileProvider per_file_provider(&our_scope); | |
| 555 our_scope.set_source_dir(file_name.GetDir()); | |
| 556 | |
| 557 ScopedTrace trace(TraceItem::TRACE_FILE_EXECUTE, file_name.value()); | |
| 558 trace.SetToolchain(info->settings.toolchain_label()); | |
| 559 | |
| 560 Err err; | |
| 561 root->Execute(&our_scope, &err); | |
| 562 if (err.has_error()) | |
| 563 g_scheduler->FailWithError(err); | |
| 564 | |
| 565 trace.Done(); | |
| 566 | |
| 567 { | |
| 568 // Check to see if any build config invocations depend on this file and | |
| 569 // invoke them. | |
| 570 base::AutoLock lock(GetLock()); | |
| 571 BuildConfigInvokeMap::iterator found_file = | |
| 572 pending_build_config_map_.find(std::make_pair(file_name, info)); | |
| 573 if (found_file != pending_build_config_map_.end()) { | |
| 574 // The toolchain state should be waiting on the definition, which | |
| 575 // should be the thing we just loaded. | |
| 576 Info* info_to_load = found_file->second; | |
| 577 DCHECK(info_to_load->state == TOOLCHAIN_DEFINITION_LOADING); | |
| 578 DCHECK(!info_to_load->toolchain_file_loaded); | |
| 579 info_to_load->toolchain_file_loaded = true; | |
| 580 | |
| 581 if (!ScheduleBuildConfigLoadLocked(info_to_load, false, &err)) | |
| 582 g_scheduler->FailWithError(err); | |
| 583 pending_build_config_map_.erase(found_file); | |
| 584 } | |
| 585 } | |
| 586 } | |
| 587 | |
| 588 g_scheduler->DecrementWorkCount(); | |
| 589 } | |
| 590 | |
| 591 base::Lock& ToolchainManager::GetLock() const { | |
| 592 return build_settings_->item_tree().lock(); | |
| 593 } | |
| OLD | NEW |