blob: 1458d1bc9e030c127f1e3c31830a191dbae232da [file] [log] [blame]
// Copyright (c) 2016, 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.
import 'dart:async';
import 'package:pub_semver/pub_semver.dart';
import '../exceptions.dart';
import '../language_version.dart';
import '../package_name.dart';
import '../pubspec.dart';
import '../sdk.dart';
import '../source.dart';
import '../system_cache.dart';
import '../utils.dart';
/// A package [Source] that gets packages from a hard-coded SDK.
class SdkSource extends Source {
@override
final name = 'sdk';
@override
BoundSource bind(SystemCache systemCache) =>
BoundSdkSource(this, systemCache);
/// Returns a reference to an SDK package named [name] from [sdk].
PackageRef refFor(String name, String sdk) => PackageRef(name, this, sdk);
/// Returns an ID for an SDK package with the given [name] and [version] from
/// [sdk].
PackageId idFor(String name, Version version, String sdk) =>
PackageId(name, this, version, sdk);
/// Parses an SDK dependency.
@override
PackageRef parseRef(String name, description,
{String? containingPath, LanguageVersion? languageVersion}) {
if (description is! String) {
throw FormatException('The description must be an SDK name.');
}
return PackageRef(name, this, description);
}
@override
PackageId parseId(String name, Version version, description,
{String? containingPath}) {
if (description is! String) {
throw FormatException('The description must be an SDK name.');
}
return PackageId(name, this, version, description);
}
@override
bool descriptionsEqual(description1, description2) =>
description1 == description2;
@override
int hashDescription(description) => description.hashCode;
}
/// The [BoundSource] for [SdkSource].
class BoundSdkSource extends BoundSource {
@override
final SdkSource source;
@override
final SystemCache systemCache;
BoundSdkSource(this.source, this.systemCache);
@override
Future<List<PackageId>> doGetVersions(
PackageRef ref, Duration? maxAge) async {
var pubspec = _loadPubspec(ref);
var id = PackageId(ref.name, source, pubspec.version, ref.description);
memoizePubspec(id, pubspec);
return [id];
}
@override
Future<Pubspec> doDescribe(PackageId id) async => _loadPubspec(id);
/// Loads the pubspec for the Flutter package named [name].
///
/// Throws a [PackageNotFoundException] if [package]'s SDK is unavailable or
/// doesn't contain the package.
Pubspec _loadPubspec(PackageName package) =>
Pubspec.load(_verifiedPackagePath(package), systemCache.sources,
expectedName: package.name);
/// Returns the path for the given [package].
///
/// Throws a [PackageNotFoundException] if [package]'s SDK is unavailable or
/// doesn't contain the package.
String _verifiedPackagePath(PackageName package) {
var identifier = package.description as String?;
var sdk = sdks[identifier!];
if (sdk == null) {
throw PackageNotFoundException('unknown SDK "$identifier"');
} else if (!sdk.isAvailable) {
throw PackageNotFoundException(
'the ${sdk.name} SDK is not available',
hint: sdk.installMessage,
);
}
var path = sdk.packagePath(package.name);
if (path != null) return path;
throw PackageNotFoundException(
'could not find package ${package.name} in the ${sdk.name} SDK');
}
@override
String getDirectory(PackageId id, {String? relativeFrom}) {
try {
return _verifiedPackagePath(id);
} on PackageNotFoundException catch (error) {
// [PackageNotFoundException]s are uncapitalized and unpunctuated because
// they're used within other sentences by the version solver, but
// [ApplicationException]s should be full sentences.
throw ApplicationException(capitalize(error.message) + '.');
}
}
}