Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / tools / gn / loader.cc
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 std::string GetOutputSubdirName(const Label& toolchain_label, bool is_default) {
25   // The default toolchain has no subdir.
26   if (is_default)
27     return std::string();
28
29   // For now just assume the toolchain name is always a valid dir name. We may
30   // want to clean up the in the future.
31   return toolchain_label.name();
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 {
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.
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);
70   }
71
72   Settings settings;
73
74   bool is_toolchain_loaded;
75   bool is_config_loaded;
76
77   std::vector<SourceFile> waiting_on_me;
78 };
79
80 // -----------------------------------------------------------------------------
81
82 const void* Loader::kDefaultToolchainKey = &kDefaultToolchainKey;
83
84 Loader::Loader() {
85 }
86
87 Loader::~Loader() {
88 }
89
90 void Loader::Load(const Label& label) {
91   Load(BuildFileForLabel(label), label.GetToolchainLabel());
92 }
93
94 // static
95 SourceFile Loader::BuildFileForLabel(const Label& label) {
96   return SourceFile(label.dir().value() + "BUILD.gn");
97 }
98
99 // -----------------------------------------------------------------------------
100
101 LoaderImpl::LoaderImpl(const BuildSettings* build_settings)
102     : main_loop_(base::MessageLoop::current()),
103       pending_loads_(0),
104       build_settings_(build_settings) {
105 }
106
107 LoaderImpl::~LoaderImpl() {
108   STLDeleteContainerPairSecondPointers(toolchain_records_.begin(),
109                                        toolchain_records_.end());
110 }
111
112 void LoaderImpl::Load(const SourceFile& file,
113     const Label& in_toolchain_name) {
114   const Label& toolchain_name = in_toolchain_name.is_null()
115       ? default_toolchain_label_ : in_toolchain_name;
116   LoadID load_id(file, toolchain_name);
117   if (!invocations_.insert(load_id).second)
118     return;  // Already in set, so this file was already loaded or schedulerd.
119
120   if (toolchain_records_.empty()) {
121     // Nothing loaded, need to load the default build config. The intial load
122     // should not specify a toolchain.
123     DCHECK(toolchain_name.is_null());
124
125     ToolchainRecord* record =
126         new ToolchainRecord(build_settings_, Label(), Label());
127     toolchain_records_[Label()] = record;
128
129     // The default build config is no dependent on the toolchain definition,
130     // since we need to load the build config before we know what the default
131     // toolchain name is.
132     record->is_toolchain_loaded = true;
133
134     record->waiting_on_me.push_back(file);
135     ScheduleLoadBuildConfig(&record->settings, Scope::KeyValueMap());
136     return;
137   }
138
139   ToolchainRecord* record;
140   if (toolchain_name.is_null())
141     record = toolchain_records_[default_toolchain_label_];
142   else
143     record = toolchain_records_[toolchain_name];
144
145   if (!record) {
146     DCHECK(!default_toolchain_label_.is_null());
147
148     // No reference to this toolchain found yet, make one.
149     record = new ToolchainRecord(build_settings_, toolchain_name,
150                                  default_toolchain_label_);
151     toolchain_records_[toolchain_name] = record;
152
153     // Schedule a load of the toolchain using the default one.
154     Load(BuildFileForLabel(toolchain_name), default_toolchain_label_);
155   }
156
157   if (record->is_config_loaded)
158     ScheduleLoadFile(&record->settings, file);
159   else
160     record->waiting_on_me.push_back(file);
161 }
162
163 void LoaderImpl::ToolchainLoaded(const Toolchain* toolchain) {
164   ToolchainRecord* record = toolchain_records_[toolchain->label()];
165   if (!record) {
166     DCHECK(!default_toolchain_label_.is_null());
167     record = new ToolchainRecord(build_settings_, toolchain->label(),
168                                  default_toolchain_label_);
169     toolchain_records_[toolchain->label()] = record;
170   }
171   record->is_toolchain_loaded = true;
172
173   // The default build config is loaded first, then its toolchain. Secondary
174   // ones are loaded in the opposite order so we can pass toolchain parameters
175   // to the build config. So we may or may not have a config at this point.
176   if (!record->is_config_loaded) {
177     ScheduleLoadBuildConfig(&record->settings, toolchain->args());
178   } else {
179     // There should be nobody waiting on this if the build config is already
180     // loaded.
181     DCHECK(record->waiting_on_me.empty());
182   }
183 }
184
185 Label LoaderImpl::GetDefaultToolchain() const {
186   return default_toolchain_label_;
187 }
188
189 const Settings* LoaderImpl::GetToolchainSettings(const Label& label) const {
190   ToolchainRecordMap::const_iterator found_toolchain;
191   if (label.is_null()) {
192     if (default_toolchain_label_.is_null())
193       return NULL;
194     found_toolchain = toolchain_records_.find(default_toolchain_label_);
195   } else {
196     found_toolchain = toolchain_records_.find(label);
197   }
198
199   if (found_toolchain == toolchain_records_.end())
200     return NULL;
201   return &found_toolchain->second->settings;
202 }
203
204 void LoaderImpl::ScheduleLoadFile(const Settings* settings,
205                                   const SourceFile& file) {
206   Err err;
207   pending_loads_++;
208   if (!AsyncLoadFile(LocationRange(), settings->build_settings(), file,
209                      base::Bind(&LoaderImpl::BackgroundLoadFile, this,
210                                 settings, file),
211                      &err)) {
212     g_scheduler->FailWithError(err);
213     DecrementPendingLoads();
214   }
215 }
216
217 void LoaderImpl::ScheduleLoadBuildConfig(
218     Settings* settings,
219     const Scope::KeyValueMap& toolchain_overrides) {
220   Err err;
221   pending_loads_++;
222   if (!AsyncLoadFile(LocationRange(), settings->build_settings(),
223                      settings->build_settings()->build_config_file(),
224                      base::Bind(&LoaderImpl::BackgroundLoadBuildConfig,
225                                 this, settings, toolchain_overrides),
226                      &err)) {
227     g_scheduler->FailWithError(err);
228     DecrementPendingLoads();
229   }
230 }
231
232 void LoaderImpl::BackgroundLoadFile(const Settings* settings,
233                                     const SourceFile& file_name,
234                                     const ParseNode* root) {
235   if (!root) {
236     main_loop_->PostTask(FROM_HERE,
237         base::Bind(&LoaderImpl::DecrementPendingLoads, this));
238     return;
239   }
240
241   if (g_scheduler->verbose_logging()) {
242     g_scheduler->Log("Running", file_name.value() + " with toolchain " +
243                      settings->toolchain_label().GetUserVisibleName(false));
244   }
245
246   Scope our_scope(settings->base_config());
247   ScopePerFileProvider per_file_provider(&our_scope);
248   our_scope.set_source_dir(file_name.GetDir());
249
250   ScopedTrace trace(TraceItem::TRACE_FILE_EXECUTE, file_name.value());
251   trace.SetToolchain(settings->toolchain_label());
252
253   Err err;
254   root->Execute(&our_scope, &err);
255   if (err.has_error())
256     g_scheduler->FailWithError(err);
257
258   trace.Done();
259
260   main_loop_->PostTask(FROM_HERE, base::Bind(&LoaderImpl::DidLoadFile, this));
261 }
262
263 void LoaderImpl::BackgroundLoadBuildConfig(
264     Settings* settings,
265     const Scope::KeyValueMap& toolchain_overrides,
266     const ParseNode* root) {
267   if (!root) {
268     main_loop_->PostTask(FROM_HERE,
269         base::Bind(&LoaderImpl::DecrementPendingLoads, this));
270     return;
271   }
272
273   Scope* base_config = settings->base_config();
274   base_config->set_source_dir(SourceDir("//"));
275
276   settings->build_settings()->build_args().SetupRootScope(
277       base_config, toolchain_overrides);
278
279   base_config->SetProcessingBuildConfig();
280
281   // See kDefaultToolchainKey in the header.
282   Label default_toolchain_label;
283   if (settings->is_default())
284     base_config->SetProperty(kDefaultToolchainKey, &default_toolchain_label);
285
286   ScopedTrace trace(TraceItem::TRACE_FILE_EXECUTE,
287       settings->build_settings()->build_config_file().value());
288   trace.SetToolchain(settings->toolchain_label());
289
290   const BlockNode* root_block = root->AsBlock();
291   Err err;
292   root_block->ExecuteBlockInScope(base_config, &err);
293
294   trace.Done();
295
296   if (err.has_error())
297     g_scheduler->FailWithError(err);
298
299   base_config->ClearProcessingBuildConfig();
300   if (settings->is_default()) {
301     // The default toolchain must have been set in the default build config
302     // file.
303     if (default_toolchain_label.is_null()) {
304       g_scheduler->FailWithError(Err(Location(),
305           "The default build config file did not call set_default_toolchain()",
306           "If you don't call this, I can't figure out what toolchain to use\n"
307           "for all of this code."));
308     } else {
309       DCHECK(settings->toolchain_label().is_null());
310       settings->set_toolchain_label(default_toolchain_label);
311     }
312   }
313
314   main_loop_->PostTask(FROM_HERE,
315       base::Bind(&LoaderImpl::DidLoadBuildConfig, this,
316                  settings->toolchain_label()));
317 }
318
319 void LoaderImpl::DidLoadFile() {
320   DecrementPendingLoads();
321 }
322
323 void LoaderImpl::DidLoadBuildConfig(const Label& label) {
324   // Do not return early, we must call DecrementPendingLoads() at the bottom.
325
326   ToolchainRecordMap::iterator found_toolchain = toolchain_records_.find(label);
327   ToolchainRecord* record = NULL;
328   if (found_toolchain == toolchain_records_.end()) {
329     // When loading the default build config, we'll insert it into the record
330     // map with an empty label since we don't yet know what to call it.
331     //
332     // In this case, we should have exactly one entry in the map with an empty
333     // label. We now need to fix up the naming so it refers to the "real" one.
334     CHECK(toolchain_records_.size() == 1);
335     ToolchainRecordMap::iterator empty_label = toolchain_records_.find(Label());
336     CHECK(empty_label != toolchain_records_.end());
337
338     // Fix up the toolchain record.
339     record = empty_label->second;
340     toolchain_records_[label] = record;
341     toolchain_records_.erase(empty_label);
342
343     // Save the default toolchain label.
344     default_toolchain_label_ = label;
345     DCHECK(record->settings.default_toolchain_label().is_null());
346     record->settings.set_default_toolchain_label(label);
347
348     // The settings object should have the toolchain label already set.
349     DCHECK(!record->settings.toolchain_label().is_null());
350
351     // Update any stored invocations that refer to the empty toolchain label.
352     // This will normally only be one, for the root build file, so brute-force
353     // is OK.
354     LoadIDSet old_loads;
355     invocations_.swap(old_loads);
356     for (LoadIDSet::iterator i = old_loads.begin();
357          i != old_loads.end(); ++i) {
358       if (i->toolchain_name.is_null()) {
359         // Fix up toolchain label
360         invocations_.insert(LoadID(i->file, label));
361       } else {
362         // Can keep the old one.
363         invocations_.insert(*i);
364       }
365     }
366   } else {
367     record = found_toolchain->second;
368   }
369
370   DCHECK(!record->is_config_loaded);
371   DCHECK(record->is_toolchain_loaded);
372   record->is_config_loaded = true;
373
374   // Schedule all waiting file loads.
375   for (size_t i = 0; i < record->waiting_on_me.size(); i++)
376     ScheduleLoadFile(&record->settings, record->waiting_on_me[i]);
377   record->waiting_on_me.clear();
378
379   DecrementPendingLoads();
380 }
381
382 void LoaderImpl::DecrementPendingLoads() {
383   DCHECK(pending_loads_ > 0);
384   pending_loads_--;
385   if (pending_loads_ == 0 && !complete_callback_.is_null())
386     complete_callback_.Run();
387 }
388
389 bool LoaderImpl::AsyncLoadFile(
390     const LocationRange& origin,
391     const BuildSettings* build_settings,
392     const SourceFile& file_name,
393     const base::Callback<void(const ParseNode*)>& callback,
394     Err* err) {
395   if (async_load_file_.is_null()) {
396     return g_scheduler->input_file_manager()->AsyncLoadFile(
397         origin, build_settings, file_name, callback, err);
398   }
399   return async_load_file_.Run(
400       origin, build_settings, file_name, callback, err);
401 }