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.
5 #include "tools/gn/loader.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/stl_util.h"
10 #include "tools/gn/build_settings.h"
11 #include "tools/gn/err.h"
12 #include "tools/gn/filesystem_utils.h"
13 #include "tools/gn/input_file_manager.h"
14 #include "tools/gn/parse_tree.h"
15 #include "tools/gn/scheduler.h"
16 #include "tools/gn/scope_per_file_provider.h"
17 #include "tools/gn/settings.h"
18 #include "tools/gn/source_dir.h"
19 #include "tools/gn/source_file.h"
20 #include "tools/gn/trace.h"
24 struct SourceFileAndOrigin
{
25 SourceFileAndOrigin(const SourceFile
& f
, const LocationRange
& o
)
36 // Identifies one time a file is loaded in a given toolchain so we don't load
38 struct LoaderImpl::LoadID
{
40 LoadID(const SourceFile
& f
, const Label
& tc_name
)
42 toolchain_name(tc_name
) {
45 bool operator<(const LoadID
& other
) const {
46 if (file
.value() == other
.file
.value())
47 return toolchain_name
< other
.toolchain_name
;
48 return file
< other
.file
;
55 // Our tracking information for a toolchain.
56 struct LoaderImpl::ToolchainRecord
{
57 // The default toolchain label can be empty for the first time the default
58 // toolchain is loaded, since we don't know it yet. This will be fixed up
59 // later. It should be valid in all other cases.
60 ToolchainRecord(const BuildSettings
* build_settings
,
61 const Label
& toolchain_label
,
62 const Label
& default_toolchain_label
)
63 : settings(build_settings
,
64 GetOutputSubdirName(toolchain_label
,
65 toolchain_label
== default_toolchain_label
)),
66 is_toolchain_loaded(false),
67 is_config_loaded(false) {
68 settings
.set_default_toolchain_label(default_toolchain_label
);
69 settings
.set_toolchain_label(toolchain_label
);
74 bool is_toolchain_loaded
;
75 bool is_config_loaded
;
77 std::vector
<SourceFileAndOrigin
> waiting_on_me
;
80 // -----------------------------------------------------------------------------
82 const void* Loader::kDefaultToolchainKey
= &kDefaultToolchainKey
;
90 void Loader::Load(const Label
& label
, const LocationRange
& origin
) {
91 Load(BuildFileForLabel(label
), origin
, label
.GetToolchainLabel());
95 SourceFile
Loader::BuildFileForLabel(const Label
& label
) {
96 return SourceFile(label
.dir().value() + "BUILD.gn");
99 // -----------------------------------------------------------------------------
101 LoaderImpl::LoaderImpl(const BuildSettings
* build_settings
)
102 : main_loop_(base::MessageLoop::current()),
104 build_settings_(build_settings
) {
107 LoaderImpl::~LoaderImpl() {
108 STLDeleteContainerPairSecondPointers(toolchain_records_
.begin(),
109 toolchain_records_
.end());
112 void LoaderImpl::Load(const SourceFile
& file
,
113 const LocationRange
& origin
,
114 const Label
& in_toolchain_name
) {
115 const Label
& toolchain_name
= in_toolchain_name
.is_null()
116 ? default_toolchain_label_
: in_toolchain_name
;
117 LoadID
load_id(file
, toolchain_name
);
118 if (!invocations_
.insert(load_id
).second
)
119 return; // Already in set, so this file was already loaded or schedulerd.
121 if (toolchain_records_
.empty()) {
122 // Nothing loaded, need to load the default build config. The intial load
123 // should not specify a toolchain.
124 DCHECK(toolchain_name
.is_null());
126 ToolchainRecord
* record
=
127 new ToolchainRecord(build_settings_
, Label(), Label());
128 toolchain_records_
[Label()] = record
;
130 // The default build config is no dependent on the toolchain definition,
131 // since we need to load the build config before we know what the default
132 // toolchain name is.
133 record
->is_toolchain_loaded
= true;
135 record
->waiting_on_me
.push_back(SourceFileAndOrigin(file
, origin
));
136 ScheduleLoadBuildConfig(&record
->settings
, Scope::KeyValueMap());
140 ToolchainRecord
* record
;
141 if (toolchain_name
.is_null())
142 record
= toolchain_records_
[default_toolchain_label_
];
144 record
= toolchain_records_
[toolchain_name
];
147 DCHECK(!default_toolchain_label_
.is_null());
149 // No reference to this toolchain found yet, make one.
150 record
= new ToolchainRecord(build_settings_
, toolchain_name
,
151 default_toolchain_label_
);
152 toolchain_records_
[toolchain_name
] = record
;
154 // Schedule a load of the toolchain using the default one.
155 Load(BuildFileForLabel(toolchain_name
), origin
, default_toolchain_label_
);
158 if (record
->is_config_loaded
)
159 ScheduleLoadFile(&record
->settings
, origin
, file
);
161 record
->waiting_on_me
.push_back(SourceFileAndOrigin(file
, origin
));
164 void LoaderImpl::ToolchainLoaded(const Toolchain
* toolchain
) {
165 ToolchainRecord
* record
= toolchain_records_
[toolchain
->label()];
167 DCHECK(!default_toolchain_label_
.is_null());
168 record
= new ToolchainRecord(build_settings_
, toolchain
->label(),
169 default_toolchain_label_
);
170 toolchain_records_
[toolchain
->label()] = record
;
172 record
->is_toolchain_loaded
= true;
174 // The default build config is loaded first, then its toolchain. Secondary
175 // ones are loaded in the opposite order so we can pass toolchain parameters
176 // to the build config. So we may or may not have a config at this point.
177 if (!record
->is_config_loaded
) {
178 ScheduleLoadBuildConfig(&record
->settings
, toolchain
->args());
180 // There should be nobody waiting on this if the build config is already
182 DCHECK(record
->waiting_on_me
.empty());
186 Label
LoaderImpl::GetDefaultToolchain() const {
187 return default_toolchain_label_
;
190 const Settings
* LoaderImpl::GetToolchainSettings(const Label
& label
) const {
191 ToolchainRecordMap::const_iterator found_toolchain
;
192 if (label
.is_null()) {
193 if (default_toolchain_label_
.is_null())
195 found_toolchain
= toolchain_records_
.find(default_toolchain_label_
);
197 found_toolchain
= toolchain_records_
.find(label
);
200 if (found_toolchain
== toolchain_records_
.end())
202 return &found_toolchain
->second
->settings
;
205 void LoaderImpl::ScheduleLoadFile(const Settings
* settings
,
206 const LocationRange
& origin
,
207 const SourceFile
& file
) {
210 if (!AsyncLoadFile(origin
, settings
->build_settings(), file
,
211 base::Bind(&LoaderImpl::BackgroundLoadFile
, this,
214 g_scheduler
->FailWithError(err
);
215 DecrementPendingLoads();
219 void LoaderImpl::ScheduleLoadBuildConfig(
221 const Scope::KeyValueMap
& toolchain_overrides
) {
224 if (!AsyncLoadFile(LocationRange(), settings
->build_settings(),
225 settings
->build_settings()->build_config_file(),
226 base::Bind(&LoaderImpl::BackgroundLoadBuildConfig
,
227 this, settings
, toolchain_overrides
),
229 g_scheduler
->FailWithError(err
);
230 DecrementPendingLoads();
234 void LoaderImpl::BackgroundLoadFile(const Settings
* settings
,
235 const SourceFile
& file_name
,
236 const ParseNode
* root
) {
238 main_loop_
->PostTask(FROM_HERE
,
239 base::Bind(&LoaderImpl::DecrementPendingLoads
, this));
243 if (g_scheduler
->verbose_logging()) {
244 g_scheduler
->Log("Running", file_name
.value() + " with toolchain " +
245 settings
->toolchain_label().GetUserVisibleName(false));
248 Scope
our_scope(settings
->base_config());
249 ScopePerFileProvider
per_file_provider(&our_scope
, true);
250 our_scope
.set_source_dir(file_name
.GetDir());
252 // Targets, etc. generated as part of running this file will end up here.
253 Scope::ItemVector collected_items
;
254 our_scope
.set_item_collector(&collected_items
);
256 ScopedTrace
trace(TraceItem::TRACE_FILE_EXECUTE
, file_name
.value());
257 trace
.SetToolchain(settings
->toolchain_label());
260 root
->Execute(&our_scope
, &err
);
262 g_scheduler
->FailWithError(err
);
264 if (!our_scope
.CheckForUnusedVars(&err
))
265 g_scheduler
->FailWithError(err
);
267 // Pass all of the items that were defined off to the builder.
268 for (size_t i
= 0; i
< collected_items
.size(); i
++)
269 settings
->build_settings()->ItemDefined(collected_items
[i
]->Pass());
273 main_loop_
->PostTask(FROM_HERE
, base::Bind(&LoaderImpl::DidLoadFile
, this));
276 void LoaderImpl::BackgroundLoadBuildConfig(
278 const Scope::KeyValueMap
& toolchain_overrides
,
279 const ParseNode
* root
) {
281 main_loop_
->PostTask(FROM_HERE
,
282 base::Bind(&LoaderImpl::DecrementPendingLoads
, this));
286 Scope
* base_config
= settings
->base_config();
287 base_config
->set_source_dir(SourceDir("//"));
289 settings
->build_settings()->build_args().SetupRootScope(
290 base_config
, toolchain_overrides
);
292 base_config
->SetProcessingBuildConfig();
294 // See kDefaultToolchainKey in the header.
295 Label default_toolchain_label
;
296 if (settings
->is_default())
297 base_config
->SetProperty(kDefaultToolchainKey
, &default_toolchain_label
);
299 ScopedTrace
trace(TraceItem::TRACE_FILE_EXECUTE
,
300 settings
->build_settings()->build_config_file().value());
301 trace
.SetToolchain(settings
->toolchain_label());
303 const BlockNode
* root_block
= root
->AsBlock();
305 root_block
->ExecuteBlockInScope(base_config
, &err
);
307 // Clear all private variables left in the scope. We want the root build
308 // config to be like a .gni file in that variables beginning with an
309 // underscore aren't exported.
310 base_config
->RemovePrivateIdentifiers();
315 g_scheduler
->FailWithError(err
);
317 base_config
->ClearProcessingBuildConfig();
318 if (settings
->is_default()) {
319 // The default toolchain must have been set in the default build config
321 if (default_toolchain_label
.is_null()) {
322 g_scheduler
->FailWithError(Err(Location(),
323 "The default build config file did not call set_default_toolchain()",
324 "If you don't call this, I can't figure out what toolchain to use\n"
325 "for all of this code."));
327 DCHECK(settings
->toolchain_label().is_null());
328 settings
->set_toolchain_label(default_toolchain_label
);
332 main_loop_
->PostTask(FROM_HERE
,
333 base::Bind(&LoaderImpl::DidLoadBuildConfig
, this,
334 settings
->toolchain_label()));
337 void LoaderImpl::DidLoadFile() {
338 DecrementPendingLoads();
341 void LoaderImpl::DidLoadBuildConfig(const Label
& label
) {
342 // Do not return early, we must call DecrementPendingLoads() at the bottom.
344 ToolchainRecordMap::iterator found_toolchain
= toolchain_records_
.find(label
);
345 ToolchainRecord
* record
= NULL
;
346 if (found_toolchain
== toolchain_records_
.end()) {
347 // When loading the default build config, we'll insert it into the record
348 // map with an empty label since we don't yet know what to call it.
350 // In this case, we should have exactly one entry in the map with an empty
351 // label. We now need to fix up the naming so it refers to the "real" one.
352 CHECK(toolchain_records_
.size() == 1);
353 ToolchainRecordMap::iterator empty_label
= toolchain_records_
.find(Label());
354 CHECK(empty_label
!= toolchain_records_
.end());
356 // Fix up the toolchain record.
357 record
= empty_label
->second
;
358 toolchain_records_
[label
] = record
;
359 toolchain_records_
.erase(empty_label
);
361 // Save the default toolchain label.
362 default_toolchain_label_
= label
;
363 DCHECK(record
->settings
.default_toolchain_label().is_null());
364 record
->settings
.set_default_toolchain_label(label
);
366 // The settings object should have the toolchain label already set.
367 DCHECK(!record
->settings
.toolchain_label().is_null());
369 // Update any stored invocations that refer to the empty toolchain label.
370 // This will normally only be one, for the root build file, so brute-force
373 invocations_
.swap(old_loads
);
374 for (LoadIDSet::iterator i
= old_loads
.begin();
375 i
!= old_loads
.end(); ++i
) {
376 if (i
->toolchain_name
.is_null()) {
377 // Fix up toolchain label
378 invocations_
.insert(LoadID(i
->file
, label
));
380 // Can keep the old one.
381 invocations_
.insert(*i
);
385 record
= found_toolchain
->second
;
388 DCHECK(!record
->is_config_loaded
);
389 DCHECK(record
->is_toolchain_loaded
);
390 record
->is_config_loaded
= true;
392 // Schedule all waiting file loads.
393 for (size_t i
= 0; i
< record
->waiting_on_me
.size(); i
++) {
394 ScheduleLoadFile(&record
->settings
, record
->waiting_on_me
[i
].origin
,
395 record
->waiting_on_me
[i
].file
);
397 record
->waiting_on_me
.clear();
399 DecrementPendingLoads();
402 void LoaderImpl::DecrementPendingLoads() {
403 DCHECK(pending_loads_
> 0);
405 if (pending_loads_
== 0 && !complete_callback_
.is_null())
406 complete_callback_
.Run();
409 bool LoaderImpl::AsyncLoadFile(
410 const LocationRange
& origin
,
411 const BuildSettings
* build_settings
,
412 const SourceFile
& file_name
,
413 const base::Callback
<void(const ParseNode
*)>& callback
,
415 if (async_load_file_
.is_null()) {
416 return g_scheduler
->input_file_manager()->AsyncLoadFile(
417 origin
, build_settings
, file_name
, callback
, err
);
419 return async_load_file_
.Run(
420 origin
, build_settings
, file_name
, callback
, err
);