| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library pub.lock_file; |
| |
| import 'package:path/path.dart' as p; |
| import 'package:pub_semver/pub_semver.dart'; |
| import 'package:source_span/source_span.dart'; |
| import 'package:yaml/yaml.dart'; |
| |
| import 'io.dart'; |
| import 'package.dart'; |
| import 'source_registry.dart'; |
| import 'utils.dart'; |
| |
| /// A parsed and validated `pubspec.lock` file. |
| class LockFile { |
| /// The packages this lockfile pins. |
| Map<String, PackageId> packages; |
| |
| /// Creates a new lockfile containing [ids]. |
| factory LockFile(List<PackageId> ids) { |
| var lockFile = new LockFile.empty(); |
| for (var id in ids) { |
| if (!id.isRoot) lockFile.packages[id.name] = id; |
| } |
| |
| return lockFile; |
| } |
| |
| LockFile._(this.packages); |
| |
| LockFile.empty() |
| : packages = <String, PackageId>{}; |
| |
| /// Loads a lockfile from [filePath]. |
| factory LockFile.load(String filePath, SourceRegistry sources) { |
| return LockFile._parse(filePath, readTextFile(filePath), sources); |
| } |
| |
| /// Parses a lockfile whose text is [contents]. |
| factory LockFile.parse(String contents, SourceRegistry sources) { |
| return LockFile._parse(null, contents, sources); |
| } |
| |
| /// Parses the lockfile whose text is [contents]. |
| /// |
| /// [filePath] is the system-native path to the lockfile on disc. It may be |
| /// `null`. |
| static LockFile _parse(String filePath, String contents, |
| SourceRegistry sources) { |
| var packages = <String, PackageId>{}; |
| |
| if (contents.trim() == '') return new LockFile.empty(); |
| |
| var sourceUrl; |
| if (filePath != null) sourceUrl = p.toUri(filePath); |
| var parsed = loadYamlNode(contents, sourceUrl: sourceUrl); |
| |
| _validate(parsed is Map, 'The lockfile must be a YAML mapping.', parsed); |
| |
| var packageEntries = parsed['packages']; |
| if (packageEntries != null) { |
| _validate( |
| packageEntries is Map, |
| 'The "packages" field must be a map.', |
| parsed.nodes['packages']); |
| |
| packageEntries.forEach((name, spec) { |
| // Parse the version. |
| _validate( |
| spec.containsKey('version'), |
| 'Package $name is missing a version.', |
| spec); |
| var version = new Version.parse(spec['version']); |
| |
| // Parse the source. |
| _validate( |
| spec.containsKey('source'), |
| 'Package $name is missing a source.', |
| spec); |
| var sourceName = spec['source']; |
| |
| _validate( |
| spec.containsKey('description'), |
| 'Package $name is missing a description.', |
| spec); |
| var description = spec['description']; |
| |
| // Let the source parse the description. |
| var source = sources[sourceName]; |
| try { |
| description = |
| source.parseDescription(filePath, description, fromLockFile: true); |
| } on FormatException catch (ex) { |
| throw new SourceSpanFormatException( |
| ex.message, |
| spec.nodes['source'].span); |
| } |
| |
| var id = new PackageId(name, sourceName, version, description); |
| |
| // Validate the name. |
| _validate( |
| name == id.name, |
| "Package name $name doesn't match ${id.name}.", |
| spec); |
| |
| packages[name] = id; |
| }); |
| } |
| |
| return new LockFile._(packages); |
| } |
| |
| /// If [condition] is `false` throws a format error with [message] for [node]. |
| static void _validate(bool condition, String message, YamlNode node) { |
| if (condition) return; |
| throw new SourceSpanFormatException(message, node.span); |
| } |
| |
| /// Returns the serialized YAML text of the lock file. |
| /// |
| /// [packageDir] is the containing directory of the root package, used to |
| /// properly serialize package descriptions. |
| String serialize(String packageDir, SourceRegistry sources) { |
| // Convert the dependencies to a simple object. |
| var data = {}; |
| packages.forEach((name, package) { |
| var description = |
| sources[package.source].serializeDescription(packageDir, package.description); |
| |
| data[name] = { |
| 'version': package.version.toString(), |
| 'source': package.source, |
| 'description': description |
| }; |
| }); |
| |
| return """ |
| # Generated by pub |
| # See http://pub.dartlang.org/doc/glossary.html#lockfile |
| ${yamlToString({'packages': data})} |
| """; |
| } |
| } |