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