blob: fe10eb41d7ff7f885ae3a3bce35009602ed4634a [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 {
static final SdkSource instance = SdkSource._();
SdkSource._();
@override
final name = 'sdk';
/// Parses an SDK dependency.
@override
PackageRef parseRef(String name, description,
{String? containingDir, LanguageVersion? languageVersion}) {
if (description is! String) {
throw FormatException('The description must be an SDK name.');
}
return PackageRef(name, SdkDescription(description));
}
@override
PackageId parseId(String name, Version version, description,
{String? containingDir}) {
if (description is! String) {
throw FormatException('The description must be an SDK name.');
}
return PackageId(
name,
version,
ResolvedSdkDescription(SdkDescription(description)),
);
}
@override
Future<List<PackageId>> doGetVersions(
PackageRef ref, Duration? maxAge, SystemCache cache) async {
final description = ref.description;
if (description is! SdkDescription) {
throw ArgumentError('Wrong source');
}
var pubspec = _loadPubspec(ref, cache);
var id = PackageId(
ref.name,
pubspec.version,
ResolvedSdkDescription(description),
);
// Store the pubspec in memory if we need to refer to it again.
cache.cachedPubspecs[id] = pubspec;
return [id];
}
@override
Future<Pubspec> doDescribe(
PackageId id,
SystemCache cache,
) async =>
_loadPubspec(id.toRef(), cache);
/// Loads the pubspec for the SDK package named [ref].
///
/// Throws a [PackageNotFoundException] if [ref]'s SDK is unavailable or
/// doesn't contain the package.
Pubspec _loadPubspec(PackageRef ref, SystemCache cache) =>
Pubspec.load(_verifiedPackagePath(ref), cache.sources,
expectedName: ref.name);
/// Returns the path for the given [ref].
///
/// Throws a [PackageNotFoundException] if [ref]'s SDK is unavailable or
/// doesn't contain the package.
String _verifiedPackagePath(PackageRef ref) {
final description = ref.description;
if (description is! SdkDescription) {
throw ArgumentError('Wrong source');
}
var sdkName = description.sdk;
var sdk = sdks[sdkName];
if (sdk == null) {
throw PackageNotFoundException('unknown SDK "$sdkName"');
} else if (!sdk.isAvailable) {
throw PackageNotFoundException(
'the ${sdk.name} SDK is not available',
hint: sdk.installMessage,
);
}
var path = sdk.packagePath(ref.name);
if (path != null) return path;
throw PackageNotFoundException(
'could not find package ${ref.name} in the ${sdk.name} SDK');
}
@override
String doGetDirectory(PackageId id, SystemCache cache,
{String? relativeFrom}) {
try {
return _verifiedPackagePath(id.toRef());
} 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) + '.');
}
}
}
class SdkDescription extends Description {
/// The sdk the described package comes from.
final String sdk;
SdkDescription(this.sdk);
@override
String format() => sdk;
@override
Object? serializeForPubspec({
required String? containingDir,
required LanguageVersion languageVersion,
}) {
return sdk;
}
@override
Source get source => SdkSource.instance;
@override
int get hashCode => sdk.hashCode;
@override
bool operator ==(Object other) {
return other is SdkDescription && other.sdk == sdk;
}
}
class ResolvedSdkDescription extends ResolvedDescription {
@override
SdkDescription get description => super.description as SdkDescription;
ResolvedSdkDescription(SdkDescription description) : super(description);
@override
Object? serializeForLockfile({required String? containingDir}) {
return description.sdk;
}
@override
int get hashCode => description.hashCode;
@override
bool operator ==(Object other) {
return other is ResolvedSdkDescription && other.description == description;
}
}