mirror of
https://mirror.ghproxy.com/https://github.com/StarCitizenToolBox/app.git
synced 2024-12-24 02:23:41 +08:00
203 lines
5.9 KiB
Dart
203 lines
5.9 KiB
Dart
/// This is copied from Cargokit (which is the official way to use it currently)
|
|
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
|
|
|
import 'dart:io';
|
|
|
|
import 'package:ed25519_edwards/ed25519_edwards.dart';
|
|
import 'package:github/github.dart';
|
|
import 'package:logging/logging.dart';
|
|
import 'package:path/path.dart' as path;
|
|
|
|
import 'artifacts_provider.dart';
|
|
import 'builder.dart';
|
|
import 'cargo.dart';
|
|
import 'crate_hash.dart';
|
|
import 'options.dart';
|
|
import 'rustup.dart';
|
|
import 'target.dart';
|
|
|
|
final _log = Logger('precompile_binaries');
|
|
|
|
class PrecompileBinaries {
|
|
PrecompileBinaries({
|
|
required this.privateKey,
|
|
required this.githubToken,
|
|
required this.repositorySlug,
|
|
required this.manifestDir,
|
|
required this.targets,
|
|
this.androidSdkLocation,
|
|
this.androidNdkVersion,
|
|
this.androidMinSdkVersion,
|
|
this.tempDir,
|
|
});
|
|
|
|
final PrivateKey privateKey;
|
|
final String githubToken;
|
|
final RepositorySlug repositorySlug;
|
|
final String manifestDir;
|
|
final List<Target> targets;
|
|
final String? androidSdkLocation;
|
|
final String? androidNdkVersion;
|
|
final int? androidMinSdkVersion;
|
|
final String? tempDir;
|
|
|
|
static String fileName(Target target, String name) {
|
|
return '${target.rust}_$name';
|
|
}
|
|
|
|
static String signatureFileName(Target target, String name) {
|
|
return '${target.rust}_$name.sig';
|
|
}
|
|
|
|
Future<void> run() async {
|
|
final crateInfo = CrateInfo.load(manifestDir);
|
|
|
|
final targets = List.of(this.targets);
|
|
if (targets.isEmpty) {
|
|
targets.addAll([
|
|
...Target.buildableTargets(),
|
|
if (androidSdkLocation != null) ...Target.androidTargets(),
|
|
]);
|
|
}
|
|
|
|
_log.info('Precompiling binaries for $targets');
|
|
|
|
final hash = CrateHash.compute(manifestDir);
|
|
_log.info('Computed crate hash: $hash');
|
|
|
|
final String tagName = 'precompiled_$hash';
|
|
|
|
final github = GitHub(auth: Authentication.withToken(githubToken));
|
|
final repo = github.repositories;
|
|
final release = await _getOrCreateRelease(
|
|
repo: repo,
|
|
tagName: tagName,
|
|
packageName: crateInfo.packageName,
|
|
hash: hash,
|
|
);
|
|
|
|
final tempDir = this.tempDir != null
|
|
? Directory(this.tempDir!)
|
|
: Directory.systemTemp.createTempSync('precompiled_');
|
|
|
|
tempDir.createSync(recursive: true);
|
|
|
|
final crateOptions = CargokitCrateOptions.load(
|
|
manifestDir: manifestDir,
|
|
);
|
|
|
|
final buildEnvironment = BuildEnvironment(
|
|
configuration: BuildConfiguration.release,
|
|
crateOptions: crateOptions,
|
|
targetTempDir: tempDir.path,
|
|
manifestDir: manifestDir,
|
|
crateInfo: crateInfo,
|
|
isAndroid: androidSdkLocation != null,
|
|
androidSdkPath: androidSdkLocation,
|
|
androidNdkVersion: androidNdkVersion,
|
|
androidMinSdkVersion: androidMinSdkVersion,
|
|
);
|
|
|
|
final rustup = Rustup();
|
|
|
|
for (final target in targets) {
|
|
final artifactNames = getArtifactNames(
|
|
target: target,
|
|
libraryName: crateInfo.packageName,
|
|
remote: true,
|
|
);
|
|
|
|
if (artifactNames.every((name) {
|
|
final fileName = PrecompileBinaries.fileName(target, name);
|
|
return (release.assets ?? []).any((e) => e.name == fileName);
|
|
})) {
|
|
_log.info("All artifacts for $target already exist - skipping");
|
|
continue;
|
|
}
|
|
|
|
_log.info('Building for $target');
|
|
|
|
final builder =
|
|
RustBuilder(target: target, environment: buildEnvironment);
|
|
builder.prepare(rustup);
|
|
final res = await builder.build();
|
|
|
|
final assets = <CreateReleaseAsset>[];
|
|
for (final name in artifactNames) {
|
|
final file = File(path.join(res, name));
|
|
if (!file.existsSync()) {
|
|
throw Exception('Missing artifact: ${file.path}');
|
|
}
|
|
|
|
final data = file.readAsBytesSync();
|
|
final create = CreateReleaseAsset(
|
|
name: PrecompileBinaries.fileName(target, name),
|
|
contentType: "application/octet-stream",
|
|
assetData: data,
|
|
);
|
|
final signature = sign(privateKey, data);
|
|
final signatureCreate = CreateReleaseAsset(
|
|
name: signatureFileName(target, name),
|
|
contentType: "application/octet-stream",
|
|
assetData: signature,
|
|
);
|
|
bool verified = verify(public(privateKey), data, signature);
|
|
if (!verified) {
|
|
throw Exception('Signature verification failed');
|
|
}
|
|
assets.add(create);
|
|
assets.add(signatureCreate);
|
|
}
|
|
_log.info('Uploading assets: ${assets.map((e) => e.name)}');
|
|
for (final asset in assets) {
|
|
// This seems to be failing on CI so do it one by one
|
|
int retryCount = 0;
|
|
while (true) {
|
|
try {
|
|
await repo.uploadReleaseAssets(release, [asset]);
|
|
break;
|
|
} on Exception catch (e) {
|
|
if (retryCount == 10) {
|
|
rethrow;
|
|
}
|
|
++retryCount;
|
|
_log.shout(
|
|
'Upload failed (attempt $retryCount, will retry): ${e.toString()}');
|
|
await Future.delayed(Duration(seconds: 2));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_log.info('Cleaning up');
|
|
tempDir.deleteSync(recursive: true);
|
|
}
|
|
|
|
Future<Release> _getOrCreateRelease({
|
|
required RepositoriesService repo,
|
|
required String tagName,
|
|
required String packageName,
|
|
required String hash,
|
|
}) async {
|
|
Release release;
|
|
try {
|
|
_log.info('Fetching release $tagName');
|
|
release = await repo.getReleaseByTagName(repositorySlug, tagName);
|
|
} on ReleaseNotFound {
|
|
_log.info('Release not found - creating release $tagName');
|
|
release = await repo.createRelease(
|
|
repositorySlug,
|
|
CreateRelease.from(
|
|
tagName: tagName,
|
|
name: 'Precompiled binaries ${hash.substring(0, 8)}',
|
|
targetCommitish: null,
|
|
isDraft: false,
|
|
isPrerelease: false,
|
|
body: 'Precompiled binaries for crate $packageName, '
|
|
'crate hash $hash.',
|
|
));
|
|
}
|
|
return release;
|
|
}
|
|
}
|