install method

  1. @override
Future<File?> install({
  1. required PrebuiltManifest manifest,
  2. required NativeTarget target,
  3. required String libraryStem,
  4. required ArtifactPayload payload,
  5. required Directory cacheDirectory,
  6. 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;
  });
}