Roll src/third_party/WebKit f36d5e0:68b67cd (svn 193299:193303)
[chromium-blink-merge.git] / tools / gn / loader.cc
blob5f5324b15e630ff76cb63eba841d40e5fb38017a
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"
7 #include "base/bind.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"
22 namespace {
24 struct SourceFileAndOrigin {
25 SourceFileAndOrigin(const SourceFile& f, const LocationRange& o)
26 : file(f),
27 origin(o) {
30 SourceFile file;
31 LocationRange origin;
34 } // namespace
36 // Identifies one time a file is loaded in a given toolchain so we don't load
37 // it more than once.
38 struct LoaderImpl::LoadID {
39 LoadID() {}
40 LoadID(const SourceFile& f, const Label& tc_name)
41 : file(f),
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;
51 SourceFile file;
52 Label toolchain_name;
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);
72 Settings settings;
74 bool is_toolchain_loaded;
75 bool is_config_loaded;
77 std::vector<SourceFileAndOrigin> waiting_on_me;
80 // -----------------------------------------------------------------------------
82 const void* Loader::kDefaultToolchainKey = &kDefaultToolchainKey;
84 Loader::Loader() {
87 Loader::~Loader() {
90 void Loader::Load(const Label& label, const LocationRange& origin) {
91 Load(BuildFileForLabel(label), origin, label.GetToolchainLabel());
94 // static
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()),
103 pending_loads_(0),
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());
137 return;
140 ToolchainRecord* record;
141 if (toolchain_name.is_null())
142 record = toolchain_records_[default_toolchain_label_];
143 else
144 record = toolchain_records_[toolchain_name];
146 if (!record) {
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);
160 else
161 record->waiting_on_me.push_back(SourceFileAndOrigin(file, origin));
164 void LoaderImpl::ToolchainLoaded(const Toolchain* toolchain) {
165 ToolchainRecord* record = toolchain_records_[toolchain->label()];
166 if (!record) {
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());
179 } else {
180 // There should be nobody waiting on this if the build config is already
181 // loaded.
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())
194 return nullptr;
195 found_toolchain = toolchain_records_.find(default_toolchain_label_);
196 } else {
197 found_toolchain = toolchain_records_.find(label);
200 if (found_toolchain == toolchain_records_.end())
201 return nullptr;
202 return &found_toolchain->second->settings;
205 void LoaderImpl::ScheduleLoadFile(const Settings* settings,
206 const LocationRange& origin,
207 const SourceFile& file) {
208 Err err;
209 pending_loads_++;
210 if (!AsyncLoadFile(origin, settings->build_settings(), file,
211 base::Bind(&LoaderImpl::BackgroundLoadFile, this,
212 settings, file),
213 &err)) {
214 g_scheduler->FailWithError(err);
215 DecrementPendingLoads();
219 void LoaderImpl::ScheduleLoadBuildConfig(
220 Settings* settings,
221 const Scope::KeyValueMap& toolchain_overrides) {
222 Err err;
223 pending_loads_++;
224 if (!AsyncLoadFile(LocationRange(), settings->build_settings(),
225 settings->build_settings()->build_config_file(),
226 base::Bind(&LoaderImpl::BackgroundLoadBuildConfig,
227 this, settings, toolchain_overrides),
228 &err)) {
229 g_scheduler->FailWithError(err);
230 DecrementPendingLoads();
234 void LoaderImpl::BackgroundLoadFile(const Settings* settings,
235 const SourceFile& file_name,
236 const ParseNode* root) {
237 if (!root) {
238 main_loop_->PostTask(FROM_HERE,
239 base::Bind(&LoaderImpl::DecrementPendingLoads, this));
240 return;
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());
259 Err err;
260 root->Execute(&our_scope, &err);
261 if (err.has_error())
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));
270 item = nullptr;
273 trace.Done();
275 main_loop_->PostTask(FROM_HERE, base::Bind(&LoaderImpl::DidLoadFile, this));
278 void LoaderImpl::BackgroundLoadBuildConfig(
279 Settings* settings,
280 const Scope::KeyValueMap& toolchain_overrides,
281 const ParseNode* root) {
282 if (!root) {
283 main_loop_->PostTask(FROM_HERE,
284 base::Bind(&LoaderImpl::DecrementPendingLoads, this));
285 return;
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());
305 Err err;
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();
313 trace.Done();
315 if (err.has_error())
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
321 // file.
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."));
327 } else {
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
372 // is OK.
373 LoadIDSet old_loads;
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));
379 } else {
380 // Can keep the old one.
381 invocations_.insert(load);
384 } else {
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);
402 pending_loads_--;
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,
412 Err* err) {
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);