404 lines
10 KiB
Dart
404 lines
10 KiB
Dart
|
|
// ignore_for_file: avoid_print
|
||
|
|
|
||
|
|
import 'dart:convert';
|
||
|
|
import 'dart:io';
|
||
|
|
import 'package:args/command_runner.dart';
|
||
|
|
import 'package:path/path.dart';
|
||
|
|
|
||
|
|
enum PlatformType {
|
||
|
|
windows,
|
||
|
|
linux,
|
||
|
|
android,
|
||
|
|
macos,
|
||
|
|
}
|
||
|
|
|
||
|
|
enum Arch {
|
||
|
|
amd64,
|
||
|
|
arm64,
|
||
|
|
}
|
||
|
|
|
||
|
|
class BuildLibItem {
|
||
|
|
PlatformType platform;
|
||
|
|
Arch arch;
|
||
|
|
String archName;
|
||
|
|
|
||
|
|
BuildLibItem({
|
||
|
|
required this.platform,
|
||
|
|
required this.arch,
|
||
|
|
required this.archName,
|
||
|
|
});
|
||
|
|
|
||
|
|
String get dynamicLibExtensionName {
|
||
|
|
final String extensionName;
|
||
|
|
switch (platform) {
|
||
|
|
case PlatformType.android || PlatformType.linux:
|
||
|
|
extensionName = "so";
|
||
|
|
break;
|
||
|
|
case PlatformType.windows:
|
||
|
|
extensionName = "dll";
|
||
|
|
break;
|
||
|
|
case PlatformType.macos:
|
||
|
|
extensionName = "dylib";
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
return extensionName;
|
||
|
|
}
|
||
|
|
|
||
|
|
String get os {
|
||
|
|
if (platform == PlatformType.macos) {
|
||
|
|
return "darwin";
|
||
|
|
}
|
||
|
|
return platform.name;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
class Build {
|
||
|
|
static List<BuildLibItem> get buildItems => [
|
||
|
|
BuildLibItem(
|
||
|
|
platform: PlatformType.macos,
|
||
|
|
arch: Arch.amd64,
|
||
|
|
archName: 'amd64',
|
||
|
|
),
|
||
|
|
BuildLibItem(
|
||
|
|
platform: PlatformType.windows,
|
||
|
|
arch: Arch.amd64,
|
||
|
|
archName: 'amd64',
|
||
|
|
),
|
||
|
|
BuildLibItem(
|
||
|
|
platform: PlatformType.android,
|
||
|
|
arch: Arch.arm64,
|
||
|
|
archName: 'arm64-v8a',
|
||
|
|
),
|
||
|
|
BuildLibItem(
|
||
|
|
platform: PlatformType.android,
|
||
|
|
arch: Arch.amd64,
|
||
|
|
archName: 'x86_64',
|
||
|
|
),
|
||
|
|
BuildLibItem(
|
||
|
|
platform: PlatformType.linux,
|
||
|
|
arch: Arch.amd64,
|
||
|
|
archName: 'amd64',
|
||
|
|
),
|
||
|
|
];
|
||
|
|
|
||
|
|
static String get appName => "FlClash";
|
||
|
|
|
||
|
|
static String get libName => "libclash";
|
||
|
|
|
||
|
|
static String get outDir => join(current, libName);
|
||
|
|
|
||
|
|
static String get _coreDir => join(current, "core");
|
||
|
|
|
||
|
|
static String get distPath => join(current, "dist");
|
||
|
|
|
||
|
|
static String _getCc(BuildLibItem buildItem) {
|
||
|
|
final environment = Platform.environment;
|
||
|
|
if (buildItem.platform == PlatformType.android) {
|
||
|
|
final ndk = environment["ANDROID_NDK"];
|
||
|
|
assert(ndk != null);
|
||
|
|
final prebuiltDir =
|
||
|
|
Directory(join(ndk!, "toolchains", "llvm", "prebuilt"));
|
||
|
|
final prebuiltDirList = prebuiltDir.listSync();
|
||
|
|
final map = {
|
||
|
|
"armeabi-v7a": "armv7a-linux-androideabi21-clang",
|
||
|
|
"arm64-v8a": "aarch64-linux-android21-clang",
|
||
|
|
"x86": "i686-linux-android21-clang",
|
||
|
|
"x86_64": "x86_64-linux-android21-clang"
|
||
|
|
};
|
||
|
|
return join(
|
||
|
|
prebuiltDirList.first.path,
|
||
|
|
"bin",
|
||
|
|
map[buildItem.archName],
|
||
|
|
);
|
||
|
|
}
|
||
|
|
return "gcc";
|
||
|
|
}
|
||
|
|
|
||
|
|
static get tags => "with_gvisor";
|
||
|
|
|
||
|
|
static Future<void> exec(
|
||
|
|
List<String> executable, {
|
||
|
|
String? name,
|
||
|
|
Map<String, String>? environment,
|
||
|
|
String? workingDirectory,
|
||
|
|
bool runInShell = true,
|
||
|
|
}) async {
|
||
|
|
if (name != null) print("run $name");
|
||
|
|
final process = await Process.start(
|
||
|
|
executable[0],
|
||
|
|
executable.sublist(1),
|
||
|
|
environment: environment,
|
||
|
|
workingDirectory: workingDirectory,
|
||
|
|
runInShell: runInShell,
|
||
|
|
);
|
||
|
|
process.stdout.listen((data) {
|
||
|
|
print(utf8.decode(data));
|
||
|
|
});
|
||
|
|
process.stderr.listen((data) {
|
||
|
|
print(utf8.decode(data));
|
||
|
|
});
|
||
|
|
final exitCode = await process.exitCode;
|
||
|
|
if (exitCode != 0 && name != null) throw "$name error";
|
||
|
|
}
|
||
|
|
|
||
|
|
static buildLib({
|
||
|
|
required PlatformType platform,
|
||
|
|
Arch? arch,
|
||
|
|
}) async {
|
||
|
|
final items = buildItems.where(
|
||
|
|
(element) {
|
||
|
|
return element.platform == platform && arch == null
|
||
|
|
? true
|
||
|
|
: element.arch == arch;
|
||
|
|
},
|
||
|
|
).toList();
|
||
|
|
for (final item in items) {
|
||
|
|
final outFileDir = join(
|
||
|
|
outDir,
|
||
|
|
item.platform.name,
|
||
|
|
item.archName,
|
||
|
|
);
|
||
|
|
final file = File(outFileDir);
|
||
|
|
if (file.existsSync()) {
|
||
|
|
file.deleteSync(recursive: true);
|
||
|
|
}
|
||
|
|
final outPath = join(
|
||
|
|
outFileDir,
|
||
|
|
"$libName.${item.dynamicLibExtensionName}",
|
||
|
|
);
|
||
|
|
final Map<String, String> env = {};
|
||
|
|
env["GOOS"] = item.os;
|
||
|
|
env["GOARCH"] = item.arch.name;
|
||
|
|
env["CGO_ENABLED"] = "1";
|
||
|
|
env["CC"] = _getCc(item);
|
||
|
|
if (item.platform == PlatformType.macos) {
|
||
|
|
env["CGO_CFLAGS"] = "-mmacosx-version-min=10.11";
|
||
|
|
env["CGO_LDFLAGS"] = "-mmacosx-version-min=10.11";
|
||
|
|
}
|
||
|
|
|
||
|
|
await exec(
|
||
|
|
[
|
||
|
|
"go",
|
||
|
|
"build",
|
||
|
|
"-ldflags=-w -s",
|
||
|
|
"-tags=$tags",
|
||
|
|
"-buildmode=c-shared",
|
||
|
|
"-o",
|
||
|
|
outPath,
|
||
|
|
],
|
||
|
|
name: "build libclash",
|
||
|
|
environment: env,
|
||
|
|
workingDirectory: _coreDir,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static List<String> getExecutable(String command) {
|
||
|
|
return command.split(" ");
|
||
|
|
}
|
||
|
|
|
||
|
|
static getDistributor() async {
|
||
|
|
final distributorDir = join(
|
||
|
|
current,
|
||
|
|
"plugins",
|
||
|
|
"flutter_distributor",
|
||
|
|
"packages",
|
||
|
|
"flutter_distributor",
|
||
|
|
);
|
||
|
|
|
||
|
|
await exec(
|
||
|
|
name: "clean distributor",
|
||
|
|
Build.getExecutable("flutter clean"),
|
||
|
|
workingDirectory: distributorDir,
|
||
|
|
);
|
||
|
|
await exec(
|
||
|
|
name: "get distributor",
|
||
|
|
Build.getExecutable("dart pub global activate -s path $distributorDir"),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
static copyFile(String sourceFilePath, String destinationFilePath) {
|
||
|
|
final sourceFile = File(sourceFilePath);
|
||
|
|
if (!sourceFile.existsSync()) {
|
||
|
|
throw "SourceFilePath not exists";
|
||
|
|
}
|
||
|
|
final destinationFile = File(destinationFilePath);
|
||
|
|
final destinationDirectory = destinationFile.parent;
|
||
|
|
if (!destinationDirectory.existsSync()) {
|
||
|
|
destinationDirectory.createSync(recursive: true);
|
||
|
|
}
|
||
|
|
try {
|
||
|
|
sourceFile.copySync(destinationFilePath);
|
||
|
|
print("File copied successfully!");
|
||
|
|
} catch (e) {
|
||
|
|
print("Failed to copy file: $e");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
class BuildCommand extends Command {
|
||
|
|
PlatformType platform;
|
||
|
|
|
||
|
|
BuildCommand({
|
||
|
|
required this.platform,
|
||
|
|
}) {
|
||
|
|
argParser.addOption(
|
||
|
|
"build",
|
||
|
|
valueHelp: [
|
||
|
|
'all',
|
||
|
|
'lib',
|
||
|
|
].join(','),
|
||
|
|
help: 'The $name build type',
|
||
|
|
);
|
||
|
|
argParser.addOption(
|
||
|
|
"arch",
|
||
|
|
valueHelp: arches.map((e) => e.name).join(','),
|
||
|
|
help: 'The $name build arch',
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
String get description => "build $name application";
|
||
|
|
|
||
|
|
@override
|
||
|
|
String get name => platform.name;
|
||
|
|
|
||
|
|
List<Arch> get arches => Build.buildItems
|
||
|
|
.where((element) => element.platform == platform)
|
||
|
|
.map((e) => e.arch)
|
||
|
|
.toList();
|
||
|
|
|
||
|
|
Future<void> _buildLib(Arch? arch) async {
|
||
|
|
await Build.buildLib(platform: platform, arch: arch);
|
||
|
|
}
|
||
|
|
|
||
|
|
_getLinuxDependencies() async {
|
||
|
|
await Build.exec(
|
||
|
|
Build.getExecutable("sudo apt update -y"),
|
||
|
|
);
|
||
|
|
await Build.exec(
|
||
|
|
Build.getExecutable("sudo apt install -y ninja-build libgtk-3-dev"),
|
||
|
|
);
|
||
|
|
await Build.exec(
|
||
|
|
Build.getExecutable("sudo apt install -y libayatana-appindicator3-dev"),
|
||
|
|
);
|
||
|
|
await Build.exec(
|
||
|
|
Build.getExecutable("sudo apt install -y rpm patchelf"),
|
||
|
|
);
|
||
|
|
await Build.exec(
|
||
|
|
Build.getExecutable("sudo apt install -y locate"),
|
||
|
|
);
|
||
|
|
await Build.exec(
|
||
|
|
Build.getExecutable("sudo apt install -y libfuse2"),
|
||
|
|
);
|
||
|
|
await Build.exec(
|
||
|
|
Build.getExecutable(
|
||
|
|
"wget -O appimagetool https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage",
|
||
|
|
),
|
||
|
|
);
|
||
|
|
await Build.exec(
|
||
|
|
Build.getExecutable(
|
||
|
|
"chmod +x appimagetool",
|
||
|
|
),
|
||
|
|
);
|
||
|
|
await Build.exec(
|
||
|
|
Build.getExecutable(
|
||
|
|
"sudo mv appimagetool /usr/local/bin/",
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
_getMacosDependencies() async {
|
||
|
|
await Build.exec(
|
||
|
|
Build.getExecutable("npm install -g appdmg"),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
_buildDistributor({
|
||
|
|
required PlatformType platform,
|
||
|
|
required String targets,
|
||
|
|
String args = '',
|
||
|
|
}) async {
|
||
|
|
await Build.getDistributor();
|
||
|
|
await Build.exec(
|
||
|
|
name: name,
|
||
|
|
Build.getExecutable(
|
||
|
|
"flutter_distributor package --skip-clean --platform ${platform.name} --targets $targets --flutter-build-args=verbose $args",
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
Future<void> run() async {
|
||
|
|
final String build = argResults?['build'] ?? 'all';
|
||
|
|
final archName = argResults?['arch'];
|
||
|
|
final currentArches =
|
||
|
|
arches.where((element) => element.name == archName).toList();
|
||
|
|
final arch = currentArches.isEmpty ? null : arches.first;
|
||
|
|
await _buildLib(arch);
|
||
|
|
if (build != "all") {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
switch (platform) {
|
||
|
|
case PlatformType.windows:
|
||
|
|
_buildDistributor(
|
||
|
|
platform: platform,
|
||
|
|
targets: "exe,zip",
|
||
|
|
args: "--description amd64",
|
||
|
|
);
|
||
|
|
break;
|
||
|
|
case PlatformType.linux:
|
||
|
|
await _getLinuxDependencies();
|
||
|
|
_buildDistributor(
|
||
|
|
platform: platform,
|
||
|
|
targets: "appimage,deb,rpm",
|
||
|
|
args: "--description amd64",
|
||
|
|
);
|
||
|
|
break;
|
||
|
|
case PlatformType.android:
|
||
|
|
final targetMap = {
|
||
|
|
Arch.amd64: "android-x64",
|
||
|
|
Arch.arm64: "android-arm64"
|
||
|
|
};
|
||
|
|
final defaultArches = [Arch.amd64, Arch.arm64];
|
||
|
|
final defaultTargets = defaultArches
|
||
|
|
.where((element) => arch == null ? true : element == arch)
|
||
|
|
.map((e) => targetMap[e])
|
||
|
|
.toList();
|
||
|
|
_buildDistributor(
|
||
|
|
platform: platform,
|
||
|
|
targets: "apk",
|
||
|
|
args:
|
||
|
|
"--flutter-build-args split-per-abi --build-target-platform ${defaultTargets.join(",")}",
|
||
|
|
);
|
||
|
|
break;
|
||
|
|
case PlatformType.macos:
|
||
|
|
await _getMacosDependencies();
|
||
|
|
_buildDistributor(
|
||
|
|
platform: platform,
|
||
|
|
targets: "dmg",
|
||
|
|
args: "--description amd64",
|
||
|
|
);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
main(args) async {
|
||
|
|
final runner = CommandRunner("setup", "build Application");
|
||
|
|
runner.addCommand(BuildCommand(platform: PlatformType.android));
|
||
|
|
if (Platform.isWindows) {
|
||
|
|
runner.addCommand(BuildCommand(platform: PlatformType.windows));
|
||
|
|
}
|
||
|
|
if (Platform.isLinux) {
|
||
|
|
runner.addCommand(BuildCommand(platform: PlatformType.linux));
|
||
|
|
}
|
||
|
|
if (Platform.isMacOS) {
|
||
|
|
runner.addCommand(BuildCommand(platform: PlatformType.macos));
|
||
|
|
}
|
||
|
|
if (args.isEmpty) {
|
||
|
|
args = [Platform.operatingSystem];
|
||
|
|
}
|
||
|
|
runner.run(args);
|
||
|
|
}
|