| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library pub.entrypoint; | 5 library pub.entrypoint; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:io'; |
| 8 | 9 |
| 9 import 'package:path/path.dart' as path; | 10 import 'package:path/path.dart' as path; |
| 10 import 'package:barback/barback.dart'; | 11 import 'package:barback/barback.dart'; |
| 11 | 12 |
| 12 import 'barback/asset_environment.dart'; | 13 import 'barback/asset_environment.dart'; |
| 13 import 'io.dart'; | 14 import 'io.dart'; |
| 14 import 'lock_file.dart'; | 15 import 'lock_file.dart'; |
| 15 import 'log.dart' as log; | 16 import 'log.dart' as log; |
| 16 import 'package.dart'; | 17 import 'package.dart'; |
| 17 import 'package_graph.dart'; | 18 import 'package_graph.dart'; |
| (...skipping 25 matching lines...) Expand all Loading... |
| 43 final Package root; | 44 final Package root; |
| 44 | 45 |
| 45 /// The system-wide cache which caches packages that need to be fetched over | 46 /// The system-wide cache which caches packages that need to be fetched over |
| 46 /// the network. | 47 /// the network. |
| 47 final SystemCache cache; | 48 final SystemCache cache; |
| 48 | 49 |
| 49 /// Whether to create and symlink a "packages" directory containing links to | 50 /// Whether to create and symlink a "packages" directory containing links to |
| 50 /// the installed packages. | 51 /// the installed packages. |
| 51 final bool _packageSymlinks; | 52 final bool _packageSymlinks; |
| 52 | 53 |
| 54 /// Whether this entrypoint is in memory only, as opposed to representing a |
| 55 /// real directory on disk. |
| 56 final bool _inMemory; |
| 57 |
| 53 /// The lockfile for the entrypoint. | 58 /// The lockfile for the entrypoint. |
| 54 /// | 59 /// |
| 55 /// If not provided to the entrypoint, it will be laoded lazily from disc. | 60 /// If not provided to the entrypoint, it will be loaded lazily from disk. |
| 56 LockFile _lockFile; | |
| 57 | |
| 58 /// The graph of all packages reachable from the entrypoint. | |
| 59 PackageGraph _packageGraph; | |
| 60 | |
| 61 /// Loads the entrypoint from a package at [rootDir]. | |
| 62 /// | |
| 63 /// If [packageSymlinks] is `true`, this will create a "packages" directory | |
| 64 /// with symlinks to the installed packages. This directory will be symlinked | |
| 65 /// into any directory that might contain an entrypoint. | |
| 66 Entrypoint(String rootDir, SystemCache cache, {bool packageSymlinks: true}) | |
| 67 : root = new Package.load(null, rootDir, cache.sources), | |
| 68 cache = cache, | |
| 69 _packageSymlinks = packageSymlinks; | |
| 70 | |
| 71 /// Creates an entrypoint given package and lockfile objects. | |
| 72 Entrypoint.inMemory(this.root, this._lockFile, this.cache) | |
| 73 : _packageSymlinks = false; | |
| 74 | |
| 75 /// The path to the entrypoint's "packages" directory. | |
| 76 String get packagesDir => root.path('packages'); | |
| 77 | |
| 78 /// The path to the entrypoint's ".packages" file. | |
| 79 String get packagesFile => root.path('.packages'); | |
| 80 | |
| 81 /// `true` if the entrypoint package currently has a lock file. | |
| 82 bool get lockFileExists => _lockFile != null || entryExists(lockFilePath); | |
| 83 | |
| 84 LockFile get lockFile { | 61 LockFile get lockFile { |
| 85 if (_lockFile != null) return _lockFile; | 62 if (_lockFile != null) return _lockFile; |
| 86 | 63 |
| 87 if (!lockFileExists) { | 64 if (!fileExists(lockFilePath)) { |
| 88 _lockFile = new LockFile.empty(cache.sources); | 65 _lockFile = new LockFile.empty(cache.sources); |
| 89 } else { | 66 } else { |
| 90 _lockFile = new LockFile.load(lockFilePath, cache.sources); | 67 _lockFile = new LockFile.load(lockFilePath, cache.sources); |
| 91 } | 68 } |
| 92 | 69 |
| 93 return _lockFile; | 70 return _lockFile; |
| 94 } | 71 } |
| 72 LockFile _lockFile; |
| 73 |
| 74 /// The package graph for the application and all of its transitive |
| 75 /// dependencies. |
| 76 /// |
| 77 /// Throws a [DataError] if the `.packages` file isn't up-to-date relative to |
| 78 /// the pubspec and the lockfile. |
| 79 PackageGraph get packageGraph { |
| 80 if (_packageGraph != null) return _packageGraph; |
| 81 |
| 82 assertUpToDate(); |
| 83 var packages = new Map.fromIterable(lockFile.packages.values, |
| 84 key: (id) => id.name, |
| 85 value: (id) { |
| 86 var dir = cache.sources[id.source].getDirectory(id); |
| 87 return new Package.load(id.name, dir, cache.sources); |
| 88 }); |
| 89 packages[root.name] = root; |
| 90 |
| 91 _packageGraph = new PackageGraph(this, lockFile, packages); |
| 92 return _packageGraph; |
| 93 } |
| 94 PackageGraph _packageGraph; |
| 95 |
| 96 /// The path to the entrypoint's "packages" directory. |
| 97 String get packagesDir => root.path('packages'); |
| 98 |
| 99 /// The path to the entrypoint's ".packages" file. |
| 100 String get packagesFile => root.path('.packages'); |
| 95 | 101 |
| 96 /// The path to the entrypoint package's pubspec. | 102 /// The path to the entrypoint package's pubspec. |
| 97 String get pubspecPath => root.path('pubspec.yaml'); | 103 String get pubspecPath => root.path('pubspec.yaml'); |
| 98 | 104 |
| 99 /// The path to the entrypoint package's lockfile. | 105 /// The path to the entrypoint package's lockfile. |
| 100 String get lockFilePath => root.path('pubspec.lock'); | 106 String get lockFilePath => root.path('pubspec.lock'); |
| 101 | 107 |
| 108 /// Loads the entrypoint from a package at [rootDir]. |
| 109 /// |
| 110 /// If [packageSymlinks] is `true`, this will create a "packages" directory |
| 111 /// with symlinks to the installed packages. This directory will be symlinked |
| 112 /// into any directory that might contain an entrypoint. |
| 113 Entrypoint(String rootDir, SystemCache cache, {bool packageSymlinks: true}) |
| 114 : root = new Package.load(null, rootDir, cache.sources), |
| 115 cache = cache, |
| 116 _packageSymlinks = packageSymlinks, |
| 117 _inMemory = false; |
| 118 |
| 119 /// Creates an entrypoint given package and lockfile objects. |
| 120 Entrypoint.inMemory(this.root, this._lockFile, this.cache) |
| 121 : _packageSymlinks = false, |
| 122 _inMemory = true; |
| 123 |
| 124 /// Creates an entrypoint given a package and a [solveResult], from which the |
| 125 /// package graph and lockfile will be computed. |
| 126 Entrypoint.fromSolveResult(this.root, this.cache, SolveResult solveResult) |
| 127 : _packageSymlinks = false, |
| 128 _inMemory = true { |
| 129 _packageGraph = new PackageGraph.fromSolveResult(this, solveResult); |
| 130 _lockFile = _packageGraph.lockFile; |
| 131 } |
| 132 |
| 102 /// Gets all dependencies of the [root] package. | 133 /// Gets all dependencies of the [root] package. |
| 103 /// | 134 /// |
| 104 /// Performs version resolution according to [SolveType]. | 135 /// Performs version resolution according to [SolveType]. |
| 105 /// | 136 /// |
| 106 /// [useLatest], if provided, defines a list of packages that will be | 137 /// [useLatest], if provided, defines a list of packages that will be |
| 107 /// unlocked and forced to their latest versions. If [upgradeAll] is | 138 /// unlocked and forced to their latest versions. If [upgradeAll] is |
| 108 /// true, the previous lockfile is ignored and all packages are re-resolved | 139 /// true, the previous lockfile is ignored and all packages are re-resolved |
| 109 /// from scratch. Otherwise, it will attempt to preserve the versions of all | 140 /// from scratch. Otherwise, it will attempt to preserve the versions of all |
| 110 /// previously locked packages. | 141 /// previously locked packages. |
| 111 /// | 142 /// |
| 112 /// Shows a report of the changes made relative to the previous lockfile. If | 143 /// Shows a report of the changes made relative to the previous lockfile. If |
| 113 /// this is an upgrade or downgrade, all transitive dependencies are shown in | 144 /// this is an upgrade or downgrade, all transitive dependencies are shown in |
| 114 /// the report. Otherwise, only dependencies that were changed are shown. If | 145 /// the report. Otherwise, only dependencies that were changed are shown. If |
| 115 /// [dryRun] is `true`, no physical changes are made. | 146 /// [dryRun] is `true`, no physical changes are made. |
| 147 /// |
| 148 /// Updates [lockFile] and [packageRoot] accordingly. |
| 116 Future acquireDependencies(SolveType type, {List<String> useLatest, | 149 Future acquireDependencies(SolveType type, {List<String> useLatest, |
| 117 bool dryRun: false}) async { | 150 bool dryRun: false}) async { |
| 118 var result = await resolveVersions(type, cache.sources, root, | 151 var result = await resolveVersions(type, cache.sources, root, |
| 119 lockFile: lockFile, useLatest: useLatest); | 152 lockFile: lockFile, useLatest: useLatest); |
| 120 if (!result.succeeded) throw result.error; | 153 if (!result.succeeded) throw result.error; |
| 121 | 154 |
| 122 result.showReport(type); | 155 result.showReport(type); |
| 123 | 156 |
| 124 if (dryRun) { | 157 if (dryRun) { |
| 125 result.summarizeChanges(type, dryRun: dryRun); | 158 result.summarizeChanges(type, dryRun: dryRun); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 136 var ids = await Future.wait(result.packages.map(_get)); | 169 var ids = await Future.wait(result.packages.map(_get)); |
| 137 _saveLockFile(ids); | 170 _saveLockFile(ids); |
| 138 | 171 |
| 139 if (_packageSymlinks) _linkSelf(); | 172 if (_packageSymlinks) _linkSelf(); |
| 140 _linkOrDeleteSecondaryPackageDirs(); | 173 _linkOrDeleteSecondaryPackageDirs(); |
| 141 | 174 |
| 142 result.summarizeChanges(type, dryRun: dryRun); | 175 result.summarizeChanges(type, dryRun: dryRun); |
| 143 | 176 |
| 144 /// Build a package graph from the version solver results so we don't | 177 /// Build a package graph from the version solver results so we don't |
| 145 /// have to reload and reparse all the pubspecs. | 178 /// have to reload and reparse all the pubspecs. |
| 146 var packageGraph = await loadPackageGraph(result); | 179 _packageGraph = new PackageGraph.fromSolveResult(this, result); |
| 147 packageGraph.loadTransformerCache().clearIfOutdated(result.changedPackages); | 180 packageGraph.loadTransformerCache().clearIfOutdated(result.changedPackages); |
| 148 | 181 |
| 149 try { | 182 try { |
| 150 await precompileDependencies(changed: result.changedPackages); | 183 await precompileDependencies(changed: result.changedPackages); |
| 151 await precompileExecutables(changed: result.changedPackages); | 184 await precompileExecutables(changed: result.changedPackages); |
| 152 } catch (error, stackTrace) { | 185 } catch (error, stackTrace) { |
| 153 // Just log exceptions here. Since the method is just about acquiring | 186 // Just log exceptions here. Since the method is just about acquiring |
| 154 // dependencies, it shouldn't fail unless that fails. | 187 // dependencies, it shouldn't fail unless that fails. |
| 155 log.exception(error, stackTrace); | 188 log.exception(error, stackTrace); |
| 156 } | 189 } |
| 157 | 190 |
| 158 writeTextFile(packagesFile, lockFile.packagesFile(root.name)); | 191 writeTextFile(packagesFile, lockFile.packagesFile(root.name)); |
| 159 } | 192 } |
| 160 | 193 |
| 161 /// Precompile any transformed dependencies of the entrypoint. | 194 /// Precompile any transformed dependencies of the entrypoint. |
| 162 /// | 195 /// |
| 163 /// If [changed] is passed, only dependencies whose contents might be changed | 196 /// If [changed] is passed, only dependencies whose contents might be changed |
| 164 /// if one of the given packages changes will be recompiled. | 197 /// if one of the given packages changes will be recompiled. |
| 165 Future precompileDependencies({Iterable<String> changed}) async { | 198 Future precompileDependencies({Iterable<String> changed}) async { |
| 166 if (changed != null) changed = changed.toSet(); | 199 if (changed != null) changed = changed.toSet(); |
| 167 | 200 |
| 168 var graph = await loadPackageGraph(); | |
| 169 | |
| 170 // Just precompile the debug version of a package. We're mostly interested | 201 // Just precompile the debug version of a package. We're mostly interested |
| 171 // in improving speed for development iteration loops, which usually use | 202 // in improving speed for development iteration loops, which usually use |
| 172 // debug mode. | 203 // debug mode. |
| 173 var depsDir = path.join('.pub', 'deps', 'debug'); | 204 var depsDir = path.join('.pub', 'deps', 'debug'); |
| 174 | 205 |
| 175 var dependenciesToPrecompile = graph.packages.values.where((package) { | 206 var dependenciesToPrecompile = packageGraph.packages.values |
| 207 .where((package) { |
| 176 if (package.pubspec.transformers.isEmpty) return false; | 208 if (package.pubspec.transformers.isEmpty) return false; |
| 177 if (graph.isPackageMutable(package.name)) return false; | 209 if (packageGraph.isPackageMutable(package.name)) return false; |
| 178 if (!dirExists(path.join(depsDir, package.name))) return true; | 210 if (!dirExists(path.join(depsDir, package.name))) return true; |
| 179 if (changed == null) return true; | 211 if (changed == null) return true; |
| 180 | 212 |
| 181 /// Only recompile [package] if any of its transitive dependencies have | 213 /// Only recompile [package] if any of its transitive dependencies have |
| 182 /// changed. We check all transitive dependencies because it's possible | 214 /// changed. We check all transitive dependencies because it's possible |
| 183 /// that a transformer makes decisions based on their contents. | 215 /// that a transformer makes decisions based on their contents. |
| 184 return overlaps( | 216 return overlaps( |
| 185 graph.transitiveDependencies(package.name) | 217 packageGraph.transitiveDependencies(package.name) |
| 186 .map((package) => package.name).toSet(), | 218 .map((package) => package.name).toSet(), |
| 187 changed); | 219 changed); |
| 188 }).map((package) => package.name).toSet(); | 220 }).map((package) => package.name).toSet(); |
| 189 | 221 |
| 190 if (dirExists(depsDir)) { | 222 if (dirExists(depsDir)) { |
| 191 // Delete any cached dependencies that are going to be recached. | 223 // Delete any cached dependencies that are going to be recached. |
| 192 for (var package in dependenciesToPrecompile) { | 224 for (var package in dependenciesToPrecompile) { |
| 193 deleteEntry(path.join(depsDir, package)); | 225 deleteEntry(path.join(depsDir, package)); |
| 194 } | 226 } |
| 195 | 227 |
| 196 // Also delete any cached dependencies that should no longer be cached. | 228 // Also delete any cached dependencies that should no longer be cached. |
| 197 for (var subdir in listDir(depsDir)) { | 229 for (var subdir in listDir(depsDir)) { |
| 198 var package = graph.packages[path.basename(subdir)]; | 230 var package = packageGraph.packages[path.basename(subdir)]; |
| 199 if (package == null || package.pubspec.transformers.isEmpty || | 231 if (package == null || package.pubspec.transformers.isEmpty || |
| 200 graph.isPackageMutable(package.name)) { | 232 packageGraph.isPackageMutable(package.name)) { |
| 201 deleteEntry(subdir); | 233 deleteEntry(subdir); |
| 202 } | 234 } |
| 203 } | 235 } |
| 204 } | 236 } |
| 205 | 237 |
| 206 if (dependenciesToPrecompile.isEmpty) return; | 238 if (dependenciesToPrecompile.isEmpty) return; |
| 207 | 239 |
| 208 try { | 240 try { |
| 209 await log.progress("Precompiling dependencies", () async { | 241 await log.progress("Precompiling dependencies", () async { |
| 210 var packagesToLoad = | 242 var packagesToLoad = |
| 211 unionAll(dependenciesToPrecompile.map(graph.transitiveDependencies)) | 243 unionAll(dependenciesToPrecompile.map( |
| 244 packageGraph.transitiveDependencies)) |
| 212 .map((package) => package.name).toSet(); | 245 .map((package) => package.name).toSet(); |
| 213 | 246 |
| 214 var environment = await AssetEnvironment.create(this, BarbackMode.DEBUG, | 247 var environment = await AssetEnvironment.create(this, BarbackMode.DEBUG, |
| 215 packages: packagesToLoad, useDart2JS: false); | 248 packages: packagesToLoad, useDart2JS: false); |
| 216 | 249 |
| 217 /// Ignore barback errors since they'll be emitted via [getAllAssets] | 250 /// Ignore barback errors since they'll be emitted via [getAllAssets] |
| 218 /// below. | 251 /// below. |
| 219 environment.barback.errors.listen((_) {}); | 252 environment.barback.errors.listen((_) {}); |
| 220 | 253 |
| 221 // TODO(nweiz): only get assets from [dependenciesToPrecompile] so as | 254 // TODO(nweiz): only get assets from [dependenciesToPrecompile] so as |
| (...skipping 30 matching lines...) Expand all Loading... |
| 252 var binDir = path.join('.pub', 'bin'); | 285 var binDir = path.join('.pub', 'bin'); |
| 253 var sdkVersionPath = path.join(binDir, 'sdk-version'); | 286 var sdkVersionPath = path.join(binDir, 'sdk-version'); |
| 254 | 287 |
| 255 // If the existing executable was compiled with a different SDK, we need to | 288 // If the existing executable was compiled with a different SDK, we need to |
| 256 // recompile regardless of what changed. | 289 // recompile regardless of what changed. |
| 257 // TODO(nweiz): Use the VM to check this when issue 20802 is fixed. | 290 // TODO(nweiz): Use the VM to check this when issue 20802 is fixed. |
| 258 var sdkMatches = fileExists(sdkVersionPath) && | 291 var sdkMatches = fileExists(sdkVersionPath) && |
| 259 readTextFile(sdkVersionPath) == "${sdk.version}\n"; | 292 readTextFile(sdkVersionPath) == "${sdk.version}\n"; |
| 260 if (!sdkMatches) changed = null; | 293 if (!sdkMatches) changed = null; |
| 261 | 294 |
| 262 var graph = await loadPackageGraph(); | |
| 263 | |
| 264 // Clean out any outdated snapshots. | 295 // Clean out any outdated snapshots. |
| 265 if (dirExists(binDir)) { | 296 if (dirExists(binDir)) { |
| 266 for (var entry in listDir(binDir)) { | 297 for (var entry in listDir(binDir)) { |
| 267 if (!dirExists(entry)) continue; | 298 if (!dirExists(entry)) continue; |
| 268 | 299 |
| 269 var package = path.basename(entry); | 300 var package = path.basename(entry); |
| 270 if (!graph.packages.containsKey(package) || | 301 if (!packageGraph.packages.containsKey(package) || |
| 271 graph.isPackageMutable(package)) { | 302 packageGraph.isPackageMutable(package)) { |
| 272 deleteEntry(entry); | 303 deleteEntry(entry); |
| 273 } | 304 } |
| 274 } | 305 } |
| 275 } | 306 } |
| 276 | 307 |
| 277 var executables = new Map.fromIterable(root.immediateDependencies, | 308 var executables = new Map.fromIterable(root.immediateDependencies, |
| 278 key: (dep) => dep.name, | 309 key: (dep) => dep.name, |
| 279 value: (dep) => _executablesForPackage(graph, dep.name, changed)); | 310 value: (dep) => _executablesForPackage(dep.name, changed)); |
| 280 | 311 |
| 281 for (var package in executables.keys.toList()) { | 312 for (var package in executables.keys.toList()) { |
| 282 if (executables[package].isEmpty) executables.remove(package); | 313 if (executables[package].isEmpty) executables.remove(package); |
| 283 } | 314 } |
| 284 | 315 |
| 285 if (!sdkMatches) deleteEntry(binDir); | 316 if (!sdkMatches) deleteEntry(binDir); |
| 286 if (executables.isEmpty) return; | 317 if (executables.isEmpty) return; |
| 287 | 318 |
| 288 await log.progress("Precompiling executables", () async { | 319 await log.progress("Precompiling executables", () async { |
| 289 ensureDir(binDir); | 320 ensureDir(binDir); |
| 290 | 321 |
| 291 // Make sure there's a trailing newline so our version file matches the | 322 // Make sure there's a trailing newline so our version file matches the |
| 292 // SDK's. | 323 // SDK's. |
| 293 writeTextFile(sdkVersionPath, "${sdk.version}\n"); | 324 writeTextFile(sdkVersionPath, "${sdk.version}\n"); |
| 294 | 325 |
| 295 var packagesToLoad = | 326 var packagesToLoad = |
| 296 unionAll(executables.keys.map(graph.transitiveDependencies)) | 327 unionAll(executables.keys.map(packageGraph.transitiveDependencies)) |
| 297 .map((package) => package.name).toSet(); | 328 .map((package) => package.name).toSet(); |
| 298 var executableIds = unionAll( | 329 var executableIds = unionAll( |
| 299 executables.values.map((ids) => ids.toSet())); | 330 executables.values.map((ids) => ids.toSet())); |
| 300 var environment = await AssetEnvironment.create(this, BarbackMode.RELEASE, | 331 var environment = await AssetEnvironment.create(this, BarbackMode.RELEASE, |
| 301 packages: packagesToLoad, | 332 packages: packagesToLoad, |
| 302 entrypoints: executableIds, | 333 entrypoints: executableIds, |
| 303 useDart2JS: false); | 334 useDart2JS: false); |
| 304 environment.barback.errors.listen((error) { | 335 environment.barback.errors.listen((error) { |
| 305 log.error(log.red("Build error:\n$error")); | 336 log.error(log.red("Build error:\n$error")); |
| 306 }); | 337 }); |
| 307 | 338 |
| 308 await waitAndPrintErrors(executables.keys.map((package) async { | 339 await waitAndPrintErrors(executables.keys.map((package) async { |
| 309 var dir = path.join(binDir, package); | 340 var dir = path.join(binDir, package); |
| 310 cleanDir(dir); | 341 cleanDir(dir); |
| 311 await environment.precompileExecutables(package, dir, | 342 await environment.precompileExecutables(package, dir, |
| 312 executableIds: executables[package]); | 343 executableIds: executables[package]); |
| 313 })); | 344 })); |
| 314 }); | 345 }); |
| 315 } | 346 } |
| 316 | 347 |
| 317 /// Returns the list of all executable assets for [packageName] that should be | 348 /// Returns the list of all executable assets for [packageName] that should be |
| 318 /// precompiled. | 349 /// precompiled. |
| 319 /// | 350 /// |
| 320 /// If [changed] isn't `null`, executables for [packageName] will only be | 351 /// If [changed] isn't `null`, executables for [packageName] will only be |
| 321 /// compiled if they might depend on a package in [changed]. | 352 /// compiled if they might depend on a package in [changed]. |
| 322 List<AssetId> _executablesForPackage(PackageGraph graph, String packageName, | 353 List<AssetId> _executablesForPackage(String packageName, |
| 323 Set<String> changed) { | 354 Set<String> changed) { |
| 324 var package = graph.packages[packageName]; | 355 var package = packageGraph.packages[packageName]; |
| 325 var binDir = package.path('bin'); | 356 var binDir = package.path('bin'); |
| 326 if (!dirExists(binDir)) return []; | 357 if (!dirExists(binDir)) return []; |
| 327 if (graph.isPackageMutable(packageName)) return []; | 358 if (packageGraph.isPackageMutable(packageName)) return []; |
| 328 | 359 |
| 329 var executables = package.executableIds; | 360 var executables = package.executableIds; |
| 330 | 361 |
| 331 // If we don't know which packages were changed, always precompile the | 362 // If we don't know which packages were changed, always precompile the |
| 332 // executables. | 363 // executables. |
| 333 if (changed == null) return executables; | 364 if (changed == null) return executables; |
| 334 | 365 |
| 335 // If any of the package's dependencies changed, recompile the executables. | 366 // If any of the package's dependencies changed, recompile the executables. |
| 336 if (graph.transitiveDependencies(packageName) | 367 if (packageGraph.transitiveDependencies(packageName) |
| 337 .any((package) => changed.contains(package.name))) { | 368 .any((package) => changed.contains(package.name))) { |
| 338 return executables; | 369 return executables; |
| 339 } | 370 } |
| 340 | 371 |
| 341 // If any executables don't exist, precompile them regardless of what | 372 // If any executables don't exist, precompile them regardless of what |
| 342 // changed. Since we delete the bin directory before recompiling, we need to | 373 // changed. Since we delete the bin directory before recompiling, we need to |
| 343 // recompile all executables. | 374 // recompile all executables. |
| 344 var executablesExist = executables.every((executable) => | 375 var executablesExist = executables.every((executable) => |
| 345 fileExists(path.join('.pub', 'bin', packageName, | 376 fileExists(path.join('.pub', 'bin', packageName, |
| 346 "${path.url.basename(executable.path)}.snapshot"))); | 377 "${path.url.basename(executable.path)}.snapshot"))); |
| (...skipping 17 matching lines...) Expand all Loading... |
| 364 if (source is! CachedSource) return null; | 395 if (source is! CachedSource) return null; |
| 365 return source.downloadToSystemCache(id); | 396 return source.downloadToSystemCache(id); |
| 366 } | 397 } |
| 367 | 398 |
| 368 var packageDir = path.join(packagesDir, id.name); | 399 var packageDir = path.join(packagesDir, id.name); |
| 369 if (entryExists(packageDir)) deleteEntry(packageDir); | 400 if (entryExists(packageDir)) deleteEntry(packageDir); |
| 370 return source.get(id, packageDir); | 401 return source.get(id, packageDir); |
| 371 }).then((_) => source.resolveId(id)); | 402 }).then((_) => source.resolveId(id)); |
| 372 } | 403 } |
| 373 | 404 |
| 374 /// Determines whether or not the lockfile is out of date with respect to the | 405 /// Throws a [DataError] if the `.packages` file doesn't exist or if it's |
| 375 /// pubspec. | 406 /// out-of-date relative to the lockfile or the pubspec. |
| 376 /// | 407 void assertUpToDate() { |
| 377 /// This will be `false` if there is no lockfile at all, or if the pubspec | 408 if (_inMemory) return; |
| 378 /// contains dependencies that are not in the lockfile or that don't match | |
| 379 /// what's in there. | |
| 380 bool _isLockFileUpToDate(LockFile lockFile) { | |
| 381 /// If this is an entrypoint for an in-memory package, trust the in-memory | |
| 382 /// lockfile provided for it. | |
| 383 if (root.dir == null) return true; | |
| 384 | 409 |
| 385 return root.immediateDependencies.every((package) { | 410 if (!entryExists(lockFilePath)) { |
| 386 var locked = lockFile.packages[package.name]; | 411 dataError('No pubspec.lock file found, please run "pub get" first.'); |
| 387 if (locked == null) return false; | |
| 388 | |
| 389 if (package.source != locked.source) return false; | |
| 390 if (!package.constraint.allows(locked.version)) return false; | |
| 391 | |
| 392 var source = cache.sources[package.source]; | |
| 393 if (source == null) return false; | |
| 394 | |
| 395 return source.descriptionsEqual(package.description, locked.description); | |
| 396 }); | |
| 397 } | |
| 398 | |
| 399 /// Determines whether all of the packages in the lockfile are already | |
| 400 /// installed and available. | |
| 401 /// | |
| 402 /// Note: this assumes [isLockFileUpToDate] has already been called and | |
| 403 /// returned `true`. | |
| 404 bool _arePackagesAvailable(LockFile lockFile) { | |
| 405 return lockFile.packages.values.every((package) { | |
| 406 var source = cache.sources[package.source]; | |
| 407 | |
| 408 // This should only be called after [_isLockFileUpToDate] has returned | |
| 409 // `true`, which ensures all of the sources in the lock file are valid. | |
| 410 assert(source != null); | |
| 411 | |
| 412 // We only care about cached sources. Uncached sources aren't "installed". | |
| 413 // If one of those is missing, we want to show the user the file not | |
| 414 // found error later since installing won't accomplish anything. | |
| 415 if (source is! CachedSource) return true; | |
| 416 | |
| 417 // Get the directory. | |
| 418 var dir = source.getDirectory(package); | |
| 419 // See if the directory is there and looks like a package. | |
| 420 return dirExists(dir) || fileExists(path.join(dir, "pubspec.yaml")); | |
| 421 }); | |
| 422 } | |
| 423 | |
| 424 /// Gets dependencies if the lockfile is out of date with respect to the | |
| 425 /// pubspec. | |
| 426 Future ensureLockFileIsUpToDate() async { | |
| 427 if (!lockFileExists) { | |
| 428 log.message( | |
| 429 "You don't have a lockfile, so we need to generate that:"); | |
| 430 } else if (_isLockFileUpToDate(lockFile)) { | |
| 431 // If we do have a lock file, we still need to make sure the packages are | |
| 432 // actually installed. The user may have just gotten a package that | |
| 433 // includes a lockfile. | |
| 434 if (_arePackagesAvailable(lockFile)) return; | |
| 435 | |
| 436 // If we don't have a current lock file, we definitely need to install. | |
| 437 log.message( | |
| 438 "You are missing some dependencies, so we need to install them " | |
| 439 "first:"); | |
| 440 } else { | |
| 441 log.message( | |
| 442 "Your pubspec has changed, so we need to update your lockfile:"); | |
| 443 } | 412 } |
| 444 | 413 |
| 445 await acquireDependencies(SolveType.GET); | 414 if (!entryExists(packagesFile)) { |
| 446 } | 415 dataError('No .packages file found, please run "pub get" first.'); |
| 416 } |
| 447 | 417 |
| 448 /// Loads the package graph for the application and all of its transitive | 418 var packagesModified = new File(packagesFile).lastModifiedSync(); |
| 449 /// dependencies. | 419 var pubspecModified = new File(pubspecPath).lastModifiedSync(); |
| 450 /// | 420 if (packagesModified.isBefore(pubspecModified)) { |
| 451 /// If [result] is passed, this loads the graph from it without re-parsing the | 421 dataError('The pubspec.yaml file has changed since the .packages file ' |
| 452 /// lockfile or any pubspecs. Otherwise, before loading, this makes sure the | 422 'was generated, please run "pub get" again.'); |
| 453 /// lockfile and dependencies are installed and up to date. | 423 } |
| 454 Future<PackageGraph> loadPackageGraph([SolveResult result]) async { | |
| 455 if (_packageGraph != null) return _packageGraph; | |
| 456 | 424 |
| 457 var graph = await log.progress("Loading package graph", () async { | 425 var lockFileModified = new File(lockFilePath).lastModifiedSync(); |
| 458 if (result != null) { | 426 if (packagesModified.isBefore(lockFileModified)) { |
| 459 var packages = new Map.fromIterable(result.packages, | 427 dataError('The pubspec.lock file has changed since the .packages file ' |
| 460 key: (id) => id.name, | 428 'was generated, please run "pub get" again.'); |
| 461 value: (id) { | 429 } |
| 462 if (id.name == root.name) return root; | |
| 463 | |
| 464 return new Package(result.pubspecs[id.name], | |
| 465 cache.sources[id.source].getDirectory(id)); | |
| 466 }); | |
| 467 | |
| 468 return new PackageGraph( | |
| 469 this, | |
| 470 new LockFile(result.packages, cache.sources), | |
| 471 packages); | |
| 472 } | |
| 473 | |
| 474 await ensureLockFileIsUpToDate(); | |
| 475 var packages = new Map.fromIterable(lockFile.packages.values, | |
| 476 key: (id) => id.name, | |
| 477 value: (id) { | |
| 478 var dir = cache.sources[id.source].getDirectory(id); | |
| 479 return new Package.load(id.name, dir, cache.sources); | |
| 480 }); | |
| 481 packages[root.name] = root; | |
| 482 return new PackageGraph(this, lockFile, packages); | |
| 483 }, fine: true); | |
| 484 | |
| 485 _packageGraph = graph; | |
| 486 return graph; | |
| 487 } | 430 } |
| 488 | 431 |
| 489 /// Saves a list of concrete package versions to the `pubspec.lock` file. | 432 /// Saves a list of concrete package versions to the `pubspec.lock` file. |
| 490 void _saveLockFile(List<PackageId> packageIds) { | 433 void _saveLockFile(List<PackageId> packageIds) { |
| 491 _lockFile = new LockFile(packageIds, cache.sources); | 434 _lockFile = new LockFile(packageIds, cache.sources); |
| 492 var lockFilePath = root.path('pubspec.lock'); | 435 var lockFilePath = root.path('pubspec.lock'); |
| 493 writeTextFile(lockFilePath, _lockFile.serialize(root.dir)); | 436 writeTextFile(lockFilePath, _lockFile.serialize(root.dir)); |
| 494 } | 437 } |
| 495 | 438 |
| 496 /// Creates a self-referential symlink in the `packages` directory that allows | 439 /// Creates a self-referential symlink in the `packages` directory that allows |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 550 /// If [packageSymlinks] is true, creates a symlink to the "packages" | 493 /// If [packageSymlinks] is true, creates a symlink to the "packages" |
| 551 /// directory in [dir]. | 494 /// directory in [dir]. |
| 552 /// | 495 /// |
| 553 /// Otherwise, deletes a "packages" directories in [dir] if one exists. | 496 /// Otherwise, deletes a "packages" directories in [dir] if one exists. |
| 554 void _linkOrDeleteSecondaryPackageDir(String dir) { | 497 void _linkOrDeleteSecondaryPackageDir(String dir) { |
| 555 var symlink = path.join(dir, 'packages'); | 498 var symlink = path.join(dir, 'packages'); |
| 556 if (entryExists(symlink)) deleteEntry(symlink); | 499 if (entryExists(symlink)) deleteEntry(symlink); |
| 557 if (_packageSymlinks) createSymlink(packagesDir, symlink, relative: true); | 500 if (_packageSymlinks) createSymlink(packagesDir, symlink, relative: true); |
| 558 } | 501 } |
| 559 } | 502 } |
| OLD | NEW |