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 |