mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
feat(desktop): build macOS as a universal binary (#1603)
This commit is contained in:
@@ -22,7 +22,7 @@ on:
|
|||||||
default: ""
|
default: ""
|
||||||
type: string
|
type: string
|
||||||
skip_targets:
|
skip_targets:
|
||||||
description: Comma-separated platforms or targets to skip, such as windows, macos-arm64, linux-x64.
|
description: Comma-separated platforms or targets to skip, such as windows, macos, linux-x64.
|
||||||
required: false
|
required: false
|
||||||
default: ""
|
default: ""
|
||||||
type: string
|
type: string
|
||||||
@@ -241,7 +241,7 @@ jobs:
|
|||||||
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9
|
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9
|
||||||
with:
|
with:
|
||||||
toolchain: "1.93.0"
|
toolchain: "1.93.0"
|
||||||
targets: ${{ matrix.platform == 'macos' && (matrix.arch == 'arm64' && 'aarch64-apple-darwin' || 'x86_64-apple-darwin') || (matrix.arch == 'arm64' && 'aarch64-unknown-linux-gnu' || 'x86_64-unknown-linux-gnu') }}
|
targets: ${{ matrix.platform == 'macos' && 'aarch64-apple-darwin,x86_64-apple-darwin' || (matrix.arch == 'arm64' && 'aarch64-unknown-linux-gnu' || 'x86_64-unknown-linux-gnu') }}
|
||||||
|
|
||||||
- name: Install MSVC ARM64 build tools
|
- name: Install MSVC ARM64 build tools
|
||||||
if: matrix.platform == 'windows' && matrix.arch == 'arm64'
|
if: matrix.platform == 'windows' && matrix.arch == 'arm64'
|
||||||
@@ -468,8 +468,8 @@ jobs:
|
|||||||
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
|
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
|
||||||
--step prepare_artifacts_unix
|
--step prepare_artifacts_unix
|
||||||
|
|
||||||
- name: Normalize updater YAML (arm64)
|
- name: Normalize updater YAML (macOS)
|
||||||
if: matrix.arch == 'arm64'
|
if: matrix.platform == 'macos'
|
||||||
run: >-
|
run: >-
|
||||||
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
|
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
|
||||||
--step normalise_updater_yaml
|
--step normalise_updater_yaml
|
||||||
|
|||||||
@@ -82,8 +82,8 @@ type FormatMapping = {
|
|||||||
|
|
||||||
const FORMAT_MAPPINGS: Record<DesktopFormat, Partial<Record<DesktopPlatform, FormatMapping>>> = {
|
const FORMAT_MAPPINGS: Record<DesktopFormat, Partial<Record<DesktopPlatform, FormatMapping>>> = {
|
||||||
setup: {win32: {ext: '.exe', arch: {x64: 'x64', arm64: 'arm64'}}},
|
setup: {win32: {ext: '.exe', arch: {x64: 'x64', arm64: 'arm64'}}},
|
||||||
dmg: {darwin: {ext: '.dmg', arch: {x64: 'x64', arm64: 'arm64'}}},
|
dmg: {darwin: {ext: '.dmg', arch: {x64: ['universal', 'x64'], arm64: ['universal', 'arm64']}}},
|
||||||
zip: {darwin: {ext: '.zip', arch: {x64: 'x64', arm64: 'arm64'}}},
|
zip: {darwin: {ext: '.zip', arch: {x64: ['universal', 'x64'], arm64: ['universal', 'arm64']}}},
|
||||||
appimage: {linux: {ext: '.AppImage', arch: {x64: 'x86_64', arm64: ['aarch64', 'arm64']}}},
|
appimage: {linux: {ext: '.AppImage', arch: {x64: 'x86_64', arm64: ['aarch64', 'arm64']}}},
|
||||||
deb: {linux: {ext: '.deb', arch: {x64: 'amd64', arm64: 'arm64'}}},
|
deb: {linux: {ext: '.deb', arch: {x64: 'amd64', arm64: 'arm64'}}},
|
||||||
rpm: {linux: {ext: '.rpm', arch: {x64: 'x86_64', arm64: 'aarch64'}}},
|
rpm: {linux: {ext: '.rpm', arch: {x64: 'x86_64', arm64: 'aarch64'}}},
|
||||||
|
|||||||
@@ -31,15 +31,22 @@ const provisioningProfile = isCanary
|
|||||||
? 'build_resources/profiles/Fluxer_Canary.provisionprofile'
|
? 'build_resources/profiles/Fluxer_Canary.provisionprofile'
|
||||||
: 'build_resources/profiles/Fluxer.provisionprofile';
|
: 'build_resources/profiles/Fluxer.provisionprofile';
|
||||||
const supportedTargetArchs = ['x64', 'arm64'];
|
const supportedTargetArchs = ['x64', 'arm64'];
|
||||||
|
const supportedMacTargetArchs = [...supportedTargetArchs, 'universal'];
|
||||||
const electronArch = process.env.ELECTRON_ARCH;
|
const electronArch = process.env.ELECTRON_ARCH;
|
||||||
const cliTargetArch = supportedTargetArchs.find((arch) => process.argv.includes(`--${arch}`)) || null;
|
const cliTargetArch = supportedMacTargetArchs.find((arch) => process.argv.includes(`--${arch}`)) || null;
|
||||||
const targetNativeArch = electronArch || cliTargetArch;
|
const targetNativeArch = electronArch || cliTargetArch;
|
||||||
|
|
||||||
if (electronArch && !supportedTargetArchs.includes(electronArch)) {
|
if (electronArch && !supportedMacTargetArchs.includes(electronArch)) {
|
||||||
throw new Error(`Unsupported ELECTRON_ARCH: ${electronArch}`);
|
throw new Error(`Unsupported ELECTRON_ARCH: ${electronArch}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetArchs = electronArch ? [electronArch] : supportedTargetArchs;
|
if (targetNativeArch === 'universal' && targetPlatform !== 'darwin') {
|
||||||
|
throw new Error(`ELECTRON_ARCH=universal is only supported for macOS builds, received platform ${targetPlatform}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetArchs =
|
||||||
|
electronArch && electronArch !== 'universal' ? [electronArch] : supportedTargetArchs;
|
||||||
|
const macTargetArchs = targetNativeArch ? [targetNativeArch] : supportedTargetArchs;
|
||||||
const winTargets = [
|
const winTargets = [
|
||||||
{
|
{
|
||||||
target: 'dir',
|
target: 'dir',
|
||||||
@@ -1190,11 +1197,11 @@ module.exports = {
|
|||||||
target: [
|
target: [
|
||||||
{
|
{
|
||||||
target: 'dmg',
|
target: 'dmg',
|
||||||
arch: targetArchs,
|
arch: macTargetArchs,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
target: 'zip',
|
target: 'zip',
|
||||||
arch: targetArchs,
|
arch: macTargetArchs,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
extendInfo: {
|
extendInfo: {
|
||||||
|
|||||||
+44
-48
@@ -32,6 +32,7 @@ const PUBLIC_DL_BASE: &str = "https://api.fluxer.app/dl";
|
|||||||
const PNPM_VERSION: &str = "10.29.3";
|
const PNPM_VERSION: &str = "10.29.3";
|
||||||
const RUST_TOOLCHAIN: &str = "1.93.0";
|
const RUST_TOOLCHAIN: &str = "1.93.0";
|
||||||
const DEFAULT_DESKTOP_VARIANT: &str = "default";
|
const DEFAULT_DESKTOP_VARIANT: &str = "default";
|
||||||
|
pub(crate) const MACOS_UNIVERSAL_ARCH: &str = "universal";
|
||||||
const WINDOWS_GAME_CAPTURE_DESKTOP_VARIANT: &str = "windows-game-capture";
|
const WINDOWS_GAME_CAPTURE_DESKTOP_VARIANT: &str = "windows-game-capture";
|
||||||
|
|
||||||
#[derive(Debug, Args, Clone)]
|
#[derive(Debug, Args, Clone)]
|
||||||
@@ -53,10 +54,6 @@ pub struct BuildDesktopArgs {
|
|||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
skip_macos: Option<String>,
|
skip_macos: Option<String>,
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
skip_macos_x64: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
skip_macos_arm64: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
skip_linux: Option<String>,
|
skip_linux: Option<String>,
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
skip_linux_x64: Option<String>,
|
skip_linux_x64: Option<String>,
|
||||||
@@ -136,17 +133,10 @@ const PLATFORMS: &[Platform] = &[
|
|||||||
},
|
},
|
||||||
Platform {
|
Platform {
|
||||||
platform: "macos",
|
platform: "macos",
|
||||||
arch: "x64",
|
arch: MACOS_UNIVERSAL_ARCH,
|
||||||
desktop_variant: DEFAULT_DESKTOP_VARIANT,
|
desktop_variant: DEFAULT_DESKTOP_VARIANT,
|
||||||
os: "fluxer-desktop-macos-arm64",
|
os: "fluxer-desktop-macos-arm64",
|
||||||
electron_arch: "x64",
|
electron_arch: MACOS_UNIVERSAL_ARCH,
|
||||||
},
|
|
||||||
Platform {
|
|
||||||
platform: "macos",
|
|
||||||
arch: "arm64",
|
|
||||||
desktop_variant: DEFAULT_DESKTOP_VARIANT,
|
|
||||||
os: "fluxer-desktop-macos-arm64",
|
|
||||||
electron_arch: "arm64",
|
|
||||||
},
|
},
|
||||||
Platform {
|
Platform {
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
@@ -381,8 +371,7 @@ fn skip_target_set(args: &BuildDesktopArgs) -> Result<BTreeSet<String>> {
|
|||||||
"windows-x64",
|
"windows-x64",
|
||||||
"windows-arm64",
|
"windows-arm64",
|
||||||
"macos",
|
"macos",
|
||||||
"macos-x64",
|
"macos-universal",
|
||||||
"macos-arm64",
|
|
||||||
"linux",
|
"linux",
|
||||||
"linux-x64",
|
"linux-x64",
|
||||||
"linux-arm64",
|
"linux-arm64",
|
||||||
@@ -424,11 +413,7 @@ fn skip_platform(
|
|||||||
|| (platform.arch == "arm64"
|
|| (platform.arch == "arm64"
|
||||||
&& flag(&args.skip_windows_arm64, "SKIP_WINDOWS_ARM64"))
|
&& flag(&args.skip_windows_arm64, "SKIP_WINDOWS_ARM64"))
|
||||||
}
|
}
|
||||||
"macos" => {
|
"macos" => flag(&args.skip_macos, "SKIP_MACOS"),
|
||||||
flag(&args.skip_macos, "SKIP_MACOS")
|
|
||||||
|| (platform.arch == "x64" && flag(&args.skip_macos_x64, "SKIP_MACOS_X64"))
|
|
||||||
|| (platform.arch == "arm64" && flag(&args.skip_macos_arm64, "SKIP_MACOS_ARM64"))
|
|
||||||
}
|
|
||||||
"linux" => {
|
"linux" => {
|
||||||
flag(&args.skip_linux, "SKIP_LINUX")
|
flag(&args.skip_linux, "SKIP_LINUX")
|
||||||
|| (platform.arch == "x64" && flag(&args.skip_linux_x64, "SKIP_LINUX_X64"))
|
|| (platform.arch == "x64" && flag(&args.skip_linux_x64, "SKIP_LINUX_X64"))
|
||||||
@@ -2618,7 +2603,7 @@ fn is_unix_upload_artifact(name: &str) -> bool {
|
|||||||
|
|
||||||
fn normalise_updater_yaml_step() -> Result<()> {
|
fn normalise_updater_yaml_step() -> Result<()> {
|
||||||
if env::var("PLATFORM").unwrap_or_default() == "macos"
|
if env::var("PLATFORM").unwrap_or_default() == "macos"
|
||||||
&& env::var("ARCH").unwrap_or_default() == "arm64"
|
&& env::var("ARCH").unwrap_or_default() == MACOS_UNIVERSAL_ARCH
|
||||||
{
|
{
|
||||||
let source = Path::new("upload_staging/latest-mac.yml");
|
let source = Path::new("upload_staging/latest-mac.yml");
|
||||||
let target = Path::new("upload_staging/latest-mac-arm64.yml");
|
let target = Path::new("upload_staging/latest-mac-arm64.yml");
|
||||||
@@ -2768,30 +2753,32 @@ fn build_payload_step() -> Result<()> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut dest = payload_root
|
for published_arch in published_arches(platform, &identity.arch) {
|
||||||
.join(&channel)
|
let mut dest = payload_root
|
||||||
.join(platform)
|
.join(&channel)
|
||||||
.join(&identity.arch);
|
.join(platform)
|
||||||
if let Some(segment) = desktop_variant_path_segment(&identity.desktop_variant) {
|
.join(published_arch);
|
||||||
dest = dest.join(segment);
|
if let Some(segment) = desktop_variant_path_segment(&identity.desktop_variant) {
|
||||||
|
dest = dest.join(segment);
|
||||||
|
}
|
||||||
|
fs::create_dir_all(&dest)?;
|
||||||
|
copy_dir_contents(&dir, &dest)?;
|
||||||
|
let manifest = build_desktop_manifest(
|
||||||
|
&dest,
|
||||||
|
&PayloadManifestInput {
|
||||||
|
channel: channel.clone(),
|
||||||
|
platform: platform.to_string(),
|
||||||
|
arch: published_arch.to_string(),
|
||||||
|
desktop_variant: identity.desktop_variant.clone(),
|
||||||
|
version: version.clone(),
|
||||||
|
pub_date: pub_date.clone(),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
if platform == "darwin" {
|
||||||
|
write_macos_releases(&dest, &s3_prefix, &channel, &manifest)?;
|
||||||
|
}
|
||||||
|
write_json_pretty(&dest.join("manifest.json"), &manifest)?;
|
||||||
}
|
}
|
||||||
fs::create_dir_all(&dest)?;
|
|
||||||
copy_dir_contents(&dir, &dest)?;
|
|
||||||
let manifest = build_desktop_manifest(
|
|
||||||
&dest,
|
|
||||||
&PayloadManifestInput {
|
|
||||||
channel: channel.clone(),
|
|
||||||
platform: platform.to_string(),
|
|
||||||
arch: identity.arch.clone(),
|
|
||||||
desktop_variant: identity.desktop_variant.clone(),
|
|
||||||
version: version.clone(),
|
|
||||||
pub_date: pub_date.clone(),
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
if platform == "darwin" {
|
|
||||||
write_macos_releases(&dest, &s3_prefix, &channel, &manifest)?;
|
|
||||||
}
|
|
||||||
write_json_pretty(&dest.join("manifest.json"), &manifest)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Payload tree:");
|
println!("Payload tree:");
|
||||||
@@ -3003,6 +2990,17 @@ fn manifest_file_entry(kind: &str, file: &Path) -> Result<DesktopManifestFile> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn published_arches(platform: &str, arch: &str) -> Vec<&'static str> {
|
||||||
|
if platform == "darwin" && arch == MACOS_UNIVERSAL_ARCH {
|
||||||
|
return vec!["x64", "arm64"];
|
||||||
|
}
|
||||||
|
match arch {
|
||||||
|
"x64" => vec!["x64"],
|
||||||
|
"arm64" => vec!["arm64"],
|
||||||
|
other => panic!("Unsupported desktop arch: {other}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn write_macos_releases(
|
fn write_macos_releases(
|
||||||
dest: &Path,
|
dest: &Path,
|
||||||
s3_prefix: &str,
|
s3_prefix: &str,
|
||||||
@@ -3332,8 +3330,6 @@ mod tests {
|
|||||||
skip_windows_x64: Some("false".to_string()),
|
skip_windows_x64: Some("false".to_string()),
|
||||||
skip_windows_arm64: Some("false".to_string()),
|
skip_windows_arm64: Some("false".to_string()),
|
||||||
skip_macos: Some("false".to_string()),
|
skip_macos: Some("false".to_string()),
|
||||||
skip_macos_x64: Some("false".to_string()),
|
|
||||||
skip_macos_arm64: Some("false".to_string()),
|
|
||||||
skip_linux: Some("false".to_string()),
|
skip_linux: Some("false".to_string()),
|
||||||
skip_linux_x64: Some("false".to_string()),
|
skip_linux_x64: Some("false".to_string()),
|
||||||
skip_linux_arm64: Some("false".to_string()),
|
skip_linux_arm64: Some("false".to_string()),
|
||||||
@@ -3414,7 +3410,7 @@ mod tests {
|
|||||||
fn matrix_selects_one_row_per_platform_arch_by_default() {
|
fn matrix_selects_one_row_per_platform_arch_by_default() {
|
||||||
let selected = selected_platforms(&matrix_args()).unwrap();
|
let selected = selected_platforms(&matrix_args()).unwrap();
|
||||||
|
|
||||||
assert_eq!(selected.len(), 6);
|
assert_eq!(selected.len(), 5);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
selected
|
selected
|
||||||
.iter()
|
.iter()
|
||||||
@@ -3462,7 +3458,7 @@ mod tests {
|
|||||||
.iter()
|
.iter()
|
||||||
.all(|platform| platform.platform != "windows")
|
.all(|platform| platform.platform != "windows")
|
||||||
);
|
);
|
||||||
assert_eq!(selected.len(), 4);
|
assert_eq!(selected.len(), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
use crate::common::{
|
use crate::common::{
|
||||||
CommandSpec, command_succeeds, output_text, remove_file_if_exists, run_command,
|
CommandSpec, command_succeeds, output_text, remove_file_if_exists, run_command,
|
||||||
};
|
};
|
||||||
|
use crate::desktop::MACOS_UNIVERSAL_ARCH;
|
||||||
use anyhow::{Context, Result, anyhow, bail, ensure};
|
use anyhow::{Context, Result, anyhow, bail, ensure};
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
@@ -358,9 +359,23 @@ fn ensure_pkg_config(requirement: &PkgConfigRequirement) -> Result<()> {
|
|||||||
fn build_rust_node_addon(addon_root: &Path, addon: &DesktopNativeAddon) -> Result<BuiltNodeAddon> {
|
fn build_rust_node_addon(addon_root: &Path, addon: &DesktopNativeAddon) -> Result<BuiltNodeAddon> {
|
||||||
let platform = current_platform();
|
let platform = current_platform();
|
||||||
let arch = electron_arch();
|
let arch = electron_arch();
|
||||||
let tag = platform_tag(&platform, &arch)?;
|
if platform == "darwin" && arch == MACOS_UNIVERSAL_ARCH {
|
||||||
let target = rust_target_for_platform(&platform, &arch)?;
|
let arm64 = build_rust_node_addon_for_arch(addon_root, addon, &platform, "arm64")?;
|
||||||
let target_root = cargo_target_root_for_build(addon_root, &platform)?;
|
build_rust_node_addon_for_arch(addon_root, addon, &platform, "x64")?;
|
||||||
|
return Ok(arm64);
|
||||||
|
}
|
||||||
|
build_rust_node_addon_for_arch(addon_root, addon, &platform, &arch)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_rust_node_addon_for_arch(
|
||||||
|
addon_root: &Path,
|
||||||
|
addon: &DesktopNativeAddon,
|
||||||
|
platform: &str,
|
||||||
|
arch: &str,
|
||||||
|
) -> Result<BuiltNodeAddon> {
|
||||||
|
let tag = platform_tag(platform, arch)?;
|
||||||
|
let target = rust_target_for_platform(platform, arch)?;
|
||||||
|
let target_root = cargo_target_root_for_build(addon_root, platform)?;
|
||||||
let mut args = vec![
|
let mut args = vec![
|
||||||
OsString::from("build"),
|
OsString::from("build"),
|
||||||
OsString::from("--release"),
|
OsString::from("--release"),
|
||||||
@@ -396,10 +411,7 @@ fn build_rust_node_addon(addon_root: &Path, addon: &DesktopNativeAddon) -> Resul
|
|||||||
let source = target_root
|
let source = target_root
|
||||||
.join(&target)
|
.join(&target)
|
||||||
.join("release")
|
.join("release")
|
||||||
.join(cargo_dynamic_library_file_name(
|
.join(cargo_dynamic_library_file_name(addon.crate_name, platform)?);
|
||||||
addon.crate_name,
|
|
||||||
&platform,
|
|
||||||
)?);
|
|
||||||
let out_file = addon_root.join(format!("{}.{}.node", addon.node_file_stem, tag));
|
let out_file = addon_root.join(format!("{}.{}.node", addon.node_file_stem, tag));
|
||||||
ensure!(
|
ensure!(
|
||||||
source.exists(),
|
source.exists(),
|
||||||
@@ -418,8 +430,8 @@ fn build_rust_node_addon(addon_root: &Path, addon: &DesktopNativeAddon) -> Resul
|
|||||||
"expected {} to exist after copy",
|
"expected {} to exist after copy",
|
||||||
out_file.display()
|
out_file.display()
|
||||||
);
|
);
|
||||||
sign_macos_node_addon(&out_file, &platform)?;
|
sign_macos_node_addon(&out_file, platform)?;
|
||||||
assert_no_redistributable_runtime_imports(&out_file, &platform)?;
|
assert_no_redistributable_runtime_imports(&out_file, platform)?;
|
||||||
Ok(BuiltNodeAddon {
|
Ok(BuiltNodeAddon {
|
||||||
out_file,
|
out_file,
|
||||||
source,
|
source,
|
||||||
|
|||||||
Reference in New Issue
Block a user