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 (auto& item
: collected_items
) {
269 settings
->build_settings()->ItemDefined(make_scoped_ptr(item
));
275 main_loop_
->PostTask(FROM_HERE
, base::Bind(&LoaderImpl::DidLoadFile
, this));
278 void LoaderImpl::BackgroundLoadBuildConfig(
280 const Scope::KeyValueMap
& toolchain_overrides
,
281 const ParseNode
* root
) {
283 main_loop_
->PostTask(FROM_HERE
,
284 base::Bind(&LoaderImpl::DecrementPendingLoads
, this));
288 Scope
* base_config
= settings
->base_config();
289 base_config
->set_source_dir(SourceDir("//"));
291 settings
->build_settings()->build_args().SetupRootScope(
292 base_config
, toolchain_overrides
);
294 base_config
->SetProcessingBuildConfig();
296 // See kDefaultToolchainKey in the header.
297 Label default_toolchain_label
;
298 if (settings
->is_default())
299 base_config
->SetProperty(kDefaultToolchainKey
, &default_toolchain_label
);
301 ScopedTrace
trace(TraceItem::TRACE_FILE_EXECUTE
,
302 settings
->build_settings()->build_config_file().value());
303 trace
.SetToolchain(settings
->toolchain_label());
306 root
->Execute(base_config
, &err
);
308 // Clear all private variables left in the scope. We want the root build
309 // config to be like a .gni file in that variables beginning with an
310 // underscore aren't exported.
311 base_config
->RemovePrivateIdentifiers();
316 g_scheduler
->FailWithError(err
);
318 base_config
->ClearProcessingBuildConfig();
319 if (settings
->is_default()) {
320 // The default toolchain must have been set in the default build config
322 if (default_toolchain_label
.is_null()) {
323 g_scheduler
->FailWithError(Err(Location(),
324 "The default build config file did not call set_default_toolchain()",
325 "If you don't call this, I can't figure out what toolchain to use\n"
326 "for all of this code."));
328 DCHECK(settings
->toolchain_label().is_null());
329 settings
->set_toolchain_label(default_toolchain_label
);
333 main_loop_
->PostTask(FROM_HERE
,
334 base::Bind(&LoaderImpl::DidLoadBuildConfig
, this,
335 settings
->toolchain_label()));
338 void LoaderImpl::DidLoadFile() {
339 DecrementPendingLoads();
342 void LoaderImpl::DidLoadBuildConfig(const Label
& label
) {
343 // Do not return early, we must call DecrementPendingLoads() at the bottom.
345 ToolchainRecordMap::iterator found_toolchain
= toolchain_records_
.find(label
);
346 ToolchainRecord
* record
= nullptr;
347 if (found_toolchain
== toolchain_records_
.end()) {
348 // When loading the default build config, we'll insert it into the record
349 // map with an empty label since we don't yet know what to call it.
351 // In this case, we should have exactly one entry in the map with an empty
352 // label. We now need to fix up the naming so it refers to the "real" one.
353 CHECK(toolchain_records_
.size() == 1);
354 ToolchainRecordMap::iterator empty_label
= toolchain_records_
.find(Label());
355 CHECK(empty_label
!= toolchain_records_
.end());
357 // Fix up the toolchain record.
358 record
= empty_label
->second
;
359 toolchain_records_
[label
] = record
;
360 toolchain_records_
.erase(empty_label
);
362 // Save the default toolchain label.
363 default_toolchain_label_
= label
;
364 DCHECK(record
->settings
.default_toolchain_label().is_null());
365 record
->settings
.set_default_toolchain_label(label
);
367 // The settings object should have the toolchain label already set.
368 DCHECK(!record
->settings
.toolchain_label().is_null());
370 // Update any stored invocations that refer to the empty toolchain label.
371 // This will normally only be one, for the root build file, so brute-force
374 invocations_
.swap(old_loads
);
375 for (const auto& load
: old_loads
) {
376 if (load
.toolchain_name
.is_null()) {
377 // Fix up toolchain label
378 invocations_
.insert(LoadID(load
.file
, label
));
380 // Can keep the old one.
381 invocations_
.insert(load
);
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 (const auto& waiting
: record
->waiting_on_me
)
394 ScheduleLoadFile(&record
->settings
, waiting
.origin
, waiting
.file
);
395 record
->waiting_on_me
.clear();
397 DecrementPendingLoads();
400 void LoaderImpl::DecrementPendingLoads() {
401 DCHECK(pending_loads_
> 0);
403 if (pending_loads_
== 0 && !complete_callback_
.is_null())
404 complete_callback_
.Run();
407 bool LoaderImpl::AsyncLoadFile(
408 const LocationRange
& origin
,
409 const BuildSettings
* build_settings
,
410 const SourceFile
& file_name
,
411 const base::Callback
<void(const ParseNode
*)>& callback
,
413 if (async_load_file_
.is_null()) {
414 return g_scheduler
->input_file_manager()->AsyncLoadFile(
415 origin
, build_settings
, file_name
, callback
, err
);
417 return async_load_file_
.Run(
418 origin
, build_settings
, file_name
, callback
, err
);