-
Notifications
You must be signed in to change notification settings - Fork 882
cpp: add Android backend support #11628
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 8 commits
5f73e4f
eb2fdad
0aa0fe8
47920be
079f353
b7fd64c
85ce0f0
bb30ab0
841152a
b633213
a6ac20d
3107315
6493c4e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,6 +24,39 @@ endif () | |
| list(PREPEND CMAKE_MODULE_PATH ${Corrosion_SOURCE_DIR}/cmake) | ||
| find_package(Rust 1.92 REQUIRED MODULE) | ||
|
|
||
| # When building for Android (NDK toolchain sets CMAKE_SYSTEM_NAME to "Android"), | ||
| # auto-configure the Rust target, enable the android backend, and set sensible defaults. | ||
| # This block must run before the feature options are defined so that the forced cache values | ||
| # are picked up by define_cargo_feature / define_cargo_dependent_feature. | ||
| if(ANDROID) | ||
| # Map Android ABI to Rust target triple | ||
| if(ANDROID_ABI STREQUAL "arm64-v8a") | ||
| set(Rust_CARGO_TARGET "aarch64-linux-android" CACHE STRING "" FORCE) | ||
| elseif(ANDROID_ABI STREQUAL "armeabi-v7a") | ||
| set(Rust_CARGO_TARGET "armv7-linux-androideabi" CACHE STRING "" FORCE) | ||
| elseif(ANDROID_ABI STREQUAL "x86_64") | ||
| set(Rust_CARGO_TARGET "x86_64-linux-android" CACHE STRING "" FORCE) | ||
| elseif(ANDROID_ABI STREQUAL "x86") | ||
| set(Rust_CARGO_TARGET "i686-linux-android" CACHE STRING "" FORCE) | ||
| else() | ||
| message(FATAL_ERROR "Unsupported ANDROID_ABI: ${ANDROID_ABI}") | ||
| endif() | ||
|
|
||
| # Android requires a shared library | ||
| set(BUILD_SHARED_LIBS ON CACHE BOOL "" FORCE) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if these defaults could be set at the source instead of here with these kind of ugly overrides. We're in the right file, aren't we?:)
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree. Although these are not default but mandatory features
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated the comment to say "Android requires the skia renderer and cannot use desktop backends".
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could integrate it into the Want me to try that approach? It would replace the FORCE overrides with conditions on the feature definitions themselves.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If mandatory perhaps that should become an option in the macro/function. But IMO it's sufficient if defaults are on/off correctly and it's all in one place. My concern is that if we remove/rename the feature we need to look at multiple places. (Not a super big issue but worth research if it can be done better) |
||
|
|
||
| # Enable skia renderer and disable desktop backends for Android | ||
| set(SLINT_FEATURE_RENDERER_SKIA ON CACHE BOOL "" FORCE) | ||
| set(SLINT_FEATURE_BACKEND_WINIT OFF CACHE BOOL "" FORCE) | ||
| set(SLINT_FEATURE_BACKEND_QT OFF CACHE BOOL "" FORCE) | ||
| set(SLINT_FEATURE_RENDERER_FEMTOVG OFF CACHE BOOL "" FORCE) | ||
|
|
||
| # Default to Material style on Android | ||
| if(NOT SLINT_STYLE) | ||
| set(SLINT_STYLE "material" CACHE STRING "" FORCE) | ||
| endif() | ||
| endif() | ||
|
|
||
| option(BUILD_SHARED_LIBS "Build Slint as shared library" ON) | ||
| option(SLINT_FEATURE_COMPILER "Enable support for compiling .slint files to C++ ahead of time" ON) | ||
| add_feature_info(SLINT_FEATURE_COMPILER SLINT_FEATURE_COMPILER "Enable support for compiling .slint files to C++ ahead of time") | ||
|
|
@@ -306,6 +339,41 @@ if (SLINT_BUILD_RUNTIME) | |
| ) | ||
| endif() | ||
|
|
||
| # Propagate Android NDK/SDK paths to the Rust build so that the android-activity | ||
| # backend can compile Java sources and produce DEX bytecode. | ||
| if(ANDROID) | ||
| if(DEFINED ENV{ANDROID_NDK_ROOT}) | ||
| set_property( | ||
| TARGET slint_cpp | ||
| APPEND | ||
| PROPERTY CORROSION_ENVIRONMENT_VARIABLES | ||
| "ANDROID_NDK_ROOT=$ENV{ANDROID_NDK_ROOT}" | ||
| ) | ||
| elseif(CMAKE_ANDROID_NDK) | ||
| set_property( | ||
| TARGET slint_cpp | ||
| APPEND | ||
| PROPERTY CORROSION_ENVIRONMENT_VARIABLES | ||
| "ANDROID_NDK_ROOT=${CMAKE_ANDROID_NDK}" | ||
| ) | ||
| endif() | ||
| if(DEFINED ENV{ANDROID_HOME}) | ||
| set_property( | ||
| TARGET slint_cpp | ||
| APPEND | ||
| PROPERTY CORROSION_ENVIRONMENT_VARIABLES | ||
| "ANDROID_HOME=$ENV{ANDROID_HOME}" | ||
| ) | ||
| elseif(CMAKE_ANDROID_SDK) | ||
| set_property( | ||
| TARGET slint_cpp | ||
| APPEND | ||
| PROPERTY CORROSION_ENVIRONMENT_VARIABLES | ||
| "ANDROID_HOME=${CMAKE_ANDROID_SDK}" | ||
| ) | ||
| endif() | ||
| endif() | ||
|
|
||
| if(SLINT_FEATURE_RENDERER_SKIA OR SLINT_FEATURE_RENDERER_SKIA_OPENGL OR SLINT_FEATURE_RENDERER_SKIA_VULKAN) | ||
| find_program(CLANGCC clang) | ||
| find_program(CLANGCXX clang++) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,10 +3,28 @@ | |
|
|
||
| #pragma once | ||
| #include <tuple> | ||
| #include <utility> | ||
| #include "private/slint_properties_internal.h" | ||
|
|
||
| namespace slint::private_api { | ||
|
|
||
| namespace detail { | ||
| // Custom apply implementation to replace std::apply. | ||
| // NDK r27's libc++ rejects std::apply with const tuple references in some configurations. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why? is that some specific version of libc++? is there some issue for that?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an issue with NDK r27's libc++ (the version shipped with the current LTS NDK). When I don't have a specific upstream issue link for this — I hit it during testing with NDK r27 (27.2.12829862). The custom An alternative would be to avoid passing const tuple refs to |
||
| template<typename F, typename Tuple, std::size_t... I> | ||
| decltype(auto) apply_impl(F &&f, Tuple &&t, std::index_sequence<I...>) | ||
| { | ||
| return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(t))...); | ||
| } | ||
| template<typename F, typename Tuple> | ||
| decltype(auto) apply(F &&f, Tuple &&t) | ||
| { | ||
| return apply_impl( | ||
| std::forward<F>(f), std::forward<Tuple>(t), | ||
| std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>> {}); | ||
| } | ||
|
ruminorix marked this conversation as resolved.
|
||
| } // namespace detail | ||
|
|
||
| /// A Callback stores a function pointer with no parameters and no return value. | ||
| /// It's possible to set that pointer via set_handler() and it can be invoked via call(). This is | ||
| /// used to implement callbacks in the `.slint` language. | ||
|
|
@@ -33,9 +51,9 @@ struct Callback<Ret(Arg...)> | |
| cbindgen_private::slint_callback_set_handler( | ||
| &inner, | ||
| [](void *user_data, const void *arg, void *ret) { | ||
| Tuple args = *reinterpret_cast<const Tuple *>(arg); | ||
| *reinterpret_cast<Ret *>(ret) = | ||
| std::apply(*reinterpret_cast<F *>(user_data), | ||
| *reinterpret_cast<const Tuple *>(arg)); | ||
| detail::apply(*reinterpret_cast<F *>(user_data), std::move(args)); | ||
| }, | ||
| new F(std::move(binding)), | ||
| [](void *user_data) { delete reinterpret_cast<F *>(user_data); }); | ||
|
|
@@ -77,8 +95,8 @@ struct Callback<void(Arg...)> | |
| cbindgen_private::slint_callback_set_handler( | ||
| &inner, | ||
| [](void *user_data, const void *arg, void *) { | ||
| std::apply(*reinterpret_cast<F *>(user_data), | ||
| *reinterpret_cast<const Tuple *>(arg)); | ||
| Tuple args = *reinterpret_cast<const Tuple *>(arg); | ||
| detail::apply(*reinterpret_cast<F *>(user_data), std::move(args)); | ||
| }, | ||
| new F(std::move(binding)), | ||
| [](void *user_data) { delete reinterpret_cast<F *>(user_data); }); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -125,7 +125,7 @@ struct Timer | |
| } | ||
|
|
||
| private: | ||
| uint64_t id = 0; | ||
| uintptr_t id = 0; | ||
| }; | ||
|
|
||
| } // namespace slint | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| // Copyright © SixtyFPS GmbH <info@slint.dev> | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| plugins { | ||
| id("com.android.application") | ||
| } | ||
|
|
||
| android { | ||
| namespace = "dev.slint.usecases" | ||
| compileSdk = 35 | ||
|
|
||
| defaultConfig { | ||
| applicationId = "dev.slint.usecases" | ||
| // API 26 required for InMemoryDexClassLoader used by the Slint Android backend | ||
| minSdk = 26 | ||
| targetSdk = 35 | ||
| versionCode = 1 | ||
| versionName = "1.0" | ||
|
|
||
| ndk { | ||
| // Add other ABIs as needed: "armeabi-v7a", "x86_64", "x86" | ||
| abiFilters += "arm64-v8a" | ||
| } | ||
| } | ||
|
|
||
| buildTypes { | ||
| release { | ||
| isMinifyEnabled = false | ||
| } | ||
| } | ||
|
|
||
| externalNativeBuild { | ||
| cmake { | ||
| path = file("src/main/cpp/CMakeLists.txt") | ||
| version = "3.22.1" | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| <!-- Copyright © SixtyFPS GmbH <info@slint.dev> --> | ||
| <!-- SPDX-License-Identifier: MIT --> | ||
|
|
||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android"> | ||
| <application | ||
| android:label="Slint Usecases" | ||
| android:hasCode="false"> | ||
| <activity | ||
| android:name="android.app.NativeActivity" | ||
| android:exported="true" | ||
| android:configChanges="orientation|screenSize|keyboardHidden"> | ||
| <meta-data | ||
| android:name="android.app.lib_name" | ||
| android:value="usecases" /> | ||
| <intent-filter> | ||
| <action android:name="android.intent.action.MAIN" /> | ||
| <category android:name="android.intent.category.LAUNCHER" /> | ||
| </intent-filter> | ||
| </activity> | ||
| </application> | ||
| </manifest> |
|
ruminorix marked this conversation as resolved.
Outdated
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| # Copyright © SixtyFPS GmbH <info@slint.dev> | ||
| # SPDX-License-Identifier: MIT | ||
|
|
||
| cmake_minimum_required(VERSION 3.21) | ||
| project(SlintUsecases LANGUAGES CXX) | ||
|
|
||
| # Use the local Slint source tree | ||
| add_subdirectory(${SLINT_SOURCE_DIR} slint_build) | ||
|
|
||
| # Shared source directory: app/src/main/cpp -> android -> cpp (the desktop example) | ||
| cmake_path(SET _usecases_cpp_dir NORMALIZE "${CMAKE_CURRENT_SOURCE_DIR}/../../../..") | ||
|
|
||
| add_library(usecases SHARED "${_usecases_cpp_dir}/main.cpp") | ||
| target_link_libraries(usecases PRIVATE Slint::Slint) | ||
| slint_target_sources(usecases "${_usecases_cpp_dir}/../ui/app.slint") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| // Copyright © SixtyFPS GmbH <info@slint.dev> | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| plugins { | ||
| id("com.android.application") version "8.7.0" apply false | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # Copyright © SixtyFPS GmbH <info@slint.dev> | ||
| # SPDX-License-Identifier: MIT | ||
|
|
||
| android.useAndroidX=true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| # Copyright © SixtyFPS GmbH <info@slint.dev> | ||
| # SPDX-License-Identifier: MIT | ||
|
|
||
| distributionBase=GRADLE_USER_HOME | ||
| distributionPath=wrapper/dists | ||
| distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From a maintenance point of view, i wonder if this is good to have this kind of file repeated for all our example. And when do we need to update this and other stuff. Could you suggest easier method than having all these files?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The main files that get duplicated are: One option to reduce duplication: a shared top-level |
||
| zipStoreBase=GRADLE_USER_HOME | ||
| zipStorePath=wrapper/dists | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| // Copyright © SixtyFPS GmbH <info@slint.dev> | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| pluginManagement { | ||
| repositories { | ||
| google() | ||
| mavenCentral() | ||
| gradlePluginPortal() | ||
| } | ||
| } | ||
|
|
||
| dependencyResolution { | ||
| repositories { | ||
| google() | ||
| mavenCentral() | ||
| } | ||
| } | ||
|
|
||
| rootProject.name = "SlintUsecases" | ||
| include(":app") |
Uh oh!
There was an error while loading. Please reload this page.