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 std::string
GetOutputSubdirName(const Label
& toolchain_label
, bool is_default
) {
25 // The default toolchain has no subdir.
29 // For now just assume the toolchain name is always a valid dir name. We may
30 // want to clean up the in the future.
31 return toolchain_label
.name();
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
<SourceFile
> waiting_on_me
;
80 // -----------------------------------------------------------------------------
82 const void* Loader::kDefaultToolchainKey
= &kDefaultToolchainKey
;
90 void Loader::Load(const Label
& label
) {
91 Load(BuildFileForLabel(label
), 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 Label
& in_toolchain_name
) {
114 const Label
& toolchain_name
= in_toolchain_name
.is_null()
115 ? default_toolchain_label_
: in_toolchain_name
;
116 LoadID
load_id(file
, toolchain_name
);
117 if (!invocations_
.insert(load_id
).second
)
118 return; // Already in set, so this file was already loaded or schedulerd.
120 if (toolchain_records_
.empty()) {
121 // Nothing loaded, need to load the default build config. The intial load
122 // should not specify a toolchain.
123 DCHECK(toolchain_name
.is_null());
125 ToolchainRecord
* record
=
126 new ToolchainRecord(build_settings_
, Label(), Label());
127 toolchain_records_
[Label()] = record
;
129 // The default build config is no dependent on the toolchain definition,
130 // since we need to load the build config before we know what the default
131 // toolchain name is.
132 record
->is_toolchain_loaded
= true;
134 record
->waiting_on_me
.push_back(file
);
135 ScheduleLoadBuildConfig(&record
->settings
, Scope::KeyValueMap());
139 ToolchainRecord
* record
;
140 if (toolchain_name
.is_null())
141 record
= toolchain_records_
[default_toolchain_label_
];
143 record
= toolchain_records_
[toolchain_name
];
146 DCHECK(!default_toolchain_label_
.is_null());
148 // No reference to this toolchain found yet, make one.
149 record
= new ToolchainRecord(build_settings_
, toolchain_name
,
150 default_toolchain_label_
);
151 toolchain_records_
[toolchain_name
] = record
;
153 // Schedule a load of the toolchain using the default one.
154 Load(BuildFileForLabel(toolchain_name
), default_toolchain_label_
);
157 if (record
->is_config_loaded
)
158 ScheduleLoadFile(&record
->settings
, file
);
160 record
->waiting_on_me
.push_back(file
);
163 void LoaderImpl::ToolchainLoaded(const Toolchain
* toolchain
) {
164 ToolchainRecord
* record
= toolchain_records_
[toolchain
->label()];
166 DCHECK(!default_toolchain_label_
.is_null());
167 record
= new ToolchainRecord(build_settings_
, toolchain
->label(),
168 default_toolchain_label_
);
169 toolchain_records_
[toolchain
->label()] = record
;
171 record
->is_toolchain_loaded
= true;
173 // The default build config is loaded first, then its toolchain. Secondary
174 // ones are loaded in the opposite order so we can pass toolchain parameters
175 // to the build config. So we may or may not have a config at this point.
176 if (!record
->is_config_loaded
) {
177 ScheduleLoadBuildConfig(&record
->settings
, toolchain
->args());
179 // There should be nobody waiting on this if the build config is already
181 DCHECK(record
->waiting_on_me
.empty());
185 Label
LoaderImpl::GetDefaultToolchain() const {
186 return default_toolchain_label_
;
189 const Settings
* LoaderImpl::GetToolchainSettings(const Label
& label
) const {
190 ToolchainRecordMap::const_iterator found_toolchain
;
191 if (label
.is_null()) {
192 if (default_toolchain_label_
.is_null())
194 found_toolchain
= toolchain_records_
.find(default_toolchain_label_
);
196 found_toolchain
= toolchain_records_
.find(label
);
199 if (found_toolchain
== toolchain_records_
.end())
201 return &found_toolchain
->second
->settings
;
204 void LoaderImpl::ScheduleLoadFile(const Settings
* settings
,
205 const SourceFile
& file
) {
208 if (!AsyncLoadFile(LocationRange(), settings
->build_settings(), file
,
209 base::Bind(&LoaderImpl::BackgroundLoadFile
, this,
212 g_scheduler
->FailWithError(err
);
213 DecrementPendingLoads();
217 void LoaderImpl::ScheduleLoadBuildConfig(
219 const Scope::KeyValueMap
& toolchain_overrides
) {
222 if (!AsyncLoadFile(LocationRange(), settings
->build_settings(),
223 settings
->build_settings()->build_config_file(),
224 base::Bind(&LoaderImpl::BackgroundLoadBuildConfig
,
225 this, settings
, toolchain_overrides
),
227 g_scheduler
->FailWithError(err
);
228 DecrementPendingLoads();
232 void LoaderImpl::BackgroundLoadFile(const Settings
* settings
,
233 const SourceFile
& file_name
,
234 const ParseNode
* root
) {
236 main_loop_
->PostTask(FROM_HERE
,
237 base::Bind(&LoaderImpl::DecrementPendingLoads
, this));
241 if (g_scheduler
->verbose_logging()) {
242 g_scheduler
->Log("Running", file_name
.value() + " with toolchain " +
243 settings
->toolchain_label().GetUserVisibleName(false));
246 Scope
our_scope(settings
->base_config());
247 ScopePerFileProvider
per_file_provider(&our_scope
);
248 our_scope
.set_source_dir(file_name
.GetDir());
250 ScopedTrace
trace(TraceItem::TRACE_FILE_EXECUTE
, file_name
.value());
251 trace
.SetToolchain(settings
->toolchain_label());
254 root
->Execute(&our_scope
, &err
);
256 g_scheduler
->FailWithError(err
);
260 main_loop_
->PostTask(FROM_HERE
, base::Bind(&LoaderImpl::DidLoadFile
, this));
263 void LoaderImpl::BackgroundLoadBuildConfig(
265 const Scope::KeyValueMap
& toolchain_overrides
,
266 const ParseNode
* root
) {
268 main_loop_
->PostTask(FROM_HERE
,
269 base::Bind(&LoaderImpl::DecrementPendingLoads
, this));
273 Scope
* base_config
= settings
->base_config();
274 base_config
->set_source_dir(SourceDir("//"));
276 settings
->build_settings()->build_args().SetupRootScope(
277 base_config
, toolchain_overrides
);
279 base_config
->SetProcessingBuildConfig();
281 // See kDefaultToolchainKey in the header.
282 Label default_toolchain_label
;
283 if (settings
->is_default())
284 base_config
->SetProperty(kDefaultToolchainKey
, &default_toolchain_label
);
286 ScopedTrace
trace(TraceItem::TRACE_FILE_EXECUTE
,
287 settings
->build_settings()->build_config_file().value());
288 trace
.SetToolchain(settings
->toolchain_label());
290 const BlockNode
* root_block
= root
->AsBlock();
292 root_block
->ExecuteBlockInScope(base_config
, &err
);
297 g_scheduler
->FailWithError(err
);
299 base_config
->ClearProcessingBuildConfig();
300 if (settings
->is_default()) {
301 // The default toolchain must have been set in the default build config
303 if (default_toolchain_label
.is_null()) {
304 g_scheduler
->FailWithError(Err(Location(),
305 "The default build config file did not call set_default_toolchain()",
306 "If you don't call this, I can't figure out what toolchain to use\n"
307 "for all of this code."));
309 DCHECK(settings
->toolchain_label().is_null());
310 settings
->set_toolchain_label(default_toolchain_label
);
314 main_loop_
->PostTask(FROM_HERE
,
315 base::Bind(&LoaderImpl::DidLoadBuildConfig
, this,
316 settings
->toolchain_label()));
319 void LoaderImpl::DidLoadFile() {
320 DecrementPendingLoads();
323 void LoaderImpl::DidLoadBuildConfig(const Label
& label
) {
324 // Do not return early, we must call DecrementPendingLoads() at the bottom.
326 ToolchainRecordMap::iterator found_toolchain
= toolchain_records_
.find(label
);
327 ToolchainRecord
* record
= NULL
;
328 if (found_toolchain
== toolchain_records_
.end()) {
329 // When loading the default build config, we'll insert it into the record
330 // map with an empty label since we don't yet know what to call it.
332 // In this case, we should have exactly one entry in the map with an empty
333 // label. We now need to fix up the naming so it refers to the "real" one.
334 CHECK(toolchain_records_
.size() == 1);
335 ToolchainRecordMap::iterator empty_label
= toolchain_records_
.find(Label());
336 CHECK(empty_label
!= toolchain_records_
.end());
338 // Fix up the toolchain record.
339 record
= empty_label
->second
;
340 toolchain_records_
[label
] = record
;
341 toolchain_records_
.erase(empty_label
);
343 // Save the default toolchain label.
344 default_toolchain_label_
= label
;
345 DCHECK(record
->settings
.default_toolchain_label().is_null());
346 record
->settings
.set_default_toolchain_label(label
);
348 // The settings object should have the toolchain label already set.
349 DCHECK(!record
->settings
.toolchain_label().is_null());
351 // Update any stored invocations that refer to the empty toolchain label.
352 // This will normally only be one, for the root build file, so brute-force
355 invocations_
.swap(old_loads
);
356 for (LoadIDSet::iterator i
= old_loads
.begin();
357 i
!= old_loads
.end(); ++i
) {
358 if (i
->toolchain_name
.is_null()) {
359 // Fix up toolchain label
360 invocations_
.insert(LoadID(i
->file
, label
));
362 // Can keep the old one.
363 invocations_
.insert(*i
);
367 record
= found_toolchain
->second
;
370 DCHECK(!record
->is_config_loaded
);
371 DCHECK(record
->is_toolchain_loaded
);
372 record
->is_config_loaded
= true;
374 // Schedule all waiting file loads.
375 for (size_t i
= 0; i
< record
->waiting_on_me
.size(); i
++)
376 ScheduleLoadFile(&record
->settings
, record
->waiting_on_me
[i
]);
377 record
->waiting_on_me
.clear();
379 DecrementPendingLoads();
382 void LoaderImpl::DecrementPendingLoads() {
383 DCHECK(pending_loads_
> 0);
385 if (pending_loads_
== 0 && !complete_callback_
.is_null())
386 complete_callback_
.Run();
389 bool LoaderImpl::AsyncLoadFile(
390 const LocationRange
& origin
,
391 const BuildSettings
* build_settings
,
392 const SourceFile
& file_name
,
393 const base::Callback
<void(const ParseNode
*)>& callback
,
395 if (async_load_file_
.is_null()) {
396 return g_scheduler
->input_file_manager()->AsyncLoadFile(
397 origin
, build_settings
, file_name
, callback
, err
);
399 return async_load_file_
.Run(
400 origin
, build_settings
, file_name
, callback
, err
);