install method
Future<File?>
install({
- required PrebuiltManifest manifest,
- required NativeTarget target,
- required String libraryStem,
- required ArtifactPayload payload,
- required Directory cacheDirectory,
- Logger? logger,
override
Implementation
@override
Future<File?> install({
required PrebuiltManifest manifest,
required NativeTarget target,
required String libraryStem,
required ArtifactPayload payload,
required Directory cacheDirectory,
Logger? logger,
}) async {
final artifact = manifest.artifacts[target.label];
if (artifact == null) {
logger?.info('No manifest entry for ${target.label}.');
return null;
}
if (!_hasValidHashes(artifact)) {
logger?.info(
'Manifest entry for ${target.label} is missing hashes; skipping prebuilt.',
);
return null;
}
logger?.info(
'Found manifest entry for ${target.label}: ${artifact.archiveName}.',
);
final canonicalName = canonicalLibraryName(
target: target,
libraryStem: libraryStem,
payload: payload,
);
final cacheDirForArtifact = _artifactCacheDirectory(
cacheDirectory,
manifest.release,
target.label,
artifact,
canonicalName,
);
final cachedFile = File(p.join(cacheDirForArtifact.path, canonicalName));
final lock = CacheLock(
File(
p.join(
cacheDirectory.path,
'.locks',
_cacheKey(manifest.release, target.label, artifact, canonicalName) +
'.lock',
),
),
);
return lock.withLock(() async {
if (cachedFile.existsSync()) {
logger?.info('Using cached prebuilt candidate: ${cachedFile.path}.');
final actualHash = await ArchiveReader.sha256Hash(cachedFile);
if (actualHash == artifact.payloadSha256) {
try {
inspector.inspect(
cachedFile,
target: target,
canonicalName: canonicalName,
);
logger?.info('Verified cached prebuilt ${target.label}.');
return cachedFile;
} on BinaryFormatException {
logger?.warning(
'Cached file failed binary inspection; deleting ${cachedFile.path}.',
);
cachedFile.deleteSync();
}
} else {
logger?.warning(
'Cached payload hash mismatch for ${target.label}; deleting ${cachedFile.path}.',
);
cachedFile.deleteSync();
}
}
final source = manifest.release;
final archiveFile = File(
p.join(cacheDirForArtifact.path, artifact.archiveName),
);
try {
logger?.info('Downloading ${artifact.archiveName} from $source.');
await downloader.downloadReleaseArtifact(
source: source,
archiveName: artifact.archiveName,
targetPath: archiveFile,
logger: logger,
);
} on HttpDownloadException {
logger?.warning(
'Download failed for ${artifact.archiveName}; will fall back.',
);
return null;
}
final archiveHash = await ArchiveReader.sha256Hash(archiveFile);
if (archiveHash != artifact.archiveSha256) {
archiveFile.deleteSync();
throw StateError(
'Archive hash mismatch for ${artifact.archiveName}:\n'
' expected: ${artifact.archiveSha256}\n'
' actual: $archiveHash',
);
}
logger?.info('Verified archive hash for ${artifact.archiveName}.');
final acceptVersioned = switch (artifact.payload) {
DynamicLibraryPayload(:final acceptVersionedNames) =>
acceptVersionedNames,
StaticLibraryPayload() => false,
};
final extracted = archiveReader.extractMatchingEntry(
archiveFile: archiveFile,
outputDir: cacheDirForArtifact,
selection: ArchiveSelectionContext(
canonicalName: canonicalName,
acceptVersionedNames: acceptVersioned,
),
);
logger?.info('Extracted archive entry for ${target.label}.');
archiveFile.deleteSync();
if (extracted == null) return null;
final payloadHash = await ArchiveReader.sha256Hash(extracted);
if (payloadHash != artifact.payloadSha256) {
extracted.deleteSync();
throw StateError(
'Payload hash mismatch for $canonicalName:\n'
' expected: ${artifact.payloadSha256}\n'
' actual: $payloadHash',
);
}
inspector.inspect(
extracted,
target: target,
canonicalName: canonicalName,
);
logger?.info('Installed prebuilt ${target.label} to ${extracted.path}.');
return extracted;
});
}