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 |