diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index f07faabc3..de6f30412 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -4,11 +4,11 @@ on:
push:
tags:
- 'v*'
+ workflow_dispatch:
env:
- IS_STABLE: ${{ !contains(github.ref, '-') }}
+ IS_STABLE: ${{ github.ref_type == 'tag' && !contains(github.ref, '-') }}
IS_PRERELEASE: ${{ contains(github.ref_name, 'pre') }}
- SHOULD_RELEASE: ${{ !contains(github.ref, '-') || contains(github.ref_name, 'pre') }}
GRADLE_OPTS: -Dorg.gradle.vfs.watch=false
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
SENTRY_BACKEND: none
@@ -16,6 +16,9 @@ env:
jobs:
build:
runs-on: ${{ matrix.os }}
+ defaults:
+ run:
+ shell: bash
strategy:
fail-fast: false
matrix:
@@ -100,7 +103,25 @@ jobs:
if: matrix.platform == 'linux'
run: |
sudo apt-get update
- sudo apt-get install -y libcurl4-openssl-dev ninja-build libgtk-3-dev rpm
+ sudo apt-get install -y \
+ libcurl4-openssl-dev \
+ ninja-build \
+ libgtk-3-dev \
+ libayatana-appindicator3-dev \
+ libkeybinder-3.0-dev \
+ locate \
+ p7zip \
+ ${{ matrix.arch == 'amd64' && 'rpm patchelf libfuse2' || '' }}
+
+ _arch=${{ matrix.arch == 'amd64' && 'x86_64' || 'aarch64' }}
+ wget -O appimagetool https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-${_arch}.AppImage
+ chmod +x appimagetool
+ sudo mv appimagetool /usr/local/bin/
+
+ - name: Install Dependencies (macOS)
+ if: matrix.platform == 'macos'
+ run: |
+ npm install -g appdmg
- name: Setup Golang
uses: actions/setup-go@v6
@@ -136,7 +157,14 @@ jobs:
run: |
flutter pub get
dart run build_runner build -d
- dart setup.dart ${{ matrix.platform }} --arch ${{ matrix.arch }} ${{ env.IS_STABLE == 'true' && '--env stable' || '' }} ${{ matrix.compatible == true && '--compatible' || '' }}
+ dart setup.dart \
+ ${{ matrix.platform }} \
+ --arch ${{ matrix.arch }} \
+ ${{ env.IS_STABLE == 'true' && '--env stable' || '' }} \
+ ${{ matrix.compatible == true && '--compatible' || '' }} \
+ ${{ matrix.platform == 'linux' && '--targets' || ''}} \
+ ${{ matrix.platform == 'linux' && matrix.arch == 'amd64' && 'deb,rpm,appimage' || '' }} \
+ ${{ matrix.platform == 'linux' && matrix.arch == 'arm64' && 'deb' || '' }}
- name: Upload Artifacts
uses: actions/upload-artifact@v6
@@ -178,7 +206,7 @@ jobs:
fi
- name: Release
- if: ${{ env.SHOULD_RELEASE == 'true' }}
+ if: ${{ env.IS_STABLE == 'true' || env.IS_PRERELEASE == 'true' }}
uses: softprops/action-gh-release@v3.0.0
with:
files: ./dist/*
diff --git a/.gitignore b/.gitignore
index b49b58431..e9a07d9f4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,7 +39,7 @@ migrate_working_dir/
/specs/
/dist/
.test/
-.vscode/
+.vscode/*
/services/helper/target/
# Symbolication related
@@ -60,7 +60,14 @@ app.*.map.json
#jniLibs
/android/app/src/main/jniLibs/
+/android/core/src/main/jniLibs/
# FVM Version Cache
.fvm/
-.fvmrc
\ No newline at end of file
+
+# Version Managers
+.fvmrc
+mise.toml
+
+# VS Code Configs
+!.vscode/launch.json
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 000000000..c2d5640eb
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,34 @@
+{
+ // 使用 IntelliSense 了解相关属性。
+ // 悬停以查看现有属性的描述。
+ // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Bettbox",
+ "request": "launch",
+ "type": "dart",
+ "args": [
+ "--dart-define-from-file=libclash/windows/.core_sha256"
+ ]
+ },
+ {
+ "name": "Bettbox (profile mode)",
+ "request": "launch",
+ "type": "dart",
+ "flutterMode": "profile",
+ "args": [
+ "--dart-define-from-file=libclash/windows/.core_sha256"
+ ]
+ },
+ {
+ "name": "Bettbox (release mode)",
+ "request": "launch",
+ "type": "dart",
+ "flutterMode": "release",
+ "args": [
+ "--dart-define-from-file=libclash/windows/.core_sha256"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 627966f05..101908c8f 100644
--- a/Makefile
+++ b/Makefile
@@ -1,10 +1,56 @@
-android_arm64:
- dart ./setup.dart android --arch arm64
-macos_arm64:
+macos_app:
+ dart ./setup.dart macos
+
+macos_app_arm64:
dart ./setup.dart macos --arch arm64
-android_app:
+
+macos_app_amd64:
+ dart ./setup.dart macos --arch amd64
+
+macos_app_universal:
+ dart .setup.dart macos --arch universal
+
+macos_core:
+ dart ./setup.dart macos --out core
+
+macos_core_arm64:
+ dart ./setup.dart macos --arch arm64 --out core
+
+macos_core_amd64:
+ dart ./setup.dart macos --arch amd64 --out core
+
+android_app_all:
dart ./setup.dart android
-android_arm64_core:
+
+android_app_arm64:
+ dart ./setup.dart android --arch arm64
+
+android_app_universal:
+ dart ./setup.dart android --arch universal
+
+android_core_arm64:
dart ./setup.dart android --arch arm64 --out core
-macos_arm64_core:
- dart ./setup.dart macos --arch arm64 --out core
\ No newline at end of file
+
+windows_core_and_helper:
+ dart ./setup.dart windows --out core,helper
+
+windows_core_and_helper_amd64_compatible:
+ dart ./setup.dart windows --out core,helper --arch amd64 --compatible
+
+windows_app:
+ dart ./setup.dart windows
+
+windows_app_amd64_compatible:
+ dart ./setup.dart windows --arch amd64 --compatible
+
+linux:
+ dart ./setup.dart linux --build-only
+
+linux_amd64_compatible:
+ dart ./setup.dart linux --arch amd64 --compatible --build-only
+
+linux_core:
+ dart ./setup.dart linux --out core
+
+linux_core_amd64_compatible:
+ dart ./setup.dart linux --out core --arch amd64 --compatible
diff --git a/README.md b/README.md
index dad1fda2f..cd8f50f6f 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-
⚡ Bettbox
+
Bettbox
Another Better Mihomo Client
@@ -13,30 +13,16 @@ Bettbox意为: Better Experience, Out of the box,卓越体验,亦可开箱
---
-
-
-## 🛠️ 安装与下载
-
-请前往 [Releases](https://github.com/appshubcc/Bettbox/releases) 页面下载最新适合您平台和系统的安装包
-
-* **桌面端**: Windows (x64/arm64), macOS (Intel/Apple Silicon), Linux (x64/arm64)
-* **安卓端**: Android (ARMv8/ ARMv7/ x86_64/ Universal)
-* **Android TV**: 已支持,可选ARMv7 32位
-* **Windows7**: 请配合 [[VxKex]](https://github.com/i486/VxKex/releases) 使用
-* **鸿蒙NEXT**: 请配合 [[卓易通]](https://harmonyos.cool/android-app) 使用
-
----
-### ✈️ Telegram 社区交流
-
-
+## 💬 社区交流
[](https://t.me/appshub_chat) [](https://t.me/appshub_channel)
----
+---
+
## 🚀 核心特性
### 深度体验优化
@@ -63,7 +49,285 @@ Bettbox意为: Better Experience, Out of the box,卓越体验,亦可开箱
---
-## 常见问题
+## 🛠️ 安装与下载
+
+请前往 [Releases](https://github.com/appshubcc/Bettbox/releases) 页面下载最新适合您平台和系统的安装包
+
+* **桌面端**: Windows (x64/arm64), macOS (Intel/Apple Silicon), Linux (x64/arm64)
+* **安卓端**: Android (ARMv8/ ARMv7/ x86_64/ Universal)
+* **Android TV**: 已支持,可选ARMv7 32位
+* **Windows7**: 请配合 [[VxKex]](https://github.com/i486/VxKex/releases) 使用
+* **鸿蒙NEXT**: 请配合 [[卓易通]](https://harmonyos.cool/android-app) 使用
+
+---
+
+## 🏗️ 从源码构建
+
+
+
+### 开发环境
+
+| |版本|备注|
+|---|---|---|
+|Flutter|3.44.0||
+|Go|1.25.x|1.20.x for old CPUs|
+|Java|temurin-17.x||
+|Android SDK|36.1||
+|Android NDK|27.0.12077973
28.2.13676358||
+|Rust|Latest stable|For Windows helper service|
+
+### Windows
+
+* Toolchain: Flutter, Golang, Cargo(for helper service), Visual C++ Build Tools
+* 打包: Inno Setup
+
+1. 克隆本仓库
+
+ ```powershell
+ git clone https://github.com/appshubcc/Bettbox.git
+ cd Bettbox
+ ```
+
+2. 配置开发环境
+
+ * Flutter
+
+ * 从 git 仓库安装
+
+ ```powershell
+ $installPath = "C:\tools" # 根据你的喜好替换
+ cd $installPath
+ git clone -b 3.44.0 https:github.com/flutter/flutter.git
+ $newPath = "$env:Path;$installPath\flutter\bin"
+ [Environment]::SetEnvironmentVariable("Path", "$newPath", "User")
+ cd -
+ ```
+ * 使用 [FVM](https://fvm.app/documentation/getting-started/installation) 或 [mise](https://mise.jdx.dev/installing-mise.html) 安装
+ ```powershell
+ # FVM
+ fvm use 3.44.0
+ # mise
+ mise use flutter@3.44.0
+ ```
+ * Golang
+ * 从[官网](https://go.dev/dl/)下载
+
+ * 使用 mise 安装
+
+ ```powershell
+ mise use go@1.20 # For compatibility for old CPUs
+ ```
+ * Rust (Cargo)
+ * 使用 winget 安装
+
+ ```powershell
+ winget install Rustlang.Rustup
+ ```
+3. 配置构建环境
+ * Visual C++ Build Tools
+
+ 下载 [Visual Studio 2022 (任意版本或生成工具) 引导程序](https://learn.microsoft.com/zh-cn/visualstudio/releases/2022/release-history#evergreen-bootstrappers) [^1] ,勾选“使用 C++ 的桌面开发”并安装
+
+ * Inno Setup (ISCC.exe) [^2]
+
+ ```powershell
+ winget install JRSoftware.InnoSetup --scope machine
+ ```
+
+ * 运行 `flutter doctor` 检查构建环境完整性
+
+4. 构建并打包 [^3] [^4]
+
+ ```powershell
+ flutter pub get
+ dart run build_runner build -d
+ dart setup.dart windows --compatible
+ ```
+
+### Linux
+
+* Toolchain: Flutter, Golang, Clang, Cmake, Ninja, pkg-config
+* 依赖库: libcurl4, gtk3, libayatana-appindicator, libkeybinder3, libfuse2(for appimagetool)
+* 打包: dpkg-deb, rpm, appimagetool, patchelf(for rpm), locate(for appimagetool)
+
+1. 克隆本仓库
+
+ ```bash
+ git clone https://github.com/appshubcc/Bettbox.git
+ cd Bettbox
+ ```
+2. 配置开发环境
+
+ * Flutter
+ * 使用 [FVM](https://fvm.app/documentation/getting-started/installation) 安装
+
+ ```bash
+ fvm use 3.44.0
+ ```
+
+ * Golang
+ * 使用包管理器安装
+
+ ```bash
+ sudo apt install golang-go
+ ```
+
+ * 使用 [mise](https://mise.jdx.dev/installing-mise.html) 安装
+
+ ```bash
+ mise use go@1.20 # For compatibility for old CPUS
+ ```
+
+3. 安装构建依赖
+
+ ```bash
+ # 按需安装
+ sudo apt install build-essential clang cmake ninja-build
+ sudo apt install libcurl4-openssl-dev libgtk-3-dev lbayatana-appindicator3-dev libkeybinder-3.0-dev libfuse2
+ sudo apt install dpkg-deb rpm patchelf locate
+ wget https://github.com/Appimage/AppimageKit/releases/download/containuous/appimagetool-x86_64.AppImage
+ chmod +x appimagetool
+ sudo mv appimagetool /usr/local/bin/
+ ```
+
+4. 构建并打包 [^3] [^4]
+
+ ```bash
+ flutter pub get
+ dart run build_runner build -d
+ # 按需填写 "--targets" 参数,使用 "," 分隔
+ dart setup.dart linux --targets=deb,rpm,appimage --compatible
+ ```
+
+### Mac OS
+
+* Toolchain: Flutter, Golang, Xcode command-line tools, CocoaPods
+* 打包: appdmg
+
+1. 克隆本仓库
+
+ ```zsh
+ git clone https://github.com/appshubcc/Bettbox.git
+ cd Bettbox
+ ```
+2. 配置开发环境
+
+ * Flutter
+ * 使用 [FVM](https://fvm.app/documentation/getting-started/installation) 安装
+
+ ```zsh
+ fvm use 3.44.0
+ ```
+
+ * Golang
+
+ * 使用包管理器安装
+
+ ```zsh
+ brew install go
+ ```
+ * 使用 [mise](https://mise.jdx.dev/installing-mise.html) 安装
+
+ ```zsh
+ mise use go@1.20 # For compatibility for old CPUS
+ ```
+ * Xcode, CocoaPods
+
+ 参见 [Flutter 官方文档](https://docs.flutter.dev/platform-integration/macos/setup)
+
+ * appdmg
+
+ ```zsh
+ npm install appdmg
+ ```
+
+3. 构建并打包 [^3] [^4]
+
+ ```zsh
+ flutter pub get
+ dart run build_runner build -d
+ dart setup.dart macos
+ ```
+
+### Android
+
+* Toolchain: Flutter, Golang, CMake, Android SDK, Android SDK Build-Tools, Android SDK Command-line Tools(optional: independent sdkmanager), Android SDK Platform-Tools, Android NDK
+
+1. 配置编程语言
+
+ * Flutter, Golang
+
+ 参见上文
+
+ * Java
+
+ * 使用 [mise](https://mise.jdx.dev/installing-mise.html) 安装
+
+ ```bash
+ mise use java@temurin-17
+ ```
+
+ * 在 Android Studio 中配置
+
+2. 配置构建环境
+
+ * Android SDK, NDK and Tools
+
+ * 使用 Android Studio 配置
+
+ 参见 [Flutter 官方文档](https://docs.flutter.dev/platform-integration/android/setup)
+
+ * 从命令行安装 (以 Linux 平台为例)
+
+ ```bash
+ # 配置环境变量
+ echo 'export ANDROID_HOME=$HOME/.local/opt/android-sdk' >> ~/.bashrc
+ echo 'export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools' >> ~/.bashrc
+ sourec ~/.bashrc
+
+ # 安装 SDK, NDK, Tools
+ ## 使用 sdkmanager.py
+ sudo apt install sdkmanager
+ sdkmanager --install "build-tools;36.0.0" "cmdline-tools;latest" "platform-tools" "platforms;android-36.1" "ndk;27.0.12077973" "ndk;28.2.13676358"
+
+ # 接受许可证
+ flutter doctor --android-lincenses
+ ```
+
+ * 配置 Keystore
+
+ 1. 生成新的 Keystore 或将已有 Keystore 导入至 `./android/app/keystore.jks`
+ 2. (无 Android Studio) 新建或打开 `./android/local.properties`, 添加以下参数
+
+ ```properties
+ keyAlias=<密钥别名>
+ storePassword=
+ keyPassword=<密钥密码>
+ ```
+ * 运行 `flutter doctor` 检查构建环境完整性
+
+3. 构建并打包 [^4]
+
+ ```bash
+ flutter pub get
+ dart run build-runner build -d
+ dart setup.dart android --arch=universal
+ ```
+
+[^1]: Flutter ≤3.38.2 与 Visual Studio 2026 不兼容,更高版本请忽略
+
+
+[^2]: 当前版本 flutter_distributor (v0.4.2) 硬编码了 Inno Setup 的安装路径 (`C:\Program Files (x86)\Inno Setup 6`), 务必以管理员权限安装
+
+[^3]: 使用 `--compatible` 参数为[旧 CPU](https://go.dev/wiki/MinimumRequirements#amd64) 构建
+
+[^4]: 运行 `dart setup.dart help ` 查看更多选项
+
+
+
+---
+
+## 💡 常见问题
1. 安装后无法启动?
- 安卓端旧设备,请检查是否支持Bettbox的最低系统要求:Android 8.0+
@@ -85,23 +349,13 @@ Bettbox意为: Better Experience, Out of the box,卓越体验,亦可开箱
---
-## 开发构建
-
-以Windows为例:
-
-* 你需要一台Windows设备(≥Windows 10)
-* 必要的软件依赖:Visual Studio,Flutter SDK≥3.35,Golang,Inno Setup,Rust
-* dart .\setup.dart windows --arch amd64 --compatible(可选兼容版本)
-
----
-
-## 赞助链接
+## ☕ 赞助链接
* **暂无**: 所有功能均免费无广告,您可以点击右上角的⭐Star,让开发者获得认同感也是一种支持方式
---
-## 致谢
+## 🤝 致谢
Bettbox 的诞生依赖以下根基项目:
@@ -116,4 +370,4 @@ Bettbox 的诞生依赖以下根基项目:
## 📄 开源协议
-延续原项目 GPL-3.0 license 开源协议
+延续原项目 [GPL-3.0 license](LICENSE) 开源协议
diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt
index 359ca5648..fab40bfd2 100644
--- a/linux/CMakeLists.txt
+++ b/linux/CMakeLists.txt
@@ -46,6 +46,8 @@ endif()
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_14)
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
+ # Delete this when https://github.com/leanflutter/tray_manager/issues/67 is resolved.
+ target_compile_options(${TARGET} PRIVATE -Wno-deprecated-declarations)
target_compile_options(${TARGET} PRIVATE "$<$>:-O3>")
target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>")
endfunction()
diff --git a/pubspec.lock b/pubspec.lock
index ff256d753..2b03c6d37 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -598,12 +598,13 @@ packages:
source: hosted
version: "0.2.3"
hotkey_manager_linux:
- dependency: transitive
+ dependency: "direct overridden"
description:
- name: hotkey_manager_linux
- sha256: "83676bda8210a3377bc6f1977f193bc1dbdd4c46f1bdd02875f44b6eff9a8473"
- url: "https://pub.dev"
- source: hosted
+ path: "packages/hotkey_manager_linux"
+ ref: fix-build-failure
+ resolved-ref: "1b31e3ca4ab017ee72f54a7065b2073a88403d61"
+ url: "https://github.com/gepbird/hotkey_manager.git"
+ source: git
version: "0.2.0"
hotkey_manager_macos:
dependency: transitive
diff --git a/pubspec.yaml b/pubspec.yaml
index 5d32e996f..75123949a 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -70,6 +70,13 @@ dependencies:
synchronized: ^3.4.0
flutter_displaymode: ^0.7.0
restart_app: ^1.8.3
+dependency_overrides:
+ # Fix compile failure: https://github.com/leanflutter/hotkey_manager/pull/66
+ hotkey_manager_linux:
+ git:
+ url: https://github.com/gepbird/hotkey_manager.git
+ path: packages/hotkey_manager_linux
+ ref: fix-build-failure
dev_dependencies:
flutter_test:
sdk: flutter
diff --git a/setup.dart b/setup.dart
index 1a06b8166..60def7a36 100644
--- a/setup.dart
+++ b/setup.dart
@@ -7,42 +7,32 @@ import 'package:args/command_runner.dart';
import 'package:crypto/crypto.dart';
import 'package:path/path.dart';
-enum Target { windows, linux, android, macos }
+enum TargetPlatform { windows, linux, android, macos }
-extension TargetExt on Target {
+extension PlatformExt on TargetPlatform {
String get os {
- if (this == Target.macos) {
+ if (this == TargetPlatform.macos) {
return 'darwin';
}
return name;
}
- bool get same {
- if (this == Target.android) {
- return true;
- }
- if (Platform.isWindows && this == Target.windows) {
- return true;
- }
- if (Platform.isLinux && this == Target.linux) {
- return true;
- }
- if (Platform.isMacOS && this == Target.macos) {
- return true;
- }
- return false;
+ bool get same => name == Platform.operatingSystem;
+
+ bool get buildable {
+ return same || this == TargetPlatform.android;
}
String get dynamicLibExtensionName {
final String extensionName;
switch (this) {
- case Target.android || Target.linux:
+ case TargetPlatform.android || TargetPlatform.linux:
extensionName = '.so';
break;
- case Target.windows:
+ case TargetPlatform.windows:
extensionName = '.dll';
break;
- case Target.macos:
+ case TargetPlatform.macos:
extensionName = '.dylib';
break;
}
@@ -52,7 +42,7 @@ extension TargetExt on Target {
String get executableExtensionName {
final String extensionName;
switch (this) {
- case Target.windows:
+ case TargetPlatform.windows:
extensionName = '.exe';
break;
default:
@@ -63,34 +53,163 @@ extension TargetExt on Target {
}
}
-enum Mode { core, lib }
+enum CoreMode { core, lib }
enum Arch { amd64, arm64, arm }
+extension ArchExt on Arch {
+ Map get archMap {
+ switch (Platform.operatingSystem) {
+ case 'windows':
+ return {
+ 'AMD64': 'amd64',
+ 'x86': 'amd32',
+ 'ARM64': 'arm64',
+ 'ARM': 'arm'
+ };
+ case 'linux' || 'android':
+ return {
+ 'x86_64': 'amd64',
+ 'i386': 'amd32',
+ 'i486': 'amd32',
+ 'i586': 'amd32',
+ 'i686': 'amd32',
+ 'aarch64': 'arm64',
+ 'armv5l': 'arm',
+ 'armv6l': 'arm',
+ 'armv7l': 'arm'
+ };
+ case 'macos':
+ return {
+ 'x86_64': 'amd64',
+ 'arm64': 'arm64',
+ 'arm64e': 'arm64'
+ };
+ default:
+ throw 'Unsupported platform!';
+ }
+ }
+
+ bool get same {
+ final String hostArchName;
+ if (Platform.isWindows) {
+ hostArchName = Platform.environment['PROCESSOR_ARCHITECTURE']!;
+ } else {
+ var info = Process.runSync('uname', ['-m']);
+ hostArchName = info.stdout.toString().trim();
+ }
+ final hostArch = archMap[hostArchName] ?? hostArchName;
+ return name == hostArch ? true : false;
+ }
+}
+
class BuildItem {
- Target target;
- Arch? arch;
+ TargetPlatform platform;
+ Arch arch;
String? archName;
- BuildItem({required this.target, this.arch, this.archName});
+ BuildItem({required this.platform, required this.arch, this.archName});
@override
String toString() {
- return 'BuildLibItem{target: $target, arch: $arch, archName: $archName}';
+ return 'BuildLibItem{platform: $platform, arch: $arch, archName: $archName}';
+ }
+}
+
+Future checkDeps({
+ List? commands,
+ Map? devLibs,
+ Map? rtLibs,
+ List? files,
+ List? ndks,
+}) async {
+ final missing = [];
+
+ if (devLibs != null && devLibs.isNotEmpty) {
+ final pkgConfigExists = (await Process.run('which', ['pkg-config'])).exitCode == 0;
+ if (!pkgConfigExists) {
+ missing.add('pkg-config');
+ } else {
+ for (final entry in devLibs.entries) {
+ final result = await Process.run('pkg-config', ['--exists', entry.value]);
+ if (result.exitCode != 0) missing.add(entry.key);
+ }
+ }
+ }
+
+ if (rtLibs != null && rtLibs.isNotEmpty) {
+ for (final entry in rtLibs.entries) {
+ final result = await Process.run('sh', ['-c', 'ldconfig -p | grep ${entry.value}']);
+ if (result.exitCode != 0) missing.add(entry.key);
+ }
+ }
+
+ if (ndks != null && ndks.isNotEmpty) {
+ final sdkmanager = join(Platform.environment['ANDROID_HOME']!, 'cmdline-tools', 'latest', 'bin', 'sdkmanager');
+ final cmdlineToolsExist = File(sdkmanager).existsSync();
+ if (!cmdlineToolsExist) {
+ missing.add('Android SDK Command-line Tools');
+ } else {
+ for (final ndkVersion in ndks) {
+ final result = await Process.run(sdkmanager, ['--list_installed']);
+ final pattern = RegExp('^\\s.${RegExp.escape('ndk;$ndkVersion')}', multiLine: true);
+ final installed = pattern.hasMatch(result.stdout);
+ if (!installed) {
+ missing.add('Android NDK $ndkVersion');
+ }
+ }
+ }
+ }
+
+ if (commands != null && commands.isNotEmpty) {
+ for (final cmd in commands) {
+ final result = Platform.isWindows
+ ? await Process.run('where.exe', [cmd])
+ : await Process.run('which', [cmd]);
+ if (result.exitCode != 0) {
+ missing.add(cmd);
+ }
+ }
+ }
+
+
+ if (files != null && files.isNotEmpty) {
+ for (final filePath in files) {
+ if (!File(filePath).existsSync()) {
+ missing.add(basename(filePath));
+ }
+ }
+ }
+
+ if (missing.isNotEmpty) {
+ throw 'Missing required dependencies: ${missing.join(", ")}. '
+ 'Please install them first. See README for details.';
}
}
class Build {
static List get buildItems => [
- BuildItem(target: Target.macos, arch: Arch.arm64),
- BuildItem(target: Target.macos, arch: Arch.amd64),
- BuildItem(target: Target.linux, arch: Arch.arm64),
- BuildItem(target: Target.linux, arch: Arch.amd64),
- BuildItem(target: Target.windows, arch: Arch.amd64),
- BuildItem(target: Target.windows, arch: Arch.arm64),
- BuildItem(target: Target.android, arch: Arch.arm, archName: 'armeabi-v7a'),
- BuildItem(target: Target.android, arch: Arch.arm64, archName: 'arm64-v8a'),
- BuildItem(target: Target.android, arch: Arch.amd64, archName: 'x86_64'),
+ BuildItem(platform: TargetPlatform.macos, arch: Arch.arm64),
+ BuildItem(platform: TargetPlatform.macos, arch: Arch.amd64),
+ BuildItem(platform: TargetPlatform.linux, arch: Arch.arm64),
+ BuildItem(platform: TargetPlatform.linux, arch: Arch.amd64),
+ BuildItem(platform: TargetPlatform.windows, arch: Arch.amd64),
+ BuildItem(platform: TargetPlatform.windows, arch: Arch.arm64),
+ BuildItem(
+ platform: TargetPlatform.android,
+ arch: Arch.arm,
+ archName: 'armeabi-v7a',
+ ),
+ BuildItem(
+ platform: TargetPlatform.android,
+ arch: Arch.arm64,
+ archName: 'arm64-v8a',
+ ),
+ BuildItem(
+ platform: TargetPlatform.android,
+ arch: Arch.amd64,
+ archName: 'x86_64',
+ ),
];
static String get appName => 'Bettbox';
@@ -107,29 +226,9 @@ class Build {
static String get distPath => join(current, 'dist');
- static String _getCc(BuildItem buildItem) {
- final environment = Platform.environment;
- if (buildItem.target == Target.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 String getTags(BuildItem buildItem) {
final baseTags = 'with_gvisor';
- if (buildItem.target == Target.android &&
+ if (buildItem.platform == TargetPlatform.android &&
buildItem.archName == 'armeabi-v7a') {
return '$baseTags,with_low_memory';
}
@@ -171,22 +270,25 @@ class Build {
}
static Future> buildCore({
- required Mode mode,
- required Target target,
+ required CoreMode mode,
+ required TargetPlatform platform,
Arch? arch,
bool compatible = false,
}) async {
- final isLib = mode == Mode.lib;
+ final isLib = mode == CoreMode.lib;
final items = buildItems.where((element) {
- return element.target == target &&
- (arch == null ? true : element.arch == arch);
+ return element.platform == platform && (arch == null || element.arch == arch);
}).toList();
-
+ final isMultiArch = items.length > 1;
final List corePaths = [];
-
+ final List macOSCorePaths = [];
for (final item in items) {
- final outFileDir = join(outDir, item.target.name, item.archName);
+ final outFileDir = join(
+ outDir,
+ item.platform.name,
+ item.platform == TargetPlatform.macos ? item.arch.name : item.archName
+ );
final file = File(outFileDir);
if (file.existsSync()) {
@@ -194,32 +296,54 @@ class Build {
}
final fileName = isLib
- ? '$libName${item.target.dynamicLibExtensionName}'
- : '$coreName${item.target.executableExtensionName}';
+ ? '$libName${item.platform.dynamicLibExtensionName}'
+ : '$coreName${item.platform.executableExtensionName}';
final outPath = join(outFileDir, fileName);
- corePaths.add(outPath);
+ if (item.platform == TargetPlatform.macos) {
+ macOSCorePaths.add(outPath);
+ } else {
+ corePaths.add(outPath);
+ }
final Map env = {};
- env['GOOS'] = item.target.os;
- if (item.arch != null) {
- env['GOARCH'] = item.arch!.name;
- }
+ env['GOOS'] = item.platform.os;
+ env['GOARCH'] = item.arch.name;
if (item.arch == Arch.amd64 &&
- (item.target == Target.windows ||
- item.target == Target.linux ||
- item.target == Target.macos)) {
+ (item.platform == TargetPlatform.windows ||
+ item.platform == TargetPlatform.linux ||
+ item.platform == TargetPlatform.macos)) {
env['GOAMD64'] = compatible ? 'v1' : 'v3';
}
if (isLib) {
env['CGO_ENABLED'] = '1';
- env['CC'] = _getCc(item);
env['CFLAGS'] = '-O3 -Werror';
+ if (item.platform == TargetPlatform.android) {
+ var ndkPath = Platform.environment['ANDROID_NDK'];
+ if (ndkPath == null) {
+ const ndkVersion = '27.0.12077973';
+ final androidHome = Platform.environment['ANDROID_HOME']!;
+ await checkDeps(ndks: [ndkVersion]);
+ ndkPath = join(androidHome, 'ndk', ndkVersion);
+ }
+ final prebuiltDir = Directory(join(ndkPath, 'toolchains', 'llvm', 'prebuilt'));
+ 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',
+ };
+ env['CC'] = join(prebuiltDir.listSync().first.path, 'bin', map[item.archName]);
+ } else {
+ env['CC'] = 'gcc';
+ await checkDeps(commands: ['gcc']);
+ }
} else {
env['CGO_ENABLED'] = '0';
}
final buildTags = getTags(item);
+ await checkDeps(commands: ['go']);
await exec(
['go', 'mod', 'tidy'],
name: 'go mod tidy',
@@ -231,7 +355,7 @@ class Build {
'go',
'build',
'-trimpath',
- '-ldflags=-w -s${item.target == Target.android && (item.arch == Arch.arm64 || item.arch == Arch.amd64) ? ' -extldflags "-Wl,-z,max-page-size=16384"' : ''}',
+ '-ldflags=-w -s${item.platform == TargetPlatform.android && (item.arch == Arch.arm64 || item.arch == Arch.amd64) ? ' -extldflags "-Wl,-z,max-page-size=16384"' : ''}',
'-tags=$buildTags',
if (isLib) '-buildmode=c-shared',
'-o',
@@ -239,16 +363,29 @@ class Build {
];
await exec(
execLines,
- name: 'build core',
+ name: 'build core ${isMultiArch ? "(${item.archName ?? item.arch.name})" : ""}',
environment: env,
workingDirectory: _coreDir,
);
}
+ if (macOSCorePaths.isNotEmpty) {
+ final outFileDir = join(outDir, TargetPlatform.macos.name);
+ final fileName = isLib
+ ? '$libName${TargetPlatform.macos.dynamicLibExtensionName}'
+ : '$coreName${TargetPlatform.macos.executableExtensionName}';
+ final outPath = join(outFileDir, fileName);
+ await exec(
+ macOSCorePaths.length > 1
+ ? ['lipo', '-create', '-output', outPath, ...macOSCorePaths]
+ : ['cp', macOSCorePaths.first, outPath],
+ );
+ corePaths.add(outPath);
+ }
return corePaths;
}
- static Future buildHelper(Target target, String token) async {
+ static Future buildHelper(TargetPlatform platform, String token) async {
await exec(
['cargo', 'build', '--release', '--features', 'windows-service'],
environment: {'TOKEN': token},
@@ -259,14 +396,16 @@ class Build {
_servicesDir,
'target',
'release',
- 'helper${target.executableExtensionName}',
+ 'helper${platform.executableExtensionName}',
);
final targetPath = join(
outDir,
- target.name,
- 'BettboxHelperService${target.executableExtensionName}',
+ platform.name,
+ 'BettboxHelperService${platform.executableExtensionName}',
);
await File(outPath).copy(targetPath);
+ final shaFile = File(join(outDir, platform.name, '.core_sha256'));
+ await shaFile.writeAsString('CORE_SHA256=$token');
}
static List getExecutable(String command) {
@@ -318,31 +457,52 @@ class Build {
}
class BuildCommand extends Command {
- Target target;
-
- BuildCommand({required this.target}) {
- if (target == Target.android || target == Target.linux) {
- argParser.addOption(
- 'arch',
- valueHelp: arches.map((e) => e.name).join(','),
- help: 'The $name build desc',
+ TargetPlatform platform;
+
+ BuildCommand({required this.platform}) {
+ argParser.addOption(
+ 'arch',
+ abbr: 'a',
+ allowed: platform == TargetPlatform.android || platform == TargetPlatform.macos
+ ? [...arches.map((e) => e.name), 'universal']
+ : arches.map((e) => e.name),
+ help: 'The architecture of $name build; omit this to select '
+ '${platform != TargetPlatform.android ? "the host" : "every"} architecture',
+ );
+ if (platform == TargetPlatform.linux) {
+ argParser.addMultiOption(
+ 'targets',
+ abbr: 't',
+ allowed: ['deb', 'rpm', 'appimage', 'zip'],
+ help: 'Linux package formats (multiple selections, seperated by ",")',
);
- } else {
- argParser.addOption('arch', help: 'The $name build archName');
}
argParser.addOption(
'out',
- valueHelp: [if (target.same) 'app', 'core'].join(','),
- help: 'The $name build arch',
+ abbr: 'o',
+ allowed: [
+ if (platform.buildable) 'app',
+ 'core',
+ if (platform == TargetPlatform.windows) 'core,helper',
+ ],
+ help: 'Build the full app; only the core; or core+helper (Windows)',
+ defaultsTo: platform.buildable ? 'app' : 'core',
);
argParser.addOption(
'env',
- valueHelp: ['pre', 'stable'].join(','),
- help: 'The $name build env',
+ abbr: 'e',
+ allowed: ['pre', 'stable'],
+ help: 'The value of dart-define APP_ENV, used to identify the release channel',
+ defaultsTo: 'pre',
);
argParser.addFlag(
'compatible',
- help: 'Build with GOAMD64=v2 for broader compatibility on amd64',
+ help: 'Build with GOAMD64=v1 for broader compatibility on amd64',
+ );
+ argParser.addFlag(
+ 'build-only',
+ abbr: 'B',
+ help: 'Skip packaging the app with flutter_distributor'
);
}
@@ -350,177 +510,149 @@ class BuildCommand extends Command {
String get description => 'build $name application';
@override
- String get name => target.name;
+ String get name => platform.name;
List get arches => Build.buildItems
- .where((element) => element.target == target && element.arch != null)
- .map((e) => e.arch!)
+ .where((element) => element.platform == platform)
+ .map((e) => e.arch)
.toList();
- Future _getLinuxDependencies(Arch arch) 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-get install -y libkeybinder-3.0-dev'),
- );
- await Build.exec(Build.getExecutable('sudo apt install -y locate'));
- if (arch == Arch.amd64) {
- await Build.exec(
- Build.getExecutable('sudo apt install -y rpm patchelf libfuse2'),
- );
-
- final downloadName = arch == Arch.amd64 ? 'x86_64' : 'aarch64';
- await Build.exec(
- Build.getExecutable(
- 'wget -O appimagetool https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-$downloadName.AppImage',
- ),
- );
- await Build.exec(Build.getExecutable('chmod +x appimagetool'));
- await Build.exec(
- Build.getExecutable('sudo mv appimagetool /usr/local/bin/'),
- );
- }
- }
-
- Future _getMacosDependencies() async {
- await Build.exec(Build.getExecutable('npm install -g appdmg'));
- }
-
- Future _setMacOSCompatibleBuild(bool enable) async {
- final infoPlistPath = 'macos/Runner/Info.plist';
- final file = File(infoPlistPath);
-
- if (!await file.exists()) {
- print('Warning: Info.plist not found at $infoPlistPath');
- return;
- }
-
- var content = await file.readAsString();
-
- // Check if FLTDisableImpeller key exists
- if (content.contains('FLTDisableImpeller')) {
- // Update existing key
- if (enable) {
- content = content.replaceAll(
- RegExp(r'FLTDisableImpeller\s*<(?:true|false)/>'),
- 'FLTDisableImpeller\n\t',
- );
- } else {
- content = content.replaceAll(
- RegExp(r'FLTDisableImpeller\s*<(?:true|false)/>'),
- 'FLTDisableImpeller\n\t',
- );
- }
- } else {
- // Add new key before
- final impellerEntry = enable
- ? '\tFLTDisableImpeller\n\t\n'
- : '\tFLTDisableImpeller\n\t\n';
- content = content.replaceFirst(
- '\n',
- '$impellerEntry\n',
- );
- }
-
- await file.writeAsString(content);
- print('macOS ${enable ? "Compatible" : "Standard"} build: FLTDisableImpeller set to $enable');
- }
-
Future _buildDistributor({
- required Target target,
+ required TargetPlatform platform,
required String targets,
String args = '',
- required String env,
+ required String appEnv,
+ Map? buildEnv,
+ final bool buildOnly = false,
}) async {
final sentryDsn = Platform.environment['SENTRY_DSN'] ?? '';
- final sentryArg = sentryDsn.isNotEmpty
- ? ' --build-dart-define=SENTRY_DSN=$sentryDsn'
- : '';
+ String sentryArg = '';
+ if (sentryDsn.isNotEmpty) {
+ sentryArg = buildOnly
+ ? ' --dart-define=SENTRY_DSN=$sentryDsn'
+ : ' --build-dart-define=SENTRY_DSN=$sentryDsn';
+ }
- await Build.getDistributor();
+ if (!buildOnly) await Build.getDistributor();
await Build.exec(
- name: name,
+ name: description,
+ environment: buildEnv,
Build.getExecutable(
- 'flutter_distributor package --skip-clean --platform ${target.name} --targets $targets --flutter-build-args=verbose$args$sentryArg --build-dart-define=APP_ENV=$env',
+ buildOnly
+ ? 'flutter build ${platform == TargetPlatform.android ? "apk" : platform.name} --release --dart-define=APP_ENV=$appEnv --verbose$args$sentryArg'
+ : 'flutter_distributor package --skip-clean --platform ${platform.name} --targets $targets --flutter-build-args=verbose$args$sentryArg --build-dart-define=APP_ENV=$appEnv',
),
);
}
- Future get systemArch async {
- if (Platform.isWindows) {
- return Platform.environment['PROCESSOR_ARCHITECTURE'];
- } else if (Platform.isLinux || Platform.isMacOS) {
- final result = await Process.run('uname', ['-m']);
- return result.stdout.toString().trim();
- }
- return null;
- }
-
@override
Future run() async {
- final mode = target == Target.android ? Mode.lib : Mode.core;
- final String out = argResults?['out'] ?? (target.same ? 'app' : 'core');
- final archName = argResults?['arch'];
- final env = argResults?['env'] ?? 'pre';
- final currentArches = arches
- .where((element) => element.name == archName)
- .toList();
- final arch = currentArches.isEmpty ? null : currentArches.first;
-
- if (arch == null && target != Target.android) {
- throw 'Invalid arch parameter';
+ final coreMode = platform == TargetPlatform.android ? CoreMode.lib : CoreMode.core;
+ final String out = argResults?['out'] ?? (platform.buildable ? 'app' : 'core');
+ final String? archParam = argResults?['arch'];
+ final String appEnv = argResults?['env'] ?? 'pre';
+
+ Arch? arch;
+ if (archParam == null) {
+ if (platform != TargetPlatform.android) {
+ arch = arches.firstWhere((element) => element.same);
+ }
+ } else if (archParam == 'universal') {
+ if (platform != TargetPlatform.android && platform != TargetPlatform.macos) {
+ throw 'Invalid arch parameter!';
+ }
+ } else {
+ arch = arches.where((element) => element.name == archParam).firstOrNull;
+ if (arch == null) throw 'Invalid arch parameter!';
}
-
+
final bool compatible = argResults?['compatible'] ?? false;
+ bool buildOnly = argResults?['build-only'] ?? false;
+ if (platform == TargetPlatform.android && buildOnly) {
+ print('Warning: Ignored --build-only.');
+ buildOnly = false;
+ }
final corePaths = await Build.buildCore(
- target: target,
+ platform: platform,
arch: arch,
- mode: mode,
+ mode: coreMode,
compatible: compatible,
);
- if (out != 'app') {
+ if (out == 'core') {
+ if (!platform.buildable) print('Platform incompatible, core built only!');
return;
}
- final String desc = compatible ? '$archName-compatible' : (archName ?? '');
+ final String desc = '${archParam ?? arch?.name}${compatible ? "-compatible" : ""}';
+
+ String? buildTargets;
+ String buildArgs = '';
+ Map? buildEnv;
- switch (target) {
- case Target.windows:
- final token = target != Target.android
+ switch (platform) {
+ case TargetPlatform.windows:
+ if (!arch!.same) {
+ throw 'Corss-build to $name ${arch.name} target is not currently supported!';
+ }
+ await checkDeps(
+ commands: ['cargo'],
+ files: buildOnly ? null : [r'C:\Program Files (x86)\Inno Setup 6\ISCC.exe'],
+ );
+ final token = platform != TargetPlatform.android
? await Build.calcSha256(corePaths.first)
: null;
- Build.buildHelper(target, token!);
- _buildDistributor(
- target: target,
- targets: 'exe',
- args: ' --description $desc --build-dart-define=CORE_SHA256=$token',
- env: env,
- );
- return;
- case Target.linux:
+ await Build.buildHelper(platform, token!);
+ if (out == 'core,helper') return;
+ buildTargets = 'exe';
+ buildArgs = buildOnly
+ ? ' --dart-define=CORE_SHA256=$token'
+ : ' --description $desc --build-dart-define=CORE_SHA256=$token';
+ break;
+
+ case TargetPlatform.linux:
final targetMap = {Arch.arm64: 'linux-arm64', Arch.amd64: 'linux-x64'};
- final targets = [
- 'deb',
- if (arch == Arch.amd64) 'appimage',
- if (arch == Arch.amd64) 'rpm',
- ].join(',');
+ List targets = argResults?['targets'];
+ if (buildOnly) {
+ if (targets.isNotEmpty) {
+ print('Warning: --build-only is specified, ignoring targets parameter.');
+ }
+ targets = [];
+ } else if (targets.isEmpty) {
+ throw 'Invalid targets parameter';
+ }
+ if (!arch!.same) {
+ throw 'Corss-build to $name ${arch.name} target is not currently supported!';
+ }
final defaultTarget = targetMap[arch];
- await _getLinuxDependencies(arch!);
- _buildDistributor(
- target: target,
- targets: targets,
- args: ' --description $desc --build-target-platform $defaultTarget',
- env: env,
+
+ final requiredCmds = ['clang', 'cmake', 'ninja'];
+ Map requiredRtLibs = {};
+ if (targets.contains('deb')) requiredCmds.add('dpkg-deb');
+ if (targets.contains('rpm')) requiredCmds.addAll(['rpm', 'patchelf']);
+ if (targets.contains('appimage')) {
+ requiredCmds.addAll(['appimagetool', 'locate']);
+ requiredRtLibs.addAll({'libfuse2': 'libfuse.so.2'});
+ }
+ await checkDeps(
+ commands: requiredCmds,
+ devLibs: {
+ 'gtk3': 'gtk+-3.0',
+ 'libayatana-appindicator': 'ayatana-appindicator3-0.1',
+ 'keybinder-3.0': 'keybinder-3.0',
+ 'libcurl': 'libcurl',
+ },
+ rtLibs: requiredRtLibs,
);
- return;
- case Target.android:
+
+ buildTargets = targets.join(',');
+ buildArgs = buildOnly
+ ? ' --target-platform $defaultTarget'
+ : ' --description $desc --build-target-platform $defaultTarget';
+ break;
+
+ case TargetPlatform.android:
final targetMap = {
Arch.arm: 'android-arm',
Arch.arm64: 'android-arm64',
@@ -532,41 +664,69 @@ class BuildCommand extends Command {
.map((e) => targetMap[e])
.toList();
- final buildArgs = archName == 'universal'
+ buildArgs = archParam == 'universal'
? ' --build-target-platform ${defaultTargets.join(",")} --description universal'
: ',split-per-abi --build-target-platform ${defaultTargets.join(",")}';
+ buildTargets = 'apk';
+ break;
+
+ case TargetPlatform.macos:
+ if (!buildOnly) await checkDeps(commands: ['appdmg']);
- _buildDistributor(
- target: target,
- targets: 'apk',
- args: buildArgs,
- env: env,
- );
- return;
- case Target.macos:
- await _getMacosDependencies();
// For compatible build, disable Impeller and use Skia renderer
- if (compatible) {
- await _setMacOSCompatibleBuild(true);
+ final infoPlist = File('macos/Runner/Info.plist');
+ if (await infoPlist.exists()) {
+ var content = await infoPlist.readAsString();
+
+ // Check if FLTDisableImpeller key exists
+ if (content.contains('FLTDisableImpeller')) {
+ content = content.replaceAll(
+ RegExp(r'FLTDisableImpeller\s*<(?:true|false)/>'),
+ 'FLTDisableImpeller\n\t<$compatible/>',
+ );
+ } else {
+ // Add new key before
+ final impellerEntry = '\tFLTDisableImpeller\n\t<$compatible/>\n';
+ content = content.replaceFirst(
+ '\n',
+ '$impellerEntry\n',
+ );
+ }
+ await infoPlist.writeAsString(content);
+ print(
+ 'macOS ${compatible ? "Compatible" : "Standard"} build: FLTDisableImpeller set to $compatible',
+ );
} else {
- await _setMacOSCompatibleBuild(false);
+ print('Warning: ${infoPlist.path} not found!');
}
- _buildDistributor(
- target: target,
- targets: 'dmg',
- args: ' --description $desc',
- env: env,
- );
- return;
+
+ final archName = archParam == 'universal'
+ ? null
+ : arch?.archMap.keys.firstWhere(
+ (k) => arch?.archMap[k] == arch?.name,
+ );
+ buildEnv = archName == null ? null : {'FLUTTER_XCODE_ARCHS': archName};
+ buildTargets = 'dmg';
+ buildArgs = buildOnly ? '' : ' --description $desc';
+ break;
}
+
+ _buildDistributor(
+ platform: platform,
+ targets: buildTargets,
+ args: buildArgs,
+ appEnv: appEnv,
+ buildEnv: buildEnv,
+ buildOnly: buildOnly,
+ );
}
}
Future main(Iterable args) async {
final runner = CommandRunner('setup', 'build Application');
- runner.addCommand(BuildCommand(target: Target.android));
- runner.addCommand(BuildCommand(target: Target.linux));
- runner.addCommand(BuildCommand(target: Target.windows));
- runner.addCommand(BuildCommand(target: Target.macos));
+ runner.addCommand(BuildCommand(platform: TargetPlatform.android));
+ runner.addCommand(BuildCommand(platform: TargetPlatform.linux));
+ runner.addCommand(BuildCommand(platform: TargetPlatform.windows));
+ runner.addCommand(BuildCommand(platform: TargetPlatform.macos));
runner.run(args);
}