diff --git a/.github/assets/splash.png b/.github/assets/splash.png deleted file mode 100644 index eda14649..00000000 Binary files a/.github/assets/splash.png and /dev/null differ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dcb291ee..4899657d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: run: | cd examples/macroquad-3d export ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME - crossbundle build android --apk --quad --release + crossbundle build android -s=native-apk --release apple-build-macos: name: Build Apple example on macOS latest @@ -44,7 +44,7 @@ jobs: - name: Build Apple app run: | cd examples/macroquad-3d - crossbundle build apple --release --target=aarch64-apple-ios --quad + crossbundle build ios --release --target=aarch64-apple-ios test-crossbundle-new: name: Generate and Build from template on macOS latest @@ -66,7 +66,7 @@ jobs: - name: Build Apple app run: | cd ~/example/ - crossbundle build android --apk --release --quad + crossbundle build android -s=native-apk --release build-example-in-docker: name: Build Crossbundle Example from Docker @@ -77,7 +77,7 @@ jobs: run: docker build -t tmp -f .github/docker/crossbundle.Dockerfile . - name: Build Crossbundle Example run: | - docker run --rm -v "$(pwd)/:/src" -w /src/examples/macroquad-permissions tmp build android --quad --release + docker run --rm -v "$(pwd)/:/src" -w /src/examples/macroquad-permissions tmp build android --release clean: name: Check code format @@ -91,14 +91,13 @@ jobs: components: rustfmt, clippy override: true - name: Check the format - run: cargo +nightly fmt --all -- --check + run: | + cd crossbundle/cli/ + cargo +nightly fmt --all -- --check - name: Run clippy - run: cargo clippy --all-targets --all-features -- -D warnings -A clippy::unnecessary-unwrap -A clippy::too-many-arguments - # Blocked by https://github.com/rust-lang/rust/issues/99261 - # - name: Run clippy on crossbundle - # run: | - # cd crossbundle/cli - # cargo clippy --all-targets --all-features -- -D warnings -A clippy::unnecessary-unwrap -A clippy::too-many-arguments + run: | + cd crossbundle/cli/ + cargo clippy --all-targets --all-features -- -D warnings -A clippy::unnecessary-unwrap -A clippy::too-many-arguments - name: Check for deadlinks run: | cargo install cargo-deadlinks diff --git a/Cargo.toml b/Cargo.toml index aff73f9a..7e3cbc70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "crossbow" -version = "0.1.7" +version = "0.1.8" edition = "2021" authors = ["DodoRare Team "] -description = "Cross-Platform Rust Toolkit for Games ๐Ÿน" +description = "Cross-Platform build tools and toolkit for games" repository = "https://github.com/dodorare/crossbow" license = "Apache-2.0" keywords = ["build", "android", "ios", "tools"] @@ -14,13 +14,17 @@ exclude = [".github/**/*"] thiserror = "1.0" displaydoc = "0.2" anyhow = "1.0" +serde = { version = "1.0", features = ["derive"] } + +android-manifest = { version = "0.1.9", optional = true } +apple-bundle = { version = "0.1.4", optional = true } [target.'cfg(target_os = "android")'.dependencies] -crossbow-android = { path = "platform/android", version = "0.1.7", optional = true } -ndk-glue = "0.6.2" +ndk-glue = "0.7.0" +crossbow-android = { path = "platform/android", version = "0.1.8", optional = true } [target.'cfg(target_os = "ios")'.dependencies] -crossbow-ios = { path = "platform/ios", version = "0.1.7", optional = true } +crossbow-ios = { path = "platform/ios", version = "0.1.8", optional = true } [patch.crates-io] winit = { git = "https://github.com/rust-windowing/winit", rev = "f93f2c158bf527ed56ab2b6f5272214f0c1d9f7d" } @@ -31,6 +35,7 @@ miniquad = { git = "https://github.com/not-fl3/miniquad", rev = "d67ffe6950cf73d default = ["android", "ios"] android = ["crossbow-android"] ios = ["crossbow-ios"] +update-manifest = ["apple-bundle", "android-manifest"] [workspace] members = [ diff --git a/README.md b/README.md index 166cb1c6..f57a8cda 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# [![Crossbow Splash Image](.github/assets/splash.png)](https://github.com/dodorare/crossbow) +# [![Crossbow Splash Image](assets/splash.png)](https://github.com/dodorare/crossbow) [![CI Info](https://github.com/dodorare/crossbow/workflows/CI/badge.svg)](https://github.com/dodorare/crossbow/actions) [![Crate Info](https://img.shields.io/crates/v/crossbow.svg)](https://crates.io/crates/crossbow) @@ -16,16 +16,17 @@ The `crossbow` project aims to provide a complete toolkit for cross-platform gam > There are already [cargo-apk](https://github.com/rust-windowing/android-ndk-rs/tree/master/cargo-apk), [cargo-mobile](https://github.com/BrainiumLLC/cargo-mobile), [cargo-xcode](https://gitlab.com/kornelski/cargo-xcode), etc. - why do I need another packaging tool? -Project `crossbow` is not only a packaging tool for **Android** and iOS - it's a toolkit. With `crossbundle-tools` you can customize and create new commands; with `crossbundle` you can create native **.apk/.aab** without any *Java* or setup *Gradle* project with fancy **Crossbow Android plugins** (**iOS** in near future); with `crossbow-android` you can write your own Android plugins in *Java/Kotlin*. +Project `crossbow` is not only a packaging tool for **Android** and **iOS** - it's cross-platform build tools and toolkit for Rust! With `crossbundle` you can create native **.apk/.aab** without any *Java* or setup *Gradle* project with fancy **Crossbow Android plugins** (**iOS** in near future); with `crossbundle-tools` you can customize and create new commands; with `crossbow-android` you can write your own Android plugins in *Java/Kotlin*. A lot of functionality was inspired by [Godot](https://github.com/godotengine/godot), [Xamarin](https://dotnet.microsoft.com/en-us/apps/xamarin), and [cargo-apk](https://github.com/rust-windowing/android-ndk-rs/tree/master/cargo-apk). ## Design Goals * **Customizable**: Create new commands with available tools. -* **Simple**: Easy to start but flexible for strong devs. +* **Simple**: Easy to install and start hacking but also pretty flexible for strong devs. * **Capable**: It's possible to build plain **.apk/.aab** or **.app/.ipa**; or with help of *Gradle/XCode*. * **Rust**: Don't leave your *Rust* code - **everything** can be configured from `Cargo.toml`. +* **Plugins**: Godot-like plugins for **Android** (and **iOS** in future) with *Rust* wrapper! ## ๐Ÿ“š Documentation @@ -64,7 +65,7 @@ Helper crates: Also, this project initially funded by [Web3 Foundation Grants Program](https://github.com/w3f/Grants-Program/blob/master/applications/crossbow.md). Big shout-out to them! -W3F Grants Badge +W3F Grants Badge ## ๐Ÿ“‘ License diff --git a/examples/bevy-explorer/assets/fonts/FiraMono-Medium.ttf b/assets/fonts/FiraMono-Medium.ttf similarity index 100% rename from examples/bevy-explorer/assets/fonts/FiraMono-Medium.ttf rename to assets/fonts/FiraMono-Medium.ttf diff --git a/examples/bevy-explorer/assets/fonts/FiraSans-Bold.ttf b/assets/fonts/FiraSans-Bold.ttf similarity index 100% rename from examples/bevy-explorer/assets/fonts/FiraSans-Bold.ttf rename to assets/fonts/FiraSans-Bold.ttf diff --git a/assets/images/icon.png b/assets/images/icon.png new file mode 100644 index 00000000..527d360c Binary files /dev/null and b/assets/images/icon.png differ diff --git a/examples/macroquad-3d/assets/bob/rust.png b/assets/images/rust.png similarity index 100% rename from examples/macroquad-3d/assets/bob/rust.png rename to assets/images/rust.png diff --git a/examples/bevy-3d/assets/models/helmet/FlightHelmet.bin b/assets/models/helmet/FlightHelmet.bin similarity index 100% rename from examples/bevy-3d/assets/models/helmet/FlightHelmet.bin rename to assets/models/helmet/FlightHelmet.bin diff --git a/examples/bevy-3d/assets/models/helmet/FlightHelmet.gltf b/assets/models/helmet/FlightHelmet.gltf similarity index 100% rename from examples/bevy-3d/assets/models/helmet/FlightHelmet.gltf rename to assets/models/helmet/FlightHelmet.gltf diff --git a/examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_GlassPlasticMat_BaseColor.png b/assets/models/helmet/FlightHelmet_Materials_GlassPlasticMat_BaseColor.png similarity index 100% rename from examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_GlassPlasticMat_BaseColor.png rename to assets/models/helmet/FlightHelmet_Materials_GlassPlasticMat_BaseColor.png diff --git a/examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_GlassPlasticMat_Normal.png b/assets/models/helmet/FlightHelmet_Materials_GlassPlasticMat_Normal.png similarity index 100% rename from examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_GlassPlasticMat_Normal.png rename to assets/models/helmet/FlightHelmet_Materials_GlassPlasticMat_Normal.png diff --git a/examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_GlassPlasticMat_OcclusionRoughMetal.png b/assets/models/helmet/FlightHelmet_Materials_GlassPlasticMat_OcclusionRoughMetal.png similarity index 100% rename from examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_GlassPlasticMat_OcclusionRoughMetal.png rename to assets/models/helmet/FlightHelmet_Materials_GlassPlasticMat_OcclusionRoughMetal.png diff --git a/examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_LeatherPartsMat_BaseColor.png b/assets/models/helmet/FlightHelmet_Materials_LeatherPartsMat_BaseColor.png similarity index 100% rename from examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_LeatherPartsMat_BaseColor.png rename to assets/models/helmet/FlightHelmet_Materials_LeatherPartsMat_BaseColor.png diff --git a/examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_LeatherPartsMat_Normal.png b/assets/models/helmet/FlightHelmet_Materials_LeatherPartsMat_Normal.png similarity index 100% rename from examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_LeatherPartsMat_Normal.png rename to assets/models/helmet/FlightHelmet_Materials_LeatherPartsMat_Normal.png diff --git a/examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_LeatherPartsMat_OcclusionRoughMetal.png b/assets/models/helmet/FlightHelmet_Materials_LeatherPartsMat_OcclusionRoughMetal.png similarity index 100% rename from examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_LeatherPartsMat_OcclusionRoughMetal.png rename to assets/models/helmet/FlightHelmet_Materials_LeatherPartsMat_OcclusionRoughMetal.png diff --git a/examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_LensesMat_BaseColor.png b/assets/models/helmet/FlightHelmet_Materials_LensesMat_BaseColor.png similarity index 100% rename from examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_LensesMat_BaseColor.png rename to assets/models/helmet/FlightHelmet_Materials_LensesMat_BaseColor.png diff --git a/examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_LensesMat_Normal.png b/assets/models/helmet/FlightHelmet_Materials_LensesMat_Normal.png similarity index 100% rename from examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_LensesMat_Normal.png rename to assets/models/helmet/FlightHelmet_Materials_LensesMat_Normal.png diff --git a/examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_LensesMat_OcclusionRoughMetal.png b/assets/models/helmet/FlightHelmet_Materials_LensesMat_OcclusionRoughMetal.png similarity index 100% rename from examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_LensesMat_OcclusionRoughMetal.png rename to assets/models/helmet/FlightHelmet_Materials_LensesMat_OcclusionRoughMetal.png diff --git a/examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_MetalPartsMat_BaseColor.png b/assets/models/helmet/FlightHelmet_Materials_MetalPartsMat_BaseColor.png similarity index 100% rename from examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_MetalPartsMat_BaseColor.png rename to assets/models/helmet/FlightHelmet_Materials_MetalPartsMat_BaseColor.png diff --git a/examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_MetalPartsMat_Normal.png b/assets/models/helmet/FlightHelmet_Materials_MetalPartsMat_Normal.png similarity index 100% rename from examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_MetalPartsMat_Normal.png rename to assets/models/helmet/FlightHelmet_Materials_MetalPartsMat_Normal.png diff --git a/examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_MetalPartsMat_OcclusionRoughMetal.png b/assets/models/helmet/FlightHelmet_Materials_MetalPartsMat_OcclusionRoughMetal.png similarity index 100% rename from examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_MetalPartsMat_OcclusionRoughMetal.png rename to assets/models/helmet/FlightHelmet_Materials_MetalPartsMat_OcclusionRoughMetal.png diff --git a/examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_RubberWoodMat_BaseColor.png b/assets/models/helmet/FlightHelmet_Materials_RubberWoodMat_BaseColor.png similarity index 100% rename from examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_RubberWoodMat_BaseColor.png rename to assets/models/helmet/FlightHelmet_Materials_RubberWoodMat_BaseColor.png diff --git a/examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_RubberWoodMat_Normal.png b/assets/models/helmet/FlightHelmet_Materials_RubberWoodMat_Normal.png similarity index 100% rename from examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_RubberWoodMat_Normal.png rename to assets/models/helmet/FlightHelmet_Materials_RubberWoodMat_Normal.png diff --git a/examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_RubberWoodMat_OcclusionRoughMetal.png b/assets/models/helmet/FlightHelmet_Materials_RubberWoodMat_OcclusionRoughMetal.png similarity index 100% rename from examples/bevy-3d/assets/models/helmet/FlightHelmet_Materials_RubberWoodMat_OcclusionRoughMetal.png rename to assets/models/helmet/FlightHelmet_Materials_RubberWoodMat_OcclusionRoughMetal.png diff --git a/assets/res/android/mipmap-anydpi-v26/ic_launcher.xml b/assets/res/android/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..90f95809 --- /dev/null +++ b/assets/res/android/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/assets/res/android/mipmap-hdpi/ic_launcher.png b/assets/res/android/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..a731e2f6 Binary files /dev/null and b/assets/res/android/mipmap-hdpi/ic_launcher.png differ diff --git a/assets/res/android/mipmap-hdpi/ic_launcher_adaptive_back.png b/assets/res/android/mipmap-hdpi/ic_launcher_adaptive_back.png new file mode 100644 index 00000000..a067d44e Binary files /dev/null and b/assets/res/android/mipmap-hdpi/ic_launcher_adaptive_back.png differ diff --git a/assets/res/android/mipmap-hdpi/ic_launcher_adaptive_fore.png b/assets/res/android/mipmap-hdpi/ic_launcher_adaptive_fore.png new file mode 100644 index 00000000..031b2326 Binary files /dev/null and b/assets/res/android/mipmap-hdpi/ic_launcher_adaptive_fore.png differ diff --git a/assets/res/android/mipmap-mdpi/ic_launcher.png b/assets/res/android/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..573a0aee Binary files /dev/null and b/assets/res/android/mipmap-mdpi/ic_launcher.png differ diff --git a/assets/res/android/mipmap-mdpi/ic_launcher_adaptive_back.png b/assets/res/android/mipmap-mdpi/ic_launcher_adaptive_back.png new file mode 100644 index 00000000..391d9414 Binary files /dev/null and b/assets/res/android/mipmap-mdpi/ic_launcher_adaptive_back.png differ diff --git a/assets/res/android/mipmap-mdpi/ic_launcher_adaptive_fore.png b/assets/res/android/mipmap-mdpi/ic_launcher_adaptive_fore.png new file mode 100644 index 00000000..19549c63 Binary files /dev/null and b/assets/res/android/mipmap-mdpi/ic_launcher_adaptive_fore.png differ diff --git a/assets/res/android/mipmap-xhdpi/ic_launcher.png b/assets/res/android/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..4d8db16f Binary files /dev/null and b/assets/res/android/mipmap-xhdpi/ic_launcher.png differ diff --git a/assets/res/android/mipmap-xhdpi/ic_launcher_adaptive_back.png b/assets/res/android/mipmap-xhdpi/ic_launcher_adaptive_back.png new file mode 100644 index 00000000..7d0a749b Binary files /dev/null and b/assets/res/android/mipmap-xhdpi/ic_launcher_adaptive_back.png differ diff --git a/assets/res/android/mipmap-xhdpi/ic_launcher_adaptive_fore.png b/assets/res/android/mipmap-xhdpi/ic_launcher_adaptive_fore.png new file mode 100644 index 00000000..283eb7e7 Binary files /dev/null and b/assets/res/android/mipmap-xhdpi/ic_launcher_adaptive_fore.png differ diff --git a/assets/res/android/mipmap-xxhdpi/ic_launcher.png b/assets/res/android/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..61053c49 Binary files /dev/null and b/assets/res/android/mipmap-xxhdpi/ic_launcher.png differ diff --git a/assets/res/android/mipmap-xxhdpi/ic_launcher_adaptive_back.png b/assets/res/android/mipmap-xxhdpi/ic_launcher_adaptive_back.png new file mode 100644 index 00000000..958e95d6 Binary files /dev/null and b/assets/res/android/mipmap-xxhdpi/ic_launcher_adaptive_back.png differ diff --git a/assets/res/android/mipmap-xxhdpi/ic_launcher_adaptive_fore.png b/assets/res/android/mipmap-xxhdpi/ic_launcher_adaptive_fore.png new file mode 100644 index 00000000..4d834b02 Binary files /dev/null and b/assets/res/android/mipmap-xxhdpi/ic_launcher_adaptive_fore.png differ diff --git a/assets/res/android/mipmap-xxxhdpi/ic_launcher.png b/assets/res/android/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..078e13f2 Binary files /dev/null and b/assets/res/android/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/assets/res/android/mipmap-xxxhdpi/ic_launcher_adaptive_back.png b/assets/res/android/mipmap-xxxhdpi/ic_launcher_adaptive_back.png new file mode 100644 index 00000000..f1b188fe Binary files /dev/null and b/assets/res/android/mipmap-xxxhdpi/ic_launcher_adaptive_back.png differ diff --git a/assets/res/android/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png b/assets/res/android/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png new file mode 100644 index 00000000..8ec314fd Binary files /dev/null and b/assets/res/android/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png differ diff --git a/assets/res/apple/icon.png b/assets/res/apple/icon.png new file mode 100644 index 00000000..d00f7831 Binary files /dev/null and b/assets/res/apple/icon.png differ diff --git a/examples/bevy-2d/assets/sounds/Windless-Slopes.mp3 b/assets/sounds/Windless-Slopes.mp3 similarity index 100% rename from examples/bevy-2d/assets/sounds/Windless-Slopes.mp3 rename to assets/sounds/Windless-Slopes.mp3 diff --git a/assets/splash.png b/assets/splash.png new file mode 100644 index 00000000..62db04f3 Binary files /dev/null and b/assets/splash.png differ diff --git a/.github/assets/w3f_grants_badge.svg b/assets/w3f_grants_badge.svg similarity index 100% rename from .github/assets/w3f_grants_badge.svg rename to assets/w3f_grants_badge.svg diff --git a/crossbundle/cli/Cargo.toml b/crossbundle/cli/Cargo.toml index be96c5b1..f2023f3d 100644 --- a/crossbundle/cli/Cargo.toml +++ b/crossbundle/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "crossbundle" -version = "0.1.7" +version = "0.1.8" edition = "2021" authors = ["DodoRare Team "] description = "Build and publish apps for Android/iOS" @@ -18,9 +18,10 @@ name = "crossbundle" path = "src/main.rs" [dependencies] -crossbundle-tools = { path = "../tools", version = "0.1.7" } -android-tools = "0.2.9" -clap = { version = "3.2.8", features = ["derive"] } +crossbow = { path = "../../", version = "0.1.8", default-features = false, features = ["update-manifest"] } +crossbundle-tools = { path = "../tools", version = "0.1.8", default-features = false } +android-tools = { version = "0.2.10", optional = true } +clap = { version = "3.2", features = ["derive"] } serde = { version = "1.0", features = ["derive"] } anyhow = "1.0" @@ -31,16 +32,16 @@ pretty_env_logger = "0.4" log = "0.4" fs_extra = "1.2" -dirs = "4.0.0" +dirs = "4.0" dunce = "1.0" -ureq = { version = "2.4.0", features = ["tls"] } +ureq = { version = "2.5", features = ["tls"] } cargo = "0.63.1" cargo-util = "0.2.0" [dev-dependencies] -tempfile = "3.2" +tempfile = "3.3" [features] -default = ["android", "ios"] -android = [] -ios = [] +default = ["android", "apple"] +android = ["crossbow/android", "crossbundle-tools/android", "android-tools"] +apple = ["crossbow/ios", "crossbundle-tools/apple"] diff --git a/crossbundle/cli/README.md b/crossbundle/cli/README.md index 9825bca8..97d3cc59 100644 --- a/crossbundle/cli/README.md +++ b/crossbundle/cli/README.md @@ -1,18 +1,28 @@ -# ๐Ÿน CrossBundle CLI +# Crossbundle CLI -![splash](https://github.com/dodorare/crossbow/blob/main/.github/assets/splash.png?raw=true) +![splash](https://github.com/dodorare/crossbow/blob/main/assets/splash.png?raw=true) The **crossbundle** is a command-line tool that encapsulates boring stuff of **Android** and **iOS** build/packaging processes and helps mobile developers to create and maintain applications written in **rust** programming language. -## ๐Ÿ‘๏ธโ€๐Ÿ—จ๏ธ Support status +## Support status -Packaging status: +Supported operating systems for build (**iOS** only on **macOS**): + +| Name | Status | +| ---- | ------ | +| Windows | โœ… | +| Linux | โœ… | +| macOS | โœ… | + +Packaging Strategy status: | Name | Description | Status | | ---- | ----------- | ------ | -| Android APK | Default build result method. | โœ… | -| Android AAB | Supported via `--aab` flag. | โœ… | -| Apple Debug IPA | Default build result method. Works only on Simulator and could be run on iPhone with Dev Certificate. | ๐Ÿ†— | +| Android APK | Supported via `-s=native-apk` flag. | โœ… | +| Android AAB | Supported via `-s=native-aab` flag. | โœ… | +| Android Gradle | Supported via `-s=gradle-apk` flag. | โœ… | +| Apple Debug APP | Default build strategy. Works only on Simulator and could be run on iPhone with Dev Certificate. | โœ… | +| Apple Debug IPA | Works only on Simulator and could be run on iPhone with Dev Certificate. | ๐Ÿ†— | | Apple Release IPA | Not supported yet. Crossbundle should generate `xcodeproj`, but user should build and sign IPA manually. | ๐Ÿ›  | Supported game engines: @@ -20,82 +30,68 @@ Supported game engines: | Name | Description | Status | | ---- | ----------- | ------ | | [Bevy](https://github.com/bevyengine/bevy) | Default build method. Injects [ndk-glue](https://github.com/rust-windowing/android-ndk-rs/tree/master/ndk-glue) into generated tmp `lib.rs` file. | ๐Ÿ†— | -| [Macroquad](https://github.com/not-fl3/macroquad) | Supported via `--quad` flag. Works as [cargo-quad-apk](https://github.com/not-fl3/cargo-quad-apk) but with all `crossbundle` features. | โœ… | +| [Macroquad](https://github.com/not-fl3/macroquad) | Supported via `app_wrapper = "sokol"` inside `Cargo.toml` metadata. Also, can work as [cargo-quad-apk](https://github.com/not-fl3/cargo-quad-apk) but with all `crossbundle` features. | โœ… | | **placeholder** | Don't find your game engine here? Open an issue! We are happy to add support for new engines. | ๐Ÿ›  | โœ… = Works and tested โ€” ๐Ÿ†— = Works but may contain bugs โ€” ๐Ÿ›  = Under development -## ๐ŸŒ€ Installation +## Installation ```sh cargo install --git=https://github.com/dodorare/crossbow crossbundle ``` -See [installation documentation](../../docs/README.md) for more details on how to setup environment on your platform. - ---- - -**_NOTE_** +See [installation documentation](https://crossbow.dodorare.com/install/index.html) for more details on how to setup environment on your platform. -For the correct work of the tool, you need to set up a development environment (ex. install some libraries and tools - such as Android SDK, Android NDK, XCode, etc). -More information about how to set up the environment in the **Android setup** and **iOS setup** wiki pages. - ---- - -## โš™๏ธ Cargo.toml Metadata syntax +## Cargo.toml Metadata syntax ```toml [[package.metadata.android]] -# The user-friendly application name for your app. Displayed in the applications menu +# Cross-platform user-friendly application name for your app. app_name = "Example" -# The version number shown to users -version_name = "0.1.0" -# Internal version number used to determine whether one version is more recent than another -# -# See https://developer.android.com/guide/topics/manifest/manifest-element -version_code = 1 -# Min SDK version -min_sdk_version = 19 -# Target SDK version -target_sdk_version = 30 -# Max SDK version -max_sdk_version = 31 -# Virtual path your application's icon as mipmap resource. -icon = "ic_launcher" +# Cross-platform assets directory path relatively to project path. +assets = "assets" +[[package.metadata.android]] +# Android application wrapper: supports ndk-glue and sokol +app_wrapper = "sokol" +# The user-friendly application name for your app. Displayed in the applications menu +app_name = "Example" # Path to AndroidManifest.xml file manifest_path = "path/to/AndroidManifest.xml" - -# Android package to place in AndroidManifest.xml. -package = "com.example.ExampleProject" # Android resources directory path relatively to project path. res = "res/android" # Android assets directory path relatively to project path. assets = "assets" -# Android build targets. -build_targets = ["aarch64-linux-android"] +# Android targets to build on debug or release. +debug_build_targets = ["aarch64-linux-android"] +release_build_targets = ["aarch64-linux-android"] + +# Complete support of ALL AndroidManifest.xml attributes +[package.metadata.android.manifest] +package = "com.example.ExampleProject" # Adds a uses-permission element to the AndroidManifest.xml. # Note that android_version 23 and higher, Android requires the application to request permissions at runtime. -[[package.metadata.android.permissions]] +[[package.metadata.android.manifest.uses_permission]] name = "android.permission.INTERNET" -# Specifies that an app wants a particular permission, but only if the app is installed on a device running +# Specifies that an app wants a particular permission, but only if the app is installed on a device running # Android 6.0 (API level 23) or higher. If the device is running API level 22 or lower, the app does not have the specified permission. # # See https://developer.android.com/guide/topics/manifest/uses-permission-sdk-23-element -[[package.metadata.android.permissions_sdk_23]] +[[package.metadata.android.manifest.uses_permission_sdk_23]] name = "android.permission.WRITE_EXTERNAL_STORAGE" max_sdk_version = 30 # See https://developer.android.com/guide/topics/manifest/service-element -[[package.metadata.android.service]] +[[package.metadata.android.manifest.service]] name = "UpdateService" intent_filter = [] meta_data = [] # See https://developer.android.com/guide/topics/manifest/queries-element#provider -[[package.metadata.android.queries.provider]] +[[package.metadata.android.manifest.queries.provider]] authorities = "org.khronos.openxr.runtime_broker;org.khronos.openxr.system_runtime_broker" # Note: The `name` attribute is normally not required for a queries provider, but is non-optional # as a workaround for aapt throwing errors about missing `android:name` attribute. @@ -105,35 +101,34 @@ name = "org.khronos.openxr" # See https://developer.android.com/guide/topics/manifest/uses-feature-element # # Note: there can be multiple .uses_feature entries. -[[package.metadata.android.features]] +[[package.metadata.android.manifest.features]] name = "android.hardware.vulkan.level" required = true version = 1 # See https://developer.android.com/guide/topics/manifest/meta-data-element -[[package.metadata.android.meta_data]] +[[package.metadata.android.manifest.application.meta_data]] name = "com.oculus.vr.focusaware" value = "true" [package.metadata.apple] # The user-friendly application name for your app. Displayed in the applications menu app_name = "Example" -# Apple build targets. -build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] +# Apple targets to build on debug or release. +debug_build_targets = ["aarch64-apple-ios"] +release_build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] # Apple resources directory path relatively to project path. res = "res/apple" -# Apple assets directory path relatively to project path. -assets = "assets" ``` -## ๐ŸŽ CLI options and flags +## CLI options and flags To see the complete documentation for each command/subcommand you can write `-h` or `--help`: ```sh crossbundle -h crossbundle build android -h -crossbundle run apple -h +crossbundle run ios -h crossbundle install -h # ... ``` @@ -155,31 +150,24 @@ SUBCOMMANDS: build Starts the process of building/packaging/signing of the rust crate help Print this message or the help of the given subcommand(s) install Installs bundletool and Android Studio's sdkmanager - log Attach logger to device with running application new Creates a new Cargo package in the given directory. Project will be ready to build with `crossbundle` run Executes `build` command and then deploy and launches the application on the device/emulator ``` -Result of `crossbundle build android -h`: +Result of `crossbundle run android -h` (this command extends `crossbundle build android`): ```text -Starts the process of building/packaging/signing of the rust crate for Android +Executes `build` command and then deploy and launches the application on the Android device/emulator USAGE: - crossbundle build android [OPTIONS] + crossbundle run android [OPTIONS] OPTIONS: - --aab - Generating native aab without Java. By default crossbow generating gradle project - --all-features Activate all available features of selected package - --apk - Generating native apk without Java. By default crossbow generating gradle project - --example Build the specified example @@ -196,17 +184,21 @@ OPTIONS: Print help information --lib - Compile rust code as a dynamic library [default: crossbow-android] + Only compile rust code as a dynamic library. By default: "crossbow-android" + + --log + Enable logging attach after run --no-default-features Do not activate the `default` feature of the current directory's package - --quad - Specifies to build macroquad-based game with Sokol application wrapper - --release Build optimized artifact with the `release` profile + -s, --strategy + Build strategy specifies what and how to build Android application: with help of Gradle, + or with our native approach [default: gradle-apk] + --sign-key-alias Signing key alias @@ -216,22 +208,22 @@ OPTIONS: --sign-key-path Path to the signing key - --target + -t, --target ... Build for the given android architecture. Supported targets are: `armv7-linux-androideabi`, `aarch64-linux-android`, `i686-linux-android`, - `x86_64-linux-android` [default: aarch64-linux-android] + `x86_64-linux-android` --target-dir Directory for generated artifact and intermediate files ``` -Result of `crossbundle build apple -h`: +Result of `crossbundle build ios -h` (this command extends `crossbundle build ios`): ```text -Starts the process of building/packaging/signing of the rust crate for iOS +Executes `build` command and then deploy and launches the application on the iOS device/emulator USAGE: - crossbundle build apple [OPTIONS] + crossbundle run ios [OPTIONS] OPTIONS: --all-features @@ -240,6 +232,15 @@ OPTIONS: --bin Specify custom cargo binary + -d, --debug + Run in debug mode + + -d, --device + Install and launch on the connected device + + -D, --device-id + Connected device id + --example Build the specified example @@ -266,16 +267,20 @@ OPTIONS: --profile-path Absolute path to provisioning profile - --quad - Specifies to build macroquad-based game with Sokol application wrapper - --release Build optimized artifact with the `release` profile - --target + -s, --strategy + Build strategy specifies what and how to build iOS application: with help of XCode, or + with our native approach [default: native-ipa] + + -s, --simulator-name + Simulator device name [default: "iPhone 13"] + + -t, --target ... Build for the given apple architecture. Supported targets are: `aarch64-apple-ios`, `aarch64-apple-ios-sim`, `armv7-apple-ios`, `armv7s-apple-ios`, `i386-apple-ios`, - `x86_64-apple-ios` [default: aarch64-apple-ios-sim] + `x86_64-apple-ios` --target-dir Directory for generated artifact and intermediate files @@ -284,12 +289,12 @@ OPTIONS: The team identifier of your signing identity ``` -## โŒ Troubleshooting +## Troubleshooting ### Shared library "" not found If you ran into problem of missing shared library in the `apk/aab` - you can fix this by placing your `.so` file into `target///tools/libname.so`. The builder will pick the library up and put it in the final package. -## ๐Ÿ“‘ License +## License Licensed under [Apache-2.0 License](../../LICENSE). diff --git a/crossbundle/cli/src/commands/build/android.rs b/crossbundle/cli/src/commands/build/android.rs index 5e37309a..2f37d888 100644 --- a/crossbundle/cli/src/commands/build/android.rs +++ b/crossbundle/cli/src/commands/build/android.rs @@ -1,81 +1,85 @@ -use crate::types::MIN_SDK_VERSION; - use super::{BuildContext, SharedBuildCommand}; +use crate::error::Result; use android_manifest::AndroidManifest; use android_tools::java_tools::{JarSigner, Key}; use clap::Parser; -use crossbundle_tools::{ - commands::android::{self, rust_compile}, - error::CommandExt, - tools::*, - types::*, - utils::Config, -}; +use crossbundle_tools::{commands::android::*, error::CommandExt, types::*}; use std::path::{Path, PathBuf}; /// Specifies flags and options needed to build application -#[derive(Parser, Clone, Debug)] +#[derive(Parser, Clone, Debug, Default)] pub struct AndroidBuildCommand { #[clap(flatten)] pub shared: SharedBuildCommand, /// Build for the given android architecture. /// Supported targets are: `armv7-linux-androideabi`, `aarch64-linux-android`, /// `i686-linux-android`, `x86_64-linux-android` - #[clap(long, default_value = "aarch64-linux-android")] + #[clap(long, short, multiple_values = true)] pub target: Vec, - /// Generating native aab without Java. By default crossbow generating gradle project - #[clap(long)] - pub aab: bool, - /// Generating native apk without Java. By default crossbow generating gradle project - #[clap(long)] - pub apk: bool, - /// Compile rust code as a dynamic library [default: crossbow-android] + /// Build strategy specifies what and how to build Android application: with help of + /// Gradle, or with our native approach. + #[clap(long, short, default_value = "gradle-apk")] + pub strategy: AndroidStrategy, + /// Only compile rust code as a dynamic library. By default: "crossbow-android" #[clap(long, default_missing_value = "crossbow_android")] pub lib: Option, - /// Path to export Gradle project. By default exports to `target/android/` folder + /// Path to export Gradle project. By default exports to `target/android/` folder. #[clap(long)] pub export_path: Option, - /// Path to the signing key + /// Path to the signing key. #[clap(long, requires_all = &["sign-key-pass", "sign-key-alias"])] pub sign_key_path: Option, - /// Signing key password + /// Signing key password. #[clap(long)] pub sign_key_pass: Option, - /// Signing key alias + /// Signing key alias. #[clap(long)] pub sign_key_alias: Option, } impl AndroidBuildCommand { - // Checks options was specified in AndroidBuildCommand and then builds application - pub fn run(&self, config: &Config) -> crate::error::Result<()> { + // Checks options was specified in AndroidBuildCommand and then builds application. + pub fn run(&self, config: &Config) -> Result<()> { if self.sign_key_path.is_some() && self.sign_key_pass.is_none() { config .shell() .warn("You provided a signing key but not password - set password please by providing `sign_key_pass` flag")?; } let context = BuildContext::new(config, self.shared.target_dir.clone())?; - if self.aab { - self.execute_aab(config, &context)?; - } else if self.apk { - self.execute_apk(config, &context)?; - } else if let Some(lib_name) = &self.lib { - self.build_rust_lib(config, &context, lib_name, None)?; - } else { - self.build_gradle(config, &context, &self.export_path)?; + if let Some(name) = &self.lib { + self.build_rust_lib(config, &context, name, None)?; + return Ok(()); + } + match &self.strategy { + AndroidStrategy::NativeApk => { + self.execute_apk(config, &context)?; + } + AndroidStrategy::NativeAab => { + self.execute_aab(config, &context)?; + } + AndroidStrategy::GradleApk => { + let (_, _, gradle_project_path) = + self.build_gradle(config, &context, &self.export_path)?; + config.status("Building Gradle project")?; + let mut gradle = gradle_init()?; + gradle + .arg("build") + .arg("-p") + .arg(dunce::simplified(&gradle_project_path)); + gradle.output_err(true)?; + } } Ok(()) } - /// Compile rust code as a dynamic library, generate Gradle project + /// Compile rust code as a dynamic library, generate Gradle project. pub fn build_gradle( &self, config: &Config, context: &BuildContext, export_path: &Option, - ) -> crate::error::Result<(AndroidManifest, AndroidSdk, PathBuf)> { + ) -> Result<(AndroidManifest, AndroidSdk, PathBuf)> { let sdk = AndroidSdk::from_env()?; - let profile = self.shared.profile(); let example = self.shared.example.as_ref(); let (_, target_dir, package_name) = Self::needed_project_dirs(example, context)?; @@ -93,23 +97,17 @@ impl AndroidBuildCommand { } config.status("Generating gradle project")?; - let gradle_project_path = android::gen_gradle_project( + let gradle_project_path = gen_gradle_project( &android_build_dir, - &context.android_config.assets, - &context.android_config.res, - &context.android_config.plugins, + &context.config.get_android_assets(), + &context.config.android.res, + &context.config.android.plugins, )?; - // Get AndroidManifest.xml from file or generate from Cargo.toml - let (android_manifest, _manifest_path) = Self::android_manifest( - config, - context, - &sdk, - &package_name, - profile, - &gradle_project_path, - true, - )?; + config.status_message("Reading", "AndroidManifest.xml")?; + let manifest = Self::get_android_manifest(context, AndroidStrategy::GradleApk)?; + config.status_message("Generating", "AndroidManifest.xml")?; + save_android_manifest(&gradle_project_path, &manifest)?; let lib_name = "crossbow_android"; self.build_rust_lib(config, context, lib_name, Some(android_build_dir))?; @@ -118,30 +116,22 @@ impl AndroidBuildCommand { "Gradle project generated", gradle_project_path.to_str().unwrap(), )?; - - config.status("Building Gradle project")?; - let mut gradle = android::gradle_init()?; - gradle - .arg("build") - .arg("-p") - .arg(dunce::simplified(&gradle_project_path)); - gradle.output_err(true)?; - Ok((android_manifest, sdk, gradle_project_path)) + Ok((manifest, sdk, gradle_project_path)) } - /// Compile rust code as a dynamic library + /// Compile rust code as a dynamic library. pub fn build_rust_lib( &self, config: &Config, context: &BuildContext, lib_name: &str, export_path: Option, - ) -> crate::error::Result<()> { + ) -> Result<()> { let profile = self.shared.profile(); let example = self.shared.example.as_ref(); let (project_path, target_dir, package_name) = Self::needed_project_dirs(example, context)?; config.status_message("Starting lib build process", &package_name)?; - let (_sdk, ndk, target_sdk_version) = Self::android_toolchain(context)?; + let (sdk, ndk) = Self::android_toolchain()?; let android_build_dir = if let Some(export_path) = export_path { export_path @@ -149,9 +139,14 @@ impl AndroidBuildCommand { target_dir.join("android").join(&package_name) }; + config.status_message("Reading", "AndroidManifest.xml")?; + let manifest = Self::get_android_manifest(context, AndroidStrategy::NativeApk)?; + config.status_message("Compiling", "lib")?; - let build_targets = context.android_build_targets(&self.target); + let target_sdk_version = Self::target_sdk_version(&manifest, &sdk); + let build_targets = Self::android_build_targets(context, profile, &self.target); let compiled_libs = self.build_target( + context, build_targets, lib_name, &ndk, @@ -178,17 +173,17 @@ impl AndroidBuildCommand { Ok(()) } - /// Builds APK with aapt tool and signs it with apksigner + /// Builds APK with aapt tool and signs it with apksigner. pub fn execute_apk( &self, config: &Config, context: &BuildContext, - ) -> crate::error::Result<(AndroidManifest, AndroidSdk, PathBuf)> { + ) -> Result<(AndroidManifest, AndroidSdk, PathBuf)> { let profile = self.shared.profile(); let example = self.shared.example.as_ref(); let (project_path, target_dir, package_name) = Self::needed_project_dirs(example, context)?; config.status_message("Starting apk build process", &package_name)?; - let (sdk, ndk, target_sdk_version) = Self::android_toolchain(context)?; + let (sdk, ndk) = Self::android_toolchain()?; let android_build_dir = target_dir.join("android").join(&package_name); let native_build_dir = android_build_dir.join("native").join("apk"); @@ -197,20 +192,16 @@ impl AndroidBuildCommand { std::fs::create_dir_all(&outputs_build_dir)?; } - // Get AndroidManifest.xml from file or generate from Cargo.toml - let (android_manifest, manifest_path) = Self::android_manifest( - config, - context, - &sdk, - &package_name, - profile, - &native_build_dir, - false, - )?; + config.status_message("Reading", "AndroidManifest.xml")?; + let manifest = Self::get_android_manifest(context, AndroidStrategy::NativeApk)?; + config.status_message("Generating", "AndroidManifest.xml")?; + let manifest_path = save_android_manifest(&native_build_dir, &manifest)?; config.status_message("Compiling", "lib")?; - let build_targets = context.android_build_targets(&self.target); + let target_sdk_version = Self::target_sdk_version(&manifest, &sdk); + let build_targets = Self::android_build_targets(context, profile, &self.target); let compiled_libs = self.build_target( + context, build_targets, &package_name, &ndk, @@ -222,53 +213,35 @@ impl AndroidBuildCommand { )?; config.status_message("Generating", "unaligned APK file")?; - let package_label = android_manifest - .application - .label - .clone() - .unwrap() - .to_string(); - - let unaligned_apk_path = android::gen_unaligned_apk( + let unaligned_apk_path = gen_unaligned_apk( &sdk, &project_path, &native_build_dir, &manifest_path, - &context.android_config.assets, - &context.android_config.res, - &package_label, + &context.config.get_android_assets(), + &context.config.android.res, + &package_name, target_sdk_version, )?; - let min_sdk_version = android_manifest - .uses_sdk - .as_ref() - .unwrap() - .min_sdk_version - .unwrap_or(MIN_SDK_VERSION); - config.status("Adding libs into APK file")?; for (compiled_lib, build_target) in compiled_libs { - android::add_libs_into_apk( + add_libs_into_apk( &sdk, &ndk, &unaligned_apk_path, &compiled_lib, build_target, profile, - min_sdk_version, + Self::min_sdk_version(&manifest), &android_build_dir, &target_dir, )?; } config.status("Aligning APK file")?; - let aligned_apk_path = android::align_apk( - &sdk, - &unaligned_apk_path, - &package_label, - &outputs_build_dir, - )?; + let aligned_apk_path = + align_apk(&sdk, &unaligned_apk_path, &package_name, &outputs_build_dir)?; config.status_message("Generating", "debug signing key")?; let key = Self::find_keystore( @@ -278,22 +251,22 @@ impl AndroidBuildCommand { )?; config.status("Signing APK file")?; - android::sign_apk(&sdk, &aligned_apk_path, key)?; + sign_apk(&sdk, &aligned_apk_path, key)?; config.status("Build finished successfully")?; - Ok((android_manifest, sdk, aligned_apk_path)) + Ok((manifest, sdk, aligned_apk_path)) } - /// Builds AAB with aapt2 tool and signs it with jarsigner + /// Builds AAB with aapt2 tool and signs it with jarsigner. pub fn execute_aab( &self, config: &Config, context: &BuildContext, - ) -> crate::error::Result<(AndroidManifest, AndroidSdk, PathBuf, String, Key)> { + ) -> Result<(AndroidManifest, AndroidSdk, PathBuf, String, Key)> { let profile = self.shared.profile(); let example = self.shared.example.as_ref(); let (project_path, target_dir, package_name) = Self::needed_project_dirs(example, context)?; config.status_message("Starting aab build process", &package_name)?; - let (sdk, ndk, target_sdk_version) = Self::android_toolchain(context)?; + let (sdk, ndk) = Self::android_toolchain()?; let android_build_dir = target_dir.join("android").join(&package_name); let native_build_dir = android_build_dir.join("native").join("aab"); @@ -302,20 +275,16 @@ impl AndroidBuildCommand { std::fs::create_dir_all(&outputs_build_dir)?; } - // Get AndroidManifest.xml from file or generate from Cargo.toml - let (android_manifest, manifest_path) = Self::android_manifest( - config, - context, - &sdk, - &package_name, - profile, - &native_build_dir, - false, - )?; + config.status_message("Reading", "AndroidManifest.xml")?; + let manifest = Self::get_android_manifest(context, AndroidStrategy::NativeAab)?; + config.status_message("Generating", "AndroidManifest.xml")?; + let manifest_path = save_android_manifest(&native_build_dir, &manifest)?; config.status_message("Compiling", "lib")?; - let build_targets = context.android_build_targets(&self.target); + let target_sdk_version = Self::target_sdk_version(&manifest, &sdk); + let build_targets = Self::android_build_targets(context, profile, &self.target); let compiled_libs = self.build_target( + context, build_targets, &package_name, &ndk, @@ -328,7 +297,7 @@ impl AndroidBuildCommand { config.status_message("Generating", "proto format APK file")?; - let compiled_res = if let Some(res) = &context.android_config.res { + let compiled_res = if let Some(res) = &context.config.android.res { let compiled_res_path = native_build_dir.join("compiled_res"); if !compiled_res_path.exists() { std::fs::create_dir_all(&compiled_res_path)?; @@ -347,7 +316,7 @@ impl AndroidBuildCommand { let mut aapt2_link = sdk.aapt2()? .link_compiled_res(compiled_res, &apk_path, &manifest_path); - if let Some(assets) = &context.android_config.assets { + if let Some(assets) = &context.config.get_android_assets() { aapt2_link.assets(assets.clone()) } else { &mut aapt2_link @@ -359,22 +328,16 @@ impl AndroidBuildCommand { config.status("Extracting apk files")?; let output_dir = native_build_dir.join("extracted_apk_files"); - let extracted_apk_path = android::extract_archive(&apk_path, &output_dir)?; + let extracted_apk_path = extract_archive(&apk_path, &output_dir)?; config.status("Adding libs")?; for (compiled_lib, build_target) in compiled_libs { - android::add_libs_into_aapt2( + add_libs_into_aapt2( &ndk, &compiled_lib, build_target, profile, - android_manifest - .clone() - .uses_sdk - .as_ref() - .unwrap() - .min_sdk_version - .unwrap_or(MIN_SDK_VERSION), + Self::min_sdk_version(&manifest), &extracted_apk_path, &target_dir, &package_name, @@ -383,7 +346,7 @@ impl AndroidBuildCommand { config.status("Generating ZIP module from extracted files")?; let gen_zip_modules = - android::gen_zip_modules(&native_build_dir, &package_name, &extracted_apk_path)?; + gen_zip_modules(&native_build_dir, &package_name, &extracted_apk_path)?; for entry in std::fs::read_dir(&native_build_dir)? { let entry = entry?; @@ -394,8 +357,7 @@ impl AndroidBuildCommand { } config.status("Generating aab from modules")?; - let aab_path = - android::gen_aab_from_modules(&package_name, &[gen_zip_modules], &outputs_build_dir)?; + let aab_path = gen_aab_from_modules(&package_name, &[gen_zip_modules], &outputs_build_dir)?; config.status_message("Generating", "debug signing key")?; let key = Self::find_keystore( @@ -421,14 +383,14 @@ impl AndroidBuildCommand { options.overwrite = true; fs_extra::file::move_file(&signed_aab, &outputs_build_dir.join(output_aab), &options)?; config.status("Build finished successfully")?; - Ok((android_manifest, sdk, aab_output_path, package_name, key)) + Ok((manifest, sdk, aab_output_path, package_name, key)) } - /// Specifies project path and target directory needed to build application + /// Specifies project path and target directory needed to build application. pub fn needed_project_dirs( example: Option<&String>, context: &BuildContext, - ) -> crate::error::Result<(PathBuf, PathBuf, String)> { + ) -> Result<(PathBuf, PathBuf, String)> { let project_path: PathBuf = context.project_path.clone(); let target_dir: PathBuf = context.target_dir.clone(); let (_target, package_name) = if let Some(example) = example { @@ -439,39 +401,19 @@ impl AndroidBuildCommand { Ok((project_path, target_dir, package_name)) } - /// Specifies path to Android SDK and Android NDK - pub fn android_toolchain( - context: &BuildContext, - ) -> crate::error::Result<(AndroidSdk, AndroidNdk, u32)> { + /// Specifies path to Android SDK and Android NDK. + pub fn android_toolchain() -> Result<(AndroidSdk, AndroidNdk)> { let sdk = AndroidSdk::from_env()?; - let ndk = AndroidNdk::from_env(Some(sdk.sdk_path()))?; - let target_sdk_version = context.target_sdk_version(&sdk); - Ok((sdk, ndk, target_sdk_version)) - } - - /// Generates or copies AndroidManifest.xml from specified path, then saves it to android folder - pub fn android_manifest( - config: &Config, - context: &BuildContext, - sdk: &AndroidSdk, - package_name: &str, - profile: Profile, - android_build_dir: &Path, - gradle: bool, - ) -> crate::error::Result<(AndroidManifest, PathBuf)> { - config.status_message("Generating", "AndroidManifest.xml")?; - let android_manifest = - context.gen_android_manifest(sdk, package_name, profile.is_debug(), gradle)?; - let manifest_path = android::save_android_manifest(android_build_dir, &android_manifest)?; - Ok((android_manifest, manifest_path)) + let ndk = AndroidNdk::from_env(sdk.sdk_path())?; + Ok((sdk, ndk)) } - /// Find keystore for signing application or create it + /// Find keystore for signing application or create it. pub fn find_keystore( sign_key_path: Option, sign_key_pass: Option, sign_key_alias: Option, - ) -> crate::error::Result { + ) -> Result { let key = if let Some(key_path) = sign_key_path { let aab_key = Key { key_path, @@ -481,7 +423,7 @@ impl AndroidBuildCommand { if aab_key.key_path.exists() { aab_key } else { - android::gen_key( + gen_key( Some(aab_key.key_path), Some(aab_key.key_pass), Some(aab_key.key_alias), @@ -492,7 +434,7 @@ impl AndroidBuildCommand { if aab_key.key_path.exists() { aab_key } else { - android::gen_key( + gen_key( Some(aab_key.key_path), Some(aab_key.key_pass), Some(aab_key.key_alias), @@ -502,9 +444,10 @@ impl AndroidBuildCommand { Ok(key) } - /// Compiling libs for architecture and write out it in vector + /// Compiling libs for architecture and write out it in vector. pub fn build_target( &self, + context: &BuildContext, build_targets: Vec, package_name: &str, ndk: &AndroidNdk, @@ -513,18 +456,13 @@ impl AndroidBuildCommand { target_sdk_version: u32, target_dir: &Path, config: &Config, - ) -> crate::error::Result> { + ) -> Result> { let mut libs = Vec::new(); for build_target in build_targets { let lib_name = format!("lib{}.so", package_name.replace('-', "_")); let rust_triple = build_target.rust_triple(); config.status_message("Compiling for architecture", rust_triple)?; - let app_wrapper = match self.shared.quad { - true => ApplicationWrapper::Sokol, - false => ApplicationWrapper::NdkGlue, - }; - // Compile rust code for android depending on application wrapper rust_compile( ndk, @@ -536,7 +474,7 @@ impl AndroidBuildCommand { self.shared.no_default_features, target_sdk_version, &lib_name, - app_wrapper, + context.config.android.app_wrapper, )?; let out_dir = target_dir.join(build_target.rust_triple()).join(&profile); @@ -545,4 +483,68 @@ impl AndroidBuildCommand { } Ok(libs) } + + /// Get target sdk version from cargo manifest + pub fn target_sdk_version(android_manifest: &AndroidManifest, sdk: &AndroidSdk) -> u32 { + if let Some(target_sdk_version) = android_manifest + .uses_sdk + .as_ref() + .and_then(|u| u.target_sdk_version) + { + return target_sdk_version; + }; + sdk.default_platform() + } + + pub fn min_sdk_version(android_manifest: &AndroidManifest) -> u32 { + android_manifest + .uses_sdk + .as_ref() + .and_then(|uses_sdk| uses_sdk.min_sdk_version) + .unwrap() + } + + /// Get android build targets from cargo manifest + pub fn android_build_targets( + context: &BuildContext, + profile: Profile, + build_targets: &Vec, + ) -> Vec { + if !build_targets.is_empty() { + return build_targets.clone(); + }; + if profile == Profile::Debug && !context.config.android.debug_build_targets.is_empty() { + return context.config.android.debug_build_targets.clone(); + }; + if profile == Profile::Release && !context.config.android.release_build_targets.is_empty() { + return context.config.android.release_build_targets.clone(); + }; + vec![AndroidTarget::Aarch64] + } + + /// Get android manifest from the path in cargo manifest or generate it with the given + /// configuration + pub fn get_android_manifest( + context: &BuildContext, + strategy: AndroidStrategy, + ) -> Result { + if let Some(manifest_path) = &context.config.android.manifest_path { + return Ok(read_android_manifest(manifest_path)?); + } + let mut manifest = if let Some(manifest) = &context.config.android.manifest { + manifest.clone() + } else { + AndroidManifest::default() + }; + update_android_manifest_with_default( + &mut manifest, + context.config.app_name.clone(), + context.package_name().as_str(), + strategy, + ); + context.config.permissions.iter().for_each(|permission| { + permission.update_manifest(&mut manifest); + }); + Ok(manifest) + } } diff --git a/crossbundle/cli/src/commands/build/apple.rs b/crossbundle/cli/src/commands/build/apple.rs index 1f09c705..3011b732 100644 --- a/crossbundle/cli/src/commands/build/apple.rs +++ b/crossbundle/cli/src/commands/build/apple.rs @@ -2,39 +2,47 @@ use super::{BuildContext, SharedBuildCommand}; use crate::error::*; use apple_bundle::prelude::InfoPlist; use clap::Parser; -use crossbundle_tools::{commands::apple, types::*, utils::Config}; +use crossbundle_tools::{commands::apple, types::*}; use std::path::{Path, PathBuf}; #[derive(Parser, Clone, Debug)] -pub struct AppleBuildCommand { +pub struct IosBuildCommand { #[clap(flatten)] pub shared: SharedBuildCommand, - /// Specify custom cargo binary + /// Specify custom cargo binary. #[clap(long, conflicts_with = "example")] pub bin: Option, /// Build for the given apple architecture. /// Supported targets are: `aarch64-apple-ios`, `aarch64-apple-ios-sim`, /// `armv7-apple-ios`, `armv7s-apple-ios`, `i386-apple-ios`, `x86_64-apple-ios` - #[clap(long, default_value = "aarch64-apple-ios-sim")] - pub target: Vec, - /// Provisioning profile name to find in this directory: `~/Library/MobileDevice/Provisioning\ Profiles/` + #[clap(long, short, multiple_values = true)] + pub target: Vec, + /// Build strategy specifies what and how to build iOS application: with help of + /// XCode, or with our native approach. + #[clap(long, short, default_value = "native-ipa")] + pub strategy: IosStrategy, + /// Provisioning profile name to find in this directory: + /// `~/Library/MobileDevice/Provisioning\ Profiles/`. #[clap(long, conflicts_with = "profile-path")] pub profile_name: Option, - /// Absolute path to provisioning profile + /// Absolute path to provisioning profile. #[clap(long)] pub profile_path: Option, - /// The team identifier of your signing identity + /// The team identifier of your signing identity. #[clap(long)] pub team_identifier: Option, - /// The id of the identity used for signing. It won't start the signing process until you provide this flag + /// The id of the identity used for signing. It won't start the signing process until + /// you provide this flag. #[clap(long)] pub identity: Option, } -impl AppleBuildCommand { +impl IosBuildCommand { pub fn run(&self, config: &Config) -> Result<()> { let context = BuildContext::new(config, self.shared.target_dir.clone())?; - self.execute(config, &context)?; + match &self.strategy { + IosStrategy::NativeIpa => self.execute(config, &context)?, + }; Ok(()) } @@ -52,10 +60,10 @@ impl AppleBuildCommand { } else { (Target::Bin(context.package_name()), context.package_name()) }; - let properties = context.gen_info_plist(&package_name)?; + let properties = Self::gen_info_plist(context, &package_name)?; config.status_message("Starting build process", &package_name)?; config.status("Compiling app")?; - let build_targets = context.apple_build_targets(&self.target); + let build_targets = Self::apple_build_targets(context, profile, &self.target); let mut app_paths = vec![]; for build_target in build_targets { let app_path = self.build_app( @@ -79,7 +87,7 @@ impl AppleBuildCommand { context: &BuildContext, target: Target, project_path: &Path, - build_target: AppleTarget, + build_target: IosTarget, properties: &InfoPlist, profile: Profile, name: &str, @@ -94,6 +102,7 @@ impl AppleBuildCommand { self.shared.features.clone(), self.shared.all_features, self.shared.no_default_features, + &[], )?; let out_dir = context.target_dir.join(rust_triple).join(&profile); let bin_path = out_dir.join(&name); @@ -107,13 +116,14 @@ impl AppleBuildCommand { apple_target_dir, name, context - .apple_config - .res + .config + .get_apple_assets() .as_ref() .map(|r| project_path.join(r)), context - .apple_config - .assets + .config + .apple + .res .as_ref() .map(|r| project_path.join(r)), )?; @@ -149,4 +159,44 @@ impl AppleBuildCommand { config.status("Build finished successfully")?; Ok(app_path) } + + /// Get apple build targets from cargo manifest + pub fn apple_build_targets( + context: &BuildContext, + profile: Profile, + build_targets: &Vec, + ) -> Vec { + if !build_targets.is_empty() { + return build_targets.clone(); + } + if profile == Profile::Debug && !context.config.apple.debug_build_targets.is_empty() { + return context.config.apple.debug_build_targets.clone(); + } + if profile == Profile::Release && !context.config.apple.release_build_targets.is_empty() { + return context.config.apple.release_build_targets.clone(); + } + vec![IosTarget::Aarch64Sim] + } + + /// Get info plist from the path in cargo manifest or generate it with the given + /// configuration + pub fn gen_info_plist(context: &BuildContext, package_name: &str) -> Result { + if let Some(info_plist_path) = &context.config.apple.info_plist_path { + return Ok(apple::read_info_plist(info_plist_path)?); + } + let mut info_plist = if let Some(info_plist) = &context.config.apple.info_plist { + info_plist.clone() + } else { + InfoPlist::default() + }; + update_info_plist_with_default( + &mut info_plist, + package_name, + context.config.app_name.clone(), + ); + context.config.permissions.iter().for_each(|permission| { + permission.update_info_plist(&mut info_plist); + }); + Ok(info_plist) + } } diff --git a/crossbundle/cli/src/commands/build/build_context.rs b/crossbundle/cli/src/commands/build/build_context.rs index f5396ef8..1f80b3ca 100644 --- a/crossbundle/cli/src/commands/build/build_context.rs +++ b/crossbundle/cli/src/commands/build/build_context.rs @@ -1,23 +1,16 @@ use crate::{error::*, types::*}; -use crossbundle_tools::{ - commands::{android::gen_manifest, *}, - tools::*, - types::{ - android_manifest::AndroidManifest, apple_bundle::prelude::InfoPlist, AndroidTarget, - AppleTarget, - }, - utils::*, -}; +use crossbundle_tools::{commands::*, types::Config}; use std::path::PathBuf; pub struct BuildContext { + // Paths pub workspace_manifest_path: PathBuf, pub package_manifest_path: PathBuf, pub project_path: PathBuf, - pub manifest: cargo::core::Manifest, - pub android_config: AndroidConfig, - pub apple_config: AppleConfig, pub target_dir: PathBuf, + // Configurations + pub manifest: cargo::core::Manifest, + pub config: CrossbowMetadata, } impl BuildContext { @@ -30,22 +23,21 @@ impl BuildContext { target_dir.unwrap_or_else(|| workspace_manifest_path.parent().unwrap().join("target")); info!("Parsing Cargo.toml"); let manifest = parse_manifest(&package_manifest_path)?; - let metadata = if let Some(cargo_metadata) = manifest.custom_metadata() { + let crossbow_metadata = if let Some(cargo_metadata) = manifest.custom_metadata() { cargo_metadata .clone() - .try_into::() + .try_into::() .map_err(|e| Error::InvalidMetadata(e.into()))? } else { - Metadata::default() + CrossbowMetadata::default() }; Ok(Self { workspace_manifest_path, package_manifest_path, - manifest, project_path, - android_config: metadata.android, - apple_config: metadata.apple, target_dir, + config: crossbow_metadata, + manifest, }) } @@ -58,136 +50,4 @@ impl BuildContext { pub fn package_version(&self) -> String { self.manifest.summary().version().to_string() } - - /// Get target sdk version from cargo manifest - pub fn target_sdk_version(&self, sdk: &AndroidSdk) -> u32 { - if let Some(target_sdk_version) = self.android_config.target_sdk_version { - return target_sdk_version; - }; - sdk.default_platform() - } - - /// Get android build targets from cargo manifest - pub fn android_build_targets(&self, build_targets: &Vec) -> Vec { - if !build_targets.is_empty() { - return build_targets.clone(); - }; - if self.android_config.build_targets.is_none() { - return vec![AndroidTarget::Aarch64LinuxAndroid]; - }; - let targets = self.android_config.build_targets.clone(); - if targets.is_some() && !targets.as_ref().unwrap().is_empty() { - return targets.unwrap(); - }; - vec![AndroidTarget::Aarch64LinuxAndroid] - } - - /// Get android package id from cargo manifest - pub fn android_package(&self, package_name: &str) -> String { - self.android_config - .package - .clone() - .unwrap_or(format!("com.rust.{}", package_name)) - .replace('-', "_") - } - - /// Get android manifest from the path in cargo manifest or generate it with the given configuration - pub fn gen_android_manifest( - &self, - sdk: &AndroidSdk, - package_name: &str, - debuggable: bool, - gradle: bool, - ) -> Result { - let android_manifest = AndroidConfig { - app_name: self.android_config.app_name.clone(), - version_name: Some( - self.android_config - .version_name - .clone() - .unwrap_or_else(|| self.package_version()), - ), - version_code: self.android_config.version_code, - min_sdk_version: Some( - self.android_config - .min_sdk_version - .unwrap_or(MIN_SDK_VERSION), - ), - target_sdk_version: Some( - self.android_config - .target_sdk_version - .unwrap_or_else(|| sdk.default_platform()), - ), - max_sdk_version: self.android_config.max_sdk_version, - icon: self.android_config.icon.clone(), - permissions_sdk_23: self.android_config.permissions_sdk_23.clone(), - permissions: self.android_config.permissions.clone(), - features: self.android_config.features.clone(), - service: self.android_config.service.clone(), - meta_data: self.android_config.meta_data.clone(), - queries: self.android_config.queries.clone(), - ..Default::default() - }; - if let Some(manifest_path) = &self.android_config.manifest_path { - Ok(android::read_android_manifest(manifest_path)?) - } else { - let manifest = gen_manifest::gen_android_manifest( - Some(format!("com.rust.{}", package_name).replace('-', "_")), - package_name.to_string(), - android_manifest.app_name, - android_manifest - .version_name - .unwrap_or_else(|| self.package_version()), - android_manifest.version_code.unwrap_or(1), - Some(android_manifest.min_sdk_version.unwrap_or(MIN_SDK_VERSION)), - android_manifest - .target_sdk_version - .unwrap_or_else(|| sdk.default_platform()), - android_manifest.max_sdk_version, - android_manifest.icon, - debuggable, - android_manifest.permissions_sdk_23, - android_manifest.permissions, - android_manifest.features, - android_manifest.service, - android_manifest.meta_data, - android_manifest.queries, - gradle, - ); - Ok(manifest) - } - } - - /// Get info plist from the path in cargo manifest or generate it with the given configuration - pub fn gen_info_plist(&self, package_name: &str) -> Result { - if let Some(info_plist_path) = &self.apple_config.info_plist_path { - Ok(apple::read_info_plist(info_plist_path)?) - } else if let Some(info_plist) = &self.apple_config.info_plist { - Ok(info_plist.clone()) - } else { - Ok(apple::gen_minimal_info_plist( - package_name, - self.apple_config.app_name.clone(), - self.apple_config - .version_name - .clone() - .unwrap_or_else(|| self.package_version()), - )) - } - } - - /// Get apple build targets from cargo manifest - pub fn apple_build_targets(&self, build_targets: &Vec) -> Vec { - if !build_targets.is_empty() { - return build_targets.clone(); - }; - if self.apple_config.build_targets.is_none() { - return vec![AppleTarget::X86_64AppleIos]; - }; - let targets = self.apple_config.clone().build_targets; - if targets.is_some() && !targets.as_ref().unwrap().is_empty() { - return targets.unwrap(); - }; - vec![AppleTarget::X86_64AppleIos] - } } diff --git a/crossbundle/cli/src/commands/build/mod.rs b/crossbundle/cli/src/commands/build/mod.rs index ea9064df..4c7fc7e9 100644 --- a/crossbundle/cli/src/commands/build/mod.rs +++ b/crossbundle/cli/src/commands/build/mod.rs @@ -1,42 +1,53 @@ +#[cfg(feature = "android")] pub mod android; +#[cfg(feature = "apple")] pub mod apple; mod build_context; pub use build_context::*; +#[cfg(feature = "android")] use android::AndroidBuildCommand; -use apple::AppleBuildCommand; +#[cfg(feature = "apple")] +use apple::IosBuildCommand; use crate::error::Result; use clap::Parser; -use crossbundle_tools::{types::Profile, utils::Config}; +use crossbundle_tools::types::{Config, Profile}; use std::path::PathBuf; #[derive(Parser, Clone, Debug)] pub enum BuildCommand { /// Starts the process of building/packaging/signing of the rust crate for Android + #[cfg(feature = "android")] Android(AndroidBuildCommand), /// Starts the process of building/packaging/signing of the rust crate for iOS - Apple(AppleBuildCommand), + #[cfg(feature = "apple")] + Ios(IosBuildCommand), } impl BuildCommand { pub fn handle_command(&self, config: &Config) -> Result<()> { + #[cfg(any(feature = "android", feature = "apple"))] match &self { - Self::Android(cmd) => cmd.run(config), - Self::Apple(cmd) => cmd.run(config), + #[cfg(feature = "android")] + Self::Android(cmd) => cmd.run(config)?, + #[cfg(feature = "apple")] + Self::Ios(cmd) => cmd.run(config)?, } + Ok(()) } } -#[derive(Parser, Clone, Debug)] +#[derive(Parser, Clone, Debug, Default)] pub struct SharedBuildCommand { /// Build the specified example #[clap(long)] pub example: Option, - /// Space or comma separated list of features to activate. These features only apply to the current - /// directory's package. Features of direct dependencies may be enabled with `/` syntax. - /// This flag may be specified multiple times, which enables all specified features + /// Space or comma separated list of features to activate. These features only apply + /// to the current directory's package. Features of direct dependencies may be + /// enabled with `/` syntax. This flag may be specified + /// multiple times, which enables all specified features #[clap(long)] pub features: Vec, /// Activate all available features of selected package @@ -51,9 +62,6 @@ pub struct SharedBuildCommand { /// Directory for generated artifact and intermediate files #[clap(long)] pub target_dir: Option, - /// Specifies to build macroquad-based game with Sokol application wrapper - #[clap(long)] - pub quad: bool, } impl SharedBuildCommand { diff --git a/crossbundle/cli/src/commands/install/bundletool.rs b/crossbundle/cli/src/commands/install/bundletool.rs index 30cc484e..30f6d545 100644 --- a/crossbundle/cli/src/commands/install/bundletool.rs +++ b/crossbundle/cli/src/commands/install/bundletool.rs @@ -1,15 +1,19 @@ use super::*; use clap::Parser; -use crossbundle_tools::utils::Config; +use crossbundle_tools::types::Config; use std::path::PathBuf; +const BUNDLETOOL_JAR_FILE_DOWNLOAD_URL: &str = + "https://github.com/google/bundletool/releases/download"; + #[derive(Parser, Clone, Debug, Default)] pub struct BundletoolInstallCommand { /// Required. Version of download bundletool. For example: - /// --version 1.11.0 - #[clap(long, short, default_value = "1.11.0")] + /// --version 1.8.2 + #[clap(long, short, default_value = "1.8.2")] version: String, - /// Path to install bundletool. By default bundletool will be downloaded and saved in home directory + /// Path to install bundletool. By default bundletool will be downloaded and saved in + /// home directory #[clap(long, short)] path: Option, /// Force install bundletool even if found. @@ -33,7 +37,7 @@ impl BundletoolInstallCommand { } } } - let download_url = std::path::Path::new(super::BUNDLETOOL_JAR_FILE_DOWNLOAD_URL) + let download_url = std::path::Path::new(BUNDLETOOL_JAR_FILE_DOWNLOAD_URL) .join(self.version.clone()) .join(self.file_name()); let download_url_str = String::from(download_url.to_str().unwrap()); diff --git a/crossbundle/cli/src/commands/install/command_line_tools.rs b/crossbundle/cli/src/commands/install/command_line_tools.rs index 45ba043e..24ce808c 100644 --- a/crossbundle/cli/src/commands/install/command_line_tools.rs +++ b/crossbundle/cli/src/commands/install/command_line_tools.rs @@ -1,12 +1,17 @@ use super::*; use clap::Parser; -use crossbundle_tools::{ - commands::android::{self, remove}, - tools::AndroidSdk, - utils::Config, -}; +use crossbundle_tools::{commands::android::*, types::AndroidSdk, types::Config}; use std::path::{Path, PathBuf}; +#[cfg(target_os = "windows")] +const OS_TAG: &str = "win"; +#[cfg(target_os = "macos")] +const OS_TAG: &str = "mac"; +#[cfg(target_os = "linux")] +const OS_TAG: &str = "linux"; + +const COMMAND_LINE_TOOLS_DOWNLOAD_URL: &str = "https://dl.google.com/android/repository/"; + #[derive(Parser, Clone, Debug, Default)] pub struct CommandLineToolsInstallCommand { /// Assign path to install command line tools @@ -18,7 +23,8 @@ pub struct CommandLineToolsInstallCommand { } impl CommandLineToolsInstallCommand { - /// Download command line tools zip archive and extract it in specified sdk root directory + /// Download command line tools zip archive and extract it in specified sdk root + /// directory pub fn install(&self, config: &Config) -> crate::error::Result<()> { if self.force { remove(vec![default_file_path(self.file_name())?])?; @@ -46,13 +52,13 @@ impl CommandLineToolsInstallCommand { "Extracting zip archive contents into", path.to_str().unwrap(), )?; - android::extract_archive(&file_path, path)?; + extract_archive(&file_path, path)?; } else { config.status_message( "Extracting zip archive contents into", &sdk_path.to_str().unwrap(), )?; - android::extract_archive(&file_path, Path::new(&sdk_path))?; + extract_archive(&file_path, Path::new(&sdk_path))?; } config.status("Deleting zip archive was left after installation")?; @@ -65,7 +71,8 @@ impl CommandLineToolsInstallCommand { format!("commandlinetools-{}-8512546_latest.zip", OS_TAG) } - /// Check home directory for zip file. If it doesn't exists download zip file and save it in the directory + /// Check home directory for zip file. If it doesn't exists download zip file and save + /// it in the directory pub fn download_and_save_file( &self, download_url: PathBuf, diff --git a/crossbundle/cli/src/commands/install/mod.rs b/crossbundle/cli/src/commands/install/mod.rs index f0c78e65..ad69d0bc 100644 --- a/crossbundle/cli/src/commands/install/mod.rs +++ b/crossbundle/cli/src/commands/install/mod.rs @@ -1,47 +1,48 @@ +#[cfg(feature = "android")] pub mod bundletool; +#[cfg(feature = "android")] pub mod command_line_tools; +#[cfg(feature = "android")] pub mod sdkmanager; use crate::error::Result; use clap::Parser; -use crossbundle_tools::utils::Config; +use crossbundle_tools::types::Config; +#[cfg(feature = "android")] use self::{ bundletool::BundletoolInstallCommand, command_line_tools::CommandLineToolsInstallCommand, sdkmanager::SdkManagerInstallCommand, }; -#[cfg(target_os = "windows")] -const OS_TAG: &str = "win"; - -#[cfg(target_os = "macos")] -const OS_TAG: &str = "mac"; - -#[cfg(target_os = "linux")] -const OS_TAG: &str = "linux"; - -const COMMAND_LINE_TOOLS_DOWNLOAD_URL: &str = "https://dl.google.com/android/repository/"; -const BUNDLETOOL_JAR_FILE_DOWNLOAD_URL: &str = - "https://github.com/google/bundletool/releases/download"; - #[derive(Parser, Clone, Debug)] pub enum InstallCommand { - /// Install bundletool. You can specify version of bundletool. By default, we have 1.8.2 bundletool version in usage + /// Install bundletool. You can specify version of bundletool. By default, we have + /// 1.8.2 bundletool version in usage + #[cfg(feature = "android")] Bundletool(BundletoolInstallCommand), - /// Download the basic Android command line tools below. You can use the included sdkmanager to download other SDK packages. - /// These tools are included in Android Studio + /// Download the basic Android command line tools below. You can use the included + /// sdkmanager to download other SDK packages. These tools are included in Android + /// Studio + #[cfg(feature = "android")] CommandLineTools(CommandLineToolsInstallCommand), /// Allows you to view, install, update, and uninstall packages for the Android SDK + #[cfg(feature = "android")] SdkManager(SdkManagerInstallCommand), } impl InstallCommand { pub fn handle_command(&self, config: &Config) -> Result<()> { + #[cfg(feature = "android")] match self { - InstallCommand::Bundletool(cmd) => cmd.install(config), - InstallCommand::CommandLineTools(cmd) => cmd.install(config), - InstallCommand::SdkManager(cmd) => cmd.run(config), + #[cfg(feature = "android")] + InstallCommand::Bundletool(cmd) => cmd.install(config)?, + #[cfg(feature = "android")] + InstallCommand::CommandLineTools(cmd) => cmd.install(config)?, + #[cfg(feature = "android")] + InstallCommand::SdkManager(cmd) => cmd.run(config)?, } + Ok(()) } } diff --git a/crossbundle/cli/src/commands/install/sdkmanager.rs b/crossbundle/cli/src/commands/install/sdkmanager.rs index d0348dc9..0d8b7837 100644 --- a/crossbundle/cli/src/commands/install/sdkmanager.rs +++ b/crossbundle/cli/src/commands/install/sdkmanager.rs @@ -1,16 +1,18 @@ use clap::Parser; use crossbundle_tools::{ - error::CommandExt, tools::AndroidSdk, utils::Config, EXECUTABLE_SUFFIX_BAT, + error::CommandExt, types::AndroidSdk, types::Config, EXECUTABLE_SUFFIX_BAT, }; use std::path::Path; #[derive(Parser, Clone, Debug, Default)] pub struct SdkManagerInstallCommand { - /// Install all preferred tools for correct crossbundle work. It will install build-tools;31.0.0, ndk;23.1.7779620 and platforms;android-30 + /// Install all preferred tools for correct crossbundle work. It will install + /// build-tools;31.0.0, ndk;23.1.7779620 and platforms;android-30 #[clap(long, short)] preferred_tools: bool, - /// List installed and available packages. Use the channel option to include a package from a channel up to and including channel_id. - /// For example, specify the canary channel to list packages from all channels + /// List installed and available packages. Use the channel option to include a package + /// from a channel up to and including channel_id. For example, specify the canary + /// channel to list packages from all channels #[clap(long, short)] list: bool, /// Install package. To see all available packages use --list. @@ -26,11 +28,12 @@ pub struct SdkManagerInstallCommand { /// Use the specified SDK path instead of the SDK containing this tool #[clap(long, short)] sdk_root: Option, - /// Include packages in channels up to and including channel_id. Available channels are: - /// 0 (Stable), 1 (Beta), 2 (Dev), and 3 (Canary) + /// Include packages in channels up to and including channel_id. Available channels + /// are: 0 (Stable), 1 (Beta), 2 (Dev), and 3 (Canary) #[clap(long, short)] channel: Option, - /// Include obsolete packages in the package listing or package updates. For use with --list and --update only + /// Include obsolete packages in the package listing or package updates. For use with + /// --list and --update only #[clap(long)] include_obsolete: bool, /// Force all connections to use HTTP rather than HTTPS @@ -39,8 +42,8 @@ pub struct SdkManagerInstallCommand { /// Verbose output mode. Errors, warnings and informational messages are printed #[clap(long, short)] verbose: bool, - /// Connect via a proxy of the given type: either http for high level protocols such as HTTP or FTP, - /// or socks for a SOCKS (V4 or V5) proxy + /// Connect via a proxy of the given type: either http for high level protocols such + /// as HTTP or FTP, or socks for a SOCKS (V4 or V5) proxy #[clap(long)] proxy: Option, /// IP or DNS address of the proxy to use @@ -59,8 +62,9 @@ impl SdkManagerInstallCommand { } } - /// List installed and available packages. Use the channel option to include a package from a channel up to and including channel_id. - /// For example, specify the canary channel to list packages from all channels + /// List installed and available packages. Use the channel option to include a package + /// from a channel up to and including channel_id. For example, specify the canary + /// channel to list packages from all channels pub fn list(&mut self, list: bool) -> &mut Self { self.list = list; self @@ -100,8 +104,8 @@ impl SdkManagerInstallCommand { self } - /// Include packages in channels up to and including channel_id. Available channels are: - /// 0 (Stable), 1 (Beta), 2 (Dev), and 3 (Canary). + /// Include packages in channels up to and including channel_id. Available channels + /// are: 0 (Stable), 1 (Beta), 2 (Dev), and 3 (Canary). /// ```sh /// --channel=channel_id /// ``` @@ -110,7 +114,8 @@ impl SdkManagerInstallCommand { self } - /// Include obsolete packages in the package listing or package updates. For use with --list and --update only. + /// Include obsolete packages in the package listing or package updates. For use with + /// --list and --update only. pub fn include_obsolete(&mut self, include_obsolete: bool) -> &mut Self { self.include_obsolete = include_obsolete; self @@ -128,8 +133,8 @@ impl SdkManagerInstallCommand { self } - /// Connect via a proxy of the given type: either http for high level protocols such as HTTP or FTP, or socks for a SOCKS (V4 or V5) proxy. - /// ```sh + /// Connect via a proxy of the given type: either http for high level protocols such + /// as HTTP or FTP, or socks for a SOCKS (V4 or V5) proxy. ```sh /// --proxy={http | socks} /// ``` pub fn proxy(&mut self, proxy: String) -> &mut Self { @@ -159,8 +164,10 @@ impl SdkManagerInstallCommand { pub fn run(&self, _config: &Config) -> crate::error::Result<()> { let sdk = AndroidSdk::from_env()?; let sdk_path = sdk.sdk_path(); - // Android Studio installs cmdline-tools into $ANDROID_SDK_ROOT/cmdline-tools//bin. - // Crossbundle install command ignores directory so we need convert cmd-line-tools path to Option to avoid confusion + // Android Studio installs cmdline-tools into + // $ANDROID_SDK_ROOT/cmdline-tools//bin. Crossbundle install command + // ignores directory so we need convert cmd-line-tools path to Option to + // avoid confusion let cmdline_tools_path = std::path::PathBuf::from(&sdk_path) .join("cmdline-tools") .join("latest") diff --git a/crossbundle/cli/src/commands/log/android.rs b/crossbundle/cli/src/commands/log/android.rs deleted file mode 100644 index 38e6cba7..00000000 --- a/crossbundle/cli/src/commands/log/android.rs +++ /dev/null @@ -1,14 +0,0 @@ -use crate::error::*; -use clap::Parser; -use crossbundle_tools::{commands::android, tools::AndroidSdk, utils::Config}; - -#[derive(Parser, Clone, Debug)] -pub struct AndroidLogCommand; - -impl AndroidLogCommand { - pub fn run(&self, _config: &Config) -> Result<()> { - let sdk = AndroidSdk::from_env()?; - android::attach_logger_only_rust(&sdk)?; - Ok(()) - } -} diff --git a/crossbundle/cli/src/commands/log/mod.rs b/crossbundle/cli/src/commands/log/mod.rs deleted file mode 100644 index 396e7529..00000000 --- a/crossbundle/cli/src/commands/log/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -mod android; - -use crate::error::Result; -use android::AndroidLogCommand; -use clap::Parser; -use crossbundle_tools::utils::Config; - -#[derive(Parser, Clone, Debug)] -pub enum LogCommand { - Android(AndroidLogCommand), -} - -impl LogCommand { - pub fn handle_command(&self, config: &Config) -> Result<()> { - match self { - LogCommand::Android(cmd) => cmd.run(config), - } - } -} diff --git a/crossbundle/cli/src/commands/mod.rs b/crossbundle/cli/src/commands/mod.rs index 500e6571..1a407fc9 100644 --- a/crossbundle/cli/src/commands/mod.rs +++ b/crossbundle/cli/src/commands/mod.rs @@ -1,26 +1,24 @@ pub mod build; pub mod install; -pub mod log; pub mod new; pub mod run; use crate::error::Result; use clap::Parser; -use crossbundle_tools::utils::Config; +use crossbundle_tools::types::Config; #[derive(Parser, Clone, Debug)] pub enum Commands { /// Starts the process of building/packaging/signing of the rust crate #[clap(subcommand)] Build(build::BuildCommand), - /// Executes `build` command and then deploy and launches the application on the device/emulator + /// Executes `build` command and then deploy and launches the application on the + /// device/emulator #[clap(subcommand)] Run(run::RunCommand), - /// Creates a new Cargo package in the given directory. Project will be ready to build with `crossbundle` + /// Creates a new Cargo package in the given directory. Project will be ready to build + /// with `crossbundle` New(new::NewCommand), - /// Attach logger to device with running application - #[clap(subcommand)] - Log(log::LogCommand), /// Installs bundletool and Android Studio's sdkmanager #[clap(subcommand)] Install(install::InstallCommand), @@ -32,7 +30,6 @@ impl Commands { Commands::Build(cmd) => cmd.handle_command(config), Commands::Run(cmd) => cmd.handle_command(config), Commands::New(cmd) => cmd.handle_command(config), - Commands::Log(cmd) => cmd.handle_command(config), Commands::Install(cmd) => cmd.handle_command(config), } } diff --git a/crossbundle/cli/src/commands/new/mod.rs b/crossbundle/cli/src/commands/new/mod.rs index 2d755eed..2f04a9c2 100644 --- a/crossbundle/cli/src/commands/new/mod.rs +++ b/crossbundle/cli/src/commands/new/mod.rs @@ -2,21 +2,22 @@ use crate::error::Result; use clap::Parser; use crossbundle_tools::{ commands::{check_cargo_generate, create_project}, - utils::Config, + types::Config, }; const TEMPLATES_REPO: &str = "https://github.com/dodorare/crossbundle-templates.git"; #[derive(Parser, Clone, Debug)] pub struct NewCommand { - /// Directory to create / project name; if the name isn't in kebab-case, it will be converted - /// to kebab-case unless `--force` is given. + /// Directory to create / project name; if the name isn't in kebab-case, it will be + /// converted to kebab-case unless `--force` is given. pub name: String, /// Name of the template to create. #[clap(long, short)] pub template: Option, /// Don't convert the project name to kebab-case before creating the directory. - /// Note that cargo generate won't overwrite an existing directory, even if `--force` is given. + /// Note that cargo generate won't overwrite an existing directory, even if `--force` + /// is given. #[clap(long, short)] pub force: bool, } diff --git a/crossbundle/cli/src/commands/run/android.rs b/crossbundle/cli/src/commands/run/android.rs index e1d2a30c..88efa230 100644 --- a/crossbundle/cli/src/commands/run/android.rs +++ b/crossbundle/cli/src/commands/run/android.rs @@ -1,81 +1,112 @@ use crate::commands::build::{android::AndroidBuildCommand, BuildContext}; -use crate::error::Result; -use android_tools::error::CommandExt; +use crate::error::*; use clap::Parser; -use crossbundle_tools::commands::android::gradle_init; -use crossbundle_tools::tools::{BuildApks, InstallApks}; -use crossbundle_tools::{commands::android, utils::Config}; +use crossbundle_tools::{ + commands::android::*, + error::CommandExt, + types::Config, + types::{AndroidStrategy, BuildApks, InstallApks}, +}; #[derive(Parser, Clone, Debug)] pub struct AndroidRunCommand { #[clap(flatten)] pub build_command: AndroidBuildCommand, + /// Enable logging attach after run. + #[clap(long)] + pub log: bool, } impl AndroidRunCommand { /// Deployes and runs application in AAB or APK format on your device or emulator pub fn run(&self, config: &Config) -> Result<()> { let context = BuildContext::new(config, self.build_command.shared.target_dir.clone())?; - if self.build_command.aab { - let (android_manifest, sdk, aab_path, package_name, key) = - self.build_command.execute_aab(config, &context)?; - config.status("Generating apks")?; - let apks = aab_path - .parent() - .unwrap() - .join(format!("{}.apks", package_name)); - let apks_path = BuildApks::new(&aab_path, &apks) - .overwrite(true) - .ks(&key.key_path) - .ks_pass_pass(key.key_pass) - .ks_key_alias(key.key_alias) - .run()?; - config.status("Starting run process")?; - config.status("Installing APKs file")?; - InstallApks::new(&apks_path).run()?; - config.status("Starting APK file")?; - android::start_apk( - &sdk, - &android_manifest.package, - "android.app.NativeActivity", - )?; - config.status("Run finished successfully")?; - } else if self.build_command.lib.is_some() { + if self.build_command.lib.is_some() { config.status("Can not run dynamic library")?; - } else if self.build_command.apk { - let (android_manifest, sdk, apk_path) = - self.build_command.execute_apk(config, &context)?; - config.status("Starting run process")?; - config.status("Installing APK file")?; - android::install_apk(&sdk, &apk_path)?; - config.status("Starting APK file")?; - android::start_apk( - &sdk, - &android_manifest.package, - "android.app.NativeActivity", - )?; - config.status("Run finished successfully")?; - } else { - let (android_manifest, sdk, gradle_project_path) = self.build_command.build_gradle( - config, - &context, - &self.build_command.export_path, - )?; - config.status("Installing APK file on device")?; - let mut gradle = gradle_init()?; - gradle - .arg("installDebug") - .arg("-p") - .arg(dunce::simplified(&gradle_project_path)); - gradle.output_err(true)?; - config.status("Starting APK file")?; - android::start_apk( - &sdk, - &android_manifest.package, - "com.crossbow.game.CrossbowApp", - )?; - config.status("Run finished successfully")?; + return Ok(()); } + match self.build_command.strategy { + AndroidStrategy::NativeApk => { + self.run_native_apk(config, &context)?; + } + AndroidStrategy::NativeAab => { + self.run_native_aab(config, &context)?; + } + AndroidStrategy::GradleApk => { + self.run_gradle_apk(config, &context)?; + } + } + Ok(()) + } + + pub fn run_native_aab(&self, config: &Config, context: &BuildContext) -> Result<()> { + let (android_manifest, sdk, aab_path, package_name, key) = + self.build_command.execute_aab(config, context)?; + config.status("Generating apks")?; + let apks = aab_path + .parent() + .unwrap() + .join(format!("{}.apks", package_name)); + let apks_path = BuildApks::new(&aab_path, &apks) + .overwrite(true) + .ks(&key.key_path) + .ks_pass_pass(key.key_pass) + .ks_key_alias(key.key_alias) + .run()?; + config.status("Starting run process")?; + config.status("Installing APKs file")?; + InstallApks::new(&apks_path).run()?; + config.status("Starting APK file")?; + start_app( + &sdk, + &android_manifest.package, + "android.app.NativeActivity", + )?; + if self.log { + config.status("Attaching logger")?; + attach_logger_only_rust(&sdk)?; + } + config.status("Run finished successfully")?; + Ok(()) + } + + pub fn run_native_apk(&self, config: &Config, context: &BuildContext) -> Result<()> { + let (android_manifest, sdk, apk_path) = self.build_command.execute_apk(config, context)?; + config.status("Starting run process")?; + config.status("Installing APK file")?; + install_apk(&sdk, &apk_path)?; + config.status("Starting APK file")?; + start_app( + &sdk, + &android_manifest.package, + "android.app.NativeActivity", + )?; + if self.log { + config.status("Attaching logger")?; + attach_logger_only_rust(&sdk)?; + } + config.status("Run finished successfully")?; + Ok(()) + } + + pub fn run_gradle_apk(&self, config: &Config, context: &BuildContext) -> Result<()> { + let (_, sdk, gradle_project_path) = + self.build_command + .build_gradle(config, context, &self.build_command.export_path)?; + config.status("Installing APK file on device")?; + let mut gradle = gradle_init()?; + gradle + .arg("installDebug") + .arg("-p") + .arg(dunce::simplified(&gradle_project_path)); + gradle.output_err(true)?; + config.status("Starting APK file")?; + start_app(&sdk, "com.crossbow.game", ".CrossbowApp")?; + if self.log { + config.status("Attaching logger")?; + attach_logger_only_rust(&sdk)?; + } + config.status("Run finished successfully")?; Ok(()) } } diff --git a/crossbundle/cli/src/commands/run/apple.rs b/crossbundle/cli/src/commands/run/apple.rs index 57c728a2..75a9b121 100644 --- a/crossbundle/cli/src/commands/run/apple.rs +++ b/crossbundle/cli/src/commands/run/apple.rs @@ -1,13 +1,13 @@ -use crate::commands::build::{apple::AppleBuildCommand, BuildContext}; +use crate::commands::build::{apple::IosBuildCommand, BuildContext}; use crate::error::*; use clap::Parser; -use crossbundle_tools::{commands::apple, types::*, utils::Config}; +use crossbundle_tools::{commands::apple, types::Config, types::*}; use std::path::PathBuf; #[derive(Parser, Clone, Debug)] -pub struct AppleRunCommand { +pub struct IosRunCommand { #[clap(flatten)] - pub build_command: AppleBuildCommand, + pub build_command: IosBuildCommand, /// Simulator device name #[clap(short, long, default_value = "iPhone 13")] pub simulator_name: String, @@ -22,18 +22,18 @@ pub struct AppleRunCommand { pub device_id: Option, } -impl AppleRunCommand { +impl IosRunCommand { pub fn run(&self, config: &Config) -> Result<()> { - let mut build_command = self.build_command.clone(); - if self.device && build_command.target.is_empty() { - build_command.target = vec![AppleTarget::Aarch64AppleIos]; - } else if build_command.target.is_empty() { - if cfg!(target_arch = "aarch64") { - build_command.target = vec![AppleTarget::Aarch64AppleIos]; - } else { - build_command.target = vec![AppleTarget::X86_64AppleIos]; - } - } + let build_command = self.build_command.clone(); + // if self.device && build_command.target.is_empty() { + // build_command.target = vec![IosTarget::Aarch64]; + // } else if build_command.target.is_empty() { + // if cfg!(target_arch = "aarch64") { + // build_command.target = vec![IosTarget::Aarch64]; + // } else { + // build_command.target = vec![IosTarget::X86_64]; + // } + // } let context = BuildContext::new(config, build_command.shared.target_dir.clone())?; let (info_plist, app_paths) = build_command.execute(config, &context)?; config.status("Starting run process")?; @@ -45,7 +45,7 @@ impl AppleRunCommand { } else { config.status("Installing and launching application on simulator")?; apple::launch_apple_app(&app_path, &self.simulator_name, bundle_id, true)?; - crossbundle_tools::simctl::Simctl::new() + crossbundle_tools::types::simctl::Simctl::new() .open() .map_err(|err| Error::CrossbundleTools(err.into()))?; } @@ -55,13 +55,13 @@ impl AppleRunCommand { fn get_app_path(&self, app_paths: &[PathBuf]) -> Result { if self.device || cfg!(target_arch = "aarch64") { - Self::get_app_path_by_target(app_paths, AppleTarget::Aarch64AppleIos) + Self::get_app_path_by_target(app_paths, IosTarget::Aarch64) } else { - Self::get_app_path_by_target(app_paths, AppleTarget::X86_64AppleIos) + Self::get_app_path_by_target(app_paths, IosTarget::X86_64) } } - fn get_app_path_by_target(app_paths: &[PathBuf], target: AppleTarget) -> Result { + fn get_app_path_by_target(app_paths: &[PathBuf], target: IosTarget) -> Result { let mut iter = app_paths.iter(); let res = iter.find(|&x| x.to_str().unwrap().contains(target.rust_triple())); Ok(res.ok_or(Error::CantFindTargetToRun)?.to_owned()) diff --git a/crossbundle/cli/src/commands/run/mod.rs b/crossbundle/cli/src/commands/run/mod.rs index fe4ce659..005f3599 100644 --- a/crossbundle/cli/src/commands/run/mod.rs +++ b/crossbundle/cli/src/commands/run/mod.rs @@ -1,23 +1,33 @@ +#[cfg(feature = "android")] mod android; +#[cfg(feature = "apple")] mod apple; use crate::error::Result; use clap::Parser; -use crossbundle_tools::utils::Config; +use crossbundle_tools::types::Config; #[derive(Parser, Clone, Debug)] pub enum RunCommand { - /// Executes `build` command and then deploy and launches the application on the Android device/emulator + /// Executes `build` command and then deploy and launches the application on the + /// Android device/emulator + #[cfg(feature = "android")] Android(android::AndroidRunCommand), - /// Executes `build` command and then deploy and launches the application on the iOS device/emulator - Apple(apple::AppleRunCommand), + /// Executes `build` command and then deploy and launches the application on the iOS + /// device/emulator + #[cfg(feature = "apple")] + Ios(apple::IosRunCommand), } impl RunCommand { pub fn handle_command(&self, config: &Config) -> Result<()> { + #[cfg(any(feature = "android", feature = "apple"))] match &self { - Self::Android(cmd) => cmd.run(config), - Self::Apple(cmd) => cmd.run(config), + #[cfg(feature = "android")] + Self::Android(cmd) => cmd.run(config)?, + #[cfg(feature = "apple")] + Self::Ios(cmd) => cmd.run(config)?, } + Ok(()) } } diff --git a/crossbundle/cli/src/error.rs b/crossbundle/cli/src/error.rs index 2248fc1d..a0bc4724 100644 --- a/crossbundle/cli/src/error.rs +++ b/crossbundle/cli/src/error.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "android")] use crossbundle_tools::types::android_manifest; use displaydoc::Display; use thiserror::Error; @@ -6,14 +7,10 @@ pub type Result = std::result::Result; #[derive(Display, Debug, Error)] pub enum Error { - /// Build targets not provided - BuildTargetsNotProvided, /// Can't find target to run CantFindTargetToRun, /// Team identifier not provided TeamIdentifierNotProvided, - /// Invalid cargo metadata values - InvalidCargoMetadata, /// Invalid metadata in manifest: {0:?} InvalidMetadata(anyhow::Error), /// IO error: {0:?} @@ -25,6 +22,7 @@ pub enum Error { /// Crossbundle Tools error: {0:?} CrossbundleTools(#[from] crossbundle_tools::error::Error), /// AndroidManifest error: {0:?} + #[cfg(feature = "android")] AndroidManifest(#[from] android_manifest::error::Error), /// FsExtra error: {0:?} FsExtra(#[from] fs_extra::error::Error), @@ -47,8 +45,9 @@ pub enum Error { } // TODO: Fix this. Is there a better casting for it? -impl From for Error { - fn from(error: crossbundle_tools::tools::AndroidToolsError) -> Self { +#[cfg(feature = "android")] +impl From for Error { + fn from(error: crossbundle_tools::types::AndroidToolsError) -> Self { Error::CrossbundleTools(error.into()) } } diff --git a/crossbundle/cli/src/lib.rs b/crossbundle/cli/src/lib.rs index d384b5fa..fe0e8726 100644 --- a/crossbundle/cli/src/lib.rs +++ b/crossbundle/cli/src/lib.rs @@ -7,8 +7,8 @@ pub mod types; use clap::Parser; use colored::Colorize; -pub use commands::*; -use crossbundle_tools::utils::{Config, Shell, Verbosity}; +use commands::*; +use crossbundle_tools::types::{Config, Shell, Verbosity}; use std::path::PathBuf; #[derive(Parser, Clone, Debug)] diff --git a/crossbundle/cli/src/types/android_config.rs b/crossbundle/cli/src/types/android_config.rs index e31238e8..1539c52e 100644 --- a/crossbundle/cli/src/types/android_config.rs +++ b/crossbundle/cli/src/types/android_config.rs @@ -1,54 +1,35 @@ use crossbundle_tools::{ - commands::android::AndroidGradlePlugins, - types::{android_manifest::*, AndroidTarget}, + commands::android::*, + types::{android_manifest::AndroidManifest, AndroidTarget, AppWrapper}, }; use serde::{Deserialize, Serialize}; use std::path::PathBuf; -pub const MIN_SDK_VERSION: u32 = 19; - +/// Full Android configuration. #[derive(Debug, Clone, Deserialize, Serialize, Default)] pub struct AndroidConfig { - /// Application name. - pub app_name: Option, - /// Application version name. - pub version_name: Option, - /// Application version code. - pub version_code: Option, - /// Minimum SDK version supported. - pub min_sdk_version: Option, - /// Target SDK version. - pub target_sdk_version: Option, - /// Maximum SDK version supported. - pub max_sdk_version: Option, - /// Icon name in resources. - pub icon: Option, - + /// Specifies what application wrapper to use on build. + #[serde(default)] + pub app_wrapper: AppWrapper, + /// AndroidManifest.xml configuration. + pub manifest: Option, /// Path to AndroidManifest.xml file. + /// + /// **Important:** If this field specified - `manifest` property will be ignored. pub manifest_path: Option, - - /// Android permissions for target sdk version = 22 and lower. - pub permissions: Option>, - /// To declare a permission only on devices that support runtime permissionsโ€”that is, - /// devices that run Android 6.0 (API level 23) or higherโ€”include the uses-permission-sdk-23 - /// element instead of the uses-permission element. - pub permissions_sdk_23: Option>, - /// Declares a single hardware or software android feature that is used by the application - pub features: Option>, - /// Android service to place in AndroidManifest.xml. - pub service: Option>, - /// Android application meta_data to place in AndroidManifest.xml. - pub meta_data: Option>, - /// Android queries to place in AndroidManifest.xml. - pub queries: Option, - /// Android package name to place in AndroidManifest.xml. - pub package: Option, /// Android resources directory path relatively to project path. pub res: Option, - /// Android assets directory path relatively to project path. + /// Custom Android assets directory path relatively to project path. + /// + /// **Important:** This property has higher priority than global property. pub assets: Option, - /// Android build targets. - pub build_targets: Option>, + /// Android debug build targets. + #[serde(default)] + pub debug_build_targets: Vec, + /// Android release build targets. + #[serde(default)] + pub release_build_targets: Vec, + /// Crossbow Android Plugins. #[serde(flatten)] pub plugins: AndroidGradlePlugins, } diff --git a/crossbundle/cli/src/types/apple_config.rs b/crossbundle/cli/src/types/apple_config.rs index eed1a589..062985c3 100644 --- a/crossbundle/cli/src/types/apple_config.rs +++ b/crossbundle/cli/src/types/apple_config.rs @@ -1,27 +1,29 @@ -use crossbundle_tools::types::{apple_bundle::prelude::*, AppleTarget}; +use crossbundle_tools::types::{apple_bundle::prelude::*, IosTarget}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; +/// Full Apple configuration. #[derive(Debug, Clone, Deserialize, Serialize, Default)] pub struct AppleConfig { - /// Application Name. - pub app_name: Option, - /// Application version name. - pub version_name: Option, - /// Application version code. - pub version_code: Option, - /// Icon name in resources. - pub icon: Option, - - /// Path to Info.plist file. - pub info_plist_path: Option, - - /// Apple Info.plist configuration. + /// `Info.plist` configuration. pub info_plist: Option, - /// Apple build targets. - pub build_targets: Option>, - /// Apple resources directory path relatively to project path. + /// Path to `Info.plist` file. + /// + /// **Important:** If this field specified - `info_plist` property will be ignored. + pub info_plist_path: Option, + /// Apple `resources` directory path relatively to project path. pub res: Option, - /// Apple assets directory path relatively to project path. + /// Custom Apple `assets` directory path relatively to project path. + /// + /// **Important:** This property has higher priority than global property. pub assets: Option, + /// Apple debug build targets. + #[serde(default)] + pub debug_build_targets: Vec, + /// Apple release build targets. + #[serde(default)] + pub release_build_targets: Vec, + // TODO: Add Apple plugins. + // #[serde(flatten)] + // pub plugins: ApplePlugins, } diff --git a/crossbundle/cli/src/types/mod.rs b/crossbundle/cli/src/types/mod.rs index f6d58bd3..1585e142 100644 --- a/crossbundle/cli/src/types/mod.rs +++ b/crossbundle/cli/src/types/mod.rs @@ -1,15 +1,54 @@ +#[cfg(feature = "android")] pub mod android_config; +#[cfg(feature = "apple")] pub mod apple_config; +#[cfg(feature = "android")] pub use android_config::*; +#[cfg(feature = "apple")] pub use apple_config::*; +use crossbow::Permission; use serde::{Deserialize, Serialize}; +use std::path::PathBuf; +/// Cross-platform configuration for Android and Apple for Crossbow. #[derive(Debug, Clone, Deserialize, Serialize, Default)] -pub struct Metadata { +pub struct CrossbowMetadata { + /// Application name for Android and Apple. + /// + /// **Important:** This property has lower priority than Android or Apple `manifest` + /// or `info_plist` property. + pub app_name: Option, + /// Assets directory path relatively to project path. + /// + /// **Important:** This property has lower priority than Android or Apple `assets` + /// property. + pub assets: Option, + /// Cross-platform permissions for Android and Apple. + /// + /// **Important:** This property has lower priority than AndroidManifest or Apple + /// Info.plist properties. + #[serde(default)] + pub permissions: Vec, + // TODO: Add `icon` field and icon generation. + // pub icon: Option, + #[cfg(feature = "android")] #[serde(default)] pub android: AndroidConfig, + #[cfg(feature = "apple")] #[serde(default)] pub apple: AppleConfig, } + +impl CrossbowMetadata { + #[cfg(feature = "android")] + pub fn get_android_assets(&self) -> Option { + self.android.assets.clone().or_else(|| self.assets.clone()) + } + + #[cfg(feature = "apple")] + pub fn get_apple_assets(&self) -> Option { + self.apple.assets.clone().or_else(|| self.assets.clone()) + } +} diff --git a/crossbundle/cli/tests/build_gradle.rs b/crossbundle/cli/tests/build_gradle.rs index c31aad88..cc932eb6 100644 --- a/crossbundle/cli/tests/build_gradle.rs +++ b/crossbundle/cli/tests/build_gradle.rs @@ -1,15 +1,16 @@ -use crossbundle_lib::build::{android::AndroidBuildCommand, BuildContext, SharedBuildCommand}; +#![cfg(feature = "android")] + +use crossbundle_lib::commands::build::{android::AndroidBuildCommand, BuildContext}; use crossbundle_tools::{ commands::gen_minimal_project, - types::AndroidTarget, - utils::{Config, Shell}, + types::{AndroidStrategy, AndroidTarget, Config, Shell}, }; #[test] -/// Use macroquad minimal project in a temporary directory to test gradle project generation. -/// It is working likewise the command below. +/// Use macroquad minimal project in a temporary directory to test gradle project +/// generation. It is working likewise the command below. /// ```sh -/// crossbundle build android --quad --gradle +/// crossbundle build android /// ``` fn test_build_gradle() { let tempdir = tempfile::tempdir().unwrap(); @@ -24,26 +25,10 @@ fn test_build_gradle() { let config = Config::new(shell, target_dir.clone()); let context = BuildContext::new(&config, Some(target_dir)).unwrap(); - let shared_build_command = SharedBuildCommand { - example: None, - features: vec![], - all_features: false, - no_default_features: false, - release: false, - target_dir: None, - quad: false, - }; - let android_build_command = AndroidBuildCommand { - shared: shared_build_command, - target: vec![AndroidTarget::Aarch64LinuxAndroid], - aab: false, - lib: None, - export_path: None, - sign_key_path: None, - sign_key_pass: None, - sign_key_alias: None, - apk: false, + target: vec![AndroidTarget::Aarch64], + strategy: AndroidStrategy::GradleApk, + ..Default::default() }; let (_, _, gradle_project_path) = AndroidBuildCommand::build_gradle( diff --git a/crossbundle/cli/tests/cargo_metadata.rs b/crossbundle/cli/tests/cargo_metadata.rs index 9ca50ae0..0779e8c8 100644 --- a/crossbundle/cli/tests/cargo_metadata.rs +++ b/crossbundle/cli/tests/cargo_metadata.rs @@ -1,12 +1,14 @@ -use crossbundle_lib::build::{android::AndroidBuildCommand, BuildContext, SharedBuildCommand}; +#![cfg(feature = "android")] + +use crossbundle_lib::commands::build::{android::AndroidBuildCommand, BuildContext}; use crossbundle_tools::{ commands::gen_minimal_project, - types::{android_manifest::from_str, AndroidTarget}, - utils::{Config, Shell}, + types::{android_manifest::from_str, AndroidStrategy, AndroidTarget, Config, Shell}, }; #[test] -/// Create macroquad minimal project with full cargo toml metadata in a temporary directory to test manifest generating. +/// Create macroquad minimal project with full cargo toml metadata in a +/// temporary directory to test manifest generating. fn test_cargo_metadata() { let tempdir = tempfile::tempdir().unwrap(); let project_path = tempdir.path(); @@ -21,57 +23,25 @@ fn test_cargo_metadata() { let config = Config::new(shell, target_dir.clone()); let context = BuildContext::new(&config, Some(target_dir)).unwrap(); - let shared_build_command = SharedBuildCommand { - example: None, - features: vec![], - all_features: false, - no_default_features: false, - release: false, - target_dir: None, - quad: false, - }; - let android_build_command = AndroidBuildCommand { - shared: shared_build_command, - target: vec![AndroidTarget::Aarch64LinuxAndroid], - aab: false, - lib: None, - export_path: None, - sign_key_path: None, - sign_key_pass: None, - sign_key_alias: None, - apk: false, + target: vec![AndroidTarget::Aarch64], + strategy: AndroidStrategy::NativeApk, + ..Default::default() }; - let profile = android_build_command.shared.profile(); let example = android_build_command.shared.example.as_ref(); - let (_project_path, target_dir, package_name) = - AndroidBuildCommand::needed_project_dirs(example, &context).unwrap(); + let (_, _, package_name) = AndroidBuildCommand::needed_project_dirs(example, &context).unwrap(); config .status_message("Starting apk build process", &package_name) .unwrap(); - let (sdk, _ndk, _target_sdk_version) = - AndroidBuildCommand::android_toolchain(&context).unwrap(); - - let android_build_dir = target_dir.join("android").join(&package_name); - let native_build_dir = android_build_dir.join("native"); - // Get AndroidManifest.xml from file or generate from Cargo.toml - let (android_manifest, manifest_path) = AndroidBuildCommand::android_manifest( - &config, - &context, - &sdk, - &package_name, - profile, - &native_build_dir, - false, - ) - .unwrap(); + let android_manifest = + AndroidBuildCommand::get_android_manifest(&context, AndroidStrategy::NativeApk).unwrap(); let expected_manifest = r#" - - - + + + @@ -84,5 +54,4 @@ fn test_cargo_metadata() { "#; let expected_manifest = from_str(expected_manifest).unwrap(); assert_eq!(expected_manifest, android_manifest); - assert!(manifest_path.exists()) } diff --git a/crossbundle/cli/tests/execute_aab.rs b/crossbundle/cli/tests/execute_aab.rs index 3f2205b0..65b751b5 100644 --- a/crossbundle/cli/tests/execute_aab.rs +++ b/crossbundle/cli/tests/execute_aab.rs @@ -1,15 +1,16 @@ -use crossbundle_lib::build::{android::AndroidBuildCommand, BuildContext, SharedBuildCommand}; +#![cfg(feature = "android")] + +use crossbundle_lib::commands::build::{android::AndroidBuildCommand, BuildContext}; use crossbundle_tools::{ commands::gen_minimal_project, - types::AndroidTarget, - utils::{Config, Shell}, + types::{AndroidStrategy, AndroidTarget, Config, Shell}, }; #[test] /// Use bevy minimal project in a temporary directory to test AAB generation. /// It is working likewise the command below. /// ```sh -/// crossbundle build android --quad --aab +/// crossbundle build android -s=native-aab /// ``` fn test_execute_aab() { let tempdir = tempfile::tempdir().unwrap(); @@ -24,26 +25,10 @@ fn test_execute_aab() { let config = Config::new(shell, target_dir.clone()); let context = BuildContext::new(&config, Some(target_dir.clone())).unwrap(); - let shared_build_command = SharedBuildCommand { - example: None, - features: vec![], - all_features: false, - no_default_features: false, - release: false, - target_dir: None, - quad: false, - }; - let android_build_command = AndroidBuildCommand { - shared: shared_build_command, - target: vec![AndroidTarget::Aarch64LinuxAndroid], - aab: false, - lib: None, - export_path: None, - sign_key_path: None, - sign_key_pass: None, - sign_key_alias: None, - apk: false, + target: vec![AndroidTarget::Aarch64], + strategy: AndroidStrategy::NativeAab, + ..Default::default() }; let (_, _, generated_aab_path, _, _) = diff --git a/crossbundle/cli/tests/execute_apk.rs b/crossbundle/cli/tests/execute_apk.rs index 693785f7..e04807bd 100644 --- a/crossbundle/cli/tests/execute_apk.rs +++ b/crossbundle/cli/tests/execute_apk.rs @@ -1,15 +1,16 @@ -use crossbundle_lib::build::{android::AndroidBuildCommand, BuildContext, SharedBuildCommand}; +#![cfg(feature = "android")] + +use crossbundle_lib::commands::build::{android::AndroidBuildCommand, BuildContext}; use crossbundle_tools::{ commands::gen_minimal_project, - types::AndroidTarget, - utils::{Config, Shell}, + types::{AndroidStrategy, AndroidTarget, Config, Shell}, }; #[test] /// Use macroquad minimal project in a temporary directory to test APK generation. /// It is working likewise the command below. /// ```sh -/// crossbundle build android --quad +/// crossbundle build android -s=native-apk /// ``` fn test_execute_apk() { let tempdir = tempfile::tempdir().unwrap(); @@ -24,26 +25,10 @@ fn test_execute_apk() { let config = Config::new(shell, target_dir.clone()); let context = BuildContext::new(&config, Some(target_dir.clone())).unwrap(); - let shared_build_command = SharedBuildCommand { - example: None, - features: vec![], - all_features: false, - no_default_features: false, - release: false, - target_dir: None, - quad: false, - }; - let android_build_command = AndroidBuildCommand { - shared: shared_build_command, - target: vec![AndroidTarget::Aarch64LinuxAndroid], - aab: false, - lib: None, - export_path: None, - sign_key_path: None, - sign_key_pass: None, - sign_key_alias: None, - apk: false, + target: vec![AndroidTarget::Aarch64], + strategy: AndroidStrategy::NativeApk, + ..Default::default() }; let (_, _, generated_apk_path) = diff --git a/crossbundle/tools/Cargo.toml b/crossbundle/tools/Cargo.toml index b03bf5d9..4b9ab6fe 100644 --- a/crossbundle/tools/Cargo.toml +++ b/crossbundle/tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "crossbundle-tools" -version = "0.1.7" +version = "0.1.8" edition = "2021" authors = ["DodoRare Team "] description = "Build and publish apps for Android/iOS" @@ -10,36 +10,41 @@ keywords = ["android", "ios"] readme = "README.md" [dependencies] +# Apple crates +apple-bundle = { version = "0.1.4", optional = true } +simctl = { version = "0.1.1", package = "creator-simctl", optional = true } +# Android crates +android-manifest = { version = "0.1.9", optional = true } +android-tools = { version = "0.2.10", optional = true } + serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0.61" -serde_plain = "0.3" +serde_plain = "1.0" + dunce = "1.0" fs_extra = "1.2" -dirs = "4.0.0" -simctl = { version = "0.1.1", package = "creator-simctl" } -android-manifest = "0.1.8" -apple-bundle = "0.1.1" -android-tools = "0.2.9" -thiserror = "1.0" -anyhow = "1.0" -displaydoc = "0.2" -which = "4.0" +dirs = "4.0" +which = "4.2" +tempfile = "3.3" +zip = "0.5" +zip-extensions = "0.6.1" + itertools = "0.10" -termcolor = "1.1.2" -atty = "0.2.14" -log = "0.4" -zip = "0.5.13" -zip-extensions = "0.6" -tempfile = "3.2" cargo = "0.63.1" cargo-util = "0.2.0" rust-embed = { version = "6.4.0", features = ["include-exclude"] } +thiserror = "1.0" +anyhow = "1.0" +displaydoc = "0.2" +log = "0.4" +termcolor = "1.1" +atty = "0.2" + [target.'cfg(unix)'.dependencies] libc = "0.2" [target.'cfg(windows)'.dependencies] -fwdansi = "1.1.0" +fwdansi = "1.1" [target.'cfg(windows)'.dependencies.winapi] version = "0.3" @@ -51,3 +56,8 @@ features = [ "wincon", "winnt", ] + +[features] +default = ["android", "apple"] +android = ["android-manifest", "android-tools"] +apple = ["apple-bundle", "simctl"] diff --git a/crossbundle/tools/README.md b/crossbundle/tools/README.md index 0d9d8bac..2b080671 100644 --- a/crossbundle/tools/README.md +++ b/crossbundle/tools/README.md @@ -1,3 +1,21 @@ -# CrossBundle Tools +# Crossbundle Tools + +![splash](https://github.com/dodorare/crossbow/blob/main/assets/splash.png?raw=true) This repository contains all tools and helpers used in CrossBundle command-line tool. + +To learn more about commands and helpers - read [crossbundle-tools' docs.rs](https://docs.rs/crossbundle-tools). + +## Support status + +Supported operating systems for build (**iOS** only on **macOS**): + +| Name | Status | +| ---- | ------ | +| Windows | โœ… | +| Linux | โœ… | +| macOS | โœ… | + +## License + +Licensed under [Apache-2.0 License](../../LICENSE). diff --git a/crossbundle/tools/src/commands/android/attach_logger.rs b/crossbundle/tools/src/commands/android/common/attach_logger.rs similarity index 88% rename from crossbundle/tools/src/commands/android/attach_logger.rs rename to crossbundle/tools/src/commands/android/common/attach_logger.rs index dcf5d19a..5e5f787e 100644 --- a/crossbundle/tools/src/commands/android/attach_logger.rs +++ b/crossbundle/tools/src/commands/android/common/attach_logger.rs @@ -1,5 +1,4 @@ -use crate::error::*; -use crate::tools::AndroidSdk; +use crate::{error::*, types::AndroidSdk}; use std::process::Command; /// Returns `adb logcat` command @@ -14,6 +13,6 @@ fn logcat_cmd(sdk: &AndroidSdk) -> Result { pub fn attach_logger_only_rust(sdk: &AndroidSdk) -> Result<()> { let mut adb = logcat_cmd(sdk)?; adb.arg("RustStdoutStderr:D").arg("SAPP:D").arg("*:S"); - adb.spawn()?; + adb.spawn()?.wait()?; Ok(()) } diff --git a/crossbundle/tools/src/commands/android/detect_abi.rs b/crossbundle/tools/src/commands/android/common/detect_abi.rs similarity index 84% rename from crossbundle/tools/src/commands/android/detect_abi.rs rename to crossbundle/tools/src/commands/android/common/detect_abi.rs index edb7281a..2528f9b2 100644 --- a/crossbundle/tools/src/commands/android/detect_abi.rs +++ b/crossbundle/tools/src/commands/android/common/detect_abi.rs @@ -1,6 +1,7 @@ -use crate::error::*; -use crate::tools::AndroidSdk; -use crate::types::AndroidTarget; +use crate::{ + error::*, + types::{AndroidSdk, AndroidTarget}, +}; /// Returns current android target. /// Runs `adb shell getprop ro.product.cpu.abi` command diff --git a/crossbundle/tools/src/commands/android/extract_apk.rs b/crossbundle/tools/src/commands/android/common/extract_archive.rs similarity index 100% rename from crossbundle/tools/src/commands/android/extract_apk.rs rename to crossbundle/tools/src/commands/android/common/extract_archive.rs diff --git a/crossbundle/tools/src/commands/android/generate/gen_key.rs b/crossbundle/tools/src/commands/android/common/gen_key.rs similarity index 100% rename from crossbundle/tools/src/commands/android/generate/gen_key.rs rename to crossbundle/tools/src/commands/android/common/gen_key.rs diff --git a/crossbundle/tools/src/commands/android/helper_functions.rs b/crossbundle/tools/src/commands/android/common/helper_functions.rs similarity index 100% rename from crossbundle/tools/src/commands/android/helper_functions.rs rename to crossbundle/tools/src/commands/android/common/helper_functions.rs diff --git a/crossbundle/tools/src/commands/android/common/mod.rs b/crossbundle/tools/src/commands/android/common/mod.rs new file mode 100644 index 00000000..9fb485e5 --- /dev/null +++ b/crossbundle/tools/src/commands/android/common/mod.rs @@ -0,0 +1,21 @@ +mod attach_logger; +mod detect_abi; +mod extract_archive; +mod gen_key; +mod helper_functions; +mod read_manifest; +mod rust_compile; +mod save_manifest; +mod start_app; +mod write_zip; + +pub use attach_logger::*; +pub use detect_abi::*; +pub use extract_archive::*; +pub use gen_key::*; +pub use helper_functions::*; +pub use read_manifest::*; +pub use rust_compile::*; +pub use save_manifest::*; +pub use start_app::*; +pub use write_zip::*; diff --git a/crossbundle/tools/src/commands/android/read_manifest.rs b/crossbundle/tools/src/commands/android/common/read_manifest.rs similarity index 100% rename from crossbundle/tools/src/commands/android/read_manifest.rs rename to crossbundle/tools/src/commands/android/common/read_manifest.rs diff --git a/crossbundle/tools/src/commands/android/rust_compile/cmake_toolchain.rs b/crossbundle/tools/src/commands/android/common/rust_compile/cmake_toolchain.rs similarity index 98% rename from crossbundle/tools/src/commands/android/rust_compile/cmake_toolchain.rs rename to crossbundle/tools/src/commands/android/common/rust_compile/cmake_toolchain.rs index 740ddaff..7e261aae 100644 --- a/crossbundle/tools/src/commands/android/rust_compile/cmake_toolchain.rs +++ b/crossbundle/tools/src/commands/android/common/rust_compile/cmake_toolchain.rs @@ -4,7 +4,7 @@ use std::io::Write; /// Sets needed environment variables pub fn set_cmake_vars( build_target: crate::types::AndroidTarget, - ndk: &crate::tools::AndroidNdk, + ndk: &AndroidNdk, target_sdk_version: u32, build_target_dir: &std::path::Path, ) -> cargo::CargoResult<()> { diff --git a/crossbundle/tools/src/commands/android/rust_compile/compile_options.rs b/crossbundle/tools/src/commands/android/common/rust_compile/compile_options.rs similarity index 100% rename from crossbundle/tools/src/commands/android/rust_compile/compile_options.rs rename to crossbundle/tools/src/commands/android/common/rust_compile/compile_options.rs diff --git a/crossbundle/tools/src/commands/android/rust_compile/consts.rs b/crossbundle/tools/src/commands/android/common/rust_compile/consts.rs similarity index 100% rename from crossbundle/tools/src/commands/android/rust_compile/consts.rs rename to crossbundle/tools/src/commands/android/common/rust_compile/consts.rs diff --git a/crossbundle/tools/src/commands/android/rust_compile/gen_tmp_lib_file.rs b/crossbundle/tools/src/commands/android/common/rust_compile/gen_tmp_lib_file.rs similarity index 100% rename from crossbundle/tools/src/commands/android/rust_compile/gen_tmp_lib_file.rs rename to crossbundle/tools/src/commands/android/common/rust_compile/gen_tmp_lib_file.rs diff --git a/crossbundle/tools/src/commands/android/rust_compile/mod.rs b/crossbundle/tools/src/commands/android/common/rust_compile/mod.rs similarity index 100% rename from crossbundle/tools/src/commands/android/rust_compile/mod.rs rename to crossbundle/tools/src/commands/android/common/rust_compile/mod.rs diff --git a/crossbundle/tools/src/commands/android/rust_compile/rust_compiler.rs b/crossbundle/tools/src/commands/android/common/rust_compile/rust_compiler.rs similarity index 91% rename from crossbundle/tools/src/commands/android/rust_compile/rust_compiler.rs rename to crossbundle/tools/src/commands/android/common/rust_compile/rust_compiler.rs index 6b14be1f..385a62be 100644 --- a/crossbundle/tools/src/commands/android/rust_compile/rust_compiler.rs +++ b/crossbundle/tools/src/commands/android/common/rust_compile/rust_compiler.rs @@ -1,7 +1,5 @@ use super::*; -use crate::error::*; -use crate::tools::*; -use crate::types::*; +use crate::{error::*, types::*}; pub fn rust_compile( ndk: &AndroidNdk, @@ -13,7 +11,7 @@ pub fn rust_compile( no_default_features: bool, target_sdk_version: u32, lib_name: &str, - app_wrapper: ApplicationWrapper, + app_wrapper: AppWrapper, ) -> Result<()> { // Specify path to workspace let rust_triple = build_target.rust_triple(); @@ -79,7 +77,7 @@ struct SharedLibraryExecutor { ndk: AndroidNdk, profile: Profile, nostrip: bool, - app_wrapper: ApplicationWrapper, + app_wrapper: AppWrapper, } impl cargo::core::compiler::Executor for SharedLibraryExecutor { @@ -99,8 +97,8 @@ impl cargo::core::compiler::Executor for SharedLibraryExecutor { let mut new_args = cmd.get_args().cloned().collect::>(); let extra_code = match self.app_wrapper { - ApplicationWrapper::Sokol => consts::SOKOL_EXTRA_CODE, - ApplicationWrapper::NdkGlue => consts::NDK_GLUE_EXTRA_CODE, + AppWrapper::Sokol => consts::SOKOL_EXTRA_CODE, + AppWrapper::NdkGlue => consts::NDK_GLUE_EXTRA_CODE, }; let path = @@ -114,12 +112,8 @@ impl cargo::core::compiler::Executor for SharedLibraryExecutor { // Generate tmp_file with bevy or quad extra code depending on either sokol or ndk glue // dependency let tmp_file = match self.app_wrapper { - ApplicationWrapper::Sokol => { - gen_tmp_lib_file::generate_lib_file(&path, extra_code)? - } - ApplicationWrapper::NdkGlue => { - gen_tmp_lib_file::generate_lib_file(&path, extra_code)? - } + AppWrapper::Sokol => gen_tmp_lib_file::generate_lib_file(&path, extra_code)?, + AppWrapper::NdkGlue => gen_tmp_lib_file::generate_lib_file(&path, extra_code)?, }; // Replace source argument @@ -179,19 +173,17 @@ impl cargo::core::compiler::Executor for SharedLibraryExecutor { if build_tag > 7272597 { let error_msg = anyhow::Error::msg("Failed to write content into libgcc.a file"); let mut args = match self.app_wrapper { - ApplicationWrapper::Sokol => { + AppWrapper::Sokol => { new_ndk_quad_args(tool_root, &self.build_target, self.target_sdk_version) .map_err(|_| error_msg)? } - ApplicationWrapper::NdkGlue => { - linker_args(&tool_root).map_err(|_| error_msg)? - } + AppWrapper::NdkGlue => linker_args(&tool_root).map_err(|_| error_msg)?, }; new_args.append(&mut args); cmd.args_replace(&new_args); cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false) .map(drop)?; - } else if self.app_wrapper == ApplicationWrapper::Sokol { + } else if self.app_wrapper == AppWrapper::Sokol { // Set linker arguments using in ndk =< 22 let mut linker_args = add_clinker_args(&self.ndk, &self.build_target, self.target_sdk_version)?; @@ -209,7 +201,7 @@ impl cargo::core::compiler::Executor for SharedLibraryExecutor { // Execute the command cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false) .map(drop)?; - } else if self.app_wrapper == ApplicationWrapper::NdkGlue { + } else if self.app_wrapper == AppWrapper::NdkGlue { cmd.args_replace(&new_args); cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false) diff --git a/crossbundle/tools/src/commands/android/rust_compile/set_linker_args.rs b/crossbundle/tools/src/commands/android/common/rust_compile/set_linker_args.rs similarity index 98% rename from crossbundle/tools/src/commands/android/rust_compile/set_linker_args.rs rename to crossbundle/tools/src/commands/android/common/rust_compile/set_linker_args.rs index 11111c5f..4ae952f6 100644 --- a/crossbundle/tools/src/commands/android/rust_compile/set_linker_args.rs +++ b/crossbundle/tools/src/commands/android/common/rust_compile/set_linker_args.rs @@ -2,7 +2,7 @@ use crate::types::*; /// Add linker args for quad engine using NDK versions <=22 pub fn add_clinker_args( - ndk: &crate::tools::AndroidNdk, + ndk: &AndroidNdk, build_target: &AndroidTarget, target_sdk_version: u32, ) -> cargo::CargoResult> { diff --git a/crossbundle/tools/src/commands/android/save_manifest.rs b/crossbundle/tools/src/commands/android/common/save_manifest.rs similarity index 100% rename from crossbundle/tools/src/commands/android/save_manifest.rs rename to crossbundle/tools/src/commands/android/common/save_manifest.rs diff --git a/crossbundle/tools/src/commands/android/start_apk.rs b/crossbundle/tools/src/commands/android/common/start_app.rs similarity index 65% rename from crossbundle/tools/src/commands/android/start_apk.rs rename to crossbundle/tools/src/commands/android/common/start_app.rs index 50119048..a85a0606 100644 --- a/crossbundle/tools/src/commands/android/start_apk.rs +++ b/crossbundle/tools/src/commands/android/common/start_app.rs @@ -1,9 +1,8 @@ -use crate::error::*; -use crate::tools::AndroidSdk; +use crate::{error::*, types::AndroidSdk}; -/// Starts installed APK or AAB on emulator or connected device. +/// Installing APK or AAB on emulator or connected device. /// Runs `adb shell am start ...` command -pub fn start_apk(sdk: &AndroidSdk, package: &str, activity: &str) -> Result<()> { +pub fn start_app(sdk: &AndroidSdk, package: &str, activity: &str) -> Result<()> { let mut adb = sdk.platform_tool(bin!("adb"))?; adb.arg("shell") .arg("am") diff --git a/crossbundle/tools/src/commands/android/write_zip.rs b/crossbundle/tools/src/commands/android/common/write_zip.rs similarity index 82% rename from crossbundle/tools/src/commands/android/write_zip.rs rename to crossbundle/tools/src/commands/android/common/write_zip.rs index a311d023..9d32eec3 100644 --- a/crossbundle/tools/src/commands/android/write_zip.rs +++ b/crossbundle/tools/src/commands/android/common/write_zip.rs @@ -3,7 +3,7 @@ use zip::ZipWriter; use zip_extensions::write::ZipWriterExtensions; /// Writing files into archive -pub fn write(source_path: &Path, archive_file: &Path) -> zip::result::ZipResult<()> { +pub fn zip_write(source_path: &Path, archive_file: &Path) -> zip::result::ZipResult<()> { let file = std::fs::File::create(archive_file)?; let mut zip = ZipWriter::new(file); zip.create_from_directory(&source_path.to_path_buf())?; @@ -11,7 +11,7 @@ pub fn write(source_path: &Path, archive_file: &Path) -> zip::result::ZipResult< } /// Moving AndroidManifest.xml file into directory to write files to archive -pub fn dirs_to_write(source_path: &Path) -> fs_extra::error::Result<()> { +pub fn zip_dirs_to_write(source_path: &Path) -> fs_extra::error::Result<()> { let path = source_path.join("AndroidManifest.xml"); if path.exists() { let manifest_path = source_path.join("manifest"); diff --git a/crossbundle/tools/src/commands/android/generate/gen_manifest.rs b/crossbundle/tools/src/commands/android/generate/gen_manifest.rs deleted file mode 100644 index 6898a3f9..00000000 --- a/crossbundle/tools/src/commands/android/generate/gen_manifest.rs +++ /dev/null @@ -1,110 +0,0 @@ -use android_manifest::*; - -/// Generates [`AndroidManifest`](android_manifest::AndroidManifest) with -/// given changes -pub fn gen_android_manifest( - app_id: Option, - package_name: String, - app_name: Option, - version_name: String, - version_code: u32, - min_sdk_version: Option, - target_sdk_version: u32, - max_sdk_version: Option, - icon: Option, - debuggable: bool, - permissions_sdk_23: Option>, - permissions: Option>, - features: Option>, - service: Option>, - meta_data: Option>, - queries: Option, - gradle: bool, -) -> AndroidManifest { - AndroidManifest { - package: app_id - .unwrap_or(format!("com.rust.{}", package_name)) - .replace('-', "_"), - version_name: Some(version_name), - version_code: Some(version_code), - uses_sdk: Some(UsesSdk { - min_sdk_version: Some(min_sdk_version.unwrap_or(9)), - target_sdk_version: Some(target_sdk_version), - max_sdk_version, - }), - uses_permission_sdk_23: permissions_sdk_23.unwrap_or_default(), - uses_permission: permissions.unwrap_or_default(), - uses_feature: features.unwrap_or_default(), - queries, - application: Application { - has_code: Some(gradle), - label: Some(StringResourceOrString::string( - app_name.as_ref().unwrap_or(&package_name), - )), - debuggable: Some(debuggable), - icon: icon.map(|i| MipmapOrDrawableResource::mipmap(&i, None)), - theme: Some(Resource::new_with_package( - "Theme.DeviceDefault.NoActionBar.Fullscreen", - Some("android".to_string()), - )), - service: service.unwrap_or_default(), - meta_data: meta_data.unwrap_or_default(), - activity: vec![Activity { - name: match gradle { - true => "com.crossbow.game.CrossbowApp".to_string(), - false => "android.app.NativeActivity".to_string(), - }, - resizeable_activity: Some(true), - config_changes: vec![ - ConfigChanges::Orientation, - ConfigChanges::KeyboardHidden, - ConfigChanges::ScreenSize, - ] - .into(), - meta_data: vec![MetaData { - name: Some("android.app.lib_name".to_string()), - value: Some(match gradle { - true => "crossbow_android".to_string(), - false => package_name.replace('-', "_"), - }), - ..Default::default() - }], - intent_filter: vec![IntentFilter { - action: vec![Action { - name: Some("android.intent.action.MAIN".to_string()), - }], - category: vec![Category { - name: Some("android.intent.category.LAUNCHER".to_string()), - }], - ..Default::default() - }], - ..Default::default() - }], - ..Default::default() - }, - ..Default::default() - } -} - -/// Generate android manifest with minimal required tags. -pub fn gen_min_android_manifest( - version_name: &str, - version_code: u32, - package_name: &str, -) -> AndroidManifest { - AndroidManifest { - package: (format!("com.rust.{}", package_name)).replace('-', "_"), - version_name: Some(version_name.to_string()), - version_code: Some(version_code), - application: Application { - has_code: Some(false), - label: Some(StringResourceOrString::string(package_name)), - activity: vec![Activity { - name: "android.app.NativeActivity".to_string(), - ..Default::default() - }], - ..Default::default() - }, - ..Default::default() - } -} diff --git a/crossbundle/tools/src/commands/android/generate/mod.rs b/crossbundle/tools/src/commands/android/generate/mod.rs deleted file mode 100644 index f75fa515..00000000 --- a/crossbundle/tools/src/commands/android/generate/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -mod gen_aab_from_modules; -mod gen_gradle_project; -mod gen_key; -pub mod gen_manifest; -mod gen_minimal_unsigned_aab; -mod gen_unaligned_apk; -mod gen_zip_modules; - -pub use gen_aab_from_modules::*; -pub use gen_gradle_project::*; -pub use gen_key::*; -pub use gen_manifest::*; -pub use gen_minimal_unsigned_aab::*; -pub use gen_unaligned_apk::*; -pub use gen_zip_modules::*; diff --git a/crossbundle/tools/src/commands/android/generate/gen_gradle_project.rs b/crossbundle/tools/src/commands/android/gradle/gen_gradle_project.rs similarity index 100% rename from crossbundle/tools/src/commands/android/generate/gen_gradle_project.rs rename to crossbundle/tools/src/commands/android/gradle/gen_gradle_project.rs diff --git a/crossbundle/tools/src/commands/android/gradle_command.rs b/crossbundle/tools/src/commands/android/gradle/gradle_init.rs similarity index 100% rename from crossbundle/tools/src/commands/android/gradle_command.rs rename to crossbundle/tools/src/commands/android/gradle/gradle_init.rs diff --git a/crossbundle/tools/src/commands/android/gradle/mod.rs b/crossbundle/tools/src/commands/android/gradle/mod.rs new file mode 100644 index 00000000..58814b8b --- /dev/null +++ b/crossbundle/tools/src/commands/android/gradle/mod.rs @@ -0,0 +1,5 @@ +pub mod gen_gradle_project; +pub mod gradle_init; + +pub use gen_gradle_project::*; +pub use gradle_init::*; diff --git a/crossbundle/tools/src/commands/android/mod.rs b/crossbundle/tools/src/commands/android/mod.rs index f5e39b55..69a41d5d 100644 --- a/crossbundle/tools/src/commands/android/mod.rs +++ b/crossbundle/tools/src/commands/android/mod.rs @@ -1,36 +1,7 @@ -///! Commands for compiling rust code for `Android`, -///! generation/aligning/signing/installing/starting on device APKs and AAB, -///! generation `AndroidManifest.xml` and so on. -mod add_libs_into_aapt2; -mod add_libs_into_apk; -mod align_apk; -mod attach_logger; -mod detect_abi; -mod extract_apk; -pub mod generate; -mod gradle_command; -mod helper_functions; -mod install_apk; -mod read_manifest; -mod rust_compile; -mod save_manifest; -mod sign_apk; -mod start_apk; -mod write_zip; +mod common; +mod gradle; +mod native; -pub use add_libs_into_aapt2::*; -pub use add_libs_into_apk::*; -pub use align_apk::*; -pub use attach_logger::*; -pub use detect_abi::*; -pub use extract_apk::*; -pub use generate::*; -pub use gradle_command::*; -pub use helper_functions::*; -pub use install_apk::*; -pub use read_manifest::*; -pub use rust_compile::*; -pub use save_manifest::*; -pub use sign_apk::*; -pub use start_apk::*; -pub use write_zip::*; +pub use common::*; +pub use gradle::*; +pub use native::*; diff --git a/crossbundle/tools/src/commands/android/add_libs_into_aapt2.rs b/crossbundle/tools/src/commands/android/native/aab/add_libs_into_aapt2.rs similarity index 93% rename from crossbundle/tools/src/commands/android/add_libs_into_aapt2.rs rename to crossbundle/tools/src/commands/android/native/aab/add_libs_into_aapt2.rs index 240e3155..8cce624b 100644 --- a/crossbundle/tools/src/commands/android/add_libs_into_aapt2.rs +++ b/crossbundle/tools/src/commands/android/native/aab/add_libs_into_aapt2.rs @@ -1,8 +1,7 @@ use crate::{ - commands::android::{get_libs_in_dir, recursively_define_needed_libs, search_dylibs}, + commands::android::native::{get_libs_in_dir, recursively_define_needed_libs, search_dylibs}, error::*, - tools::AndroidNdk, - types::{AndroidTarget, IntoRustTriple, Profile}, + types::{AndroidNdk, AndroidTarget, IntoRustTriple, Profile}, }; use std::path::{Path, PathBuf}; diff --git a/crossbundle/tools/src/commands/android/generate/gen_aab_from_modules.rs b/crossbundle/tools/src/commands/android/native/aab/gen_aab_from_modules.rs similarity index 83% rename from crossbundle/tools/src/commands/android/generate/gen_aab_from_modules.rs rename to crossbundle/tools/src/commands/android/native/aab/gen_aab_from_modules.rs index c55ec03a..9d3b987a 100644 --- a/crossbundle/tools/src/commands/android/generate/gen_aab_from_modules.rs +++ b/crossbundle/tools/src/commands/android/native/aab/gen_aab_from_modules.rs @@ -1,15 +1,14 @@ -use crate::error::*; -use crate::tools::*; +use crate::{error::*, types::*}; use std::path::{Path, PathBuf}; /// Allows to generate aab from archive with files extracted from apk or set of archives /// to specified storage pub fn gen_aab_from_modules( - package_label: &str, + package_name: &str, zip_modules: &[PathBuf], build_dir: &Path, ) -> Result { - let aab = build_dir.join(format!("{}_unsigned.aab", package_label)); + let aab = build_dir.join(format!("{}_unsigned.aab", package_name)); BuildBundle::new(zip_modules, &aab).run()?; Ok(aab) } diff --git a/crossbundle/tools/src/commands/android/generate/gen_minimal_unsigned_aab.rs b/crossbundle/tools/src/commands/android/native/aab/gen_minimal_unsigned_aab.rs similarity index 72% rename from crossbundle/tools/src/commands/android/generate/gen_minimal_unsigned_aab.rs rename to crossbundle/tools/src/commands/android/native/aab/gen_minimal_unsigned_aab.rs index 1f27d16a..7644c859 100644 --- a/crossbundle/tools/src/commands/android/generate/gen_minimal_unsigned_aab.rs +++ b/crossbundle/tools/src/commands/android/native/aab/gen_minimal_unsigned_aab.rs @@ -1,5 +1,5 @@ -use super::gen_manifest; -use crate::{error::*, tools::AndroidSdk}; +use crate::{commands::android::*, error::*, types::*}; +use android_manifest::AndroidManifest; use std::path::{Path, PathBuf}; /// Generates minimal unsigned aab @@ -9,13 +9,18 @@ pub fn gen_minimal_unsigned_aab( target_sdk_version: u32, aab_build_dir: &Path, ) -> Result { - let version_code = 1_u32; - let version_name = "1"; + let mut manifest = AndroidManifest { + package: "com.crossbow.minimal".to_owned(), + ..Default::default() + }; + update_android_manifest_with_default( + &mut manifest, + Some("Minimal".to_owned()), + "minimal", + AndroidStrategy::NativeAab, + ); - let android_manifest = - gen_manifest::gen_min_android_manifest(version_name, version_code, package_name); - - let manifest_path = super::super::save_android_manifest(aab_build_dir, &android_manifest)?; + let manifest_path = save_android_manifest(aab_build_dir, &manifest)?; let apk_path = aab_build_dir.join(format!("{}_module.apk", package_name)); if !aab_build_dir.exists() { std::fs::create_dir_all(&aab_build_dir)?; @@ -32,14 +37,14 @@ pub fn gen_minimal_unsigned_aab( aapt2_link.run()?; let output_dir = aab_build_dir.join("extracted_apk_files"); - let extracted_apk_path = super::super::extract_archive(&apk_path, &output_dir)?; + let extracted_apk_path = extract_archive(&apk_path, &output_dir)?; let gen_zip_modules = super::gen_zip_modules(aab_build_dir, package_name, &extracted_apk_path)?; let aab_path = super::gen_aab_from_modules(package_name, &[gen_zip_modules.clone()], aab_build_dir)?; - super::super::remove(vec![gen_zip_modules, extracted_apk_path])?; + remove(vec![gen_zip_modules, extracted_apk_path])?; Ok(aab_path) } diff --git a/crossbundle/tools/src/commands/android/generate/gen_zip_modules.rs b/crossbundle/tools/src/commands/android/native/aab/gen_zip_modules.rs similarity index 57% rename from crossbundle/tools/src/commands/android/generate/gen_zip_modules.rs rename to crossbundle/tools/src/commands/android/native/aab/gen_zip_modules.rs index b29cd106..043d3ec8 100644 --- a/crossbundle/tools/src/commands/android/generate/gen_zip_modules.rs +++ b/crossbundle/tools/src/commands/android/native/aab/gen_zip_modules.rs @@ -1,15 +1,14 @@ -use crate::commands::android::write_zip; -use crate::error::*; +use crate::{commands::android::*, error::*}; use std::path::{Path, PathBuf}; /// Allows to generate archive from files extracted from APK pub fn gen_zip_modules( build_dir: &Path, - package_label: &str, + package_name: &str, extracted_apk_files: &Path, ) -> Result { - let zip_path = build_dir.join(format!("{}_module.zip", package_label)); - write_zip::dirs_to_write(extracted_apk_files)?; - write_zip::write(extracted_apk_files, &zip_path)?; + let zip_path = build_dir.join(format!("{}_module.zip", package_name)); + zip_dirs_to_write(extracted_apk_files)?; + zip_write(extracted_apk_files, &zip_path)?; Ok(zip_path) } diff --git a/crossbundle/tools/src/commands/android/native/aab/mod.rs b/crossbundle/tools/src/commands/android/native/aab/mod.rs new file mode 100644 index 00000000..9bbaa333 --- /dev/null +++ b/crossbundle/tools/src/commands/android/native/aab/mod.rs @@ -0,0 +1,9 @@ +pub mod add_libs_into_aapt2; +pub mod gen_aab_from_modules; +pub mod gen_minimal_unsigned_aab; +pub mod gen_zip_modules; + +pub use add_libs_into_aapt2::*; +pub use gen_aab_from_modules::*; +pub use gen_minimal_unsigned_aab::*; +pub use gen_zip_modules::*; diff --git a/crossbundle/tools/src/commands/android/add_libs_into_apk.rs b/crossbundle/tools/src/commands/android/native/apk/add_libs_into_apk.rs similarity index 98% rename from crossbundle/tools/src/commands/android/add_libs_into_apk.rs rename to crossbundle/tools/src/commands/android/native/apk/add_libs_into_apk.rs index e91df8c1..3f7e14cb 100644 --- a/crossbundle/tools/src/commands/android/add_libs_into_apk.rs +++ b/crossbundle/tools/src/commands/android/native/apk/add_libs_into_apk.rs @@ -1,7 +1,6 @@ use crate::{ error::*, - tools::{AndroidNdk, AndroidSdk}, - types::{AndroidTarget, IntoRustTriple, Profile}, + types::{AndroidNdk, AndroidSdk, AndroidTarget, IntoRustTriple, Profile}, }; use std::{ fs::File, diff --git a/crossbundle/tools/src/commands/android/align_apk.rs b/crossbundle/tools/src/commands/android/native/apk/align_apk.rs similarity index 88% rename from crossbundle/tools/src/commands/android/align_apk.rs rename to crossbundle/tools/src/commands/android/native/apk/align_apk.rs index a52cbab0..fdd2caca 100644 --- a/crossbundle/tools/src/commands/android/align_apk.rs +++ b/crossbundle/tools/src/commands/android/native/apk/align_apk.rs @@ -1,5 +1,4 @@ -use crate::error::*; -use crate::tools::*; +use crate::{error::*, types::*}; use std::path::{Path, PathBuf}; /// Aligns APK on 4-byte memory boundary. @@ -7,10 +6,10 @@ use std::path::{Path, PathBuf}; pub fn align_apk( sdk: &AndroidSdk, unaligned_apk_path: &Path, - package: &str, + package_name: &str, build_dir: &Path, ) -> Result { - let unsigned_apk_path = build_dir.join(format!("{}.apk", package)); + let unsigned_apk_path = build_dir.join(format!("{}.apk", package_name)); let mut zipalign = sdk.build_tool(bin!("zipalign"), None)?; zipalign .arg("-f") diff --git a/crossbundle/tools/src/commands/android/generate/gen_unaligned_apk.rs b/crossbundle/tools/src/commands/android/native/apk/gen_unaligned_apk.rs similarity index 87% rename from crossbundle/tools/src/commands/android/generate/gen_unaligned_apk.rs rename to crossbundle/tools/src/commands/android/native/apk/gen_unaligned_apk.rs index cd099b5a..593dcdb1 100644 --- a/crossbundle/tools/src/commands/android/generate/gen_unaligned_apk.rs +++ b/crossbundle/tools/src/commands/android/native/apk/gen_unaligned_apk.rs @@ -1,7 +1,8 @@ -use crate::error::*; -use crate::tools::*; -use std::fs::create_dir_all; -use std::path::{Path, PathBuf}; +use crate::{error::*, types::*}; +use std::{ + fs::create_dir_all, + path::{Path, PathBuf}, +}; /// Generates unaligned APK with given `manifest_path`, `assets` and `res`. /// Uses `aapt` build tool @@ -12,13 +13,13 @@ pub fn gen_unaligned_apk( manifest_path: &Path, assets: &Option, res: &Option, - package_label: &str, + package_name: &str, target_sdk_version: u32, ) -> Result { if !build_dir.exists() { create_dir_all(&build_dir)?; } - let apk_path = build_dir.join(format!("{}-unaligned.apk", package_label)); + let apk_path = build_dir.join(format!("{}-unaligned.apk", package_name)); let mut aapt = sdk.build_tool(bin!("aapt"), Some(project_path))?; aapt.arg("package") .arg("-f") diff --git a/crossbundle/tools/src/commands/android/install_apk.rs b/crossbundle/tools/src/commands/android/native/apk/install_apk.rs similarity index 86% rename from crossbundle/tools/src/commands/android/install_apk.rs rename to crossbundle/tools/src/commands/android/native/apk/install_apk.rs index 067ae213..5c817cd4 100644 --- a/crossbundle/tools/src/commands/android/install_apk.rs +++ b/crossbundle/tools/src/commands/android/native/apk/install_apk.rs @@ -1,5 +1,4 @@ -use crate::error::*; -use crate::tools::AndroidSdk; +use crate::{error::*, types::AndroidSdk}; use std::path::Path; /// Installs given APK in emulator or connected device. diff --git a/crossbundle/tools/src/commands/android/native/apk/mod.rs b/crossbundle/tools/src/commands/android/native/apk/mod.rs new file mode 100644 index 00000000..b9e3f60b --- /dev/null +++ b/crossbundle/tools/src/commands/android/native/apk/mod.rs @@ -0,0 +1,11 @@ +pub mod add_libs_into_apk; +pub mod align_apk; +pub mod gen_unaligned_apk; +pub mod install_apk; +pub mod sign_apk; + +pub use add_libs_into_apk::*; +pub use align_apk::*; +pub use gen_unaligned_apk::*; +pub use install_apk::*; +pub use sign_apk::*; diff --git a/crossbundle/tools/src/commands/android/sign_apk.rs b/crossbundle/tools/src/commands/android/native/apk/sign_apk.rs similarity index 92% rename from crossbundle/tools/src/commands/android/sign_apk.rs rename to crossbundle/tools/src/commands/android/native/apk/sign_apk.rs index 135fd784..60b55c98 100644 --- a/crossbundle/tools/src/commands/android/sign_apk.rs +++ b/crossbundle/tools/src/commands/android/native/apk/sign_apk.rs @@ -1,5 +1,4 @@ -use crate::error::*; -use crate::tools::*; +use crate::{error::*, types::AndroidSdk}; use android_tools::java_tools::Key; use std::path::Path; diff --git a/crossbundle/tools/src/commands/android/native/mod.rs b/crossbundle/tools/src/commands/android/native/mod.rs new file mode 100644 index 00000000..2b9fea60 --- /dev/null +++ b/crossbundle/tools/src/commands/android/native/mod.rs @@ -0,0 +1,5 @@ +pub mod aab; +pub mod apk; + +pub use aab::*; +pub use apk::*; diff --git a/crossbundle/tools/src/commands/apple/gen_plist.rs b/crossbundle/tools/src/commands/apple/gen_plist.rs deleted file mode 100644 index bfa33e2f..00000000 --- a/crossbundle/tools/src/commands/apple/gen_plist.rs +++ /dev/null @@ -1,55 +0,0 @@ -use apple_bundle::prelude::*; - -/// Generates minimal [`InfoPlist`](apple_bundle::prelude::InfoPlist) with given changes. -pub fn gen_minimal_info_plist( - package_name: &str, - app_name: Option, - version_name: String, -) -> InfoPlist { - InfoPlist { - localization: Localization { - bundle_development_region: Some("en".to_owned()), - ..Default::default() - }, - launch: Launch { - bundle_executable: Some(package_name.to_owned()), - ..Default::default() - }, - identification: Identification { - bundle_identifier: format!("com.rust.{}", package_name), - ..Default::default() - }, - bundle_version: BundleVersion { - bundle_version: Some(version_name.clone()), - bundle_info_dictionary_version: Some(version_name.clone()), - bundle_short_version_string: Some(version_name), - ..Default::default() - }, - naming: Naming { - bundle_name: Some(app_name.unwrap_or_else(|| package_name.to_owned())), - ..Default::default() - }, - categorization: Categorization { - bundle_package_type: Some("APPL".to_owned()), - ..Default::default() - }, - launch_interface: LaunchInterface { - launch_storyboard_name: Some("LaunchScreen".to_owned()), - ..Default::default() - }, - styling: Styling { - requires_full_screen: Some(false), - ..Default::default() - }, - orientation: Orientation { - supported_interface_orientations: Some(vec![ - InterfaceOrientation::Portrait, - InterfaceOrientation::PortraitUpsideDown, - InterfaceOrientation::LandscapeLeft, - InterfaceOrientation::LandscapeRight, - ]), - ..Default::default() - }, - ..Default::default() - } -} diff --git a/crossbundle/tools/src/commands/apple/mod.rs b/crossbundle/tools/src/commands/apple/mod.rs index 71a5d36e..76ff759b 100644 --- a/crossbundle/tools/src/commands/apple/mod.rs +++ b/crossbundle/tools/src/commands/apple/mod.rs @@ -2,7 +2,6 @@ mod codesign; mod copy_profile; mod gen_app_folder; mod gen_ipa; -mod gen_plist; mod gen_xcent; mod launch_app; mod read_plist; @@ -14,7 +13,6 @@ pub use codesign::*; pub use copy_profile::*; pub use gen_app_folder::*; pub use gen_ipa::*; -pub use gen_plist::*; pub use gen_xcent::*; pub use launch_app::*; pub use read_plist::*; diff --git a/crossbundle/tools/src/commands/apple/rust_compile.rs b/crossbundle/tools/src/commands/apple/rust_compile.rs index 550f92fb..3b57229f 100644 --- a/crossbundle/tools/src/commands/apple/rust_compile.rs +++ b/crossbundle/tools/src/commands/apple/rust_compile.rs @@ -1,28 +1,50 @@ -use crate::commands::common::cargo_rustc_command; -use crate::error::*; -use crate::types::*; -use std::path::Path; +use crate::{error::*, types::*}; +use std::{path::Path, process::Command}; /// Compiles rust code for iOS. +/// +/// Initialises `cargo rustc` [`Command`] with given args and return it. +/// +/// [`Command`]: std::process::Command pub fn compile_rust_for_ios( target: Target, - build_target: AppleTarget, + build_target: IosTarget, project_path: &Path, profile: Profile, features: Vec, all_features: bool, no_default_features: bool, + crate_types: &[CrateType], ) -> Result<()> { - let cargo = cargo_rustc_command( - &target, - project_path, - &profile, - &features, - all_features, - no_default_features, - &build_target.into(), - &[], - ); + let mut cargo = Command::new("cargo"); + cargo.arg("rustc"); + match &target { + Target::Bin(name) => cargo.args(&["--bin", name]), + Target::Example(name) => cargo.args(&["--example", name]), + Target::Lib => cargo.arg("--lib"), + }; + cargo.current_dir(project_path); + if profile == Profile::Release { + cargo.arg("--release"); + }; + for feature in features.iter() { + cargo.args(&["--feature", feature]); + } + if all_features { + cargo.arg("--all-features"); + }; + if no_default_features { + cargo.arg("--no-default-features"); + }; + let triple = build_target.rust_triple(); + cargo.args(&["--target", triple]); + if !crate_types.is_empty() { + // Creates a comma-separated string + let crate_types: String = + itertools::Itertools::intersperse(crate_types.iter().map(|v| v.as_ref()), ",") + .collect(); + cargo.args(&["--", "--crate-type", &crate_types]); + }; cargo.output_err(true)?; Ok(()) } diff --git a/crossbundle/tools/src/commands/common/gen_minimal_project/consts.rs b/crossbundle/tools/src/commands/common/gen_minimal_project/consts.rs index 4e452846..d22f4963 100644 --- a/crossbundle/tools/src/commands/common/gen_minimal_project/consts.rs +++ b/crossbundle/tools/src/commands/common/gen_minimal_project/consts.rs @@ -22,6 +22,9 @@ edition = "2021" crossbow = { git = "https://github.com/dodorare/crossbow" } anyhow = "1.0" macroquad = "0.3.7" + +[package.metadata.android] +app_wrapper = "sokol" "#; pub const MINIMAL_MQ_GRADLE_CARGO_TOML_VALUE: &str = r#" @@ -37,6 +40,7 @@ anyhow = "1.0" macroquad = "0.3.7" [package.metadata.android] +app_wrapper = "sokol" target_sdk_version = 30 [[package.metadata.android.plugins_local_projects]] @@ -50,41 +54,29 @@ include = ":crossbow:lib" pub const CARGO_TOML_VALUE: &str = r#" [package.metadata.android] -app_name = "example" -target_sdk_version = 30 -version_code = 1 +app_name = "Example" +release_build_targets = ["aarch64-linux-android"] -build_targets = ["aarch64-linux-android"] +[package.metadata.android.manifest] +package = "com.crossbow.example" +[package.metadata.android.manifest.uses_sdk] +min_sdk_version = 19 +target_sdk_version = 30 -[[package.metadata.android.features]] +[[package.metadata.android.manifest.uses_feature]] name = "android.hardware.vulkan.level" required = true version = 1 -[[package.metadata.android.permissions]] +[[package.metadata.android.manifest.permission]] name = "android.permission.WRITE_EXTERNAL_STORAGE" -max_sdk_version = 30 -[[package.metadata.android.permissions_sdk_23]] +[[package.metadata.android.manifest.uses_permission_sdk_23]] name = "android.permission.INTERNET" -max_sdk_version = 30 - -[[package.metadata.android.queries.provider]] -authorities = "org.khronos.openxr.runtime_broker;org.khronos.openxr.system_runtime_broker" -name = "org.khronos.openxr" - -[[package.metadata.android.meta_data]] -name = "com.oculus.vr.focusaware" -value = "true" - -[[package.metadata.android.service]] -name = "UpdateService" -intent_filter = [] -meta_data = [] [package.metadata.apple] app_name = "Macroquad_3D" -build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] +release_build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] "#; pub const MQ_MAIN_RS_VALUE: &str = r#" diff --git a/crossbundle/tools/src/commands/common/gen_minimal_project/gen_min_project.rs b/crossbundle/tools/src/commands/common/gen_minimal_project/gen_min_project.rs index f4ce286a..105e151d 100644 --- a/crossbundle/tools/src/commands/common/gen_minimal_project/gen_min_project.rs +++ b/crossbundle/tools/src/commands/common/gen_minimal_project/gen_min_project.rs @@ -5,6 +5,8 @@ use std::{ io::Write, }; +// TODO: Fix this file logic. + /// Generates a new minimal project in given path. pub fn gen_minimal_project( out_dir: &std::path::Path, diff --git a/crossbundle/tools/src/commands/common/mod.rs b/crossbundle/tools/src/commands/common/mod.rs index 52189d3a..88d242ed 100644 --- a/crossbundle/tools/src/commands/common/mod.rs +++ b/crossbundle/tools/src/commands/common/mod.rs @@ -1,12 +1,11 @@ -///! Common commands used in all platforms. +//! Common commands used in all platforms. + mod create_project; mod find_cargo_manifest_path; mod gen_minimal_project; mod parse_manifest; -mod rust_compile; pub use create_project::*; pub use find_cargo_manifest_path::*; pub use gen_minimal_project::*; pub use parse_manifest::*; -pub use rust_compile::*; diff --git a/crossbundle/tools/src/commands/common/rust_compile.rs b/crossbundle/tools/src/commands/common/rust_compile.rs deleted file mode 100644 index 3de2520a..00000000 --- a/crossbundle/tools/src/commands/common/rust_compile.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::types::*; -use std::path::Path; -use std::process::Command; - -/// Initialises `cargo rustc` [`Command`] with given args and return it. -/// -/// [`Command`]: std::process::Command -pub fn cargo_rustc_command( - target: &Target, - project_path: &Path, - profile: &Profile, - features: &[String], - all_features: bool, - no_default_features: bool, - build_target: &BuildTarget, - crate_types: &[CrateType], -) -> Command { - let mut cargo = Command::new("cargo"); - cargo.arg("rustc"); - match &target { - Target::Bin(name) => cargo.args(&["--bin", name]), - Target::Example(name) => cargo.args(&["--example", name]), - Target::Lib => cargo.arg("--lib"), - }; - cargo.current_dir(project_path); - if profile == &Profile::Release { - cargo.arg("--release"); - }; - for feature in features.iter() { - cargo.args(&["--feature", feature]); - } - if all_features { - cargo.arg("--all-features"); - }; - if no_default_features { - cargo.arg("--no-default-features"); - }; - let triple = build_target.rust_triple(); - cargo.args(&["--target", triple]); - if !crate_types.is_empty() { - // Creates a comma-separated string - let crate_types: String = - itertools::Itertools::intersperse(crate_types.iter().map(|v| v.as_ref()), ",") - .collect(); - cargo.args(&["--", "--crate-type", &crate_types]); - }; - cargo -} diff --git a/crossbundle/tools/src/commands/mod.rs b/crossbundle/tools/src/commands/mod.rs index cbde4c1b..0125c9e1 100644 --- a/crossbundle/tools/src/commands/mod.rs +++ b/crossbundle/tools/src/commands/mod.rs @@ -1,4 +1,6 @@ +#[cfg(feature = "android")] pub mod android; +#[cfg(feature = "apple")] pub mod apple; mod common; diff --git a/crossbundle/tools/src/error.rs b/crossbundle/tools/src/error.rs index dff163f0..6759b01f 100644 --- a/crossbundle/tools/src/error.rs +++ b/crossbundle/tools/src/error.rs @@ -1,5 +1,6 @@ //! Contains `Error`, `AndroidError`, `AppleError` types used by `crossbundle-tools`. +#[cfg(feature = "apple")] use apple_bundle::plist; use displaydoc::Display; use std::path::PathBuf; @@ -10,10 +11,9 @@ use thiserror::Error; pub type Result = std::result::Result; /// Android specific error type. +#[cfg(feature = "android")] #[derive(Display, Debug, Error)] pub enum AndroidError { - /// Android SDK is not found - AndroidSdkNotFound, /// Android NDK is not found AndroidNdkNotFound, /// Gradle Dependency project dir not found: {0} @@ -26,8 +26,6 @@ pub enum AndroidError { BuildToolsNotFound, /// Android SDK has no platforms installed NoPlatformsFound, - /// Failed to create directory - DirectoryWasNotCreated, /// Platform {0} is not installed PlatformNotFound(u32), /// Target is not supported @@ -38,6 +36,10 @@ pub enum AndroidError { InvalidSemver, /// Unsupported or invalid target: {0} InvalidBuildTarget(String), + /// Unsupported or invalid app wrapper: {0} + InvalidAppWrapper(String), + /// Unsupported or invalid build strategy: {0} + InvalidBuildStrategy(String), /// Failed to find AndroidManifest.xml in path: {0} FailedToFindAndroidManifest(String), /// Unable to find NDK file @@ -49,6 +51,7 @@ pub enum AndroidError { } /// Apple specific error type. +#[cfg(feature = "apple")] #[derive(Display, Debug, Error)] pub enum AppleError { /// Code signing profile not found @@ -67,6 +70,10 @@ pub enum AppleError { TargetNotFound, /// Resources dir does not exists ResourcesNotFound, + /// Unsupported or invalid build strategy: {0} + InvalidBuildStrategy(String), + /// Unsupported or invalid target: {0} + InvalidBuildTarget(String), /// Assets dir does not exists AssetsNotFound, /// Failed to find Info.plist in path: {0} @@ -92,15 +99,6 @@ pub enum Error { FailedToFindManifest(PathBuf), /// Invalid profile: {0} InvalidProfile(String), - /// Invalid interface orientation: {0:?} - InvalidInterfaceOrientation(String), - /// Home dir not found - HomeDirNotFound, - /// Failed to create jar file in specified path `{path}` cause of `{cause}` - JarFileCreationFailed { - path: PathBuf, - cause: std::io::Error, - }, /// GNU toolchain binary `{gnu_bin}` nor LLVM toolchain binary `{llvm_bin}` found in /// `{toolchain_path:?}` ToolchainBinaryNotFound { @@ -122,8 +120,10 @@ pub enum Error { /// Zip error: {0:?} Zip(#[from] zip::result::ZipError), /// Android error: {0:?} + #[cfg(feature = "android")] Android(#[from] AndroidError), /// Apple error: {0:?} + #[cfg(feature = "apple")] Apple(#[from] AppleError), /// Anyhow error: {0:?} AnyhowError(#[from] anyhow::Error), @@ -159,18 +159,21 @@ impl CommandExt for Command { } } +#[cfg(feature = "apple")] impl From for Error { fn from(error: plist::Error) -> Self { AppleError::from(error).into() } } +#[cfg(feature = "apple")] impl From for Error { fn from(error: simctl::Error) -> Self { AppleError::Simctl(error).into() } } +#[cfg(feature = "android")] impl From for Error { fn from(error: android_tools::error::Error) -> Self { AndroidError::AndroidTools(error).into() diff --git a/crossbundle/tools/src/lib.rs b/crossbundle/tools/src/lib.rs index 0530e758..9a24081b 100644 --- a/crossbundle/tools/src/lib.rs +++ b/crossbundle/tools/src/lib.rs @@ -28,8 +28,4 @@ pub const EXECUTABLE_SUFFIX_BAT: &str = ""; pub mod commands; pub mod error; -pub mod tools; pub mod types; -pub mod utils; - -pub use simctl; diff --git a/crossbundle/tools/src/tools/mod.rs b/crossbundle/tools/src/tools/mod.rs deleted file mode 100644 index 718fa108..00000000 --- a/crossbundle/tools/src/tools/mod.rs +++ /dev/null @@ -1,29 +0,0 @@ -mod android_ndk; -mod android_sdk; - -pub use android_ndk::*; -pub use android_sdk::*; -pub use android_tools::aapt2::*; -pub use android_tools::bundletool::*; -pub use android_tools::error::Error as AndroidToolsError; - -#[derive(Clone, PartialEq, Eq, Hash)] -pub struct CheckInfo { - pub dependency_name: String, - pub check_name: String, - pub passed: bool, -} - -impl CheckInfo { - fn invert_passed(mut self) -> CheckInfo { - self.passed = !self.passed; - self - } -} - -pub trait IntoCheckInfo: Sized { - fn check_passed(self) -> CheckInfo; - fn check_failed(self) -> CheckInfo { - self.check_passed().invert_passed() - } -} diff --git a/crossbundle/tools/src/tools/android_ndk.rs b/crossbundle/tools/src/types/android/android_ndk.rs similarity index 95% rename from crossbundle/tools/src/tools/android_ndk.rs rename to crossbundle/tools/src/types/android/android_ndk.rs index 1970e37c..849a71b2 100644 --- a/crossbundle/tools/src/tools/android_ndk.rs +++ b/crossbundle/tools/src/types/android/android_ndk.rs @@ -13,7 +13,7 @@ pub struct AndroidNdk { impl AndroidNdk { /// Using environment variables - pub fn from_env(sdk_path: Option<&Path>) -> Result { + pub fn from_env(sdk_path: &Path) -> Result { let ndk_path = { let ndk_path = std::env::var("ANDROID_NDK_ROOT") .ok() @@ -23,18 +23,10 @@ impl AndroidNdk { // Default ndk installation path if let Some(ndk_path) = ndk_path { PathBuf::from(ndk_path) - } else if ndk_path.is_none() - && sdk_path.is_some() - && sdk_path.as_ref().unwrap().join("ndk-bundle").exists() - { - sdk_path.unwrap().join("ndk-bundle") + } else if ndk_path.is_none() && sdk_path.join("ndk-bundle").exists() { + sdk_path.join("ndk-bundle") } else { - let ndk_path = if let Some(sdk_path) = sdk_path { - sdk_path.to_owned() - } else { - android_tools::sdk_install_path()? - } - .join("ndk"); + let ndk_path = sdk_path.join("ndk"); let ndk_ver = std::fs::read_dir(&ndk_path) .map_err(|_| Error::PathNotFound(ndk_path.clone()))? .filter_map(|path| path.ok()) diff --git a/crossbundle/tools/src/tools/android_sdk.rs b/crossbundle/tools/src/types/android/android_sdk.rs similarity index 96% rename from crossbundle/tools/src/tools/android_sdk.rs rename to crossbundle/tools/src/types/android/android_sdk.rs index f3927ae4..0521202d 100644 --- a/crossbundle/tools/src/tools/android_sdk.rs +++ b/crossbundle/tools/src/types/android/android_sdk.rs @@ -1,4 +1,4 @@ -use crate::{error::*, tools::Aapt2}; +use crate::{error::*, types::Aapt2}; use std::path::{Path, PathBuf}; use std::process::Command as ProcessCommand; @@ -117,13 +117,13 @@ impl AndroidSdk { /// Default platforms pub fn default_platform(&self) -> u32 { - // Currently, Android SDK 33 and 32 not supported as they + // Currently, Android SDK 33, 32 and 31 not supported as they // miss -32-clang<.cmd> linker. Try to use // older versions of android SDK. self.platforms() .iter() .cloned() - .filter(|x| x.ne(&33u32) && x.ne(&32u32)) + .filter(|x| x.ne(&33u32) && x.ne(&32u32) && x.ne(&31u32)) .max() .unwrap() } diff --git a/crossbundle/tools/src/types/android/app_wrapper.rs b/crossbundle/tools/src/types/android/app_wrapper.rs new file mode 100644 index 00000000..af671d73 --- /dev/null +++ b/crossbundle/tools/src/types/android/app_wrapper.rs @@ -0,0 +1,24 @@ +use crate::error::AndroidError; +use serde::{Deserialize, Serialize}; + +/// Stands for what application wrapper to use on build. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)] +pub enum AppWrapper { + #[default] + #[serde(rename = "ndk-glue")] + NdkGlue, + #[serde(rename = "sokol")] + Sokol, +} + +impl std::str::FromStr for AppWrapper { + type Err = AndroidError; + + fn from_str(s: &str) -> std::result::Result { + match s { + "ndk-glue" => Ok(Self::NdkGlue), + "sokol" => Ok(Self::Sokol), + _ => Err(AndroidError::InvalidAppWrapper(s.to_owned())), + } + } +} diff --git a/crossbundle/tools/src/types/android/build_target.rs b/crossbundle/tools/src/types/android/build_target.rs new file mode 100644 index 00000000..6f2ac8e8 --- /dev/null +++ b/crossbundle/tools/src/types/android/build_target.rs @@ -0,0 +1,94 @@ +use crate::{ + error::{AndroidError, Result}, + types::IntoRustTriple, +}; +use serde::{Deserialize, Serialize}; + +/// Android Target. +/// +/// More details: https://doc.rust-lang.org/nightly/rustc/platform-support.html +#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)] +pub enum AndroidTarget { + #[serde(rename = "armv7-linux-androideabi")] + Armv7, + #[serde(rename = "aarch64-linux-android")] + Aarch64, + #[serde(rename = "i686-linux-android")] + I686, + #[serde(rename = "x86_64-linux-android")] + X8664, +} + +impl AndroidTarget { + /// Identifier used in the NDK to refer to the ABI. + pub fn android_abi(self) -> &'static str { + match self { + Self::Armv7 => "armeabi-v7a", + Self::Aarch64 => "arm64-v8a", + Self::I686 => "x86", + Self::X8664 => "x86_64", + } + } + + // Returns the triple NDK provided LLVM. + pub fn ndk_llvm_triple(self) -> &'static str { + match self { + Self::Armv7 => "armv7a-linux-androideabi", + Self::Aarch64 => "aarch64-linux-android", + Self::I686 => "i686-linux-android", + Self::X8664 => "x86_64-linux-android", + } + } + + /// Returns the triple used by the non-LLVM parts of the NDK. + pub fn ndk_triple(self) -> &'static str { + match self { + Self::Armv7 => "arm-linux-androideabi", + Self::Aarch64 => "aarch64-linux-android", + Self::I686 => "i686-linux-android", + Self::X8664 => "x86_64-linux-android", + } + } + + /// Returns `AndroidTarget` for abi. + pub fn from_android_abi(abi: &str) -> Result { + match abi { + "armeabi-v7a" => Ok(Self::Armv7), + "arm64-v8a" => Ok(Self::Aarch64), + "x86" => Ok(Self::I686), + "x86_64" => Ok(Self::X8664), + _ => Err(AndroidError::UnsupportedTarget.into()), + } + } +} + +impl Default for AndroidTarget { + fn default() -> Self { + Self::Aarch64 + } +} + +impl std::str::FromStr for AndroidTarget { + type Err = AndroidError; + + fn from_str(s: &str) -> std::result::Result { + match s { + "armv7-linux-androideabi" => Ok(Self::Armv7), + "aarch64-linux-android" => Ok(Self::Aarch64), + "i686-linux-android" => Ok(Self::I686), + "x86_64-linux-android" => Ok(Self::X8664), + _ => Err(AndroidError::InvalidBuildTarget(s.to_owned())), + } + } +} + +impl IntoRustTriple for AndroidTarget { + fn rust_triple(&self) -> &'static str { + match self { + Self::Armv7 => "armv7-linux-androideabi", + Self::Aarch64 => "aarch64-linux-android", + Self::I686 => "i686-linux-android", + Self::X8664 => "x86_64-linux-android", + } + } +} diff --git a/crossbundle/tools/src/types/android/manifest.rs b/crossbundle/tools/src/types/android/manifest.rs new file mode 100644 index 00000000..93d32ebb --- /dev/null +++ b/crossbundle/tools/src/types/android/manifest.rs @@ -0,0 +1,83 @@ +pub use android_manifest; + +use super::AndroidStrategy; +use android_manifest::*; + +/// Updates [`AndroidManifest`](android_manifest::AndroidManifest) with default values. +pub fn update_android_manifest_with_default( + manifest: &mut AndroidManifest, + app_name: Option, + package_name: &str, + strategy: super::AndroidStrategy, +) { + if manifest.package.is_empty() { + manifest.package = format!("com.crossbow.{}", package_name.replace('-', "_")); + } + if manifest.version_name.is_none() { + manifest.version_name = Some("0.1.0".to_owned()); + } + if manifest.version_code.is_none() { + manifest.version_code = Some(1_u32); + } + if manifest.uses_sdk.is_none() { + manifest.uses_sdk = Some(UsesSdk { + min_sdk_version: Some(19), + target_sdk_version: Some(30), + max_sdk_version: None, + }); + } + if manifest.application.has_code.is_none() { + manifest.application.has_code = Some(strategy == AndroidStrategy::GradleApk); + } + if manifest.application.label.is_none() { + manifest.application.label = Some(StringResourceOrString::string( + &app_name.unwrap_or_else(|| "Crossbow".to_owned()), + )); + } + if manifest.application.theme.is_none() { + manifest.application.theme = Some(Resource::new_with_package( + "Theme.DeviceDefault.NoActionBar.Fullscreen", + Some("android".to_string()), + )); + } + if manifest.application.activity.is_empty() { + manifest.application.activity = vec![Activity::default()]; + } + if manifest.application.activity.len() == 1 { + let mut activity = manifest.application.activity.get_mut(0).unwrap(); + if activity.name.is_empty() { + activity.name = match strategy == AndroidStrategy::GradleApk { + true => "com.crossbow.game.CrossbowApp".to_string(), + false => "android.app.NativeActivity".to_string(), + }; + } + if activity.resizeable_activity.is_none() { + activity.resizeable_activity = Some(true); + } + if !activity + .meta_data + .iter() + .any(|m| m.name == Some("android.app.lib_name".to_string())) + { + activity.meta_data.push(MetaData { + name: Some("android.app.lib_name".to_string()), + value: Some(match strategy == AndroidStrategy::GradleApk { + true => "crossbow_android".to_string(), + false => package_name.replace('-', "_"), + }), + ..Default::default() + }); + } + if activity.intent_filter.is_empty() { + activity.intent_filter = vec![IntentFilter { + action: vec![Action { + name: Some("android.intent.action.MAIN".to_string()), + }], + category: vec![Category { + name: Some("android.intent.category.LAUNCHER".to_string()), + }], + ..Default::default() + }]; + } + } +} diff --git a/crossbundle/tools/src/types/android/mod.rs b/crossbundle/tools/src/types/android/mod.rs new file mode 100644 index 00000000..8c56ad50 --- /dev/null +++ b/crossbundle/tools/src/types/android/mod.rs @@ -0,0 +1,19 @@ +mod android_ndk; +mod android_sdk; +mod app_wrapper; +mod build_target; +mod manifest; +mod strategies; +mod version_code; + +pub use android_ndk::*; +pub use android_sdk::*; +pub use app_wrapper::*; +pub use build_target::*; +pub use manifest::*; +pub use strategies::*; +pub use version_code::*; + +pub use android_tools::aapt2::*; +pub use android_tools::bundletool::*; +pub use android_tools::error::Error as AndroidToolsError; diff --git a/crossbundle/tools/src/types/android/strategies.rs b/crossbundle/tools/src/types/android/strategies.rs new file mode 100644 index 00000000..da004c1a --- /dev/null +++ b/crossbundle/tools/src/types/android/strategies.rs @@ -0,0 +1,32 @@ +use crate::error::AndroidError; +use serde::{Deserialize, Serialize}; + +/// Supported strategies for building Android application. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)] +pub enum AndroidStrategy { + /// Generate .apk with Gradle. Default strategy. + #[default] + #[serde(rename = "gradle-apk")] + GradleApk, + /// Generate native .aab without Gradle. This strategy currently doesn't support + /// Crossbow plugins. + #[serde(rename = "native-apk")] + NativeApk, + /// Generate native .apk without Gradle. This strategy currently doesn't support + /// Crossbow plugins. + #[serde(rename = "native-aab")] + NativeAab, +} + +impl std::str::FromStr for AndroidStrategy { + type Err = AndroidError; + + fn from_str(s: &str) -> std::result::Result { + match s { + "gradle-apk" => Ok(Self::GradleApk), + "native-apk" => Ok(Self::NativeApk), + "native-aab" => Ok(Self::NativeAab), + _ => Err(AndroidError::InvalidBuildStrategy(s.to_owned())), + } + } +} diff --git a/crossbundle/tools/src/types/version_code.rs b/crossbundle/tools/src/types/android/version_code.rs similarity index 100% rename from crossbundle/tools/src/types/version_code.rs rename to crossbundle/tools/src/types/android/version_code.rs diff --git a/crossbundle/tools/src/types/apple/build_target.rs b/crossbundle/tools/src/types/apple/build_target.rs new file mode 100644 index 00000000..84517595 --- /dev/null +++ b/crossbundle/tools/src/types/apple/build_target.rs @@ -0,0 +1,50 @@ +use crate::{error::AppleError, types::IntoRustTriple}; +use serde::{Deserialize, Serialize}; + +/// iOS Target. +/// +/// More details: https://doc.rust-lang.org/nightly/rustc/platform-support.html +#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)] +pub enum IosTarget { + #[serde(rename = "x86_64-apple-ios")] + X86_64, + #[serde(rename = "i386-apple-ios")] + I386, + #[serde(rename = "aarch64-apple-ios")] + Aarch64, + #[serde(rename = "aarch64-apple-ios-sim")] + Aarch64Sim, + #[serde(rename = "armv7-apple-ios")] + Armv7, + #[serde(rename = "armv7s-apple-ios")] + Armv7s, +} + +impl IntoRustTriple for IosTarget { + fn rust_triple(&self) -> &'static str { + match self { + Self::X86_64 => "x86_64-apple-ios", + Self::I386 => "i386-apple-ios", + Self::Aarch64 => "aarch64-apple-ios", + Self::Aarch64Sim => "aarch64-apple-ios-sim", + Self::Armv7 => "armv7-apple-ios", + Self::Armv7s => "armv7s-apple-ios", + } + } +} + +impl std::str::FromStr for IosTarget { + type Err = AppleError; + + fn from_str(s: &str) -> std::result::Result { + match s { + "x86_64-apple-ios" => Ok(Self::X86_64), + "i386-apple-ios" => Ok(Self::I386), + "aarch64-apple-ios" => Ok(Self::Aarch64), + "aarch64-apple-ios-sim" => Ok(Self::Aarch64Sim), + "armv7-apple-ios" => Ok(Self::Armv7), + "armv7s-apple-ios" => Ok(Self::Armv7s), + _ => Err(AppleError::InvalidBuildTarget(s.to_owned())), + } + } +} diff --git a/crossbundle/tools/src/types/apple/info_plist.rs b/crossbundle/tools/src/types/apple/info_plist.rs new file mode 100644 index 00000000..0355dac0 --- /dev/null +++ b/crossbundle/tools/src/types/apple/info_plist.rs @@ -0,0 +1,43 @@ +pub use apple_bundle; + +use apple_bundle::prelude::*; + +/// Updates [`InfoPlist`](InfoPlist) with default values. +pub fn update_info_plist_with_default( + props: &mut InfoPlist, + package_name: &str, + app_name: Option, +) { + if props.launch.bundle_executable.is_none() { + props.launch.bundle_executable = Some(package_name.to_owned()); + } + if props.localization.bundle_development_region.is_none() { + props.localization.bundle_development_region = Some("en".to_owned()); + } + if props.identification.bundle_identifier.is_empty() { + props.identification.bundle_identifier = + format!("com.crossbow.{}", package_name.replace('-', "_")); + } + if props.bundle_version.bundle_version.is_none() { + props.bundle_version.bundle_version = Some("0.1.0".to_owned()); + } + if props + .bundle_version + .bundle_info_dictionary_version + .is_none() + { + props.bundle_version.bundle_info_dictionary_version = Some("0.1.0".to_owned()); + } + if props.bundle_version.bundle_short_version_string.is_none() { + props.bundle_version.bundle_short_version_string = Some("0.1.0".to_owned()); + } + if props.naming.bundle_name.is_none() { + props.naming.bundle_name = Some(app_name.unwrap_or_else(|| package_name.to_owned())); + } + if props.categorization.bundle_package_type.is_none() { + props.categorization.bundle_package_type = Some("APPL".to_owned()); + } + if props.launch_interface.launch_storyboard_name.is_none() { + props.launch_interface.launch_storyboard_name = Some("LaunchScreen".to_owned()); + } +} diff --git a/crossbundle/tools/src/types/apple/mod.rs b/crossbundle/tools/src/types/apple/mod.rs new file mode 100644 index 00000000..e8f97712 --- /dev/null +++ b/crossbundle/tools/src/types/apple/mod.rs @@ -0,0 +1,9 @@ +mod build_target; +mod info_plist; +mod strategies; + +pub use build_target::*; +pub use info_plist::*; +pub use strategies::*; + +pub use simctl; diff --git a/crossbundle/tools/src/types/apple/strategies.rs b/crossbundle/tools/src/types/apple/strategies.rs new file mode 100644 index 00000000..ca878c4d --- /dev/null +++ b/crossbundle/tools/src/types/apple/strategies.rs @@ -0,0 +1,22 @@ +use crate::error::AppleError; +use serde::{Deserialize, Serialize}; + +/// Supported strategies for building application for Apple devices. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)] +pub enum IosStrategy { + #[default] + /// Generate .app and .ipa without XCode. Default strategy. + #[serde(rename = "native-ipa")] + NativeIpa, +} + +impl std::str::FromStr for IosStrategy { + type Err = AppleError; + + fn from_str(s: &str) -> std::result::Result { + match s { + "native-ipa" => Ok(Self::NativeIpa), + _ => Err(AppleError::InvalidBuildStrategy(s.to_owned())), + } + } +} diff --git a/crossbundle/tools/src/types/application_wrapper.rs b/crossbundle/tools/src/types/application_wrapper.rs deleted file mode 100644 index dd7e4c6e..00000000 --- a/crossbundle/tools/src/types/application_wrapper.rs +++ /dev/null @@ -1,6 +0,0 @@ -/// Stands for what application wrapper to use on build. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ApplicationWrapper { - NdkGlue, - Sokol, -} diff --git a/crossbundle/tools/src/types/build_target.rs b/crossbundle/tools/src/types/build_target.rs deleted file mode 100644 index 270f362a..00000000 --- a/crossbundle/tools/src/types/build_target.rs +++ /dev/null @@ -1,214 +0,0 @@ -use crate::error::{AndroidError, Result}; -use serde::{Deserialize, Serialize}; - -pub trait IntoRustTriple { - /// Returns the triple used by the rust build tools - fn rust_triple(&self) -> &'static str; -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)] -pub enum AndroidTarget { - #[serde(rename = "armv7-linux-androideabi")] - Armv7LinuxAndroideabi, - #[serde(rename = "aarch64-linux-android")] - Aarch64LinuxAndroid, - #[serde(rename = "i686-linux-android")] - I686LinuxAndroid, - #[serde(rename = "x86_64-linux-android")] - X8664LinuxAndroid, -} - -impl AndroidTarget { - /// Identifier used in the NDK to refer to the ABI - pub fn android_abi(self) -> &'static str { - match self { - Self::Armv7LinuxAndroideabi => "armeabi-v7a", - Self::Aarch64LinuxAndroid => "arm64-v8a", - Self::I686LinuxAndroid => "x86", - Self::X8664LinuxAndroid => "x86_64", - } - } - - // Returns the triple NDK provided LLVM - pub fn ndk_llvm_triple(self) -> &'static str { - match self { - Self::Armv7LinuxAndroideabi => "armv7a-linux-androideabi", - Self::Aarch64LinuxAndroid => "aarch64-linux-android", - Self::I686LinuxAndroid => "i686-linux-android", - Self::X8664LinuxAndroid => "x86_64-linux-android", - } - } - - /// Returns the triple used by the non-LLVM parts of the NDK - pub fn ndk_triple(self) -> &'static str { - match self { - Self::Armv7LinuxAndroideabi => "arm-linux-androideabi", - Self::Aarch64LinuxAndroid => "aarch64-linux-android", - Self::I686LinuxAndroid => "i686-linux-android", - Self::X8664LinuxAndroid => "x86_64-linux-android", - } - } - - /// Returns `AndroidTarget` for abi. - pub fn from_android_abi(abi: &str) -> Result { - match abi { - "armeabi-v7a" => Ok(Self::Armv7LinuxAndroideabi), - "arm64-v8a" => Ok(Self::Aarch64LinuxAndroid), - "x86" => Ok(Self::I686LinuxAndroid), - "x86_64" => Ok(Self::X8664LinuxAndroid), - _ => Err(AndroidError::UnsupportedTarget.into()), - } - } -} - -impl Default for AndroidTarget { - fn default() -> Self { - Self::Aarch64LinuxAndroid - } -} - -impl std::str::FromStr for AndroidTarget { - type Err = AndroidError; - - fn from_str(s: &str) -> std::result::Result { - match s { - "armv7-linux-androideabi" => Ok(Self::Armv7LinuxAndroideabi), - "aarch64-linux-android" => Ok(Self::Aarch64LinuxAndroid), - "i686-linux-android" => Ok(Self::I686LinuxAndroid), - "x86_64-linux-android" => Ok(Self::X8664LinuxAndroid), - _ => Err(AndroidError::InvalidBuildTarget(s.to_owned())), - } - } -} - -impl IntoRustTriple for AndroidTarget { - fn rust_triple(&self) -> &'static str { - match self { - Self::Armv7LinuxAndroideabi => "armv7-linux-androideabi", - Self::Aarch64LinuxAndroid => "aarch64-linux-android", - Self::I686LinuxAndroid => "i686-linux-android", - Self::X8664LinuxAndroid => "x86_64-linux-android", - } - } -} - -/// Apple Target architectures. -/// List of Apple processors: https://en.wikipedia.org/wiki/Apple-designed_processors#List_of_Apple_processors. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)] -pub enum AppleTarget { - #[serde(rename = "x86_64-apple-ios")] - X86_64AppleIos, - #[serde(rename = "i386-apple-ios")] - I386AppleIos, - #[serde(rename = "aarch64-apple-ios")] - Aarch64AppleIos, - #[serde(rename = "aarch64-apple-ios-sim")] - Aarch64AppleIosSim, - #[serde(rename = "armv7-apple-ios")] - Armv7AppleIos, - #[serde(rename = "armv7s-apple-ios")] - Armv7sAppleIos, -} - -impl IntoRustTriple for AppleTarget { - fn rust_triple(&self) -> &'static str { - match self { - Self::X86_64AppleIos => "x86_64-apple-ios", - Self::I386AppleIos => "i386-apple-ios", - Self::Aarch64AppleIos => "aarch64-apple-ios", - Self::Aarch64AppleIosSim => "aarch64-apple-ios-sim", - Self::Armv7AppleIos => "armv7-apple-ios", - Self::Armv7sAppleIos => "armv7s-apple-ios", - } - } -} - -impl std::str::FromStr for AppleTarget { - type Err = AndroidError; - - fn from_str(s: &str) -> std::result::Result { - match s { - "x86_64-apple-ios" => Ok(Self::X86_64AppleIos), - "i386-apple-ios" => Ok(Self::I386AppleIos), - "aarch64-apple-ios" => Ok(Self::Aarch64AppleIos), - "aarch64-apple-ios-sim" => Ok(Self::Aarch64AppleIosSim), - "armv7-apple-ios" => Ok(Self::Armv7AppleIos), - "armv7s-apple-ios" => Ok(Self::Armv7sAppleIos), - _ => Err(AndroidError::InvalidBuildTarget(s.to_owned())), - } - } -} - -#[derive(Debug, Clone)] -pub enum BuildTarget { - Android(AndroidTarget), - Apple(AppleTarget), -} - -impl IntoRustTriple for BuildTarget { - fn rust_triple(&self) -> &'static str { - match self { - Self::Android(target) => target.rust_triple(), - Self::Apple(target) => target.rust_triple(), - } - } -} - -impl From for BuildTarget { - fn from(target: AppleTarget) -> Self { - Self::Apple(target) - } -} - -impl From for BuildTarget { - fn from(target: AndroidTarget) -> Self { - Self::Android(target) - } -} - -#[derive(Debug, Clone)] -pub enum BuildTargets { - Android(Vec), - Apple(Vec), -} - -impl From> for BuildTargets { - fn from(targets: Vec) -> Self { - Self::Android(targets) - } -} - -impl From> for BuildTargets { - fn from(targets: Vec) -> Self { - Self::Apple(targets) - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum CrateType { - /// A runnable executable. - Bin, - /// A Rust library. - Lib, - /// A dynamic Rust library. - Dylib, - /// A static system library. - Staticlib, - /// A dynamic system library. - Cdylib, - /// A "Rust library" file. - Rlib, -} - -impl AsRef for CrateType { - fn as_ref(&self) -> &str { - match self { - Self::Bin => "bin", - Self::Lib => "lib", - Self::Dylib => "dylib", - Self::Staticlib => "staticlib", - Self::Cdylib => "cdylib", - Self::Rlib => "rlib", - } - } -} diff --git a/crossbundle/tools/src/utils/config.rs b/crossbundle/tools/src/types/common/config.rs similarity index 100% rename from crossbundle/tools/src/utils/config.rs rename to crossbundle/tools/src/types/common/config.rs diff --git a/crossbundle/tools/src/types/common/crate_type.rs b/crossbundle/tools/src/types/common/crate_type.rs new file mode 100644 index 00000000..06818baf --- /dev/null +++ b/crossbundle/tools/src/types/common/crate_type.rs @@ -0,0 +1,28 @@ +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum CrateType { + /// A runnable executable. + Bin, + /// A Rust library. + Lib, + /// A dynamic Rust library. + Dylib, + /// A static system library. + Staticlib, + /// A dynamic system library. + Cdylib, + /// A "Rust library" file. + Rlib, +} + +impl AsRef for CrateType { + fn as_ref(&self) -> &str { + match self { + Self::Bin => "bin", + Self::Lib => "lib", + Self::Dylib => "dylib", + Self::Staticlib => "staticlib", + Self::Cdylib => "cdylib", + Self::Rlib => "rlib", + } + } +} diff --git a/crossbundle/tools/src/types/common/mod.rs b/crossbundle/tools/src/types/common/mod.rs new file mode 100644 index 00000000..d2581ab6 --- /dev/null +++ b/crossbundle/tools/src/types/common/mod.rs @@ -0,0 +1,11 @@ +mod config; +mod crate_type; +mod profile; +mod shell; +mod target; + +pub use config::*; +pub use crate_type::*; +pub use profile::*; +pub use shell::*; +pub use target::*; diff --git a/crossbundle/tools/src/types/profile.rs b/crossbundle/tools/src/types/common/profile.rs similarity index 100% rename from crossbundle/tools/src/types/profile.rs rename to crossbundle/tools/src/types/common/profile.rs diff --git a/crossbundle/tools/src/utils/shell.rs b/crossbundle/tools/src/types/common/shell.rs similarity index 98% rename from crossbundle/tools/src/utils/shell.rs rename to crossbundle/tools/src/types/common/shell.rs index 50c2de06..6758447a 100644 --- a/crossbundle/tools/src/utils/shell.rs +++ b/crossbundle/tools/src/types/common/shell.rs @@ -340,11 +340,6 @@ impl Shell { self.err().write_all(message)?; Ok(()) } - - pub fn print_json(&mut self, obj: &T) { - let encoded = serde_json::to_string(&obj).unwrap(); - drop(writeln!(self.out(), "{}", encoded)); - } } impl Default for Shell { diff --git a/crossbundle/tools/src/types/common/target.rs b/crossbundle/tools/src/types/common/target.rs new file mode 100644 index 00000000..a1be89e3 --- /dev/null +++ b/crossbundle/tools/src/types/common/target.rs @@ -0,0 +1,11 @@ +#[derive(Debug, Clone)] +pub enum Target { + Bin(String), + Example(String), + Lib, +} + +pub trait IntoRustTriple { + /// Returns the triple used by the rust build tools. + fn rust_triple(&self) -> &'static str; +} diff --git a/crossbundle/tools/src/types/mod.rs b/crossbundle/tools/src/types/mod.rs index e6712e5a..6ce45a38 100644 --- a/crossbundle/tools/src/types/mod.rs +++ b/crossbundle/tools/src/types/mod.rs @@ -1,14 +1,11 @@ -mod application_wrapper; -mod build_target; -mod profile; -mod target; -mod version_code; +#[cfg(feature = "android")] +mod android; +#[cfg(feature = "apple")] +mod apple; +mod common; -pub use application_wrapper::*; -pub use build_target::*; -pub use profile::*; -pub use target::*; -pub use version_code::*; - -pub use android_manifest; -pub use apple_bundle; +#[cfg(feature = "android")] +pub use android::*; +#[cfg(feature = "apple")] +pub use apple::*; +pub use common::*; diff --git a/crossbundle/tools/src/types/target.rs b/crossbundle/tools/src/types/target.rs deleted file mode 100644 index ffe39dcc..00000000 --- a/crossbundle/tools/src/types/target.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[derive(Debug, Clone)] -pub enum Target { - Bin(String), - Example(String), - Lib, -} diff --git a/crossbundle/tools/src/utils/mod.rs b/crossbundle/tools/src/utils/mod.rs deleted file mode 100644 index 66d27cb2..00000000 --- a/crossbundle/tools/src/utils/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod config; -mod shell; - -pub use config::*; -pub use shell::*; diff --git a/crossbundle/tools/tests/aapt2.rs b/crossbundle/tools/tests/aapt2.rs index 132865c6..15486956 100644 --- a/crossbundle/tools/tests/aapt2.rs +++ b/crossbundle/tools/tests/aapt2.rs @@ -1,7 +1,6 @@ -use crossbundle_tools::{ - commands::android::{self, save_android_manifest}, - tools::AndroidSdk, -}; +#![cfg(feature = "android")] + +use crossbundle_tools::{commands::android::*, types::*}; #[test] fn test_aapt2_compile() { @@ -14,8 +13,7 @@ fn test_aapt2_compile() { let user_dirs = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); let dir = user_dirs.parent().unwrap().parent().unwrap().to_path_buf(); let res_path = dir - .join("examples") - .join("bevy-2d") + .join("assets") .join("res") .join("android") .join("mipmap-hdpi"); @@ -43,14 +41,10 @@ fn test_aapt2_link() { // Specifies path to needed resources let sdk = AndroidSdk::from_env().unwrap(); - let version_code = 1_u32; - let version_name = "1"; - let package_name = "example"; let user_dirs = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); let dir = user_dirs.parent().unwrap().parent().unwrap().to_path_buf(); let res_path = dir - .join("examples") - .join("bevy-2d") + .join("assets") .join("res") .join("android") .join("mipmap-hdpi"); @@ -65,10 +59,15 @@ fn test_aapt2_link() { assert!(compiled_res.exists()); // Generates minimal android manifest - let android_manifest = android::generate::gen_manifest::gen_min_android_manifest( - version_name, - version_code, - package_name, + let mut android_manifest = android_manifest::AndroidManifest { + package: "com.crossbow.example".to_owned(), + ..Default::default() + }; + update_android_manifest_with_default( + &mut android_manifest, + Some("Example".to_owned()), + "example", + AndroidStrategy::NativeApk, ); // Saves android manifest into temporary directory diff --git a/crossbundle/tools/tests/add_libs.rs b/crossbundle/tools/tests/add_libs.rs index 412e206c..946daa67 100644 --- a/crossbundle/tools/tests/add_libs.rs +++ b/crossbundle/tools/tests/add_libs.rs @@ -1,10 +1,8 @@ +#![cfg(feature = "android")] + use crossbundle_tools::{ - commands::{ - android::{self, rust_compile}, - gen_minimal_project, - }, - tools::{AndroidNdk, AndroidSdk}, - types::{AndroidTarget, ApplicationWrapper, IntoRustTriple, Profile}, + commands::{android::*, gen_minimal_project}, + types::*, }; #[test] @@ -18,12 +16,12 @@ fn add_bevy_libs() { // Assign needed configuration to compile rust for android with bevy let sdk = AndroidSdk::from_env().unwrap(); - let ndk = AndroidNdk::from_env(Some(sdk.sdk_path())).unwrap(); - let build_target = AndroidTarget::Aarch64LinuxAndroid; + let ndk = AndroidNdk::from_env(sdk.sdk_path()).unwrap(); + let build_target = AndroidTarget::Aarch64; let profile = Profile::Release; let target_sdk_version = 30; let bevy_lib_name = format!("lib{}.so", bevy_package_name.replace('-', "_")); - let app_wrapper_for_bevy = ApplicationWrapper::NdkGlue; + let app_wrapper_for_bevy = AppWrapper::NdkGlue; // Compile rust code for android with bevy engine rust_compile( @@ -53,7 +51,7 @@ fn add_bevy_libs() { // Add libs into the directory ./target/aarch64-linux-android/debug/ for (compiled_lib, build_target) in libs { - let lib = android::add_libs_into_aapt2( + let lib = add_libs_into_aapt2( &ndk, &compiled_lib, build_target, @@ -87,12 +85,12 @@ fn add_quad_libs() { // Assign needed configuration to compile rust for android with bevy let sdk = AndroidSdk::from_env().unwrap(); - let ndk = AndroidNdk::from_env(Some(sdk.sdk_path())).unwrap(); - let build_target = AndroidTarget::Aarch64LinuxAndroid; + let ndk = AndroidNdk::from_env(sdk.sdk_path()).unwrap(); + let build_target = AndroidTarget::Aarch64; let profile = Profile::Release; let target_sdk_version = 30; let quad_lib_name = format!("lib{}.so", quad_package_name.replace('-', "_")); - let app_wrapper_for_quad = ApplicationWrapper::Sokol; + let app_wrapper_for_quad = AppWrapper::Sokol; // Compile rust code for android with bevy engine rust_compile( @@ -122,7 +120,7 @@ fn add_quad_libs() { // Adds libs into ./target/aarch64-linux-android/debug/ for (compiled_lib, build_target) in libs { - let lib = android::add_libs_into_aapt2( + let lib = add_libs_into_aapt2( &ndk, &compiled_lib, build_target, diff --git a/crossbundle/tools/tests/apple_full.rs b/crossbundle/tools/tests/apple_full.rs index 325982f1..972affd3 100644 --- a/crossbundle/tools/tests/apple_full.rs +++ b/crossbundle/tools/tests/apple_full.rs @@ -1,4 +1,4 @@ -#![cfg(target_os = "macos")] +#![cfg(all(target_os = "macos", feature = "apple"))] use apple_bundle::prelude::*; use crossbundle_tools::{ @@ -70,7 +70,7 @@ fn test_apple_full() { assert!(app_dir.exists()); // Compile app - let build_target = AppleTarget::X86_64AppleIos; + let build_target = IosTarget::X86_64; let profile = Profile::Release; compile_rust_for_ios( Target::Bin(name.clone()), @@ -80,6 +80,7 @@ fn test_apple_full() { vec![], false, false, + &[], ) .unwrap(); let out_dir = dir diff --git a/crossbundle/tools/tests/bundletool.rs b/crossbundle/tools/tests/bundletool.rs index 5c298bf8..3de7725c 100644 --- a/crossbundle/tools/tests/bundletool.rs +++ b/crossbundle/tools/tests/bundletool.rs @@ -1,7 +1,9 @@ -use android_tools::java_tools::{android_dir, JarSigner, Key, KeyAlgorithm, Keytool}; +#![cfg(feature = "android")] + +use android_tools::java_tools::*; use crossbundle_tools::{ - commands::android::{extract_archive, gen_minimal_unsigned_aab, gen_zip_modules, remove}, - tools::{AndroidSdk, BuildApks, BuildBundle, GetSizeTotal}, + commands::android::*, + types::{AndroidSdk, BuildApks, BuildBundle, GetSizeTotal}, }; #[test] diff --git a/crossbundle/tools/tests/gen_apple_app.rs b/crossbundle/tools/tests/gen_apple_app.rs index b53ba035..3eee57be 100644 --- a/crossbundle/tools/tests/gen_apple_app.rs +++ b/crossbundle/tools/tests/gen_apple_app.rs @@ -1,4 +1,4 @@ -#![cfg(target_os = "macos")] +#![cfg(all(target_os = "macos", feature = "apple"))] use crossbundle_tools::commands::{apple::*, gen_minimal_project}; diff --git a/crossbundle/tools/tests/rust_compile_for_macos.rs b/crossbundle/tools/tests/rust_compile_for_macos.rs index 9cc8e427..c2a7a1fa 100644 --- a/crossbundle/tools/tests/rust_compile_for_macos.rs +++ b/crossbundle/tools/tests/rust_compile_for_macos.rs @@ -1,11 +1,11 @@ -#[cfg(target_os = "macos")] +#![cfg(all(target_os = "macos", feature = "apple"))] + use crossbundle_tools::{ commands::{apple::*, gen_minimal_project}, - types::{AppleTarget, Profile, Target}, + types::{IosTarget, Profile, Target}, }; #[test] -#[cfg(target_os = "macos")] fn test_compile_apple() { let tempdir = tempfile::tempdir().unwrap(); let dir = tempdir.path(); @@ -13,12 +13,13 @@ fn test_compile_apple() { compile_rust_for_ios( Target::Bin(name), - AppleTarget::Aarch64AppleIos, + IosTarget::Aarch64, dir, Profile::Release, vec![], false, false, + &[], ) .unwrap(); } diff --git a/docs/src/crossbow/configuration.md b/docs/src/crossbow/configuration.md index 98293054..8acc74ec 100644 --- a/docs/src/crossbow/configuration.md +++ b/docs/src/crossbow/configuration.md @@ -21,7 +21,7 @@ app_name = "Game" target_sdk_version = 30 icon = "ic_launcher" -build_targets = ["aarch64-linux-android"] +release_build_targets = ["aarch64-linux-android"] assets = "assets" res = "res/android" @@ -29,7 +29,7 @@ res = "res/android" app_name = "Game" icon = "ic_launcher" -build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] +release_build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] assets = "assets" res = "res/apple" ``` diff --git a/docs/src/crossbow/permissions.md b/docs/src/crossbow/permissions.md index 781738ac..665ea8a6 100644 --- a/docs/src/crossbow/permissions.md +++ b/docs/src/crossbow/permissions.md @@ -1,6 +1,27 @@ # Crossbow permissions -To request permissions with Crossbow you will need to add `crossbow` dependency to your Cargo.toml file. Then invoke request_permission function. This function checks the permission status in the application and will request permission if it's not granted yet: +To request permissions with Crossbow you will need to add `crossbow` dependency to your Cargo.toml file. And add permissions to `AndroidManifest.xml` or `Info.plist`: + +```toml +# This one: +[package.metadata] +permissions = ["camera", "microphone", "photos", "storage-read"] + +# Will make the same as this one: +[[package.metadata.android.manifest.uses_permission]] +name = "android.permission.READ_EXTERNAL_STORAGE" +[[package.metadata.android.manifest.uses_permission]] +name = "android.permission.CAMERA" +[[package.metadata.android.manifest.uses_permission]] +name = "android.permission.RECORD_AUDIO" + +[package.metadata.apple.info_plist] +NSCameraUsageDescription = "This app needs access to your phone's camera." +NSMicrophoneUsageDescription = "This app needs access to your phone's microphone." +NSPhotoLibraryUsageDescription = "This app needs access to your phone's photo library." +``` + +Then invoke `request_async` function. This function checks the permission status in the application and will request permission if it's not granted yet: ```rust use crossbow::Permission; diff --git a/docs/src/crossbundle/command-build.md b/docs/src/crossbundle/command-build.md index 7c5e6829..61b7286f 100644 --- a/docs/src/crossbundle/command-build.md +++ b/docs/src/crossbundle/command-build.md @@ -5,10 +5,10 @@ Crossbow default build process requires installed Gradle on your PC. You can dow To create a project go to the example you want to build and use the command below. The command belongs to macroquad engine examples building: ```sh -crossbundle build android --quad +crossbundle build android # To specify custom export gradle directory -crossbundle build android --quad --export-path=./gen/ +crossbundle build android --export-path=./gen/ ``` By default build directory is `target/android//gradle`. But you can specify your own build directory via `--export-path=` flag. Go to the directory where Gradle project was built and use command below to manually install APK on the device. diff --git a/docs/src/install/docker.md b/docs/src/install/docker.md index 434e747d..55e4d868 100644 --- a/docs/src/install/docker.md +++ b/docs/src/install/docker.md @@ -25,9 +25,9 @@ Run the following command at the root of `crossbow` project: ```sh # For unix systems: -docker run --rm -it -v "$(pwd)/:/src" -w /src/examples/macroquad-permissions ghcr.io/dodorare/crossbundle build android --quad --release +docker run --rm -it -v "$(pwd)/:/src" -w /src/examples/macroquad-permissions ghcr.io/dodorare/crossbundle build android --release # For Windows: -docker run --rm -it -v "${pwd}/:/src" -w /src/examples/macroquad-permissions ghcr.io/dodorare/crossbundle build android --quad --release +docker run --rm -it -v "${pwd}/:/src" -w /src/examples/macroquad-permissions ghcr.io/dodorare/crossbundle build android --release ``` Install APK on connected Android phone via USB: diff --git a/docs/src/install/ios-macos.md b/docs/src/install/ios-macos.md index ac43d53b..47c604d7 100644 --- a/docs/src/install/ios-macos.md +++ b/docs/src/install/ios-macos.md @@ -78,13 +78,13 @@ cd game/ Now run the build command: ```sh -crossbundle build apple --target=x86_64-apple-ios +crossbundle build ios --target=x86_64-apple-ios ``` And if you want to run on the simulator: ```sh -crossbundle run apple --target=x86_64-apple-ios +crossbundle run ios --target=x86_64-apple-ios ``` ## Run your app on a real device @@ -117,7 +117,7 @@ Next to `OU` - you will find Team ID. (in our case `AS9UV719T7`). Now we are good to install our app on a real device. To run/build app for a real device you will need similar to this command to be run in your cargo project: ```sh -crossbundle run apple --release --device --profile-name=aec73e2f-c2f9-4e3b-9393-be19cc52fea3.mobileprovision --team-identifier=AS9UV719T7 --identity=AF96DABFC5DEE81E339ED8755DA8D1E48A87CBFE +crossbundle run ios --release --device --profile-name=aec73e2f-c2f9-4e3b-9393-be19cc52fea3.mobileprovision --team-identifier=AS9UV719T7 --identity=AF96DABFC5DEE81E339ED8755DA8D1E48A87CBFE ``` Now replace test data (`aec73e2f-c2f9-4e3b-9393-be19cc52fea3.mobileprovision`, `AS9UV719T7`, `AF96DABFC5DEE81E339ED8755DA8D1E48A87CBFE`) - with your own and run command. If everything worked well - you will see new app on your device. diff --git a/docs/src/introduction.md b/docs/src/introduction.md index 739d530a..33a52365 100644 --- a/docs/src/introduction.md +++ b/docs/src/introduction.md @@ -1,6 +1,6 @@ # Introduction -![splash](https://github.com/dodorare/crossbow/blob/main/.github/assets/splash.png?raw=true) +![splash](https://github.com/dodorare/crossbow/blob/main/assets/splash.png?raw=true) ## What is Crossbow? @@ -10,16 +10,17 @@ The `crossbow` project aims to provide a complete toolkit for cross-platform gam > There are already [cargo-apk](https://github.com/rust-windowing/android-ndk-rs/tree/master/cargo-apk), [cargo-mobile](https://github.com/BrainiumLLC/cargo-mobile), [cargo-xcode](https://gitlab.com/kornelski/cargo-xcode), etc. - why do I need another packaging tool? -Project `crossbow` is not only a packaging tool for **Android** and iOS - it's a toolkit. With `crossbundle-tools` you can customize and create new commands; with `crossbundle` you can create native **.apk/.aab** without any *Java* or setup *Gradle* project with fancy **Crossbow Android plugins** (**iOS** in near future); with `crossbow-android` you can write your own Android plugins in *Java/Kotlin*. +Project `crossbow` is not only a packaging tool for **Android** and **iOS** - it's cross-platform build tools and toolkit for Rust! With `crossbundle` you can create native **.apk/.aab** without any *Java* or setup *Gradle* project with fancy **Crossbow Android plugins** (**iOS** in near future); with `crossbundle-tools` you can customize and create new commands; with `crossbow-android` you can write your own Android plugins in *Java/Kotlin*. A lot of functionality was inspired by [Godot](https://github.com/godotengine/godot), [Xamarin](https://dotnet.microsoft.com/en-us/apps/xamarin), and [cargo-apk](https://github.com/rust-windowing/android-ndk-rs/tree/master/cargo-apk). ## Design Goals * **Customizable**: Create new commands with available tools. -* **Simple**: Easy to start but flexible for strong devs. +* **Simple**: Easy to install and start hacking but also pretty flexible for strong devs. * **Capable**: It's possible to build plain **.apk/.aab** or **.app/.ipa**; or with help of *Gradle/XCode*. * **Rust**: Don't leave your *Rust* code - **everything** can be configured from `Cargo.toml`. +* **Plugins**: Godot-like plugins for **Android** (and **iOS** in future) with *Rust* wrapper! ## Next steps diff --git a/docs/src/roadmap.md b/docs/src/roadmap.md index e2d7519e..b6672496 100644 --- a/docs/src/roadmap.md +++ b/docs/src/roadmap.md @@ -8,13 +8,13 @@ | 4. | Support Cross-platform permissions | Provide a single cross-platform permission API that works with any [iOS](https://developer.apple.com/design/human-interface-guidelines/ios/app-architecture/accessing-user-data/), [Android](https://developer.android.com/games/develop/permissions), etc application that can be accessed from shared code no matter how the user interface is created. | โœ… | | 5. | Simple installation | Simple installation with environment variables, libs, etc. Make installation of Android SDK, NDK, tools more robust. | โœ… | | 6. | Support iOS Plugins | Add support of iOS plugins to help add additional functionality provided by the Apple platforms and ecosystem (like Ads, Auth, In-app purchases, etc.). Something similar to [Godot iOS plugins](https://docs.godotengine.org/en/stable/tutorials/platform/ios/ios_plugin.html). | ๐Ÿ›  | -| 7. | Sign in with Google | Add support of [Google Sign In](https://developers.google.com/games/services/common/concepts/sign-in) inside any application. | ๐Ÿ“ | +| 7. | Sign in with Google | Add support of [Google Sign In](https://developers.google.com/games/services/common/concepts/sign-in) inside any application. | ๐Ÿ›  | | 8. | Sign in with Apple | Add support of [Apple Sign In](https://github.com/lupidan/apple-signin-unity) inside any application. | ๐Ÿ“ | | 9. | Better support for Apple xcrun, xcode proj | Add better support and rust wrappers for Apple xcode tools, xcrun. Make cool xcode project generation library. | ๐Ÿ“ | | 10. | Apple Game Center | Add [Apple Game Center](https://developer.apple.com/documentation/gamekit) support. | ๐Ÿ“ | -| 11. | Android In-App purchases & Google Play Billing | Add support for [Google Play Billing](https://github.com/godotengine/godot-google-play-billing). Make it possible to buy items from your application. | ๐Ÿ“ | +| 11. | Android In-App purchases & Google Play Billing | Add support for [Google Play Billing](https://github.com/godotengine/godot-google-play-billing). Make it possible to buy items from your application. | ๐Ÿ›  | | 12. | Support Apple In-App purchases | Support Apple [StoreKit](https://developer.apple.com/documentation/storekit/in-app_purchase). Make it possible to buy items from your application. | ๐Ÿ“ | -| 13. | Support Android In-App updates | Add support for [Android In-App updates](https://developer.android.com/guide/playcore/in-app-updates). | ๐Ÿ“ | +| 13. | Support Android In-App updates | Add support for [Android In-App updates](https://developer.android.com/guide/playcore/in-app-updates). | ๐Ÿ›  | โœ… = Works and tested โ€” ๐Ÿ†— = Works but may contain bugs โ€” ๐Ÿ›  = Under development โ€” ๐Ÿ“ = Planned diff --git a/docs/src/tutorials/hello-world.md b/docs/src/tutorials/hello-world.md index 41e72994..26815842 100644 --- a/docs/src/tutorials/hello-world.md +++ b/docs/src/tutorials/hello-world.md @@ -59,12 +59,12 @@ Let's build and run our first `crossbundle` application. Android commands below # cd project-name crossbundle run android # or (if your project uses macroquad) -crossbundle run android --quad +crossbundle run android # or -crossbundle run apple +crossbundle run ios ``` -If you want to build the application for android as native AAB - add `--aab` flag or add `--apk` to build native APK. +If you want to build the application for android as native AAB - add `-s=native-aab` flag or add `-s=native-apk` to build native APK. When the application deploys on your device, you can attach a logger. diff --git a/docs/src/tutorials/subxt-with-bevy.md b/docs/src/tutorials/subxt-with-bevy.md index 7b729f31..eb081795 100644 --- a/docs/src/tutorials/subxt-with-bevy.md +++ b/docs/src/tutorials/subxt-with-bevy.md @@ -44,13 +44,13 @@ By default `crossbundle build android` command will generate gradle project and To build native APK and run it on the device using the command. If you want to build an application replaces `run` with `build`. ```sh -crossbundle run android --apk +crossbundle run android -s=native-apk # or -crossbundle run apple +crossbundle run ios ``` To build native AAB and run it on the device using the command. If you want to build an application replaces `run` with `build`. ```sh -crossbundle run android --aab +crossbundle run android -s=native-aab ``` diff --git a/examples/bevy-2d/Cargo.toml b/examples/bevy-2d/Cargo.toml index 15e2d293..77986f6d 100644 --- a/examples/bevy-2d/Cargo.toml +++ b/examples/bevy-2d/Cargo.toml @@ -1,31 +1,23 @@ [package] name = "bevy-2d" -version = "0.1.7" +version = "0.1.8" authors = ["DodoRare Team "] edition = "2021" [dependencies] -crossbow = { version = "0.1.7", path = "../../" } +crossbow = { version = "0.1.8", path = "../../" } log = "0.4" anyhow = "1.0" bevy = { version = "0.7.0", features = ["mp3"] } -[package.metadata.android] +[package.metadata] app_name = "Bevy 2D" -version_code = 1 -target_sdk_version = 30 -icon = "ic_launcher" +assets = "../../assets" -build_targets = ["aarch64-linux-android"] -assets = "assets" -res = "res/android" +[package.metadata.android] +release_build_targets = ["aarch64-linux-android"] +res = "../../assets/res/android" [package.metadata.apple] -app_name = "Bevy 2D" -version_code = 1 -target_sdk_version = 30 -icon = "ic_launcher" - -build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] -assets = "assets" -res = "res/apple" +release_build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] +res = "../../assets/res/apple" diff --git a/examples/bevy-2d/assets/branding/icon.png b/examples/bevy-2d/assets/branding/icon.png deleted file mode 100644 index 4f3873b2..00000000 Binary files a/examples/bevy-2d/assets/branding/icon.png and /dev/null differ diff --git a/examples/bevy-2d/res/android/mipmap-hdpi/ic_launcher.png b/examples/bevy-2d/res/android/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index e2ef6738..00000000 Binary files a/examples/bevy-2d/res/android/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/examples/bevy-2d/res/android/mipmap-mdpi/ic_launcher.png b/examples/bevy-2d/res/android/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 36b09136..00000000 Binary files a/examples/bevy-2d/res/android/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/examples/bevy-2d/res/android/mipmap-xhdpi/ic_launcher.png b/examples/bevy-2d/res/android/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index be8e658e..00000000 Binary files a/examples/bevy-2d/res/android/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/examples/bevy-2d/res/android/mipmap-xxhdpi/ic_launcher.png b/examples/bevy-2d/res/android/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index a45ce7ee..00000000 Binary files a/examples/bevy-2d/res/android/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/examples/bevy-2d/res/android/mipmap-xxxhdpi/ic_launcher.png b/examples/bevy-2d/res/android/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index eeb46f0c..00000000 Binary files a/examples/bevy-2d/res/android/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/examples/bevy-2d/res/apple/icon.png b/examples/bevy-2d/res/apple/icon.png deleted file mode 100644 index 97183f5d..00000000 Binary files a/examples/bevy-2d/res/apple/icon.png and /dev/null differ diff --git a/examples/bevy-2d/src/main.rs b/examples/bevy-2d/src/main.rs index b1eee2bf..b38854aa 100644 --- a/examples/bevy-2d/src/main.rs +++ b/examples/bevy-2d/src/main.rs @@ -4,10 +4,6 @@ fn main() { println!("Initialization."); std::thread::sleep(std::time::Duration::from_secs(2)); App::new() - // .insert_resource(LogSettings { - // level: Level::DEBUG, - // filter: "wgpu=debug,wgpu_hal=debug,bevy_render=info".to_string(), - // }) .add_plugins(DefaultPlugins) .add_startup_system(audio) .add_startup_system(icon) @@ -17,7 +13,7 @@ fn main() { fn icon(mut commands: Commands, asset_server: Res) { commands.spawn_bundle(OrthographicCameraBundle::new_2d()); commands.spawn_bundle(SpriteBundle { - texture: asset_server.load("branding/icon.png"), + texture: asset_server.load("images/icon.png"), ..Default::default() }); } diff --git a/examples/bevy-3d/Cargo.toml b/examples/bevy-3d/Cargo.toml index 57d9f79b..dd0748c2 100644 --- a/examples/bevy-3d/Cargo.toml +++ b/examples/bevy-3d/Cargo.toml @@ -1,31 +1,23 @@ [package] name = "bevy-3d" -version = "0.1.7" +version = "0.1.8" authors = ["DodoRare Team "] edition = "2021" [dependencies] -crossbow = { version = "0.1.7", path = "../../" } +crossbow = { version = "0.1.8", path = "../../" } log = "0.4" anyhow = "1.0" bevy = "0.7.0" -[package.metadata.android] +[package.metadata] app_name = "Bevy 3D" -target_sdk_version = 30 -version_code = 1 -icon = "ic_launcher" +assets = "../../assets" -build_targets = ["aarch64-linux-android", "armv7-linux-androideabi", "i686-linux-android", "x86_64-linux-android"] -assets = "assets" -res = "res/android" +[package.metadata.android] +release_build_targets = ["aarch64-linux-android", "armv7-linux-androideabi", "i686-linux-android", "x86_64-linux-android"] +res = "../../assets/res/android" [package.metadata.apple] -app_name = "Bevy 3D" -target_sdk_version = 30 -version_code = 1 -icon = "ic_launcher" - -build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] -assets = "assets" -res = "res/apple" +release_build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] +res = "../../assets/res/apple" diff --git a/examples/bevy-3d/res/android/mipmap-hdpi/ic_launcher.png b/examples/bevy-3d/res/android/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index e2ef6738..00000000 Binary files a/examples/bevy-3d/res/android/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/examples/bevy-3d/res/android/mipmap-mdpi/ic_launcher.png b/examples/bevy-3d/res/android/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 36b09136..00000000 Binary files a/examples/bevy-3d/res/android/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/examples/bevy-3d/res/android/mipmap-xhdpi/ic_launcher.png b/examples/bevy-3d/res/android/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index be8e658e..00000000 Binary files a/examples/bevy-3d/res/android/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/examples/bevy-3d/res/android/mipmap-xxhdpi/ic_launcher.png b/examples/bevy-3d/res/android/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index a45ce7ee..00000000 Binary files a/examples/bevy-3d/res/android/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/examples/bevy-3d/res/android/mipmap-xxxhdpi/ic_launcher.png b/examples/bevy-3d/res/android/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index eeb46f0c..00000000 Binary files a/examples/bevy-3d/res/android/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/examples/bevy-3d/res/apple/icon.png b/examples/bevy-3d/res/apple/icon.png deleted file mode 100644 index 97183f5d..00000000 Binary files a/examples/bevy-3d/res/apple/icon.png and /dev/null differ diff --git a/examples/bevy-explorer/Cargo.toml b/examples/bevy-explorer/Cargo.toml index 53d579bd..11f55ab7 100644 --- a/examples/bevy-explorer/Cargo.toml +++ b/examples/bevy-explorer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bevy-explorer" -version = "0.1.7" +version = "0.1.8" authors = ["DodoRare Team "] edition = "2021" @@ -14,26 +14,14 @@ bevy = { version = "0.7.0", features = ["mp3"] } codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "full", "bit-vec"] } jsonrpsee = { version = "0.10.1", features = ["async-client", "client-ws-transport"] } -[package.metadata.android] +[package.metadata] app_name = "Bevy Explorer" -version_code = 1 -target_sdk_version = 30 -icon = "ic_launcher" +assets = "../../assets" -build_targets = ["aarch64-linux-android"] -assets = "assets" -res = "res/android" +[package.metadata.android] +release_build_targets = ["aarch64-linux-android"] +res = "../../assets/res/android" [package.metadata.apple] -app_name = "Bevy Explorer" -version_code = 1 -target_sdk_version = 30 -icon = "ic_launcher" - -build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] -assets = "assets" -res = "res/apple" - -[[package.metadata.android_permissions]] -name = "android.permission.INTERNET" -max_sdk_version = 31 +release_build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] +res = "../../assets/res/apple" diff --git a/examples/bevy-explorer/res/android/mipmap-hdpi/ic_launcher.png b/examples/bevy-explorer/res/android/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index e2ef6738..00000000 Binary files a/examples/bevy-explorer/res/android/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/examples/bevy-explorer/res/android/mipmap-mdpi/ic_launcher.png b/examples/bevy-explorer/res/android/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 36b09136..00000000 Binary files a/examples/bevy-explorer/res/android/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/examples/bevy-explorer/res/android/mipmap-xhdpi/ic_launcher.png b/examples/bevy-explorer/res/android/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index be8e658e..00000000 Binary files a/examples/bevy-explorer/res/android/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/examples/bevy-explorer/res/android/mipmap-xxhdpi/ic_launcher.png b/examples/bevy-explorer/res/android/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index a45ce7ee..00000000 Binary files a/examples/bevy-explorer/res/android/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/examples/bevy-explorer/res/android/mipmap-xxxhdpi/ic_launcher.png b/examples/bevy-explorer/res/android/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index eeb46f0c..00000000 Binary files a/examples/bevy-explorer/res/android/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/examples/bevy-explorer/res/apple/icon.png b/examples/bevy-explorer/res/apple/icon.png deleted file mode 100644 index 97183f5d..00000000 Binary files a/examples/bevy-explorer/res/apple/icon.png and /dev/null differ diff --git a/examples/macroquad-3d/Cargo.toml b/examples/macroquad-3d/Cargo.toml index 62db0e10..da271bd0 100644 --- a/examples/macroquad-3d/Cargo.toml +++ b/examples/macroquad-3d/Cargo.toml @@ -1,28 +1,25 @@ [package] name = "macroquad-3d" -version = "0.1.7" +version = "0.1.8" authors = ["DodoRare Team "] edition = "2021" [dependencies] -crossbow = { version = "0.1.7", path = "../../" } +crossbow = { version = "0.1.8", path = "../../" } log = "0.4" anyhow = "1.0" macroquad = "0.3.7" +[package.metadata] +app_wrapper = "sokol" +app_name = "Macroquad 3D" +assets = "../../assets" + [package.metadata.android] -app_name = "Macroquad_3D" -target_sdk_version = 30 -version_code = 1 -icon = "ic_launcher" manifest_path = "res/AndroidManifest.xml" - -build_targets = ["aarch64-linux-android"] -assets = "assets" -res = "res/android" +release_build_targets = ["aarch64-linux-android"] +res = "../../assets/res/android" [package.metadata.apple] -app_name = "Macroquad_3D" -build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] -assets = "assets" -res = "res/apple" +release_build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] +res = "../../assets/res/apple" diff --git a/examples/macroquad-3d/assets/bob/ferris.png b/examples/macroquad-3d/assets/bob/ferris.png deleted file mode 100644 index ebce1a14..00000000 Binary files a/examples/macroquad-3d/assets/bob/ferris.png and /dev/null differ diff --git a/examples/macroquad-3d/res/android/mipmap-hdpi/ic_launcher.png b/examples/macroquad-3d/res/android/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index e2ef6738..00000000 Binary files a/examples/macroquad-3d/res/android/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/examples/macroquad-3d/res/android/mipmap-mdpi/ic_launcher.png b/examples/macroquad-3d/res/android/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 36b09136..00000000 Binary files a/examples/macroquad-3d/res/android/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/examples/macroquad-3d/res/android/mipmap-xhdpi/ic_launcher.png b/examples/macroquad-3d/res/android/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index be8e658e..00000000 Binary files a/examples/macroquad-3d/res/android/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/examples/macroquad-3d/res/android/mipmap-xxhdpi/ic_launcher.png b/examples/macroquad-3d/res/android/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index a45ce7ee..00000000 Binary files a/examples/macroquad-3d/res/android/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/examples/macroquad-3d/res/android/mipmap-xxxhdpi/ic_launcher.png b/examples/macroquad-3d/res/android/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index eeb46f0c..00000000 Binary files a/examples/macroquad-3d/res/android/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/examples/macroquad-3d/res/apple/icon.png b/examples/macroquad-3d/res/apple/icon.png deleted file mode 100644 index 97183f5d..00000000 Binary files a/examples/macroquad-3d/res/apple/icon.png and /dev/null differ diff --git a/examples/macroquad-3d/src/main.rs b/examples/macroquad-3d/src/main.rs index 9d3aaef6..167c8b91 100644 --- a/examples/macroquad-3d/src/main.rs +++ b/examples/macroquad-3d/src/main.rs @@ -2,8 +2,7 @@ use macroquad::prelude::*; #[macroquad::main("Macroquad 3D")] async fn main() -> anyhow::Result<()> { - let rust_logo = load_texture("bob/rust.png").await?; - let ferris = load_texture("bob/ferris.png").await?; + let rust_logo = load_texture("images/rust.png").await?; loop { clear_background(LIGHTGRAY); @@ -21,10 +20,10 @@ async fn main() -> anyhow::Result<()> { draw_cube_wires(vec3(0., 1., 6.), vec3(2., 2., 2.), DARKBLUE); draw_cube_wires(vec3(2., 1., 2.), vec3(2., 2., 2.), YELLOW); - draw_plane(vec3(-8., 0., -8.), vec2(5., 5.), ferris, WHITE); + draw_plane(vec3(-8., 0., -8.), vec2(5., 5.), rust_logo, WHITE); draw_cube(vec3(-5., 1., -2.), vec3(2., 2., 2.), rust_logo, WHITE); - draw_cube(vec3(-5., 1., 2.), vec3(2., 2., 2.), ferris, WHITE); + draw_cube(vec3(-5., 1., 2.), vec3(2., 2., 2.), rust_logo, WHITE); draw_cube(vec3(2., 0., -2.), vec3(0.4, 0.4, 0.4), None, BLACK); draw_sphere(vec3(-8., 0., 0.), 1., None, BLUE); diff --git a/examples/macroquad-permissions/Cargo.toml b/examples/macroquad-permissions/Cargo.toml index 00103763..340083c1 100644 --- a/examples/macroquad-permissions/Cargo.toml +++ b/examples/macroquad-permissions/Cargo.toml @@ -1,68 +1,52 @@ [package] name = "macroquad-permissions" -version = "0.1.7" +version = "0.1.8" authors = ["DodoRare Team "] edition = "2021" [dependencies] -crossbow = { version = "0.1.7", path = "../../" } +crossbow = { version = "0.1.8", path = "../../" } log = "0.4" anyhow = "1.0" macroquad = "0.3.7" [target.'cfg(target_os = "android")'.dependencies] -crossbow-admob = { version = "0.1.7", path = "../../plugins/admob" } +crossbow-admob = { version = "0.1.8", path = "../../plugins/admob" } -[package.metadata.android] +[package.metadata] app_name = "Permissions" -target_sdk_version = 30 -version_code = 1 -icon = "ic_launcher" - -build_targets = ["aarch64-linux-android"] -assets = "assets" -res = "res/android" +permissions = ["camera", "microphone", "photos", "storage-read"] -# plugins_remote = ["com.crossbow.admob:admob:0.1.7"] +[package.metadata.android] +app_wrapper = "sokol" +release_build_targets = ["aarch64-linux-android"] +res = "../../assets/res/android" +# plugins_remote = ["com.crossbow.admob:admob:0.1.8"] [[package.metadata.android.plugins_local_projects]] include = ":crossbow" dont_implement = true project_dir = "../../platform/android/java" - [[package.metadata.android.plugins_local_projects]] include = ":crossbow:lib" - [[package.metadata.android.plugins_local_projects]] include = ":admob" project_dir = "../../plugins/admob/android" -[[package.metadata.android.permissions]] -name = "android.permission.READ_EXTERNAL_STORAGE" - -[[package.metadata.android.permissions]] -name = "android.permission.CAMERA" - -[[package.metadata.android.permissions]] -name = "android.permission.RECORD_AUDIO" +[package.metadata.android.manifest] +package = "com.crossbow.permissions" +[package.metadata.android.manifest.uses_sdk] +min_sdk_version = 19 +target_sdk_version = 30 +[package.metadata.android.manifest.application] +icon = "@mipmap/ic_launcher" +round_icon="@mipmap/ic_launcher" [package.metadata.apple] -app_name = "Permissions" -version_code = 1 -icon = "icon" - -build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] -assets = "assets" -res = "res/apple" +release_build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] +res = "../../assets/res/apple" [package.metadata.apple.info_plist] -CFBundleIdentifier = "com.crossbow.macroquad-permissions" +CFBundleIdentifier = "com.crossbow.permissions" CFBundleVersion = "1.0" CFBundleShortVersionString = "1.0" -CFBundleExecutable = "macroquad-permissions" -UILaunchStoryboardName = "LaunchScreen" -CFBundleName = "Permissions" -# Permissions -NSCameraUsageDescription = "Permissions App requires access to your phone's camera." -NSMicrophoneUsageDescription = "Permissions App requires access to your phone's microphone." -NSPhotoLibraryUsageDescription = "Permissions App requires access to your phone's photo library." diff --git a/examples/macroquad-permissions/assets/HTOWERT.TTF b/examples/macroquad-permissions/assets/HTOWERT.TTF deleted file mode 100644 index 48ad007c..00000000 Binary files a/examples/macroquad-permissions/assets/HTOWERT.TTF and /dev/null differ diff --git a/examples/macroquad-permissions/assets/button_background.png b/examples/macroquad-permissions/assets/button_background.png deleted file mode 100644 index a8b01f33..00000000 Binary files a/examples/macroquad-permissions/assets/button_background.png and /dev/null differ diff --git a/examples/macroquad-permissions/assets/button_clicked_background.png b/examples/macroquad-permissions/assets/button_clicked_background.png deleted file mode 100644 index d6c66548..00000000 Binary files a/examples/macroquad-permissions/assets/button_clicked_background.png and /dev/null differ diff --git a/examples/macroquad-permissions/assets/button_hovered_background.png b/examples/macroquad-permissions/assets/button_hovered_background.png deleted file mode 100644 index d004de32..00000000 Binary files a/examples/macroquad-permissions/assets/button_hovered_background.png and /dev/null differ diff --git a/examples/macroquad-permissions/assets/window_background.png b/examples/macroquad-permissions/assets/window_background.png deleted file mode 100644 index 2bd90b36..00000000 Binary files a/examples/macroquad-permissions/assets/window_background.png and /dev/null differ diff --git a/examples/macroquad-permissions/res/android/mipmap-hdpi/ic_launcher.png b/examples/macroquad-permissions/res/android/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index e2ef6738..00000000 Binary files a/examples/macroquad-permissions/res/android/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/examples/macroquad-permissions/res/android/mipmap-mdpi/ic_launcher.png b/examples/macroquad-permissions/res/android/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 36b09136..00000000 Binary files a/examples/macroquad-permissions/res/android/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/examples/macroquad-permissions/res/android/mipmap-xhdpi/ic_launcher.png b/examples/macroquad-permissions/res/android/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index be8e658e..00000000 Binary files a/examples/macroquad-permissions/res/android/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/examples/macroquad-permissions/res/android/mipmap-xxhdpi/ic_launcher.png b/examples/macroquad-permissions/res/android/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index a45ce7ee..00000000 Binary files a/examples/macroquad-permissions/res/android/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/examples/macroquad-permissions/res/android/mipmap-xxxhdpi/ic_launcher.png b/examples/macroquad-permissions/res/android/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index eeb46f0c..00000000 Binary files a/examples/macroquad-permissions/res/android/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/examples/macroquad-permissions/res/apple/icon.png b/examples/macroquad-permissions/res/apple/icon.png deleted file mode 100644 index 97183f5d..00000000 Binary files a/examples/macroquad-permissions/res/apple/icon.png and /dev/null differ diff --git a/examples/macroquad-permissions/src/main.rs b/examples/macroquad-permissions/src/main.rs index e1466384..04ee3525 100644 --- a/examples/macroquad-permissions/src/main.rs +++ b/examples/macroquad-permissions/src/main.rs @@ -59,7 +59,7 @@ async fn main() -> anyhow::Result<()> { clear_background(WHITE); root_ui().push_skin(&window_skin); - root_ui().window(hash!(), vec2(0.0, 250.0), vec2(500.0, 500.0), |ui| { + root_ui().window(hash!(), vec2(0.0, 50.0), vec2(1000.0, 1000.0), |ui| { #[cfg(target_os = "android")] ui.label(vec2(15.0, 0.0), "AdMob"); ui.label(vec2(15.0, 50.0), &label); @@ -72,7 +72,9 @@ async fn main() -> anyhow::Result<()> { if ui.button(vec2(-15.0, 300.0), btn_camera) { btn_clicked = btn_camera; } + #[cfg(target_os = "ios")] let btn_camera = "Photos permission"; + #[cfg(target_os = "ios")] if ui.button(vec2(-15.0, 450.0), btn_camera) { btn_clicked = btn_camera; } @@ -109,6 +111,7 @@ async fn main() -> anyhow::Result<()> { let res = Permission::Microphone.request_async().await?; label = format!("Microphone {:?}", res); } + #[cfg(target_os = "ios")] "Photos permission" => { let res = Permission::Photos.request_async().await?; label = format!("Photos {:?}", res); diff --git a/platform/android/Cargo.toml b/platform/android/Cargo.toml index c8ef7701..a4b1941e 100644 --- a/platform/android/Cargo.toml +++ b/platform/android/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "crossbow-android" -version = "0.1.7" +version = "0.1.8" edition = "2021" authors = ["DodoRare Team "] -description = "Cross-Platform Rust Toolkit for Games ๐Ÿน" +description = "Cross-Platform build tools and toolkit for games" repository = "https://github.com/dodorare/crossbow" license = "Apache-2.0" keywords = ["crossbow", "android", "port"] @@ -21,4 +21,4 @@ async-channel = "1.6" [package.metadata] app_name = "crossbow_android" target_sdk_version = 30 -android_build_targets = ["aarch64-linux-android"] +android_release_build_targets = ["aarch64-linux-android"] diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle index 051ce6bf..8f08b435 100644 --- a/platform/android/java/app/config.gradle +++ b/platform/android/java/app/config.gradle @@ -1,5 +1,5 @@ ext.versions = [ - crossbowLibrary : "0.1.7", + crossbowLibrary : "0.1.8", androidGradlePlugin: "7.0.0", compileSdk : 31, minSdk : 19, diff --git a/platform/android/java/lib/src/com/crossbow/library/Crossbow.kt b/platform/android/java/lib/src/com/crossbow/library/Crossbow.kt index 2031902e..9c5ac3b4 100644 --- a/platform/android/java/lib/src/com/crossbow/library/Crossbow.kt +++ b/platform/android/java/lib/src/com/crossbow/library/Crossbow.kt @@ -35,8 +35,7 @@ class Crossbow : Fragment() { pluginRegistry = CrossbowPluginRegistry.initializePluginRegistry(this) Log.v(TAG, "Initializing CrossbowLib Instance") - // CrossbowLib.initialize(activity, this, activity!!.assets) - onRenderInit() + CrossbowLib.initialize(activity, this, activity!!.assets) } override fun onAttach(context: Context) { @@ -151,7 +150,7 @@ class Crossbow : Fragment() { } } if (shouldQuit) { - // CrossbowLib.back() + CrossbowLib.onBackPressed() } } diff --git a/platform/android/java/lib/src/com/crossbow/library/CrossbowLib.kt b/platform/android/java/lib/src/com/crossbow/library/CrossbowLib.kt index 7fffdcd2..eefdfead 100644 --- a/platform/android/java/lib/src/com/crossbow/library/CrossbowLib.kt +++ b/platform/android/java/lib/src/com/crossbow/library/CrossbowLib.kt @@ -29,11 +29,11 @@ object CrossbowLib { @JvmStatic external fun onDestroy() - // /** - // * Forward [Activity.onBackPressed] event from the main thread to the GL thread. - // */ - // @JvmStatic - // external fun back() + /** + * Forward [Activity.onBackPressed] event from the main thread to the GL thread. + */ + @JvmStatic + external fun onBackPressed() /** * Forward the results from a permission request. diff --git a/platform/android/java/lib/src/com/crossbow/library/PermissionsUtil.kt b/platform/android/java/lib/src/com/crossbow/library/PermissionsUtil.kt index 10a2e6cd..8a5ff7ca 100644 --- a/platform/android/java/lib/src/com/crossbow/library/PermissionsUtil.kt +++ b/platform/android/java/lib/src/com/crossbow/library/PermissionsUtil.kt @@ -11,8 +11,7 @@ import android.os.Build import android.os.Environment import android.provider.Settings import android.util.Log -import java.util.ArrayList -import java.util.List +import kotlin.collections.List import androidx.core.content.ContextCompat /** @@ -40,7 +39,7 @@ object PermissionsUtil { if (name == "RECORD_AUDIO" && ContextCompat.checkSelfPermission( activity, Manifest.permission.RECORD_AUDIO - ) !== PackageManager.PERMISSION_GRANTED + ) != PackageManager.PERMISSION_GRANTED ) { activity.requestPermissions( arrayOf(Manifest.permission.RECORD_AUDIO), @@ -51,7 +50,7 @@ object PermissionsUtil { if (name == "CAMERA" && ContextCompat.checkSelfPermission( activity, Manifest.permission.CAMERA - ) !== PackageManager.PERMISSION_GRANTED + ) != PackageManager.PERMISSION_GRANTED ) { activity.requestPermissions( arrayOf(Manifest.permission.CAMERA), @@ -62,7 +61,7 @@ object PermissionsUtil { if (name == "VIBRATE" && ContextCompat.checkSelfPermission( activity, Manifest.permission.VIBRATE - ) !== PackageManager.PERMISSION_GRANTED + ) != PackageManager.PERMISSION_GRANTED ) { activity.requestPermissions( arrayOf(Manifest.permission.VIBRATE), @@ -121,12 +120,12 @@ object PermissionsUtil { } else { val permissionInfo: PermissionInfo = getPermissionInfo(activity, manifestPermission) - val protectionLevel: Int = + val protectionLevel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) permissionInfo.getProtection() else permissionInfo.protectionLevel if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission( activity, manifestPermission - ) !== PackageManager.PERMISSION_GRANTED + ) != PackageManager.PERMISSION_GRANTED ) { requestedPermissions.add(manifestPermission) } @@ -171,12 +170,12 @@ object PermissionsUtil { } else { val permissionInfo: PermissionInfo = getPermissionInfo(activity, manifestPermission) - val protectionLevel: Int = + val protectionLevel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) permissionInfo.getProtection() else permissionInfo.protectionLevel if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission( activity, manifestPermission - ) === PackageManager.PERMISSION_GRANTED + ) == PackageManager.PERMISSION_GRANTED ) { grantedPermissions.add(manifestPermission) } diff --git a/platform/android/java/lib/src/com/crossbow/library/plugin/CrossbowPlugin.kt b/platform/android/java/lib/src/com/crossbow/library/plugin/CrossbowPlugin.kt index c8d3a52e..1774d656 100644 --- a/platform/android/java/lib/src/com/crossbow/library/plugin/CrossbowPlugin.kt +++ b/platform/android/java/lib/src/com/crossbow/library/plugin/CrossbowPlugin.kt @@ -53,6 +53,7 @@ abstract class CrossbowPlugin( * Provides access to the underlying [Activity]. */ protected val activity: Activity? + @Suppress("DEPRECATION") get() = crossbow.activity /** @@ -78,7 +79,7 @@ abstract class CrossbowPlugin( * @see Activity.onCreate * @return the plugin's view to be included; null if no views should be included. */ - open fun onMainCreate(activity: Activity): View? { + open fun onMainCreate(pActivity: Activity): View? { return null } diff --git a/platform/android/src/crossbow/mod.rs b/platform/android/src/crossbow/mod.rs new file mode 100644 index 00000000..f2987868 --- /dev/null +++ b/platform/android/src/crossbow/mod.rs @@ -0,0 +1,28 @@ +use crate::error::*; +use jni::{objects::JObject, JNIEnv}; + +pub(crate) fn crossbow_initialize( + env: JNIEnv, + activity: JObject, + crossbow_instance: JObject, + _asset_manager: JObject, +) -> Result<()> { + println!("CrossbowLib_initialize: {:?}", activity); + + env.call_method(crossbow_instance, "onRenderInit", "()V", &[])?; + env.exception_check()?; + + // TODO: Create wrapper around CrossbowInstance + + Ok(()) +} + +pub(crate) fn crossbow_on_back_pressed(_env: JNIEnv) -> Result<()> { + println!("CrossbowLib_onBackPressed"); + Ok(()) +} + +pub(crate) fn crossbow_on_destroy(_env: JNIEnv) -> Result<()> { + println!("CrossbowLib_onDestroy"); + Ok(()) +} diff --git a/platform/android/src/externs/crossbow_lib.rs b/platform/android/src/externs/crossbow_lib.rs index 92e0297d..74b3d66a 100644 --- a/platform/android/src/externs/crossbow_lib.rs +++ b/platform/android/src/externs/crossbow_lib.rs @@ -1,4 +1,4 @@ -use crate::permission::*; +use crate::{crossbow::*, permission::*}; use jni::{ objects::{JClass, JObject, JString}, sys::jboolean, @@ -12,20 +12,21 @@ pub extern "C" fn Java_com_crossbow_library_CrossbowLib_initialize( _class: JClass, activity: JObject, crossbow_instance: JObject, - _asset_manager: JObject, + asset_manager: JObject, ) { - println!("CrossbowLib_initialize: {:?}", activity); - - env.call_method(crossbow_instance, "onRenderInit", "()V", &[]) - .unwrap(); + crossbow_initialize(env, activity, crossbow_instance, asset_manager).unwrap(); +} - // TODO: Create wrapper around CrossbowInstance +#[no_mangle] +#[allow(non_snake_case)] +pub extern "C" fn Java_com_crossbow_library_CrossbowLib_onBackPressed(env: JNIEnv, _class: JClass) { + crossbow_on_back_pressed(env).unwrap(); } #[no_mangle] #[allow(non_snake_case)] -pub extern "C" fn Java_com_crossbow_library_CrossbowLib_onDestroy(_env: JNIEnv, _class: JClass) { - println!("CrossbowLib_onDestroy"); +pub extern "C" fn Java_com_crossbow_library_CrossbowLib_onDestroy(env: JNIEnv, _class: JClass) { + crossbow_on_destroy(env).unwrap(); } #[no_mangle] diff --git a/platform/android/src/lib.rs b/platform/android/src/lib.rs index 800c88d0..1daea8ec 100644 --- a/platform/android/src/lib.rs +++ b/platform/android/src/lib.rs @@ -1,6 +1,7 @@ pub(crate) mod externs; pub(crate) mod utils; +pub mod crossbow; pub mod error; pub mod permission; pub mod plugin; diff --git a/platform/android/src/permission/android_permission.rs b/platform/android/src/permission/android_permission.rs index 5c7bc5d3..b47a687a 100644 --- a/platform/android/src/permission/android_permission.rs +++ b/platform/android/src/permission/android_permission.rs @@ -239,7 +239,8 @@ pub enum AndroidPermission { /// /// [TvInputService]: https://developer.android.com/reference/android/media/tv/TvInputService BindTvInput, - /// Must be required by a TvInteractiveAppService to ensure that only the system can bind to it. + /// Must be required by a TvInteractiveAppService to ensure that only the system can + /// bind to it. /// /// Protection level: signature|privileged BindTvInteractiveApp, @@ -306,7 +307,10 @@ pub enum AndroidPermission { /// /// Protection level: dangerous BodySensors, - /// Allows an application to access data from sensors that the user uses to measure what is happening inside their body, such as heart rate. If you're requesting this permission, you must also request BODY_SENSORS. Requesting this permission by itself doesn't give you Body sensors access. + /// Allows an application to access data from sensors that the user uses to measure + /// what is happening inside their body, such as heart rate. If you're requesting this + /// permission, you must also request BODY_SENSORS. Requesting this permission by + /// itself doesn't give you Body sensors access. /// /// Protection level: dangerous BodySensorsBackground, @@ -536,8 +540,8 @@ pub enum AndroidPermission { /// granted the READ_EXTERNAL_STORAGE or MANAGE_EXTERNAL_STORAGE} permissions for this /// permission to take effect. /// - /// Even if applications are granted this permission, if applications want to modify or - /// delete media files, they also must get the access by calling + /// Even if applications are granted this permission, if applications want to modify + /// or delete media files, they also must get the access by calling /// MediaStore.createWriteRequest(ContentResolver, Collection), /// MediaStore.createDeleteRequest(ContentResolver, Collection), or /// MediaStore.createTrashRequest(ContentResolver, Collection, boolean). @@ -559,11 +563,14 @@ pub enum AndroidPermission { /// /// [ConnectionService]: https://developer.android.com/reference/android/telecom/ConnectionService ManageOwnCalls, - /// Allows applications to enable/disable wifi auto join. This permission is used to let OEMs grant their trusted app access to a subset of privileged wifi APIs to improve wifi performance. - /// Not for use by third-party applications. + /// Allows applications to enable/disable wifi auto join. This permission is used to + /// let OEMs grant their trusted app access to a subset of privileged wifi APIs to + /// improve wifi performance. Not for use by third-party applications. ManageWifiAutoJoin, - /// Allows applications to get notified when a Wi-Fi interface request cannot be satisfied without tearing down one or more other interfaces, and provide a decision whether to approve the request or reject it. - /// Not for use by third-party applications. + /// Allows applications to get notified when a Wi-Fi interface request cannot be + /// satisfied without tearing down one or more other interfaces, and provide a + /// decision whether to approve the request or reject it. Not for use by + /// third-party applications. ManageWifiInterfaces, /// Not for use by third-party applications. MasterClear, @@ -605,8 +612,9 @@ pub enum AndroidPermission { /// /// Protection level: normal NfcTransactionEvent, - /// Allows an application to modify any wifi configuration, even if created by another application. Once reconfigured the original creator cannot make any further modifications. - /// Not for use by third-party applications. + /// Allows an application to modify any wifi configuration, even if created by another + /// application. Once reconfigured the original creator cannot make any further + /// modifications. Not for use by third-party applications. OverrideWifiConfig, /// Allows an application to collect component usage statistics. /// @@ -637,9 +645,11 @@ pub enum AndroidPermission { /// /// Protection level: normal QueryAllPackages, - /// Allows an application to query over global data in AppSearch that's visible to the ASSISTANT role. + /// Allows an application to query over global data in AppSearch that's visible to the + /// ASSISTANT role. ReadAssistantAppSearchData, - /// Allows read only access to phone state with a non dangerous permission, including the information like cellular network type, software version. + /// Allows read only access to phone state with a non dangerous permission, including + /// the information like cellular network type, software version. ReadBasicPhoneState, /// Allows an application to read the user's calendar data. /// @@ -707,23 +717,36 @@ pub enum AndroidPermission { ReadLogs, /// Allows an application to read audio files from external storage. /// - /// This permission is enforced starting in API level Build.VERSION_CODES.TIRAMISU. For apps with a targetSdkVersion of Build.VERSION_CODES.S or lower, this permission must not be used and the READ_EXTERNAL_STORAGE permission must be used instead. + /// This permission is enforced starting in API level Build.VERSION_CODES.TIRAMISU. + /// For apps with a targetSdkVersion of Build.VERSION_CODES.S or lower, this + /// permission must not be used and the READ_EXTERNAL_STORAGE permission must be used + /// instead. /// /// Protection level: dangerous ReadMediaAudio, /// Allows an application to read image files from external storage. /// - /// This permission is enforced starting in API level Build.VERSION_CODES.TIRAMISU. For apps with a targetSdkVersion of Build.VERSION_CODES.S or lower, this permission must not be used and the READ_EXTERNAL_STORAGE permission must be used instead. + /// This permission is enforced starting in API level Build.VERSION_CODES.TIRAMISU. + /// For apps with a targetSdkVersion of Build.VERSION_CODES.S or lower, this + /// permission must not be used and the READ_EXTERNAL_STORAGE permission must be used + /// instead. /// /// Protection level: dangerous ReadMediaImage, /// Allows an application to read audio files from external storage. /// - /// This permission is enforced starting in API level Build.VERSION_CODES.TIRAMISU. For apps with a targetSdkVersion of Build.VERSION_CODES.S or lower, this permission must not be used and the READ_EXTERNAL_STORAGE permission must be used instead. + /// This permission is enforced starting in API level Build.VERSION_CODES.TIRAMISU. + /// For apps with a targetSdkVersion of Build.VERSION_CODES.S or lower, this + /// permission must not be used and the READ_EXTERNAL_STORAGE permission must be used + /// instead. /// /// Protection level: dangerous ReadMediaVideo, - /// Allows an application to read nearby streaming policy. The policy controls whether to allow the device to stream its notifications and apps to nearby devices. Applications that are not the device owner will need this permission to call DevicePolicyManager.getNearbyNotificationStreamingPolicy() or DevicePolicyManager.getNearbyAppStreamingPolicy(). + /// Allows an application to read nearby streaming policy. The policy controls whether + /// to allow the device to stream its notifications and apps to nearby devices. + /// Applications that are not the device owner will need this permission to call + /// DevicePolicyManager.getNearbyNotificationStreamingPolicy() or + /// DevicePolicyManager.getNearbyAppStreamingPolicy(). ReadNearbyStreamingPolicy, /// Allows read access to the device's phone number(s). This is a subset of /// the capabilities granted by READ_PHONE_STATE but is exposed to instant @@ -816,15 +839,23 @@ pub enum AndroidPermission { /// /// Protection level: normal ReorderTasks, - /// Allows an application to read nearby streaming policy. The policy controls whether to allow the device to stream its notifications and apps to nearby devices. Applications that are not the device owner will need this permission to call DevicePolicyManager.getNearbyNotificationStreamingPolicy() or DevicePolicyManager.getNearbyAppStreamingPolicy(). + /// Allows an application to read nearby streaming policy. The policy controls whether + /// to allow the device to stream its notifications and apps to nearby devices. + /// Applications that are not the device owner will need this permission to call + /// DevicePolicyManager.getNearbyNotificationStreamingPolicy() or + /// DevicePolicyManager.getNearbyAppStreamingPolicy(). /// /// Not for use by third-party applications. RequestCompanionProfileAppStreaming, - /// Allows application to request to be associated with a vehicle head unit capable of automotive projection (AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION) by CompanionDeviceManager. + /// Allows application to request to be associated with a vehicle head unit capable of + /// automotive projection (AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION) by + /// CompanionDeviceManager. /// /// Not for use by third-party applications. RequestCompanionProfileAutomotiveProjection, - /// Allows application to request to be associated with a computer to share functionality and/or data with other devices, such as notifications, photos and media (AssociationRequest.DEVICE_PROFILE_COMPUTER) by CompanionDeviceManager. + /// Allows application to request to be associated with a computer to share + /// functionality and/or data with other devices, such as notifications, photos and + /// media (AssociationRequest.DEVICE_PROFILE_COMPUTER) by CompanionDeviceManager. /// /// Not for use by third-party applications. RequestCompanionProfileComputer, @@ -946,11 +977,11 @@ pub enum AndroidPermission { /// /// Protection level: signature|appop SMSFinancialTransactions, - /// Allows an application to start foreground services from the background at any time. - /// This permission is not for use by third-party applications, with the only - /// exception being if the app is the default SMS app. Otherwise, it's only usable by - /// privileged apps, app verifier app, and apps with any of the EMERGENCY or SYSTEM - /// GALLERY roles. + /// Allows an application to start foreground services from the background at any + /// time. This permission is not for use by third-party applications, with the + /// only exception being if the app is the default SMS app. Otherwise, it's only + /// usable by privileged apps, app verifier app, and apps with any of the + /// EMERGENCY or SYSTEM GALLERY roles. StartForegroundServicesFromBackground, /// Allows the holder to start the screen with a list of app features. /// @@ -1001,9 +1032,13 @@ pub enum AndroidPermission { /// /// Protection level: normal UseBiometric, - /// Allows apps to use exact alarms just like with SCHEDULE_EXACT_ALARM but without needing to request this permission from the user. + /// Allows apps to use exact alarms just like with SCHEDULE_EXACT_ALARM but without + /// needing to request this permission from the user. /// - /// This is only for apps that rely on exact alarms for their core functionality. App stores may enforce policies to audit and review the use of this permission. Any app that requests this but is found to not require exact alarms for its primary function may be removed from the app store. + /// This is only for apps that rely on exact alarms for their core functionality. App + /// stores may enforce policies to audit and review the use of this permission. Any + /// app that requests this but is found to not require exact alarms for its primary + /// function may be removed from the app store. UseExactAlarm, /// This constant was deprecated in API level 28. Applications should request /// [USE_BIOMETRIC] instead. diff --git a/platform/android/src/permission/request_permission.rs b/platform/android/src/permission/request_permission.rs index 93f7a4cc..e923eb2f 100644 --- a/platform/android/src/permission/request_permission.rs +++ b/platform/android/src/permission/request_permission.rs @@ -54,7 +54,8 @@ pub fn permission_status<'a>( Ok((permission_granted, permission_denied)) } -/// Provides checking permission status in the application and will request permission if it is denied. +/// Provides checking permission status in the application and will request permission if +/// it is denied. pub fn request_permission(permission: &AndroidPermission) -> Result { let (_, vm) = crate::create_java_vm()?; let jnienv = vm.attach_current_thread()?; diff --git a/platform/android/src/plugin/jni_singleton.rs b/platform/android/src/plugin/jni_singleton.rs index dd0767aa..00450052 100644 --- a/platform/android/src/plugin/jni_singleton.rs +++ b/platform/android/src/plugin/jni_singleton.rs @@ -91,6 +91,7 @@ impl JniSingleton { method.signature.ret.clone(), args, )?; + env.exception_check()?; Ok(result) } } diff --git a/platform/ios/Cargo.toml b/platform/ios/Cargo.toml index c5d74423..48d523fe 100644 --- a/platform/ios/Cargo.toml +++ b/platform/ios/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "crossbow-ios" -version = "0.1.7" +version = "0.1.8" edition = "2021" authors = ["DodoRare Team "] -description = "Cross-Platform Rust Toolkit for Games ๐Ÿน" +description = "Cross-Platform build tools and toolkit for games" repository = "https://github.com/dodorare/crossbow" license = "Apache-2.0" keywords = ["crossbow", "ios", "port"] diff --git a/platform/ios/src/permission/types.rs b/platform/ios/src/permission/types.rs index e9994be2..c51ada72 100644 --- a/platform/ios/src/permission/types.rs +++ b/platform/ios/src/permission/types.rs @@ -5,7 +5,8 @@ use cocoa_foundation::{base::id, foundation::NSUInteger}; pub enum IosPermission { /// EKEventStore. /// - /// An object that accesses the userโ€™s calendar events and reminders and supports the scheduling of new events. + /// An object that accesses the userโ€™s calendar events and reminders and supports the + /// scheduling of new events. /// /// More details: https://developer.apple.com/documentation/eventkit/ekeventstore EventStore(EntityType), @@ -48,7 +49,8 @@ pub enum IosPermission { MotionActivityManager, /// CLLocationManager. /// - /// The object that you use to start and stop the delivery of location-related events to your app. + /// The object that you use to start and stop the delivery of location-related events + /// to your app. /// /// More details: https://developer.apple.com/documentation/corelocation/cllocationmanager LocationManager(LocationAuthorizationType), @@ -59,7 +61,8 @@ pub enum IosPermission { pub enum LocationAuthorizationType { /// Requests the userโ€™s permission to use location services while the app is in use. WhenInUse, - /// Requests the userโ€™s permission to use location services regardless of whether the app is in use. + /// Requests the userโ€™s permission to use location services regardless of whether the + /// app is in use. Always, } @@ -100,7 +103,8 @@ impl Into for &MediaType { pub enum AccessLevel { /// A value that indicates the app may only add to the userโ€™s photo library. AddOnly, - /// A value that indicates the app can read from and write to the userโ€™s photo library. + /// A value that indicates the app can read from and write to the userโ€™s photo + /// library. ReadWrite, } @@ -122,7 +126,8 @@ impl Into for &AccessLevel { pub enum AuthorizationStatus { /// The user hasnโ€™t set the appโ€™s authorization status. NotDetermined, - /// The app isnโ€™t authorized to access the photo library, and the user canโ€™t grant such permission. + /// The app isnโ€™t authorized to access the photo library, and the user canโ€™t grant + /// such permission. Restricted, /// The user explicitly denied this app access to the photo library. Denied, diff --git a/plugins/admob/Cargo.toml b/plugins/admob/Cargo.toml index b84333fa..42f250e5 100644 --- a/plugins/admob/Cargo.toml +++ b/plugins/admob/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "crossbow-admob" -version = "0.1.7" +version = "0.1.8" edition = "2021" authors = ["DodoRare Team "] description = "AdMob Plugin for Crossbow" @@ -10,4 +10,4 @@ keywords = ["crossbow", "google", "admob", "android", "ads"] readme = "README.md" [dependencies] -crossbow-android = { path = "../../platform/android", version = "0.1.7" } +crossbow-android = { path = "../../platform/android", version = "0.1.8" } diff --git a/plugins/admob/README.md b/plugins/admob/README.md index c533d7ef..39a09a91 100644 --- a/plugins/admob/README.md +++ b/plugins/admob/README.md @@ -22,15 +22,15 @@ Just add Rust dependencies like this: ```toml [dependencies] -crossbow = "0.1.7" -crossbow-admob = "0.1.7" +crossbow = "0.1.8" +crossbow-admob = "0.1.8" ``` And finally, add this to your Crossbow Android configuration: ```toml [package.metadata.android] -plugins_remote = ["com.crossbow.admob:admob:0.1.7"] +plugins_remote = ["com.crossbow.admob:admob:0.1.8"] ``` > That's it, now you can start using AdMob ads! @@ -38,7 +38,7 @@ plugins_remote = ["com.crossbow.admob:admob:0.1.7"] If you want to configure custom APPLICATION_ID add this to your Cargo.toml file: ```toml -[[package.metadata.android.meta_data]] +[[package.metadata.android.manifest.application.meta_data]] name = "com.google.android.gms.ads.APPLICATION_ID" value = "" # By default: ca-app-pub-3940256099942544~3347511713 diff --git a/plugins/admob/android/config.gradle b/plugins/admob/android/config.gradle index 54a07f96..ca588348 100644 --- a/plugins/admob/android/config.gradle +++ b/plugins/admob/android/config.gradle @@ -1,5 +1,5 @@ ext.versions = [ - crossbowLibrary : "0.1.7", + crossbowLibrary : "0.1.8", androidGradlePlugin: "7.0.0", compileSdk : 31, minSdk : 19, diff --git a/plugins/admob/android/src/com/crossbow/admob/AdMob.kt b/plugins/admob/android/src/com/crossbow/admob/AdMob.kt index 92292ee5..f1d1ccb7 100644 --- a/plugins/admob/android/src/com/crossbow/admob/AdMob.kt +++ b/plugins/admob/android/src/com/crossbow/admob/AdMob.kt @@ -162,18 +162,18 @@ class AdMob(crossbow: Crossbow) : CrossbowPlugin(crossbow) { ) { if (!aIsInitialized) { aIsForChildDirectedTreatment = pIsForChildDirectedTreatment - aConsentInformation = UserMessagingPlatform.getConsentInformation(aActivity) + aConsentInformation = UserMessagingPlatform.getConsentInformation(aActivity!!) aIsTestEuropeUserConsent = pIsTestEuropeUserConsent setMobileAdsRequestConfiguration( aIsForChildDirectedTreatment, pMaxAdContentRating, pIsReal - ) //First call MobileAds.setRequestConfiguration https://groups.google.com/g/google-admob-ads-sdk/c/17oVu0sABjs - MobileAds.initialize(aActivity) { initializationStatus -> + ) // First call MobileAds.setRequestConfiguration https://groups.google.com/g/google-admob-ads-sdk/c/17oVu0sABjs + MobileAds.initialize(aActivity!!) { initializationStatus -> val statusGADMobileAds: Int = Objects.requireNonNull( initializationStatus.getAdapterStatusMap() - .get("com.google.android.gms.ads.MobileAds") - )!!.getInitializationState().ordinal + .get("com.google.android.gms.ads.MobileAds")!! + ).getInitializationState().ordinal if (statusGADMobileAds == 0) { aIsInitialized = false } else if (statusGADMobileAds == 1) { @@ -186,14 +186,14 @@ class AdMob(crossbow: Crossbow) : CrossbowPlugin(crossbow) { @ExposedToCrossbow fun request_user_consent() { - aConsentInformation = UserMessagingPlatform.getConsentInformation(aActivity) + aConsentInformation = UserMessagingPlatform.getConsentInformation(aActivity!!) val paramsBuilder: ConsentRequestParameters.Builder = ConsentRequestParameters.Builder().setTagForUnderAgeOfConsent(aIsForChildDirectedTreatment) val params: ConsentRequestParameters params = if (aIsTestEuropeUserConsent) //https://developers.google.com/admob/ump/android/quick-start#testing { - val debugSettings: ConsentDebugSettings = ConsentDebugSettings.Builder(aActivity) + val debugSettings: ConsentDebugSettings = ConsentDebugSettings.Builder(aActivity!!) .setDebugGeography(ConsentDebugSettings.DebugGeography.DEBUG_GEOGRAPHY_EEA) .addTestDeviceHashedId(deviceId) .build() @@ -201,7 +201,7 @@ class AdMob(crossbow: Crossbow) : CrossbowPlugin(crossbow) { } else { paramsBuilder.build() } - aConsentInformation!!.requestConsentInfoUpdate(aActivity, params, + aConsentInformation!!.requestConsentInfoUpdate(aActivity!!, params, { if (aConsentInformation!!.isConsentFormAvailable()) { emitSignal("consent_info_update_success", "Consent Form Available") @@ -236,16 +236,15 @@ class AdMob(crossbow: Crossbow) : CrossbowPlugin(crossbow) { aActivity!!.runOnUiThread(Runnable { if (aIsInitialized) { if (aAdView != null) destroy_banner() - aAdView = AdView(aActivity) - aAdView!!.setAdUnitId(pAdUnitId) + aAdView = AdView(aActivity!!) + aAdView!!.setAdUnitId(pAdUnitId!!) when (pSize) { "BANNER" -> aAdView!!.setAdSize(AdSize.BANNER) "LARGE_BANNER" -> aAdView!!.setAdSize(AdSize.LARGE_BANNER) "MEDIUM_RECTANGLE" -> aAdView!!.setAdSize(AdSize.MEDIUM_RECTANGLE) "FULL_BANNER" -> aAdView!!.setAdSize(AdSize.FULL_BANNER) "LEADERBOARD" -> aAdView!!.setAdSize(AdSize.LEADERBOARD) - "ADAPTIVE" -> aAdView!!.setAdSize(adSizeAdaptive) - else -> aAdView!!.setAdSize(AdSize.SMART_BANNER) + else -> aAdView!!.setAdSize(adSizeAdaptive) } aAdSize = aAdView!!.getAdSize() //store AdSize of banner due a bug (throws error when do aAdView!!.getAdSize() called by Crossbow) @@ -296,11 +295,11 @@ class AdMob(crossbow: Crossbow) : CrossbowPlugin(crossbow) { if (pPosition == 0) //BOTTOM { aCrossbowLayoutParams!!.gravity = Gravity.BOTTOM - if (pRespectSafeArea) aAdView!!.setY(-safeArea.bottom as Float) //Need to validate if this value will be positive or negative + if (pRespectSafeArea) aAdView!!.setY(-safeArea.bottom.toFloat()) // Need to validate if this value will be positive or negative } else if (pPosition == 1) //TOP { aCrossbowLayoutParams!!.gravity = Gravity.TOP - if (pRespectSafeArea) aAdView!!.setY(safeArea.top as Float) + if (pRespectSafeArea) aAdView!!.setY(safeArea.top.toFloat()) } aCrossbowLayout!!.addView(aAdView, aCrossbowLayoutParams) aAdView!!.loadAd(adRequest) @@ -326,7 +325,7 @@ class AdMob(crossbow: Crossbow) : CrossbowPlugin(crossbow) { fun show_banner() { aActivity!!.runOnUiThread(Runnable { if (aIsInitialized && aAdView != null) { - if (aAdView!!.getVisibility() !== View.VISIBLE) { + if (aAdView!!.getVisibility() == View.VISIBLE) { aAdView!!.setVisibility(View.VISIBLE) aAdView!!.resume() } @@ -338,7 +337,7 @@ class AdMob(crossbow: Crossbow) : CrossbowPlugin(crossbow) { fun hide_banner() { aActivity!!.runOnUiThread(Runnable { if (aIsInitialized && aAdView != null) { - if (aAdView!!.getVisibility() !== View.GONE) { + if (aAdView!!.getVisibility() == View.GONE) { aAdView!!.setVisibility(View.GONE) aAdView!!.pause() } @@ -363,14 +362,14 @@ class AdMob(crossbow: Crossbow) : CrossbowPlugin(crossbow) { @ExposedToCrossbow fun get_banner_width_in_pixels(): Int { return if (aIsInitialized && aAdSize != null) { - aAdSize!!.getWidthInPixels(aActivity) + aAdSize!!.getWidthInPixels(aActivity!!) } else 0 } @ExposedToCrossbow fun get_banner_height_in_pixels(): Int { return if (aIsInitialized && aAdSize != null) { - aAdSize!!.getHeightInPixels(aActivity) + aAdSize!!.getHeightInPixels(aActivity!!) } else 0 } @@ -381,8 +380,8 @@ class AdMob(crossbow: Crossbow) : CrossbowPlugin(crossbow) { aActivity!!.runOnUiThread(Runnable { if (aIsInitialized) { InterstitialAd.load( - aActivity, - pAdUnitId, + aActivity!!, + pAdUnitId!!, adRequest, object : InterstitialAdLoadCallback() { override fun onAdLoaded(interstitialAd: InterstitialAd) { @@ -428,7 +427,7 @@ class AdMob(crossbow: Crossbow) : CrossbowPlugin(crossbow) { aActivity!!.runOnUiThread(Runnable { if (aIsInitialized) { if (aInterstitialAd != null) { - aInterstitialAd!!.show(aActivity) + aInterstitialAd!!.show(aActivity!!) } } }) @@ -440,7 +439,7 @@ class AdMob(crossbow: Crossbow) : CrossbowPlugin(crossbow) { fun load_rewarded(pAdUnitId: String?) { aActivity!!.runOnUiThread(Runnable { if (aIsInitialized) { - RewardedAd.load(aActivity, pAdUnitId, adRequest, object : RewardedAdLoadCallback() { + RewardedAd.load(aActivity!!, pAdUnitId!!, adRequest, object : RewardedAdLoadCallback() { override fun onAdFailedToLoad(loadAdError: LoadAdError) { // Handle the error. aRewardedAd = null @@ -481,7 +480,7 @@ class AdMob(crossbow: Crossbow) : CrossbowPlugin(crossbow) { aIsRewardedLoaded = false } }) - aRewardedAd!!.show(aActivity) { rewardItem -> + aRewardedAd!!.show(aActivity!!) { rewardItem -> // Handle the reward. emitSignal( "user_earned_rewarded", @@ -499,8 +498,8 @@ class AdMob(crossbow: Crossbow) : CrossbowPlugin(crossbow) { aActivity!!.runOnUiThread(Runnable { if (aIsInitialized) { RewardedInterstitialAd.load( - aActivity, - pAdUnitId, + aActivity!!, + pAdUnitId!!, adRequest, object : RewardedInterstitialAdLoadCallback() { override fun onAdFailedToLoad(loadAdError: LoadAdError) { @@ -548,7 +547,7 @@ class AdMob(crossbow: Crossbow) : CrossbowPlugin(crossbow) { aIsRewardedInterstitialLoaded = false } }) - aRewardedInterstitialAd!!.show(aActivity) { rewardItem -> + aRewardedInterstitialAd!!.show(aActivity!!) { rewardItem -> // Handle the reward. emitSignal( "user_earned_rewarded", @@ -563,13 +562,13 @@ class AdMob(crossbow: Crossbow) : CrossbowPlugin(crossbow) { private fun loadConsentForm() { UserMessagingPlatform.loadConsentForm( - aActivity, + aActivity!!, { consentForm -> var consentStatusMsg = "" - if (aConsentInformation!!.getConsentStatus() === ConsentInformation.ConsentStatus.REQUIRED) { + if (aConsentInformation!!.getConsentStatus() == ConsentInformation.ConsentStatus.REQUIRED) { consentForm.show( - aActivity - ) { formError -> + aActivity!! + ) { _ -> loadConsentForm() emitSignal("consent_form_dismissed") } @@ -619,13 +618,13 @@ class AdMob(crossbow: Crossbow) : CrossbowPlugin(crossbow) { } private val adRequest: AdRequest - private get() { + get() { val adRequestBuilder = AdRequest.Builder() return adRequestBuilder.build() } private val safeArea: Rect - private get() { + get() { val safeInsetRect = Rect() if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { return safeInsetRect @@ -648,11 +647,19 @@ class AdMob(crossbow: Crossbow) : CrossbowPlugin(crossbow) { // Determine the screen width (less decorations) to use for the ad width. private val adSizeAdaptive: AdSize // If the ad hasn't been laid out, default to the full screen width. - private get() { + get() { // Determine the screen width (less decorations) to use for the ad width. - val display: Display = aActivity!!.getWindowManager().getDefaultDisplay() val outMetrics = DisplayMetrics() - display.getMetrics(outMetrics) + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { + val display = aActivity!!.display + @Suppress("DEPRECATION") + display?.getRealMetrics(outMetrics) + } else { + @Suppress("DEPRECATION") + val display = aActivity!!.windowManager.defaultDisplay + @Suppress("DEPRECATION") + display.getMetrics(outMetrics) + } val density: Float = outMetrics.density var adWidthPixels: Float = aCrossbowLayout!!.getWidth().toFloat() @@ -661,7 +668,7 @@ class AdMob(crossbow: Crossbow) : CrossbowPlugin(crossbow) { adWidthPixels = outMetrics.widthPixels.toFloat() } val adWidth = (adWidthPixels / density).toInt() - return AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(aActivity, adWidth) + return AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(aActivity!!, adWidth) } /** @@ -695,7 +702,7 @@ class AdMob(crossbow: Crossbow) : CrossbowPlugin(crossbow) { * @return String Device ID */ private val deviceId: String - private get() { + get() { val android_id = Settings.Secure.getString( aActivity!!.getContentResolver(), Settings.Secure.ANDROID_ID diff --git a/crossbundle/tools/rustfmt.toml b/rustfmt.toml similarity index 100% rename from crossbundle/tools/rustfmt.toml rename to rustfmt.toml diff --git a/src/permission/request_permission.rs b/src/permission/request_permission.rs index e3fd25fe..6aa9d59e 100644 --- a/src/permission/request_permission.rs +++ b/src/permission/request_permission.rs @@ -4,9 +4,10 @@ use crate::error::*; use crossbow_android::permission::*; #[cfg(all(target_os = "ios", feature = "ios"))] use crossbow_ios::permission::*; +use serde::{Deserialize, Serialize}; /// Generic Permission type. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] pub enum Permission { /// Read Access to the Calendar. /// @@ -17,6 +18,7 @@ pub enum Permission { /// /// Required Permissions for **iOS**: /// * **NSCalendarsUsageDescription** + #[serde(rename = "calendar-read")] CalendarRead, /// Read and Write Access to the Calendar. /// @@ -27,6 +29,7 @@ pub enum Permission { /// /// Required Permissions for **iOS**: /// * **NSCalendarsUsageDescription** + #[serde(rename = "calendar-write")] CalendarWrite, /// Access to the Camera. /// @@ -37,6 +40,7 @@ pub enum Permission { /// /// Required Permissions for **iOS**: /// * **NSCameraUsageDescription** + #[serde(rename = "camera")] Camera, /// Read Access to the Contacts. /// @@ -47,6 +51,7 @@ pub enum Permission { /// /// Required Permissions for **iOS**: /// * **NSContactsUsageDescription** + #[serde(rename = "contacts-read")] ContactsRead, /// Read and Write Access to the Contacts. /// @@ -57,6 +62,7 @@ pub enum Permission { /// /// Required Permissions for **iOS**: /// * **NSContactsUsageDescription** + #[serde(rename = "contacts-write")] ContactsWrite, /// Access to the Flashlight. /// @@ -65,6 +71,7 @@ pub enum Permission { /// Required Permissions for **Android**: /// * **android.permission.CAMERA** /// * **android.permission.FLASHLIGHT** + #[serde(rename = "flashlight")] Flashlight, /// Access to the Location when in use. /// @@ -76,6 +83,7 @@ pub enum Permission { /// /// Required Permissions for **iOS**: /// * **NSLocationWhenInUseUsageDescription** + #[serde(rename = "location-when-in-use")] LocationWhenInUse, /// Permanent Access to the Location. /// @@ -89,6 +97,7 @@ pub enum Permission { /// Required Permissions for **iOS**: /// * **NSLocationAlwaysAndWhenInUseUsageDescription** /// * **NSLocationAlwaysUsageDescription** + #[serde(rename = "location-always")] LocationAlways, /// Access to the Media. /// @@ -96,6 +105,7 @@ pub enum Permission { /// /// Required Permissions for **iOS**: /// * **NSAppleMusicUsageDescription** + #[serde(rename = "media")] Media, /// Access to the Microphone. /// @@ -106,6 +116,7 @@ pub enum Permission { /// /// Required Permissions for **iOS**: /// * **NSMicrophoneUsageDescription** + #[serde(rename = "microphone")] Microphone, /// Access to the Phone. /// @@ -120,6 +131,7 @@ pub enum Permission { /// * **android.permission.USE_SIP** /// * **android.permission.ANSWER_PHONE_CALLS** /// * **android.permission.PROCESS_OUTGOING_CALLS** + #[serde(rename = "phone")] Phone, /// Access to the Photos. /// @@ -128,6 +140,7 @@ pub enum Permission { /// Required Permissions for **iOS**: /// * **NSPhotoLibraryAddUsageDescription** /// * **NSPhotoLibraryUsageDescription** + #[serde(rename = "photos")] Photos, /// Access to the Reminders. /// @@ -135,6 +148,7 @@ pub enum Permission { /// /// Required Permissions for **iOS**: /// * **NSRemindersUsageDescription** + #[serde(rename = "reminders")] Reminders, /// Access to the Sensors. /// @@ -145,6 +159,7 @@ pub enum Permission { /// /// Required Permissions for **iOS**: /// * **NSMotionUsageDescription** + #[serde(rename = "sensors")] Sensors, /// Access to the SMS. /// @@ -156,6 +171,7 @@ pub enum Permission { /// * **android.permission.READ_SMS** /// * **android.permission.RECEIVE_WAP_PUSH** /// * **android.permission.RECEIVE_MMS** + #[serde(rename = "sms")] Sms, /// Access to the Speech Service. /// @@ -166,6 +182,7 @@ pub enum Permission { /// /// Required Permissions for **iOS**: /// * **NSSpeechRecognitionUsageDescription** + #[serde(rename = "speech")] Speech, /// Read Access to the Storage. /// @@ -173,6 +190,7 @@ pub enum Permission { /// /// Required Permissions for **Android**: /// * **android.permission.READ_EXTERNAL_STORAGE** + #[serde(rename = "storage-read")] StorageRead, /// Read and Write Access to the Storage. /// @@ -180,6 +198,7 @@ pub enum Permission { /// /// Required Permissions for **Android**: /// * **android.permission.WRITE_EXTERNAL_STORAGE** + #[serde(rename = "storage-write")] StorageWrite, } @@ -416,4 +435,158 @@ impl Permission { } } } + + #[cfg(feature = "update-manifest")] + pub fn update_manifest(&self, manifest: &mut android_manifest::AndroidManifest) { + let mut permissions = vec![]; + let mut add_p = |p: &str| { + permissions.push(android_manifest::UsesPermission { + name: Some(p.to_owned()), + ..Default::default() + }); + }; + match self { + Permission::CalendarRead => { + add_p("android.permission.READ_CALENDAR"); + } + Permission::CalendarWrite => { + add_p("android.permission.WRITE_CALENDAR"); + } + Permission::Camera => { + add_p("android.permission.CAMERA"); + } + Permission::ContactsRead => { + add_p("android.permission.READ_CONTACTS"); + } + Permission::ContactsWrite => { + add_p("android.permission.WRITE_CONTACTS"); + } + Permission::Flashlight => { + add_p("android.permission.CAMERA"); + add_p("android.permission.FLASHLIGHT"); + } + Permission::LocationWhenInUse => { + add_p("android.permission.ACCESS_COARSE_LOCATION"); + add_p("android.permission.ACCESS_FINE_LOCATION"); + } + Permission::LocationAlways => { + add_p("android.permission.ACCESS_COARSE_LOCATION"); + add_p("android.permission.ACCESS_FINE_LOCATION"); + add_p("android.permission.ACCESS_BACKGROUND_LOCATION"); + } + Permission::Media => {} + Permission::Microphone => { + add_p("android.permission.RECORD_AUDIO"); + } + Permission::Phone => { + add_p("android.permission.READ_PHONE_STATE"); + add_p("android.permission.CALL_PHONE"); + add_p("android.permission.READ_CALL_LOG"); + add_p("android.permission.WRITE_CALL_LOG"); + add_p("android.permission.ADD_VOICEMAIL"); + add_p("android.permission.USE_SIP"); + add_p("android.permission.ANSWER_PHONE_CALLS"); + add_p("android.permission.PROCESS_OUTGOING_CALLS"); + } + Permission::Photos => {} + Permission::Reminders => {} + Permission::Sensors => { + add_p("android.permission.BODY_SENSORS"); + } + Permission::Sms => { + add_p("android.permission.RECEIVE_SMS"); + add_p("android.permission.SEND_SMS"); + add_p("android.permission.READ_SMS"); + add_p("android.permission.RECEIVE_WAP_PUSH"); + add_p("android.permission.RECEIVE_MMS"); + } + Permission::Speech => { + add_p("android.permission.RECORD_AUDIO"); + } + Permission::StorageRead => { + add_p("android.permission.READ_EXTERNAL_STORAGE"); + } + Permission::StorageWrite => { + add_p("android.permission.WRITE_EXTERNAL_STORAGE"); + } + } + // If AndroidManifest already has permission we want to add - don't add it. + let mut filtered = permissions + .iter() + .filter(|p| !manifest.uses_permission.iter().any(|x| x.name == p.name)) + .cloned() + .collect(); + manifest.uses_permission.append(&mut filtered); + } + + #[cfg(feature = "update-manifest")] + pub fn update_info_plist(&self, props: &mut apple_bundle::info_plist::InfoPlist) { + match self { + Permission::CalendarRead => { + props.calendar_and_reminders.calendars_usage_description = + Some("This app needs access to your phone's calendar.".to_string()); + } + Permission::CalendarWrite => { + props.calendar_and_reminders.calendars_usage_description = + Some("This app needs access to your phone's calendar.".to_string()); + } + Permission::Camera => { + props.camera_and_microphone.camera_usage_description = + Some("This app needs access to your phone's camera.".to_string()); + } + Permission::ContactsRead => { + props.contacts.contacts_usage_description = + Some("This app needs access to your phone's contacts.".to_string()); + } + Permission::ContactsWrite => { + props.contacts.contacts_usage_description = + Some("This app needs access to your phone's contacts.".to_string()); + } + Permission::Flashlight => {} + Permission::LocationWhenInUse => { + props.location.location_when_in_use_usage_description = + Some("This app needs access to your phone's location when in use.".to_string()); + } + Permission::LocationAlways => { + props + .location + .location_always_and_when_in_use_usage_description = + Some("This app needs permanent access to your phone's location.".to_string()); + // #[warn(deprecated)] + // props.location.location_always_usage_description = + // Some("This app needs permanent access to your phone's + // location.".to_string()); + } + Permission::Media => { + props.media_player.apple_music_usage_description = + Some("This app needs access to your phone's Apple Music.".to_string()); + } + Permission::Microphone => { + props.camera_and_microphone.microphone_usage_description = + Some("This app needs access to your phone's microphone.".to_string()); + } + Permission::Phone => {} + Permission::Photos => { + props.photos.photo_library_add_usage_description = + Some("This app needs add access to your phone's photo library.".to_string()); + props.photos.photo_library_usage_description = + Some("This app needs access to your phone's photo library.".to_string()); + } + Permission::Reminders => { + props.calendar_and_reminders.reminders_usage_description = + Some("This app needs access to your phone's reminders.".to_string()); + } + Permission::Sensors => { + props.motion.motion_usage_description = + Some("This app needs access to your phone's motion sensors.".to_string()); + } + Permission::Sms => {} + Permission::Speech => { + props.speech.speech_recognition_usage_description = + Some("This app needs access to your phone's speech recognition.".to_string()); + } + Permission::StorageRead => {} + Permission::StorageWrite => {} + } + } }