| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 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) { | 
|  | 190   ToolchainRecordMap::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   base_config->ClearProcessingBuildConfig(); | 
|  | 297   if (settings->is_default()) { | 
|  | 298     // The default toolchain must have been set in the default build config | 
|  | 299     // file. | 
|  | 300     if (default_toolchain_label.is_null()) { | 
|  | 301       g_scheduler->FailWithError(Err(Location(), | 
|  | 302           "The default build config file did not call set_default_toolchain()", | 
|  | 303           "If you don't call this, I can't figure out what toolchain to use\n" | 
|  | 304           "for all of this code.")); | 
|  | 305     } else { | 
|  | 306       DCHECK(settings->toolchain_label().is_null()); | 
|  | 307       settings->set_toolchain_label(default_toolchain_label); | 
|  | 308     } | 
|  | 309   } | 
|  | 310 | 
|  | 311   main_loop_->PostTask(FROM_HERE, | 
|  | 312       base::Bind(&LoaderImpl::DidLoadBuildConfig, this, | 
|  | 313                  settings->toolchain_label())); | 
|  | 314 } | 
|  | 315 | 
|  | 316 void LoaderImpl::DidLoadFile() { | 
|  | 317   DecrementPendingLoads(); | 
|  | 318 } | 
|  | 319 | 
|  | 320 void LoaderImpl::DidLoadBuildConfig(const Label& label) { | 
|  | 321   // Do not return early, we must call DecrementPendingLoads() at the bottom. | 
|  | 322 | 
|  | 323   ToolchainRecordMap::iterator found_toolchain = toolchain_records_.find(label); | 
|  | 324   ToolchainRecord* record = NULL; | 
|  | 325   if (found_toolchain == toolchain_records_.end()) { | 
|  | 326     // When loading the default build config, we'll insert it into the record | 
|  | 327     // map with an empty label since we don't yet know what to call it. | 
|  | 328     // | 
|  | 329     // In this case, we should have exactly one entry in the map with an empty | 
|  | 330     // label. We now need to fix up the naming so it refers to the "real" one. | 
|  | 331     CHECK(toolchain_records_.size() == 1); | 
|  | 332     ToolchainRecordMap::iterator empty_label = toolchain_records_.find(Label()); | 
|  | 333     CHECK(empty_label != toolchain_records_.end()); | 
|  | 334 | 
|  | 335     // Fix up the toolchain record. | 
|  | 336     record = empty_label->second; | 
|  | 337     toolchain_records_[label] = record; | 
|  | 338     toolchain_records_.erase(empty_label); | 
|  | 339 | 
|  | 340     // Save the default toolchain label. | 
|  | 341     default_toolchain_label_ = label; | 
|  | 342     DCHECK(record->settings.default_toolchain_label().is_null()); | 
|  | 343     record->settings.set_default_toolchain_label(label); | 
|  | 344 | 
|  | 345     // The settings object should have the toolchain label already set. | 
|  | 346     DCHECK(!record->settings.toolchain_label().is_null()); | 
|  | 347 | 
|  | 348     // Update any stored invocations that refer to the empty toolchain label. | 
|  | 349     // This will normally only be one, for the root build file, so brute-force | 
|  | 350     // is OK. | 
|  | 351     LoadIDSet old_loads; | 
|  | 352     invocations_.swap(old_loads); | 
|  | 353     for (LoadIDSet::iterator i = old_loads.begin(); | 
|  | 354          i != old_loads.end(); ++i) { | 
|  | 355       if (i->toolchain_name.is_null()) { | 
|  | 356         // Fix up toolchain label | 
|  | 357         invocations_.insert(LoadID(i->file, label)); | 
|  | 358       } else { | 
|  | 359         // Can keep the old one. | 
|  | 360         invocations_.insert(*i); | 
|  | 361       } | 
|  | 362     } | 
|  | 363   } else { | 
|  | 364     record = found_toolchain->second; | 
|  | 365   } | 
|  | 366 | 
|  | 367   DCHECK(!record->is_config_loaded); | 
|  | 368   DCHECK(record->is_toolchain_loaded); | 
|  | 369   record->is_config_loaded = true; | 
|  | 370 | 
|  | 371   // Schedule all waiting file loads. | 
|  | 372   for (size_t i = 0; i < record->waiting_on_me.size(); i++) | 
|  | 373     ScheduleLoadFile(&record->settings, record->waiting_on_me[i]); | 
|  | 374   record->waiting_on_me.clear(); | 
|  | 375 | 
|  | 376   DecrementPendingLoads(); | 
|  | 377 } | 
|  | 378 | 
|  | 379 void LoaderImpl::DecrementPendingLoads() { | 
|  | 380   DCHECK(pending_loads_ > 0); | 
|  | 381   pending_loads_--; | 
|  | 382   if (pending_loads_ == 0 && !complete_callback_.is_null()) | 
|  | 383     complete_callback_.Run(); | 
|  | 384 } | 
|  | 385 | 
|  | 386 bool LoaderImpl::AsyncLoadFile( | 
|  | 387     const LocationRange& origin, | 
|  | 388     const BuildSettings* build_settings, | 
|  | 389     const SourceFile& file_name, | 
|  | 390     const base::Callback<void(const ParseNode*)>& callback, | 
|  | 391     Err* err) { | 
|  | 392   if (async_load_file_.is_null()) { | 
|  | 393     return g_scheduler->input_file_manager()->AsyncLoadFile( | 
|  | 394         origin, build_settings, file_name, callback, err); | 
|  | 395   } | 
|  | 396   return async_load_file_.Run( | 
|  | 397       origin, build_settings, file_name, callback, err); | 
|  | 398 } | 
| OLD | NEW | 
|---|