blob: 94bcfa038bc6ca6c19dc176e48a434070dba31d8 [file] [log] [blame]
// Copyright 2024, 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 'useful_directory.dart';
import 'package:file/file.dart';
import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
/// Wraps a function in a dry-run [Zone].
///
/// [Package.isDryRun] returns [isDryRun] if accessed from the zone.
///
/// Returns the return value of the function [code].
R dryRun<R>(R Function() code, bool isDryRun) =>
runZoned(code, zoneValues: {#_dryRun: isDryRun});
/// A resource creation strategy.
///
/// A package [fetch]es inputs from a [source], creates a [build], and then
/// [upload]s the resulting artifacts to an implementation dependent location.
///
/// Packages must implement the [fetch], [build] and [upload] methods to decide
/// the strategy for each operation. Implementations should use [log] to log
/// additional information.
///
/// No artifacts must be uploaded if [isDryRun] is `true`.
abstract class Package {
final String name;
final Uri source;
Logger log;
bool get isDryRun => Zone.current[#_dryRun] ?? false;
Package(this.name, this.source) : log = Logger(name);
/// Creates the package by fetching the [source], building the the artifacts,
/// and uploading them.
///
/// The package is created in subdirectory of [base] with the [name] of this
/// package.
Future<void> create(Directory base, bool provenance) async {
final workingDir = await base.resolve(name).create(recursive: true);
log.info("Created working directory: $workingDir");
log.info("Fetching $source...");
final fetched = await fetch(workingDir);
log.info("Building...");
final built = await build(fetched, workingDir);
log.info("Checking build...");
if (!await check(built)) {
log.severe("Build failed to produce the expected outputs.");
throw StateError("Build failed!");
}
log.info("Uploading $built...");
await upload(built, provenance);
log.info("Done!");
}
/// Fetches the [source] into [workingDir].
///
/// This method is intended to be called by [create] and must be implemented
/// to specify how [source] is fetched.
///
/// Returns the fetched source files and directories.
@visibleForOverriding
Future<List<FileSystemEntity>> fetch(Directory workingDir);
/// Builds artifacts from the [fetch]ed [inputs].
///
/// This method is intended to be called by [create] and must be implemented
/// to specify how artifacts are built.
///
/// Returns a directory containing the build outputs.
@visibleForOverriding
Future<Directory> build(List<FileSystemEntity> inputs, Directory workingDir);
/// Checks the [artifacts] created by [build].
///
/// This method is intended to be called by [create] and must be implemented
/// to check that the build has produced the expected outputs.
@visibleForOverriding
Future<bool> check(Directory artifacts);
/// Uploads the [artifacts] created by [build].
///
/// This method is intended to be called by [create] and must be implemented
/// to specify how artifacts are uploaded.
///
/// Must not upload any artifacts if [dryRun] is `true`.
@visibleForOverriding
Future<void> upload(Directory artifacts, bool provenance);
}