diff --git a/README.md b/README.md index 601ab5a161..2301475243 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,8 @@ We would love to have you join the development! You can help us with: - writing a tutorial or recipes about hosting servers, chat bots, etc. - developing features - please connect to us via chat so we can help you get started. +To build the apps yourself, see the [contributing guide](./docs/CONTRIBUTING.md) and [building the core library](./docs/contributing/CORE-LIBS.md) - the Haskell core all apps link against has to be built first. + ## Help translating SimpleX Chat Thanks to our users and [Weblate](https://hosted.weblate.org/engage/simplex-chat/), SimpleX Chat apps, website and documents are translated to many other languages. diff --git a/apps/ios/README.md b/apps/ios/README.md index 1e987f655e..157fec773c 100644 --- a/apps/ios/README.md +++ b/apps/ios/README.md @@ -8,6 +8,8 @@ The iOS app is a SwiftUI application that interfaces with the Haskell core libra ## Build & Development +Before the first build, build the Haskell core library and place it in `apps/ios/Libraries` - see [building the core library](../../docs/contributing/CORE-LIBS.md). + Open `SimpleX.xcodeproj` in Xcode. The project has five targets: - **SimpleX (iOS)** - Main app (Bundle ID: `chat.simplex.app`) - **SimpleXChat** - Framework containing FFI bridge and shared types diff --git a/apps/multiplatform/README.md b/apps/multiplatform/README.md index eef1048ada..eb16143976 100644 --- a/apps/multiplatform/README.md +++ b/apps/multiplatform/README.md @@ -8,6 +8,8 @@ This is the **Kotlin Multiplatform (KMP)** mobile and desktop client for SimpleX ## Build Commands +Before the first build, build the Haskell core library for your target - it is not in the repo and Gradle does not build it, see [building the core library](../../docs/contributing/CORE-LIBS.md). + ```bash # Android debug APK ./gradlew assembleDebug diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 3ecfa17409..31f676e9bb 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -32,6 +32,10 @@ These files can be used with LLM prompts, e.g. if you use Claude Code you can cr For Android/Desktop and iOS apps you can additionally import `apps/multiplatform/README.md` and `apps/ios/README.md`. +## Building the core library + +All client apps link against the Haskell core library, which is not in the repo and is not built by the app build systems. ./contributing/CORE-LIBS.md has the commands per target (Android, desktop, iOS) and their prerequisites. + ## Compiling with SQLCipher encryption enabled Add `cabal.project.local` to project root with the location of OpenSSL headers and libraries and flag setting encryption mode: diff --git a/docs/contributing/CORE-LIBS.md b/docs/contributing/CORE-LIBS.md new file mode 100644 index 0000000000..c0ef71ba0a --- /dev/null +++ b/docs/contributing/CORE-LIBS.md @@ -0,0 +1,70 @@ +--- +title: Building the core library +revision: 05.08.2026 +--- + +# Building the core library + +Every client app — Android, desktop, iOS — links against the Haskell core (`libsimplex`) through FFI. The core is not in the repo, and no app build system produces it: Gradle and Xcode expect it to be already in place. Build it for your target first, otherwise the app build fails, on Android with: + +``` +ninja: error: '.../cpp/android/libs/arm64-v8a/libsimplex.so', needed by '.../libapp-lib.so', missing and no known rule to make it +``` + +`scripts/build-core.sh ` builds it and puts it where the app build looks for it. Run it from the repo root. + +## Prerequisites + +Android and iOS cores are cross-compiled with nix from `flake.nix`, and need Nix >= 2.22 with `nix-command flakes` enabled and roughly 50 GB free in `/nix`. The Android jobs are defined only under the `x86_64-linux` builder, the iOS jobs only under the darwin ones, so build each on a matching host. Add the IOG cache to `/etc/nix/nix.conf` before the first build, or nix compiles the cross-GHC itself and the build takes 12-24 hours: + +``` +substituters = https://cache.nixos.org https://cache.iog.io +trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ= +``` + +Note that distro nix packages are usually too old — Ubuntu's `nix-bin` (2.6) fails to evaluate the flake with `input 'haskellNix/hydra/nixpkgs' follows a non-existent input 'nix/nixpkgs'`. Use the upstream installer. + +Desktop cores are built natively with cabal and need ghcup's GHC 9.6.3 and cabal 3.10.2, plus a `cabal.project.local` for SQLCipher (`cp scripts/cabal.project.local.linux cabal.project.local`, or `.mac`). Windows additionally needs MinGW/UCRT64 — see the header comment of `scripts/desktop/build-lib-windows.sh`. + +## Android + +```sh +./scripts/build-core.sh android-aarch64 # arm64-v8a +./scripts/build-core.sh android-armv7a # armeabi-v7a +``` + +Produces `libsimplex.so` (~190 MB) and `libsupport.so` (~20 MB, stripped to ~14 MB when AGP packages the APK) in `apps/multiplatform/common/src/commonMain/cpp/android/libs//`, which is gitignored. OpenSSL is linked statically, so no other `.so` files are needed. + +Build only the ABIs you actually deploy to. Studio injects `android.injected.build.abi` for the connected device, so AGP configures only that CMake variant; from the command line pass it yourself: + +```sh +./gradlew -Pandroid.injected.build.abi=arm64-v8a assembleDebug +``` + +There is no x86/x86_64 core library, so x86_64 emulator images cannot run the app — use an `arm64-v8a` AVD (system images exist up to API 36; on an x86_64 host they run under full emulation and are slow) or a physical device. + +Per [CONTRIBUTING](../CONTRIBUTING.md), the armv7a core is built from the `master-android` branch with GHC 8.10.7. + +## Desktop + +```sh +./scripts/build-core.sh desktop-linux [sqlite|postgres] +./scripts/build-core.sh desktop-mac [x86_64|arm64] [sqlite|postgres] +./scripts/build-core.sh desktop-windows [x86_64] +``` + +Produces `libsimplex.{so,dylib,dll}` and its runtime dependencies in `apps/multiplatform/common/src/commonMain/cpp/desktop/libs/-/`, prepares the bundled VLC, and creates the symlink under `apps/multiplatform/build/links/` that the Compose packaging uses. After that `./gradlew :desktop:run` or `:desktop:packageDistributionForCurrentOS` work. + +The JNI wrapper around the core is a separate, much smaller library; Gradle does build that one itself (`cmakeBuildAndCopy` in `desktop/build.gradle.kts`). + +## iOS + +```sh +./scripts/build-core.sh ios +``` + +macOS only. Builds the `aarch64-darwin-ios` job and runs `scripts/ios/prepare.sh`, which fills `apps/ios/Libraries/{mac,ios,sim}` using `mac2ios` from the flake input. + +## Using prebuilt cores instead + +The libraries the release apps ship with are built by the project's nix CI. If you don't need to build the core yourself, download the job artifacts and unpack them with `scripts/android/prepare.sh` (Android, expects the zips in `~/Downloads`) or `scripts/ios/download-libs.sh ` (iOS). diff --git a/scripts/build-core.sh b/scripts/build-core.sh new file mode 100755 index 0000000000..2d4ba41938 --- /dev/null +++ b/scripts/build-core.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env sh +# Builds the Haskell core library for one target and puts it where the app +# build expects it. Run from the repo root. +# +# ./scripts/build-core.sh android-aarch64 +# ./scripts/build-core.sh android-armv7a +# ./scripts/build-core.sh desktop-linux [sqlite|postgres] +# ./scripts/build-core.sh desktop-mac [x86_64|arm64] [sqlite|postgres] +# ./scripts/build-core.sh desktop-windows [x86_64] +# ./scripts/build-core.sh ios # macOS only +# +# nix targets (android-*, ios) need Nix >= 2.22 with flakes enabled and +# https://cache.iog.io as a substituter, otherwise the cross-GHC is compiled +# from source (12-24 hours). Desktop targets need ghcup's GHC 9.6.3 + cabal +# and a cabal.project.local, see docs/CONTRIBUTING.md. +set -eu + +android_libs="apps/multiplatform/common/src/commonMain/cpp/android/libs" + +# $1 = flake attribute path after .#hydraJobs., $2 = zip in ./result, $3 = destination +nix_unzip() { + nix build ".#hydraJobs.$1" + mkdir -p "$3" + unzip -o "result/$2" -d "$3" +} + +build_android() { + arch="$1" + abi="$2" + nix_unzip "x86_64-linux.\"${arch}-android:lib:simplex-chat\"" \ + "pkg-${arch}-android-libsimplex.zip" "$android_libs/$abi" + nix_unzip "x86_64-linux.\"${arch}-android:lib:support\"" \ + "pkg-${arch}-android-libsupport.zip" "$android_libs/$abi" +} + +target="${1:-}" +if [ $# -gt 0 ]; then shift; fi + +case "$target" in + android-aarch64|arm64-v8a) + build_android aarch64 arm64-v8a + ;; + android-armv7a|armeabi-v7a) + build_android armv7a armeabi-v7a + ;; + desktop-linux) + scripts/desktop/build-lib-linux.sh "$@" + ;; + desktop-mac) + scripts/desktop/build-lib-mac.sh "$@" + ;; + desktop-windows) + scripts/desktop/build-lib-windows.sh "$@" + ;; + ios) + nix build '.#hydraJobs.aarch64-darwin."aarch64-darwin-ios:lib:simplex-chat"' + cp result/pkg-ios-aarch64-swift-json.zip "$HOME/Downloads/" + scripts/ios/prepare.sh + ;; + *) + printf 'usage: %s \n' "$0" + exit 1 + ;; +esac