• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "gn/loader.h"
6 
7 #include <memory>
8 
9 #include "gn/build_settings.h"
10 #include "gn/err.h"
11 #include "gn/filesystem_utils.h"
12 #include "gn/input_file_manager.h"
13 #include "gn/parse_tree.h"
14 #include "gn/scheduler.h"
15 #include "gn/scope_per_file_provider.h"
16 #include "gn/settings.h"
17 #include "gn/source_dir.h"
18 #include "gn/source_file.h"
19 #include "gn/trace.h"
20 
21 namespace {
22 
23 struct SourceFileAndOrigin {
SourceFileAndOrigin__anona273f6ec0111::SourceFileAndOrigin24   SourceFileAndOrigin(const SourceFile& f, const LocationRange& o)
25       : file(f), origin(o) {}
26 
27   SourceFile file;
28   LocationRange origin;
29 };
30 
31 }  // namespace
32 
33 // Identifies one time a file is loaded in a given toolchain so we don't load
34 // it more than once.
35 struct LoaderImpl::LoadID {
36   LoadID() = default;
LoadIDLoaderImpl::LoadID37   LoadID(const SourceFile& f, const Label& tc_name)
38       : file(f), toolchain_name(tc_name) {}
39 
operator <LoaderImpl::LoadID40   bool operator<(const LoadID& other) const {
41     if (file == other.file)
42       return toolchain_name < other.toolchain_name;
43     return file < other.file;
44   }
45 
46   SourceFile file;
47   Label toolchain_name;
48 };
49 
50 // Our tracking information for a toolchain.
51 struct LoaderImpl::ToolchainRecord {
52   // The default toolchain label can be empty for the first time the default
53   // toolchain is loaded, since we don't know it yet. This will be fixed up
54   // later. It should be valid in all other cases.
ToolchainRecordLoaderImpl::ToolchainRecord55   ToolchainRecord(const BuildSettings* build_settings,
56                   const Label& toolchain_label,
57                   const Label& default_toolchain_label)
58       : settings(
59             build_settings,
60             GetOutputSubdirName(toolchain_label,
61                                 toolchain_label == default_toolchain_label)),
62         is_toolchain_loaded(false),
63         is_config_loaded(false) {
64     settings.set_default_toolchain_label(default_toolchain_label);
65     settings.set_toolchain_label(toolchain_label);
66   }
67 
68   Settings settings;
69 
70   bool is_toolchain_loaded;
71   bool is_config_loaded;
72 
73   std::vector<SourceFileAndOrigin> waiting_on_me;
74 };
75 
76 // -----------------------------------------------------------------------------
77 
78 const void* const Loader::kDefaultToolchainKey = &kDefaultToolchainKey;
79 
80 Loader::Loader() = default;
81 
82 Loader::~Loader() = default;
83 
Load(const Label & label,const LocationRange & origin)84 void Loader::Load(const Label& label, const LocationRange& origin) {
85   Load(BuildFileForLabel(label), origin, label.GetToolchainLabel());
86 }
87 
88 // -----------------------------------------------------------------------------
89 
LoaderImpl(const BuildSettings * build_settings)90 LoaderImpl::LoaderImpl(const BuildSettings* build_settings)
91     : pending_loads_(0), build_settings_(build_settings) {
92   // There may not be an active TaskRunner at this point. When that's the case,
93   // the calling code is expected to call set_task_runner().
94   task_runner_ = MsgLoop::Current();
95 }
96 
97 LoaderImpl::~LoaderImpl() = default;
98 
Load(const SourceFile & file,const LocationRange & origin,const Label & in_toolchain_name)99 void LoaderImpl::Load(const SourceFile& file,
100                       const LocationRange& origin,
101                       const Label& in_toolchain_name) {
102   const Label& toolchain_name = in_toolchain_name.is_null()
103                                     ? default_toolchain_label_
104                                     : in_toolchain_name;
105   LoadID load_id(file, toolchain_name);
106   if (!invocations_.insert(load_id).second)
107     return;  // Already in set, so this file was already loaded or schedulerd.
108 
109   if (toolchain_records_.empty()) {
110     // Nothing loaded, need to load the default build config. The initial load
111     // should not specify a toolchain.
112     DCHECK(toolchain_name.is_null());
113 
114     std::unique_ptr<ToolchainRecord> new_record =
115         std::make_unique<ToolchainRecord>(build_settings_, Label(), Label());
116     ToolchainRecord* record = new_record.get();
117     Label empty_label;  // VS issues spurious warning using ...[Label()].
118     toolchain_records_[empty_label] = std::move(new_record);
119 
120     // The default build config is no dependent on the toolchain definition,
121     // since we need to load the build config before we know what the default
122     // toolchain name is.
123     record->is_toolchain_loaded = true;
124 
125     record->waiting_on_me.push_back(SourceFileAndOrigin(file, origin));
126     ScheduleLoadBuildConfig(&record->settings, Scope::KeyValueMap());
127 
128     return;
129   }
130 
131   ToolchainRecord* record;
132   if (toolchain_name.is_null())
133     record = toolchain_records_[default_toolchain_label_].get();
134   else
135     record = toolchain_records_[toolchain_name].get();
136 
137   if (!record) {
138     DCHECK(!default_toolchain_label_.is_null());
139 
140     // No reference to this toolchain found yet, make one.
141     std::unique_ptr<ToolchainRecord> new_record =
142         std::make_unique<ToolchainRecord>(build_settings_, toolchain_name,
143                                           default_toolchain_label_);
144     record = new_record.get();
145     toolchain_records_[toolchain_name] = std::move(new_record);
146 
147     // Schedule a load of the toolchain using the default one.
148     Load(BuildFileForLabel(toolchain_name), origin, default_toolchain_label_);
149   }
150 
151   if (record->is_config_loaded)
152     ScheduleLoadFile(&record->settings, origin, file);
153   else
154     record->waiting_on_me.push_back(SourceFileAndOrigin(file, origin));
155 }
156 
ToolchainLoaded(const Toolchain * toolchain)157 void LoaderImpl::ToolchainLoaded(const Toolchain* toolchain) {
158   ToolchainRecord* record = toolchain_records_[toolchain->label()].get();
159   if (!record) {
160     DCHECK(!default_toolchain_label_.is_null());
161     std::unique_ptr<ToolchainRecord> new_record =
162         std::make_unique<ToolchainRecord>(build_settings_, toolchain->label(),
163                                           default_toolchain_label_);
164     record = new_record.get();
165     toolchain_records_[toolchain->label()] = std::move(new_record);
166   }
167   record->is_toolchain_loaded = true;
168 
169   // The default build config is loaded first, then its toolchain. Secondary
170   // ones are loaded in the opposite order so we can pass toolchain parameters
171   // to the build config. So we may or may not have a config at this point.
172   if (!record->is_config_loaded) {
173     ScheduleLoadBuildConfig(&record->settings, toolchain->args());
174   } else {
175     // There should be nobody waiting on this if the build config is already
176     // loaded.
177     DCHECK(record->waiting_on_me.empty());
178   }
179 }
180 
GetDefaultToolchain() const181 Label LoaderImpl::GetDefaultToolchain() const {
182   return default_toolchain_label_;
183 }
184 
GetToolchainSettings(const Label & label) const185 const Settings* LoaderImpl::GetToolchainSettings(const Label& label) const {
186   ToolchainRecordMap::const_iterator found_toolchain;
187   if (label.is_null()) {
188     if (default_toolchain_label_.is_null())
189       return nullptr;
190     found_toolchain = toolchain_records_.find(default_toolchain_label_);
191   } else {
192     found_toolchain = toolchain_records_.find(label);
193   }
194 
195   if (found_toolchain == toolchain_records_.end())
196     return nullptr;
197   return &found_toolchain->second->settings;
198 }
199 
BuildFileForLabel(const Label & label) const200 SourceFile LoaderImpl::BuildFileForLabel(const Label& label) const {
201   return SourceFile(
202       label.dir().value() + "BUILD" + build_file_extension_ + ".gn");
203 }
204 
ScheduleLoadFile(const Settings * settings,const LocationRange & origin,const SourceFile & file)205 void LoaderImpl::ScheduleLoadFile(const Settings* settings,
206                                   const LocationRange& origin,
207                                   const SourceFile& file) {
208   Err err;
209   pending_loads_++;
210   if (!AsyncLoadFile(
211           origin, settings->build_settings(), file,
212           [this, settings, file, origin](const ParseNode* parse_node) {
213             BackgroundLoadFile(settings, file, origin, parse_node);
214           },
215           &err)) {
216     g_scheduler->FailWithError(err);
217     DecrementPendingLoads();
218   }
219 }
220 
ScheduleLoadBuildConfig(Settings * settings,const Scope::KeyValueMap & toolchain_overrides)221 void LoaderImpl::ScheduleLoadBuildConfig(
222     Settings* settings,
223     const Scope::KeyValueMap& toolchain_overrides) {
224   Err err;
225   pending_loads_++;
226   if (!AsyncLoadFile(
227           LocationRange(), settings->build_settings(),
228           settings->build_settings()->build_config_file(),
229           [this, settings, toolchain_overrides](const ParseNode* root) {
230             BackgroundLoadBuildConfig(settings, toolchain_overrides, root);
231           },
232           &err)) {
233     g_scheduler->FailWithError(err);
234     DecrementPendingLoads();
235   }
236 }
237 
BackgroundLoadFile(const Settings * settings,const SourceFile & file_name,const LocationRange & origin,const ParseNode * root)238 void LoaderImpl::BackgroundLoadFile(const Settings* settings,
239                                     const SourceFile& file_name,
240                                     const LocationRange& origin,
241                                     const ParseNode* root) {
242   if (!root) {
243     task_runner_->PostTask([this]() { DecrementPendingLoads(); });
244     return;
245   }
246 
247   if (g_scheduler->verbose_logging()) {
248     g_scheduler->Log("Running",
249                      file_name.value() + " with toolchain " +
250                          settings->toolchain_label().GetUserVisibleName(false));
251   }
252 
253   Scope our_scope(settings->base_config());
254   ScopePerFileProvider per_file_provider(&our_scope, true);
255   our_scope.set_source_dir(file_name.GetDir());
256   our_scope.AddBuildDependencyFile(file_name);
257 
258   // Targets, etc. generated as part of running this file will end up here.
259   Scope::ItemVector collected_items;
260   our_scope.set_item_collector(&collected_items);
261 
262   ScopedTrace trace(TraceItem::TRACE_FILE_EXECUTE, file_name.value());
263   trace.SetToolchain(settings->toolchain_label());
264 
265   Err err;
266   root->Execute(&our_scope, &err);
267   if (!err.has_error())
268     our_scope.CheckForUnusedVars(&err);
269 
270   if (err.has_error()) {
271     if (!origin.is_null())
272       err.AppendSubErr(Err(origin, "which caused the file to be included."));
273 
274     if (!settings->is_default())
275       err.set_toolchain_label(settings->toolchain_label());
276 
277     g_scheduler->FailWithError(err);
278   }
279 
280   // Pass all of the items that were defined off to the builder.
281   for (auto& item : collected_items)
282     settings->build_settings()->ItemDefined(std::move(item));
283 
284   trace.Done();
285 
286   task_runner_->PostTask([this]() { DidLoadFile(); });
287 }
288 
BackgroundLoadBuildConfig(Settings * settings,const Scope::KeyValueMap & toolchain_overrides,const ParseNode * root)289 void LoaderImpl::BackgroundLoadBuildConfig(
290     Settings* settings,
291     const Scope::KeyValueMap& toolchain_overrides,
292     const ParseNode* root) {
293   if (!root) {
294     task_runner_->PostTask([this]() { DecrementPendingLoads(); });
295     return;
296   }
297 
298   Scope* base_config = settings->base_config();
299   base_config->set_source_dir(SourceDir("//"));
300   base_config->AddBuildDependencyFile(
301       settings->build_settings()->build_config_file());
302 
303   settings->build_settings()->build_args().SetupRootScope(base_config,
304                                                           toolchain_overrides);
305 
306   base_config->SetProcessingBuildConfig();
307 
308   // See kDefaultToolchainKey in the header.
309   Label default_toolchain_label;
310   if (settings->is_default())
311     base_config->SetProperty(kDefaultToolchainKey, &default_toolchain_label);
312 
313   ScopedTrace trace(TraceItem::TRACE_FILE_EXECUTE,
314                     settings->build_settings()->build_config_file().value());
315   trace.SetToolchain(settings->toolchain_label());
316 
317   // Run the BUILDCONFIG with its directory as the current one. We want
318   // BUILDCONFIG to modify the base_config so can't make a copy or a nested one.
319   base_config->set_source_dir(
320       settings->build_settings()->build_config_file().GetDir());
321 
322   Err err;
323   root->Execute(base_config, &err);
324 
325   // Put back the root as the default source dir. This probably isn't necessary
326   // as other scopes will set their directories to their own path, but it's a
327   // better default than the build config's directory.
328   base_config->set_source_dir(SourceDir("//"));
329 
330   // Clear all private variables left in the scope. We want the root build
331   // config to be like a .gni file in that variables beginning with an
332   // underscore aren't exported.
333   base_config->RemovePrivateIdentifiers();
334 
335   trace.Done();
336 
337   if (err.has_error()) {
338     if (!settings->is_default())
339       err.set_toolchain_label(settings->toolchain_label());
340 
341     g_scheduler->FailWithError(err);
342   }
343 
344   base_config->ClearProcessingBuildConfig();
345   if (settings->is_default()) {
346     // The default toolchain must have been set in the default build config
347     // file.
348     if (default_toolchain_label.is_null()) {
349       g_scheduler->FailWithError(Err(
350           Location(),
351           "The default build config file did not call set_default_toolchain()",
352           "If you don't call this, I can't figure out what toolchain to use\n"
353           "for all of this code."));
354     } else {
355       DCHECK(settings->toolchain_label().is_null());
356       settings->set_toolchain_label(default_toolchain_label);
357     }
358   }
359 
360   task_runner_->PostTask(
361       [this, toolchain_label = settings->toolchain_label()]() {
362         DidLoadBuildConfig(toolchain_label);
363       });
364 }
365 
DidLoadFile()366 void LoaderImpl::DidLoadFile() {
367   DecrementPendingLoads();
368 }
369 
DidLoadBuildConfig(const Label & label)370 void LoaderImpl::DidLoadBuildConfig(const Label& label) {
371   // Do not return early, we must call DecrementPendingLoads() at the bottom.
372 
373   ToolchainRecordMap::iterator found_toolchain = toolchain_records_.find(label);
374   ToolchainRecord* record = nullptr;
375   if (found_toolchain == toolchain_records_.end()) {
376     // When loading the default build config, we'll insert it into the record
377     // map with an empty label since we don't yet know what to call it.
378     //
379     // In this case, we should have exactly one entry in the map with an empty
380     // label. We now need to fix up the naming so it refers to the "real" one.
381     CHECK_EQ(1U, toolchain_records_.size());
382     ToolchainRecordMap::iterator empty_label = toolchain_records_.find(Label());
383     CHECK(empty_label != toolchain_records_.end());
384 
385     // Fix up the toolchain record.
386     std::unique_ptr<ToolchainRecord> moved_record =
387         std::move(empty_label->second);
388     record = moved_record.get();
389     toolchain_records_[label] = std::move(moved_record);
390     toolchain_records_.erase(empty_label);
391 
392     // Save the default toolchain label.
393     default_toolchain_label_ = label;
394     DCHECK(record->settings.default_toolchain_label().is_null());
395     record->settings.set_default_toolchain_label(label);
396 
397     // The settings object should have the toolchain label already set.
398     DCHECK(!record->settings.toolchain_label().is_null());
399 
400     // Update any stored invocations that refer to the empty toolchain label.
401     // This will normally only be one, for the root build file, so brute-force
402     // is OK.
403     LoadIDSet old_loads;
404     invocations_.swap(old_loads);
405     for (const auto& load : old_loads) {
406       if (load.toolchain_name.is_null()) {
407         // Fix up toolchain label
408         invocations_.emplace(load.file, label);
409       } else {
410         // Can keep the old one.
411         invocations_.insert(load);
412       }
413     }
414   } else {
415     record = found_toolchain->second.get();
416   }
417 
418   DCHECK(!record->is_config_loaded);
419   DCHECK(record->is_toolchain_loaded);
420   record->is_config_loaded = true;
421 
422   // Schedule all waiting file loads.
423   for (const auto& waiting : record->waiting_on_me)
424     ScheduleLoadFile(&record->settings, waiting.origin, waiting.file);
425   record->waiting_on_me.clear();
426 
427   DecrementPendingLoads();
428 }
429 
DecrementPendingLoads()430 void LoaderImpl::DecrementPendingLoads() {
431   DCHECK_GT(pending_loads_, 0);
432   pending_loads_--;
433   if (pending_loads_ == 0 && complete_callback_)
434     complete_callback_();
435 }
436 
AsyncLoadFile(const LocationRange & origin,const BuildSettings * build_settings,const SourceFile & file_name,std::function<void (const ParseNode *)> callback,Err * err)437 bool LoaderImpl::AsyncLoadFile(const LocationRange& origin,
438                                const BuildSettings* build_settings,
439                                const SourceFile& file_name,
440                                std::function<void(const ParseNode*)> callback,
441                                Err* err) {
442   if (async_load_file_) {
443     return async_load_file_(origin, build_settings, file_name,
444                             std::move(callback), err);
445   }
446   return g_scheduler->input_file_manager()->AsyncLoadFile(
447       origin, build_settings, file_name, std::move(callback), err);
448 }
449