Home | History | Annotate | Download | only in gn
      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 {
     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 
     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<SourceFileAndOrigin> 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, const LocationRange& origin) {
     91   Load(BuildFileForLabel(label), origin, 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 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 
    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 
    186 Label LoaderImpl::GetDefaultToolchain() const {
    187   return default_toolchain_label_;
    188 }
    189 
    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 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 
    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 
    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 
    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 
    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 
    337 void LoaderImpl::DidLoadFile() {
    338   DecrementPendingLoads();
    339 }
    340 
    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 
    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 
    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