• 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 "tools/gn/loader.h"
6 
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"
21 
22 namespace {
23 
24 struct SourceFileAndOrigin {
SourceFileAndOrigin__anon3fb9dd9e0111::SourceFileAndOrigin25   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 
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 {
LoadIDLoaderImpl::LoadID39   LoadID() {}
LoadIDLoaderImpl::LoadID40   LoadID(const SourceFile& f, const Label& tc_name)
41       : file(f),
42         toolchain_name(tc_name) {
43   }
44 
operator <LoaderImpl::LoadID45   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.
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.
ToolchainRecordLoaderImpl::ToolchainRecord60   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 
77   std::vector<SourceFileAndOrigin> waiting_on_me;
78 };
79 
80 // -----------------------------------------------------------------------------
81 
82 const void* Loader::kDefaultToolchainKey = &kDefaultToolchainKey;
83 
Loader()84 Loader::Loader() {
85 }
86 
~Loader()87 Loader::~Loader() {
88 }
89 
Load(const Label & label,const LocationRange & origin)90 void Loader::Load(const Label& label, const LocationRange& origin) {
91   Load(BuildFileForLabel(label), origin, label.GetToolchainLabel());
92 }
93 
94 // static
BuildFileForLabel(const Label & label)95 SourceFile Loader::BuildFileForLabel(const Label& label) {
96   return SourceFile(label.dir().value() + "BUILD.gn");
97 }
98 
99 // -----------------------------------------------------------------------------
100 
LoaderImpl(const BuildSettings * build_settings)101 LoaderImpl::LoaderImpl(const BuildSettings* build_settings)
102     : main_loop_(base::MessageLoop::current()),
103       pending_loads_(0),
104       build_settings_(build_settings) {
105 }
106 
~LoaderImpl()107 LoaderImpl::~LoaderImpl() {
108   STLDeleteContainerPairSecondPointers(toolchain_records_.begin(),
109                                        toolchain_records_.end());
110 }
111 
Load(const SourceFile & file,const LocationRange & origin,const Label & in_toolchain_name)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.
120 
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());
125 
126     ToolchainRecord* record =
127         new ToolchainRecord(build_settings_, Label(), Label());
128     toolchain_records_[Label()] = record;
129 
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;
134 
135     record->waiting_on_me.push_back(SourceFileAndOrigin(file, origin));
136     ScheduleLoadBuildConfig(&record->settings, Scope::KeyValueMap());
137     return;
138   }
139 
140   ToolchainRecord* record;
141   if (toolchain_name.is_null())
142     record = toolchain_records_[default_toolchain_label_];
143   else
144     record = toolchain_records_[toolchain_name];
145 
146   if (!record) {
147     DCHECK(!default_toolchain_label_.is_null());
148 
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;
153 
154     // Schedule a load of the toolchain using the default one.
155     Load(BuildFileForLabel(toolchain_name), origin, default_toolchain_label_);
156   }
157 
158   if (record->is_config_loaded)
159     ScheduleLoadFile(&record->settings, origin, file);
160   else
161     record->waiting_on_me.push_back(SourceFileAndOrigin(file, origin));
162 }
163 
ToolchainLoaded(const Toolchain * toolchain)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;
171   }
172   record->is_toolchain_loaded = true;
173 
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());
183   }
184 }
185 
GetDefaultToolchain() const186 Label LoaderImpl::GetDefaultToolchain() const {
187   return default_toolchain_label_;
188 }
189 
GetToolchainSettings(const Label & label) const190 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 NULL;
195     found_toolchain = toolchain_records_.find(default_toolchain_label_);
196   } else {
197     found_toolchain = toolchain_records_.find(label);
198   }
199 
200   if (found_toolchain == toolchain_records_.end())
201     return NULL;
202   return &found_toolchain->second->settings;
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(origin, settings->build_settings(), file,
211                      base::Bind(&LoaderImpl::BackgroundLoadFile, this,
212                                 settings, file),
213                      &err)) {
214     g_scheduler->FailWithError(err);
215     DecrementPendingLoads();
216   }
217 }
218 
ScheduleLoadBuildConfig(Settings * settings,const Scope::KeyValueMap & toolchain_overrides)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();
231   }
232 }
233 
BackgroundLoadFile(const Settings * settings,const SourceFile & file_name,const ParseNode * root)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;
241   }
242 
243   if (g_scheduler->verbose_logging()) {
244     g_scheduler->Log("Running", file_name.value() + " with toolchain " +
245                      settings->toolchain_label().GetUserVisibleName(false));
246   }
247 
248   Scope our_scope(settings->base_config());
249   ScopePerFileProvider per_file_provider(&our_scope, true);
250   our_scope.set_source_dir(file_name.GetDir());
251 
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);
255 
256   ScopedTrace trace(TraceItem::TRACE_FILE_EXECUTE, file_name.value());
257   trace.SetToolchain(settings->toolchain_label());
258 
259   Err err;
260   root->Execute(&our_scope, &err);
261   if (err.has_error())
262     g_scheduler->FailWithError(err);
263 
264   if (!our_scope.CheckForUnusedVars(&err))
265     g_scheduler->FailWithError(err);
266 
267   // Pass all of the items that were defined off to the builder.
268   for (size_t i = 0; i < collected_items.size(); i++)
269     settings->build_settings()->ItemDefined(collected_items[i]->Pass());
270 
271   trace.Done();
272 
273   main_loop_->PostTask(FROM_HERE, base::Bind(&LoaderImpl::DidLoadFile, this));
274 }
275 
BackgroundLoadBuildConfig(Settings * settings,const Scope::KeyValueMap & toolchain_overrides,const ParseNode * root)276 void LoaderImpl::BackgroundLoadBuildConfig(
277     Settings* settings,
278     const Scope::KeyValueMap& toolchain_overrides,
279     const ParseNode* root) {
280   if (!root) {
281     main_loop_->PostTask(FROM_HERE,
282         base::Bind(&LoaderImpl::DecrementPendingLoads, this));
283     return;
284   }
285 
286   Scope* base_config = settings->base_config();
287   base_config->set_source_dir(SourceDir("//"));
288 
289   settings->build_settings()->build_args().SetupRootScope(
290       base_config, toolchain_overrides);
291 
292   base_config->SetProcessingBuildConfig();
293 
294   // See kDefaultToolchainKey in the header.
295   Label default_toolchain_label;
296   if (settings->is_default())
297     base_config->SetProperty(kDefaultToolchainKey, &default_toolchain_label);
298 
299   ScopedTrace trace(TraceItem::TRACE_FILE_EXECUTE,
300       settings->build_settings()->build_config_file().value());
301   trace.SetToolchain(settings->toolchain_label());
302 
303   const BlockNode* root_block = root->AsBlock();
304   Err err;
305   root_block->ExecuteBlockInScope(base_config, &err);
306 
307   // Clear all private variables left in the scope. We want the root build
308   // config to be like a .gni file in that variables beginning with an
309   // underscore aren't exported.
310   base_config->RemovePrivateIdentifiers();
311 
312   trace.Done();
313 
314   if (err.has_error())
315     g_scheduler->FailWithError(err);
316 
317   base_config->ClearProcessingBuildConfig();
318   if (settings->is_default()) {
319     // The default toolchain must have been set in the default build config
320     // file.
321     if (default_toolchain_label.is_null()) {
322       g_scheduler->FailWithError(Err(Location(),
323           "The default build config file did not call set_default_toolchain()",
324           "If you don't call this, I can't figure out what toolchain to use\n"
325           "for all of this code."));
326     } else {
327       DCHECK(settings->toolchain_label().is_null());
328       settings->set_toolchain_label(default_toolchain_label);
329     }
330   }
331 
332   main_loop_->PostTask(FROM_HERE,
333       base::Bind(&LoaderImpl::DidLoadBuildConfig, this,
334                  settings->toolchain_label()));
335 }
336 
DidLoadFile()337 void LoaderImpl::DidLoadFile() {
338   DecrementPendingLoads();
339 }
340 
DidLoadBuildConfig(const Label & label)341 void LoaderImpl::DidLoadBuildConfig(const Label& label) {
342   // Do not return early, we must call DecrementPendingLoads() at the bottom.
343 
344   ToolchainRecordMap::iterator found_toolchain = toolchain_records_.find(label);
345   ToolchainRecord* record = NULL;
346   if (found_toolchain == toolchain_records_.end()) {
347     // When loading the default build config, we'll insert it into the record
348     // map with an empty label since we don't yet know what to call it.
349     //
350     // In this case, we should have exactly one entry in the map with an empty
351     // label. We now need to fix up the naming so it refers to the "real" one.
352     CHECK(toolchain_records_.size() == 1);
353     ToolchainRecordMap::iterator empty_label = toolchain_records_.find(Label());
354     CHECK(empty_label != toolchain_records_.end());
355 
356     // Fix up the toolchain record.
357     record = empty_label->second;
358     toolchain_records_[label] = record;
359     toolchain_records_.erase(empty_label);
360 
361     // Save the default toolchain label.
362     default_toolchain_label_ = label;
363     DCHECK(record->settings.default_toolchain_label().is_null());
364     record->settings.set_default_toolchain_label(label);
365 
366     // The settings object should have the toolchain label already set.
367     DCHECK(!record->settings.toolchain_label().is_null());
368 
369     // Update any stored invocations that refer to the empty toolchain label.
370     // This will normally only be one, for the root build file, so brute-force
371     // is OK.
372     LoadIDSet old_loads;
373     invocations_.swap(old_loads);
374     for (LoadIDSet::iterator i = old_loads.begin();
375          i != old_loads.end(); ++i) {
376       if (i->toolchain_name.is_null()) {
377         // Fix up toolchain label
378         invocations_.insert(LoadID(i->file, label));
379       } else {
380         // Can keep the old one.
381         invocations_.insert(*i);
382       }
383     }
384   } else {
385     record = found_toolchain->second;
386   }
387 
388   DCHECK(!record->is_config_loaded);
389   DCHECK(record->is_toolchain_loaded);
390   record->is_config_loaded = true;
391 
392   // Schedule all waiting file loads.
393   for (size_t i = 0; i < record->waiting_on_me.size(); i++) {
394     ScheduleLoadFile(&record->settings, record->waiting_on_me[i].origin,
395                      record->waiting_on_me[i].file);
396   }
397   record->waiting_on_me.clear();
398 
399   DecrementPendingLoads();
400 }
401 
DecrementPendingLoads()402 void LoaderImpl::DecrementPendingLoads() {
403   DCHECK(pending_loads_ > 0);
404   pending_loads_--;
405   if (pending_loads_ == 0 && !complete_callback_.is_null())
406     complete_callback_.Run();
407 }
408 
AsyncLoadFile(const LocationRange & origin,const BuildSettings * build_settings,const SourceFile & file_name,const base::Callback<void (const ParseNode *)> & callback,Err * err)409 bool LoaderImpl::AsyncLoadFile(
410     const LocationRange& origin,
411     const BuildSettings* build_settings,
412     const SourceFile& file_name,
413     const base::Callback<void(const ParseNode*)>& callback,
414     Err* err) {
415   if (async_load_file_.is_null()) {
416     return g_scheduler->input_file_manager()->AsyncLoadFile(
417         origin, build_settings, file_name, callback, err);
418   }
419   return async_load_file_.Run(
420       origin, build_settings, file_name, callback, err);
421 }
422