Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ splitViewAddChild(split, content);
App({ title: 'My App', width: 800, height: 500, body: split });
```

**10 target outputs from one codebase:**
**11 target outputs from one codebase:**

| Platform | Backend | Target Flag |
|----------|---------|-------------|
Expand All @@ -362,6 +362,7 @@ App({ title: 'My App', width: 800, height: 500, body: split });
| tvOS | UIKit | `--target tvos` / `--target tvos-simulator` |
| watchOS | WatchKit | `--target watchos` / `--target watchos-simulator` |
| Android | Android Views (JNI) | `--target android` |
| Wear OS | Android Views (JNI) | `--target wearos` |
| Windows | Win32 | *(default on Windows)* |
| Linux | GTK4 | *(default on Linux)* |
| Web | DOM (JS codegen) | `--target web` |
Expand Down Expand Up @@ -446,6 +447,7 @@ perry compile src/main.ts --target android -o MyApp
# TV / Watch
perry compile src/main.ts --target tvos -o MyApp
perry compile src/main.ts --target watchos -o MyApp
perry compile src/main.ts --target wearos -o MyApp # Wear OS (Android on a watch)

# Web
perry compile src/main.ts --target web -o app.html # JavaScript output
Expand Down
2 changes: 2 additions & 0 deletions crates/perry-codegen/src/codegen/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,8 @@ pub fn resolve_target_triple(name: &str) -> Option<String> {
"harmonyos" => Some("aarch64-unknown-linux-ohos".to_string()),
"harmonyos-simulator" => Some("x86_64-unknown-linux-ohos".to_string()),
"android" => Some("aarch64-unknown-linux-android".to_string()),
// Wear OS is Android-on-a-watch: same arm64 Android object format.
"wearos" => Some("aarch64-unknown-linux-android".to_string()),
"linux" => Some("x86_64-unknown-linux-gnu".to_string()),
"linux-aarch64" => Some("aarch64-unknown-linux-gnu".to_string()),
// musl targets — fully static binaries that run on Lambda
Expand Down
5 changes: 3 additions & 2 deletions crates/perry/src/commands/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1703,6 +1703,7 @@ pub fn run_with_parse_cache(
| Some("visionos")
| Some("visionos-simulator")
| Some("android")
| Some("wearos")
| Some("watchos")
| Some("watchos-simulator")
| Some("tvos")
Expand Down Expand Up @@ -4499,7 +4500,7 @@ pub fn run_with_parse_cache(
}
// Platform detection for nm tool and symbol prefix
let _is_ios = matches!(target.as_deref(), Some("ios-simulator") | Some("ios"));
let is_android = matches!(target.as_deref(), Some("android"));
let is_android = matches!(target.as_deref(), Some("android") | Some("wearos"));
let is_harmonyos = matches!(
target.as_deref(),
Some("harmonyos") | Some("harmonyos-simulator")
Expand Down Expand Up @@ -5051,7 +5052,7 @@ pub fn run_with_parse_cache(
target.as_deref(),
Some("visionos-simulator") | Some("visionos")
);
let is_android = matches!(target.as_deref(), Some("android"));
let is_android = matches!(target.as_deref(), Some("android") | Some("wearos"));
let is_harmonyos = matches!(
target.as_deref(),
Some("harmonyos") | Some("harmonyos-simulator")
Expand Down
4 changes: 4 additions & 0 deletions crates/perry/src/commands/compile/app_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ pub(super) fn target_bundle_section(target: Option<&str>) -> Option<&'static str
Some("watchos") | Some("watchos-simulator") => Some("watchos"),
Some("tvos") | Some("tvos-simulator") => Some("tvos"),
Some("android") => Some("android"),
// Wear OS reuses the [android] perry.toml section (bundle_id, etc.).
Some("wearos") => Some("android"),
Some("macos") => Some("macos"),
// WinUI shares the [windows] perry.toml section (#4680).
Some("windows") | Some("windows-winui") => Some("windows"),
Expand Down Expand Up @@ -171,6 +173,8 @@ pub(super) fn rust_target_triple(target: Option<&str>) -> Option<&'static str> {
Some("harmonyos") => Some("aarch64-unknown-linux-ohos"),
Some("harmonyos-simulator") => Some("x86_64-unknown-linux-ohos"),
Some("android") => Some("aarch64-linux-android"),
// Wear OS is Android-on-a-watch: same arm64 Android .so + toolchain.
Some("wearos") => Some("aarch64-linux-android"),
Some("linux") | Some("linux-x86_64") => Some("x86_64-unknown-linux-gnu"),
Some("linux-arm64") | Some("linux-aarch64") => Some("aarch64-unknown-linux-gnu"),
// Fully-static musl targets (#4826). The perry-runtime / perry-stdlib
Expand Down
29 changes: 25 additions & 4 deletions crates/perry/src/commands/compile/library_search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -951,7 +951,8 @@ pub(super) fn find_ui_library(target: Option<&str>) -> Option<PathBuf> {
let lib_name = match target {
Some("ios-simulator") | Some("ios") => "libperry_ui_ios.a",
Some("visionos-simulator") | Some("visionos") => "libperry_ui_visionos.a",
Some("android") => "libperry_ui_android.a",
// Wear OS reuses the Android View backend.
Some("android") | Some("wearos") => "libperry_ui_android.a",
Some("watchos-simulator") | Some("watchos") => "libperry_ui_watchos.a",
Some("tvos-simulator") | Some("tvos") => "libperry_ui_tvos.a",
Some("linux") => "libperry_ui_gtk4.a",
Expand Down Expand Up @@ -1090,6 +1091,18 @@ pub(super) fn android_cross_env(ndk_home: &Path, target: Option<&str>) -> Vec<(S
};
let clang = bin.join(format!("{}-clang{}", clang_target, ext));
let clangpp = bin.join(format!("{}-clang++{}", clang_target, ext));
// NDK r27+ removed the per-target `aarch64-linux-android-ar` wrapper that
// cc-rs probes for by default; the archiver is now the unprefixed
// `llvm-ar`. Without an explicit `AR_<triple>` the runtime/stdlib C
// dependencies (e.g. mimalloc) fail to build with
// `failed to find tool "aarch64-linux-android-ar"`. `llvm-ar` has no `.cmd`
// wrapper on Windows — it's the bare executable (+`.exe`).
let ar_ext = if cfg!(target_os = "windows") {
".exe"
} else {
""
};
let llvm_ar = bin.join(format!("llvm-ar{}", ar_ext));

let triple_upper = triple.to_uppercase().replace('-', "_");
let triple_under = triple.replace('-', "_");
Expand All @@ -1102,6 +1115,11 @@ pub(super) fn android_cross_env(ndk_home: &Path, target: Option<&str>) -> Vec<(S
format!("CXX_{}", triple_under),
clangpp.display().to_string(),
),
(format!("AR_{}", triple), llvm_ar.display().to_string()),
(
format!("AR_{}", triple_under),
llvm_ar.display().to_string(),
),
(
format!("CARGO_TARGET_{}_LINKER", triple_upper),
clang.display().to_string(),
Expand Down Expand Up @@ -1268,7 +1286,7 @@ pub(super) fn find_geisterhand_ui(target: Option<&str>) -> Option<PathBuf> {
"libperry_ui_ios.a"
} else if matches!(target, Some("visionos-simulator") | Some("visionos")) {
return None;
} else if matches!(target, Some("android")) {
} else if matches!(target, Some("android") | Some("wearos")) {
"libperry_ui_android.a"
} else if matches!(target, Some("linux")) || cfg!(target_os = "linux") {
"libperry_ui_gtk4.a"
Expand All @@ -1291,7 +1309,7 @@ pub(super) fn build_geisterhand_libs(target: Option<&str>, format: OutputFormat)
// Determine which UI crate to build based on target platform
let ui_crate = match target {
Some("ios-simulator") | Some("ios") => "perry-ui-ios",
Some("android") => "perry-ui-android",
Some("android") | Some("wearos") => "perry-ui-android",
Some("linux") => "perry-ui-gtk4",
Some("windows-winui") => "perry-ui-windows-winui",
Some("windows") => "perry-ui-windows",
Expand Down Expand Up @@ -1362,7 +1380,10 @@ pub(super) fn build_geisterhand_libs(target: Option<&str>, format: OutputFormat)
// `libperry_app.so` on Android; force global-dynamic TLS so the IE
// model doesn't crash at load. (RUSTFLAGS scopes to the cross target,
// so host build-scripts/proc-macros are unaffected.)
if matches!(target, Some("android") | Some("android-x86_64")) {
if matches!(
target,
Some("android") | Some("android-x86_64") | Some("wearos")
) {
let tls_flag = super::optimized_libs::android_global_dynamic_tls_rustflag(&mut cargo_cmd);
cargo_cmd.env("RUSTFLAGS", tls_flag);
}
Expand Down
3 changes: 2 additions & 1 deletion crates/perry/src/commands/compile/link/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,8 @@ pub(super) fn build_and_run_link(

let is_ios = matches!(target, Some("ios-simulator") | Some("ios"));
let is_visionos = matches!(target, Some("visionos-simulator") | Some("visionos"));
let is_android = matches!(target, Some("android"));
// Wear OS links exactly like Android (same triple, NDK, cdylib + TLS model).
let is_android = matches!(target, Some("android") | Some("wearos"));
let is_harmonyos = matches!(target, Some("harmonyos") | Some("harmonyos-simulator"));
let is_linux = matches!(target, Some(t) if t.starts_with("linux"))
|| (target.is_none() && cfg!(target_os = "linux"));
Expand Down
3 changes: 2 additions & 1 deletion crates/perry/src/commands/compile/lock_scan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ fn derive_target_key(target: Option<&str>) -> String {
Some("watchos-simulator") => "watchos-simulator".to_string(),
Some("visionos") => "visionos".to_string(),
Some("visionos-simulator") => "visionos-simulator".to_string(),
Some("android") => "android".to_string(),
// Wear OS reuses Android's resolved native-dependency set.
Some("android") | Some("wearos") => "android".to_string(),
Some("harmonyos") => "harmonyos".to_string(),
Some("harmonyos-simulator") => "harmonyos-simulator".to_string(),
Some("web") => "web".to_string(),
Expand Down
12 changes: 9 additions & 3 deletions crates/perry/src/commands/compile/optimized_libs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,10 @@ pub(super) fn build_optimized_libs(
// #1508: same shape for Android — cc-rs can't find the NDK clang
// otherwise (silent on Unix where `clang` happens to exist, hard fail
// on Windows with `clang.exe not found`).
if matches!(target, Some("android") | Some("android-x86_64")) {
if matches!(
target,
Some("android") | Some("android-x86_64") | Some("wearos")
) {
if let Some(ndk) = std::env::var_os("ANDROID_NDK_HOME") {
for (k, v) in
super::library_search::android_cross_env(std::path::Path::new(&ndk), target)
Expand Down Expand Up @@ -821,7 +824,10 @@ pub(super) fn build_optimized_libs(
// shadow stack), so those IE TLS relocations get baked into the final
// cdylib. Force global-dynamic so the dynamic linker can resolve TLS
// slots after the process has started.
if matches!(target, Some("android") | Some("android-x86_64")) {
if matches!(
target,
Some("android") | Some("android-x86_64") | Some("wearos")
) {
rustflags.push(android_global_dynamic_tls_rustflag(&mut cargo_cmd));
}
if !rustflags.is_empty() {
Expand Down Expand Up @@ -1108,7 +1114,7 @@ pub(super) fn build_optimized_libs(
| Some("ios-widget")
| Some("ios-widget-simulator") => "perry-ui-ios",
Some("visionos-simulator") | Some("visionos") => "perry-ui-visionos",
Some("android") => "perry-ui-android",
Some("android") | Some("wearos") => "perry-ui-android",
Some("watchos-simulator") | Some("watchos") => "perry-ui-watchos",
Some("tvos-simulator") | Some("tvos") => "perry-ui-tvos",
Some("linux") => "perry-ui-gtk4",
Expand Down
3 changes: 3 additions & 0 deletions crates/perry/src/commands/compile/post_link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ pub(super) fn strip_final_binary(
|| is_watchos
|| is_harmonyos
|| target == Some("android")
// Wear OS ships the same dlopen'd .so — stripping would drop the
// no_mangle JNI/FFI symbols PerryActivity resolves at load.
|| target == Some("wearos")
|| std::env::var("PERRY_DEBUG_SYMBOLS").is_ok()
{
return;
Expand Down
5 changes: 3 additions & 2 deletions crates/perry/src/commands/compile/resolve/native_library.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ pub(super) fn native_manifest_target_key(target: Option<&str>) -> &'static str {
match target {
Some("ios-simulator") | Some("ios") => "ios",
Some("visionos-simulator") | Some("visionos") => "visionos",
Some("android") => "android",
// Wear OS resolves native-addon config from the [android] target.
Some("android") | Some("wearos") => "android",
Some("tvos-simulator") | Some("tvos") => "tvos",
Some("watchos-simulator") | Some("watchos") => "watchos",
Some("harmonyos-simulator") | Some("harmonyos") => "harmonyos",
Expand Down Expand Up @@ -1314,7 +1315,7 @@ fn arch_for_target_key(target: Option<&str>) -> Option<&'static str> {
Some("macos") => Some("arm64"),
Some("linux") => Some("x64"),
Some("windows") | Some("windows-winui") => Some("x64"),
Some("android") => Some("arm64"),
Some("android") | Some("wearos") => Some("arm64"),
Some("harmonyos") => Some("arm64"),
Some("harmonyos-simulator") => Some("x64"),
// ios/tvos/watchos/visionos: device builds are always arm64 (or
Expand Down
4 changes: 3 additions & 1 deletion crates/perry/src/commands/publish/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub fn run(args: PublishArgs, format: OutputFormat, use_color: bool, _verbose: u
let target_hint = match args.platform {
Some(Platform::Ios) => Some("ios"),
Some(Platform::Visionos) => Some("visionos"),
Some(Platform::Android) => Some("android"),
Some(Platform::Android) | Some(Platform::Wearos) => Some("android"),
Some(Platform::Linux) => Some("linux"),
Some(Platform::Windows) => Some("windows"),
Some(Platform::Web) => Some("web"),
Expand Down Expand Up @@ -150,6 +150,8 @@ async fn run_async(args: PublishArgs, format: OutputFormat, _use_color: bool) ->
Platform::Watchos => "watchos".to_string(),
Platform::Tvos => "tvos".to_string(),
Platform::Android => "android".to_string(),
// Wear OS ships through Google Play exactly like an Android app.
Platform::Wearos => "android".to_string(),
Platform::Linux => "linux".to_string(),
Platform::Windows => "windows".to_string(),
Platform::Web => "web".to_string(),
Expand Down
1 change: 1 addition & 0 deletions crates/perry/src/commands/publish/preflight.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ pub(super) async fn run_security_audit_step(
Some(Platform::Ios)
| Some(Platform::Visionos)
| Some(Platform::Android)
| Some(Platform::Wearos)
| Some(Platform::Macos)
| Some(Platform::Tvos)
| Some(Platform::Watchos)
Expand Down
Loading