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 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 } 402