blob: ba214c2166d441d296602bd1e238643c515e382f [file] [log] [blame]
[email protected]26542b02013-11-08 23:25:041// 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.
Dirk Prankec9126ec982017-08-17 15:05:074
[email protected]26542b02013-11-08 23:25:045#include "tools/gn/loader.h"
6
7#include "base/bind.h"
dchenga500b692016-04-08 19:55:428#include "base/memory/ptr_util.h"
fdorayf0544fc2016-07-18 16:05:159#include "base/threading/thread_task_runner_handle.h"
[email protected]26542b02013-11-08 23:25:0410#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"
21
[email protected]a59f7132014-06-10 17:48:3922namespace {
23
24struct SourceFileAndOrigin {
25 SourceFileAndOrigin(const SourceFile& f, const LocationRange& o)
26 : file(f),
27 origin(o) {
28 }
29
30 SourceFile file;
31 LocationRange origin;
32};
33
34} // namespace
35
[email protected]26542b02013-11-08 23:25:0436// Identifies one time a file is loaded in a given toolchain so we don't load
37// it more than once.
38struct LoaderImpl::LoadID {
39 LoadID() {}
40 LoadID(const SourceFile& f, const Label& tc_name)
41 : file(f),
42 toolchain_name(tc_name) {
43 }
44
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;
49 }
50
51 SourceFile file;
52 Label toolchain_name;
53};
54
55// Our tracking information for a toolchain.
56struct 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);
70 }
71
72 Settings settings;
73
74 bool is_toolchain_loaded;
75 bool is_config_loaded;
76
[email protected]a59f7132014-06-10 17:48:3977 std::vector<SourceFileAndOrigin> waiting_on_me;
[email protected]26542b02013-11-08 23:25:0478};
79
80// -----------------------------------------------------------------------------
81
thestig3b6a2f12015-09-25 08:17:2082const void* const Loader::kDefaultToolchainKey = &kDefaultToolchainKey;
[email protected]26542b02013-11-08 23:25:0483
84Loader::Loader() {
85}
86
87Loader::~Loader() {
88}
89
[email protected]a59f7132014-06-10 17:48:3990void Loader::Load(const Label& label, const LocationRange& origin) {
91 Load(BuildFileForLabel(label), origin, label.GetToolchainLabel());
[email protected]26542b02013-11-08 23:25:0492}
93
94// static
95SourceFile Loader::BuildFileForLabel(const Label& label) {
96 return SourceFile(label.dir().value() + "BUILD.gn");
97}
98
99// -----------------------------------------------------------------------------
100
101LoaderImpl::LoaderImpl(const BuildSettings* build_settings)
fdorayf0544fc2016-07-18 16:05:15102 : pending_loads_(0), build_settings_(build_settings) {
103 // There may not be an active TaskRunner at this point. When that's the case,
104 // the calling code is expected to call set_task_runner().
105 if (base::ThreadTaskRunnerHandle::IsSet())
106 task_runner_ = base::ThreadTaskRunnerHandle::Get();
[email protected]26542b02013-11-08 23:25:04107}
108
109LoaderImpl::~LoaderImpl() {
[email protected]26542b02013-11-08 23:25:04110}
111
112void LoaderImpl::Load(const SourceFile& file,
[email protected]a59f7132014-06-10 17:48:39113 const LocationRange& origin,
114 const Label& in_toolchain_name) {
[email protected]26542b02013-11-08 23:25:04115 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.
120
121 if (toolchain_records_.empty()) {
jbromanf9b6b5a2016-04-18 20:59:38122 // Nothing loaded, need to load the default build config. The initial load
[email protected]26542b02013-11-08 23:25:04123 // should not specify a toolchain.
124 DCHECK(toolchain_name.is_null());
125
jbromanf9b6b5a2016-04-18 20:59:38126 std::unique_ptr<ToolchainRecord> new_record(
127 new ToolchainRecord(build_settings_, Label(), Label()));
128 ToolchainRecord* record = new_record.get();
129 toolchain_records_[Label()] = std::move(new_record);
[email protected]26542b02013-11-08 23:25:04130
131 // The default build config is no dependent on the toolchain definition,
132 // since we need to load the build config before we know what the default
133 // toolchain name is.
134 record->is_toolchain_loaded = true;
135
[email protected]a59f7132014-06-10 17:48:39136 record->waiting_on_me.push_back(SourceFileAndOrigin(file, origin));
[email protected]26542b02013-11-08 23:25:04137 ScheduleLoadBuildConfig(&record->settings, Scope::KeyValueMap());
jbromanf9b6b5a2016-04-18 20:59:38138
[email protected]26542b02013-11-08 23:25:04139 return;
140 }
141
142 ToolchainRecord* record;
143 if (toolchain_name.is_null())
jbromanf9b6b5a2016-04-18 20:59:38144 record = toolchain_records_[default_toolchain_label_].get();
[email protected]26542b02013-11-08 23:25:04145 else
jbromanf9b6b5a2016-04-18 20:59:38146 record = toolchain_records_[toolchain_name].get();
[email protected]26542b02013-11-08 23:25:04147
148 if (!record) {
149 DCHECK(!default_toolchain_label_.is_null());
150
151 // No reference to this toolchain found yet, make one.
jbromanf9b6b5a2016-04-18 20:59:38152 std::unique_ptr<ToolchainRecord> new_record(new ToolchainRecord(
153 build_settings_, toolchain_name, default_toolchain_label_));
154 record = new_record.get();
155 toolchain_records_[toolchain_name] = std::move(new_record);
[email protected]26542b02013-11-08 23:25:04156
157 // Schedule a load of the toolchain using the default one.
[email protected]a59f7132014-06-10 17:48:39158 Load(BuildFileForLabel(toolchain_name), origin, default_toolchain_label_);
[email protected]26542b02013-11-08 23:25:04159 }
160
161 if (record->is_config_loaded)
[email protected]a59f7132014-06-10 17:48:39162 ScheduleLoadFile(&record->settings, origin, file);
[email protected]26542b02013-11-08 23:25:04163 else
[email protected]a59f7132014-06-10 17:48:39164 record->waiting_on_me.push_back(SourceFileAndOrigin(file, origin));
[email protected]26542b02013-11-08 23:25:04165}
166
167void LoaderImpl::ToolchainLoaded(const Toolchain* toolchain) {
jbromanf9b6b5a2016-04-18 20:59:38168 ToolchainRecord* record = toolchain_records_[toolchain->label()].get();
[email protected]26542b02013-11-08 23:25:04169 if (!record) {
170 DCHECK(!default_toolchain_label_.is_null());
jbromanf9b6b5a2016-04-18 20:59:38171 std::unique_ptr<ToolchainRecord> new_record(new ToolchainRecord(
172 build_settings_, toolchain->label(), default_toolchain_label_));
173 record = new_record.get();
174 toolchain_records_[toolchain->label()] = std::move(new_record);
[email protected]26542b02013-11-08 23:25:04175 }
176 record->is_toolchain_loaded = true;
177
178 // The default build config is loaded first, then its toolchain. Secondary
179 // ones are loaded in the opposite order so we can pass toolchain parameters
180 // to the build config. So we may or may not have a config at this point.
181 if (!record->is_config_loaded) {
182 ScheduleLoadBuildConfig(&record->settings, toolchain->args());
183 } else {
184 // There should be nobody waiting on this if the build config is already
185 // loaded.
186 DCHECK(record->waiting_on_me.empty());
187 }
188}
189
190Label LoaderImpl::GetDefaultToolchain() const {
191 return default_toolchain_label_;
192}
193
[email protected]003361c2014-02-12 11:27:00194const Settings* LoaderImpl::GetToolchainSettings(const Label& label) const {
195 ToolchainRecordMap::const_iterator found_toolchain;
[email protected]26542b02013-11-08 23:25:04196 if (label.is_null()) {
197 if (default_toolchain_label_.is_null())
tfarina9b636af2014-12-23 00:52:07198 return nullptr;
[email protected]26542b02013-11-08 23:25:04199 found_toolchain = toolchain_records_.find(default_toolchain_label_);
200 } else {
201 found_toolchain = toolchain_records_.find(label);
202 }
203
204 if (found_toolchain == toolchain_records_.end())
tfarina9b636af2014-12-23 00:52:07205 return nullptr;
[email protected]26542b02013-11-08 23:25:04206 return &found_toolchain->second->settings;
207}
208
209void LoaderImpl::ScheduleLoadFile(const Settings* settings,
[email protected]a59f7132014-06-10 17:48:39210 const LocationRange& origin,
[email protected]26542b02013-11-08 23:25:04211 const SourceFile& file) {
212 Err err;
213 pending_loads_++;
[email protected]a59f7132014-06-10 17:48:39214 if (!AsyncLoadFile(origin, settings->build_settings(), file,
[email protected]26542b02013-11-08 23:25:04215 base::Bind(&LoaderImpl::BackgroundLoadFile, this,
agrievedbecf022016-04-22 00:58:08216 settings, file, origin),
[email protected]26542b02013-11-08 23:25:04217 &err)) {
218 g_scheduler->FailWithError(err);
219 DecrementPendingLoads();
220 }
221}
222
223void LoaderImpl::ScheduleLoadBuildConfig(
224 Settings* settings,
225 const Scope::KeyValueMap& toolchain_overrides) {
226 Err err;
227 pending_loads_++;
228 if (!AsyncLoadFile(LocationRange(), settings->build_settings(),
229 settings->build_settings()->build_config_file(),
230 base::Bind(&LoaderImpl::BackgroundLoadBuildConfig,
231 this, settings, toolchain_overrides),
232 &err)) {
233 g_scheduler->FailWithError(err);
234 DecrementPendingLoads();
235 }
236}
237
238void LoaderImpl::BackgroundLoadFile(const Settings* settings,
239 const SourceFile& file_name,
agrievedbecf022016-04-22 00:58:08240 const LocationRange& origin,
[email protected]26542b02013-11-08 23:25:04241 const ParseNode* root) {
242 if (!root) {
fdorayf0544fc2016-07-18 16:05:15243 task_runner_->PostTask(
fdoray01c8157e2016-06-22 18:49:15244 FROM_HERE, base::Bind(&LoaderImpl::DecrementPendingLoads, this));
[email protected]26542b02013-11-08 23:25:04245 return;
246 }
247
248 if (g_scheduler->verbose_logging()) {
249 g_scheduler->Log("Running", file_name.value() + " with toolchain " +
250 settings->toolchain_label().GetUserVisibleName(false));
251 }
252
253 Scope our_scope(settings->base_config());
[email protected]3aa6f812014-04-08 22:35:18254 ScopePerFileProvider per_file_provider(&our_scope, true);
[email protected]26542b02013-11-08 23:25:04255 our_scope.set_source_dir(file_name.GetDir());
256
[email protected]e1bd79f2014-05-05 20:27:53257 // Targets, etc. generated as part of running this file will end up here.
258 Scope::ItemVector collected_items;
259 our_scope.set_item_collector(&collected_items);
260
[email protected]26542b02013-11-08 23:25:04261 ScopedTrace trace(TraceItem::TRACE_FILE_EXECUTE, file_name.value());
262 trace.SetToolchain(settings->toolchain_label());
263
264 Err err;
265 root->Execute(&our_scope, &err);
agrievedbecf022016-04-22 00:58:08266 if (!err.has_error())
267 our_scope.CheckForUnusedVars(&err);
[email protected]26542b02013-11-08 23:25:04268
agrievedbecf022016-04-22 00:58:08269 if (err.has_error()) {
270 if (!origin.is_null())
271 err.AppendSubErr(Err(origin, "which caused the file to be included."));
[email protected]afb30e32014-05-16 19:31:36272 g_scheduler->FailWithError(err);
agrievedbecf022016-04-22 00:58:08273 }
274
[email protected]afb30e32014-05-16 19:31:36275
[email protected]e1bd79f2014-05-05 20:27:53276 // Pass all of the items that were defined off to the builder.
avif6016b02017-07-06 21:24:13277 for (auto& item : collected_items)
278 settings->build_settings()->ItemDefined(std::move(item));
[email protected]e1bd79f2014-05-05 20:27:53279
[email protected]26542b02013-11-08 23:25:04280 trace.Done();
281
fdorayf0544fc2016-07-18 16:05:15282 task_runner_->PostTask(FROM_HERE, base::Bind(&LoaderImpl::DidLoadFile, this));
[email protected]26542b02013-11-08 23:25:04283}
284
285void LoaderImpl::BackgroundLoadBuildConfig(
286 Settings* settings,
287 const Scope::KeyValueMap& toolchain_overrides,
288 const ParseNode* root) {
289 if (!root) {
fdorayf0544fc2016-07-18 16:05:15290 task_runner_->PostTask(
fdoray01c8157e2016-06-22 18:49:15291 FROM_HERE, base::Bind(&LoaderImpl::DecrementPendingLoads, this));
[email protected]26542b02013-11-08 23:25:04292 return;
293 }
294
295 Scope* base_config = settings->base_config();
296 base_config->set_source_dir(SourceDir("//"));
297
298 settings->build_settings()->build_args().SetupRootScope(
299 base_config, toolchain_overrides);
300
301 base_config->SetProcessingBuildConfig();
302
303 // See kDefaultToolchainKey in the header.
304 Label default_toolchain_label;
305 if (settings->is_default())
306 base_config->SetProperty(kDefaultToolchainKey, &default_toolchain_label);
307
308 ScopedTrace trace(TraceItem::TRACE_FILE_EXECUTE,
309 settings->build_settings()->build_config_file().value());
310 trace.SetToolchain(settings->toolchain_label());
311
[email protected]26542b02013-11-08 23:25:04312 Err err;
mdempsky3ad7aeb32015-03-17 22:38:02313 root->Execute(base_config, &err);
[email protected]26542b02013-11-08 23:25:04314
[email protected]afb30e32014-05-16 19:31:36315 // Clear all private variables left in the scope. We want the root build
316 // config to be like a .gni file in that variables beginning with an
317 // underscore aren't exported.
318 base_config->RemovePrivateIdentifiers();
319
[email protected]26542b02013-11-08 23:25:04320 trace.Done();
321
[email protected]42b013f2013-12-16 17:58:16322 if (err.has_error())
323 g_scheduler->FailWithError(err);
324
[email protected]26542b02013-11-08 23:25:04325 base_config->ClearProcessingBuildConfig();
326 if (settings->is_default()) {
327 // The default toolchain must have been set in the default build config
328 // file.
329 if (default_toolchain_label.is_null()) {
330 g_scheduler->FailWithError(Err(Location(),
331 "The default build config file did not call set_default_toolchain()",
332 "If you don't call this, I can't figure out what toolchain to use\n"
333 "for all of this code."));
334 } else {
335 DCHECK(settings->toolchain_label().is_null());
336 settings->set_toolchain_label(default_toolchain_label);
337 }
338 }
339
fdorayf0544fc2016-07-18 16:05:15340 task_runner_->PostTask(FROM_HERE,
341 base::Bind(&LoaderImpl::DidLoadBuildConfig, this,
342 settings->toolchain_label()));
[email protected]26542b02013-11-08 23:25:04343}
344
345void LoaderImpl::DidLoadFile() {
346 DecrementPendingLoads();
347}
348
349void LoaderImpl::DidLoadBuildConfig(const Label& label) {
350 // Do not return early, we must call DecrementPendingLoads() at the bottom.
351
352 ToolchainRecordMap::iterator found_toolchain = toolchain_records_.find(label);
tfarina9b636af2014-12-23 00:52:07353 ToolchainRecord* record = nullptr;
[email protected]26542b02013-11-08 23:25:04354 if (found_toolchain == toolchain_records_.end()) {
355 // When loading the default build config, we'll insert it into the record
356 // map with an empty label since we don't yet know what to call it.
357 //
358 // In this case, we should have exactly one entry in the map with an empty
359 // label. We now need to fix up the naming so it refers to the "real" one.
thestig3b6a2f12015-09-25 08:17:20360 CHECK_EQ(1U, toolchain_records_.size());
[email protected]26542b02013-11-08 23:25:04361 ToolchainRecordMap::iterator empty_label = toolchain_records_.find(Label());
362 CHECK(empty_label != toolchain_records_.end());
363
364 // Fix up the toolchain record.
jbromanf9b6b5a2016-04-18 20:59:38365 std::unique_ptr<ToolchainRecord> moved_record =
366 std::move(empty_label->second);
367 record = moved_record.get();
368 toolchain_records_[label] = std::move(moved_record);
[email protected]26542b02013-11-08 23:25:04369 toolchain_records_.erase(empty_label);
370
371 // Save the default toolchain label.
372 default_toolchain_label_ = label;
373 DCHECK(record->settings.default_toolchain_label().is_null());
374 record->settings.set_default_toolchain_label(label);
375
376 // The settings object should have the toolchain label already set.
377 DCHECK(!record->settings.toolchain_label().is_null());
378
379 // Update any stored invocations that refer to the empty toolchain label.
380 // This will normally only be one, for the root build file, so brute-force
381 // is OK.
382 LoadIDSet old_loads;
383 invocations_.swap(old_loads);
brettwd1033b62014-09-30 21:44:05384 for (const auto& load : old_loads) {
385 if (load.toolchain_name.is_null()) {
[email protected]26542b02013-11-08 23:25:04386 // Fix up toolchain label
brettwd1033b62014-09-30 21:44:05387 invocations_.insert(LoadID(load.file, label));
[email protected]26542b02013-11-08 23:25:04388 } else {
389 // Can keep the old one.
brettwd1033b62014-09-30 21:44:05390 invocations_.insert(load);
[email protected]26542b02013-11-08 23:25:04391 }
392 }
393 } else {
jbromanf9b6b5a2016-04-18 20:59:38394 record = found_toolchain->second.get();
[email protected]26542b02013-11-08 23:25:04395 }
396
397 DCHECK(!record->is_config_loaded);
398 DCHECK(record->is_toolchain_loaded);
399 record->is_config_loaded = true;
400
401 // Schedule all waiting file loads.
brettwd1033b62014-09-30 21:44:05402 for (const auto& waiting : record->waiting_on_me)
403 ScheduleLoadFile(&record->settings, waiting.origin, waiting.file);
[email protected]26542b02013-11-08 23:25:04404 record->waiting_on_me.clear();
405
406 DecrementPendingLoads();
407}
408
409void LoaderImpl::DecrementPendingLoads() {
thestig3b6a2f12015-09-25 08:17:20410 DCHECK_GT(pending_loads_, 0);
[email protected]26542b02013-11-08 23:25:04411 pending_loads_--;
412 if (pending_loads_ == 0 && !complete_callback_.is_null())
413 complete_callback_.Run();
414}
415
416bool LoaderImpl::AsyncLoadFile(
417 const LocationRange& origin,
418 const BuildSettings* build_settings,
419 const SourceFile& file_name,
420 const base::Callback<void(const ParseNode*)>& callback,
421 Err* err) {
422 if (async_load_file_.is_null()) {
423 return g_scheduler->input_file_manager()->AsyncLoadFile(
424 origin, build_settings, file_name, callback, err);
425 }
426 return async_load_file_.Run(
427 origin, build_settings, file_name, callback, err);
428}