diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f1b278e..1fd1db8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -148,8 +148,12 @@ jobs: path: build/ build-android: - name: 'linux / libsdsAndroid' + name: 'linux / ${{ matrix.task }}' runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + task: [libsdsAndroidArm64, libsdsAndroidAmd64, libsdsAndroidX86] steps: - uses: actions/checkout@v4 with: @@ -159,13 +163,20 @@ jobs: with: nim-version: '2.2.4' + - uses: nttld/setup-ndk@v1 + id: setup-ndk + with: + ndk-version: r27c + - name: Install dependencies run: nimble setup -l - - name: Build libsdsAndroid - run: nimble libsdsAndroid + - name: Build ${{ matrix.task }} + env: + ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }} + run: nimble ${{ matrix.task }} - uses: actions/upload-artifact@v4 with: - name: libsdsAndroid + name: ${{ matrix.task }} path: build/ diff --git a/Makefile b/Makefile deleted file mode 100644 index ecff78e..0000000 --- a/Makefile +++ /dev/null @@ -1,141 +0,0 @@ -.PHONY: libsds deps - -LINK_PCRE := 0 - -# default target, because it's the first one that doesn't start with '.' -all: | libsds - -clean: - rm -rf build - -## Git version -GIT_VERSION ?= $(shell git describe --abbrev=6 --always --tags) -## Compilation parameters. If defined in the CLI the assignments won't be executed -NIM_PARAMS := $(NIM_PARAMS) -d:git_version=\"$(GIT_VERSION)\" - -ifeq ($(DEBUG), 0) -NIM_PARAMS := $(NIM_PARAMS) -d:release -else -NIM_PARAMS := $(NIM_PARAMS) -d:debug -endif - -STATIC ?= 0 - -detected_OS ?= Linux -ifeq ($(OS),Windows_NT) -detected_OS := Windows -else -detected_OS := $(shell uname -s) -endif - -BUILD_COMMAND ?= libsdsDynamic -ifeq ($(STATIC), 1) - BUILD_COMMAND = libsdsStatic -endif - -ifeq ($(detected_OS),Windows) - BUILD_COMMAND := $(BUILD_COMMAND)Windows -else ifeq ($(detected_OS),Darwin) - BUILD_COMMAND := $(BUILD_COMMAND)Mac - export IOS_SDK_PATH := $(shell xcrun --sdk iphoneos --show-sdk-path) -else ifeq ($(detected_OS),Linux) - BUILD_COMMAND := $(BUILD_COMMAND)Linux -endif - -libsds: - nimble --verbose $(BUILD_COMMAND) $(NIM_PARAMS) sds.nimble - -##################### -## Mobile Bindings ## -##################### -.PHONY: libsds-android \ - libsds-android-precheck \ - libsds-android-arm64 \ - libsds-android-amd64 \ - libsds-android-x86 \ - libsds-android-arm \ - build-libsds-for-android-arch - -ANDROID_TARGET ?= 30 -ifeq ($(detected_OS),Darwin) - ANDROID_TOOLCHAIN_DIR := $(ANDROID_NDK_ROOT)/toolchains/llvm/prebuilt/darwin-x86_64 -else - ANDROID_TOOLCHAIN_DIR := $(ANDROID_NDK_ROOT)/toolchains/llvm/prebuilt/linux-x86_64 -endif -# Fixes "clang: not found" errors -PATH := $(ANDROID_TOOLCHAIN_DIR)/bin:$(PATH) - -libsds-android-precheck: -ifndef ANDROID_NDK_ROOT - $(error ANDROID_NDK_ROOT is not set) -endif - -build-libsds-for-android-arch: NIM_PARAMS := $(NIM_PARAMS) --passC="--sysroot=$(ANDROID_TOOLCHAIN_DIR)/sysroot" -build-libsds-for-android-arch: NIM_PARAMS := $(NIM_PARAMS) --passL="--sysroot=$(ANDROID_TOOLCHAIN_DIR)/sysroot" -build-libsds-for-android-arch: NIM_PARAMS := $(NIM_PARAMS) --passC="--target=$(ANDROID_ARCH)$(ANDROID_TARGET)" -build-libsds-for-android-arch: NIM_PARAMS := $(NIM_PARAMS) --passL="--target=$(ANDROID_ARCH)$(ANDROID_TARGET)" -build-libsds-for-android-arch: NIM_PARAMS := $(NIM_PARAMS) --passC="-I$(ANDROID_TOOLCHAIN_DIR)/sysroot/usr/include" -build-libsds-for-android-arch: NIM_PARAMS := $(NIM_PARAMS) --passC="-I$(ANDROID_TOOLCHAIN_DIR)/sysroot/usr/include/$(ARCH_DIRNAME)" -build-libsds-for-android-arch: NIM_PARAMS := $(NIM_PARAMS) --passL="-L$(ANDROID_TOOLCHAIN_DIR)/sysroot/usr/lib/$(ARCH_DIRNAME)/$(ANDROID_TARGET)" -build-libsds-for-android-arch: - CC=$(ANDROID_TOOLCHAIN_DIR)/bin/$(ANDROID_ARCH)$(ANDROID_TARGET)-clang \ - ARCH=$(ARCH) ABIDIR=$(ABIDIR) \ - ARCH_DIRNAME=$(ARCH_DIRNAME) \ - ANDROID_ARCH=$(ANDROID_ARCH) \ - ANDROID_TOOLCHAIN_DIR=$(ANDROID_TOOLCHAIN_DIR) \ - nimble libsdsAndroid $(NIM_PARAMS) sds.nimble - -libsds-android-arm64: ANDROID_ARCH=aarch64-linux-android -libsds-android-arm64: ARCH=arm64 -libsds-android-arm64: ABIDIR=arm64-v8a -libsds-android-arm64: ARCH_DIRNAME=aarch64-linux-android -libsds-android-arm64: | libsds-android-precheck - $(MAKE) build-libsds-for-android-arch ANDROID_ARCH=$(ANDROID_ARCH) \ - ARCH=$(ARCH) ABIDIR=$(ABIDIR) ARCH_DIRNAME=$(ARCH_DIRNAME) - -libsds-android-amd64: ANDROID_ARCH=x86_64-linux-android -libsds-android-amd64: ARCH=amd64 -libsds-android-amd64: ABIDIR=x86_64 -libsds-android-amd64: ARCH_DIRNAME=x86_64-linux-android -libsds-android-amd64: | libsds-android-precheck - $(MAKE) build-libsds-for-android-arch ANDROID_ARCH=$(ANDROID_ARCH) \ - ARCH=$(ARCH) ABIDIR=$(ABIDIR) ARCH_DIRNAME=$(ARCH_DIRNAME) - -libsds-android-x86: ANDROID_ARCH=i686-linux-android -libsds-android-x86: ARCH=i386 -libsds-android-x86: ABIDIR=x86 -libsds-android-x86: ARCH_DIRNAME=i686-linux-android -libsds-android-x86: | libsds-android-precheck - $(MAKE) build-libsds-for-android-arch ANDROID_ARCH=$(ANDROID_ARCH) \ - ARCH=$(ARCH) ABIDIR=$(ABIDIR) ARCH_DIRNAME=$(ARCH_DIRNAME) - -libsds-android-arm: ANDROID_ARCH=armv7a-linux-androideabi -libsds-android-arm: ARCH=arm -libsds-android-arm: ABIDIR=armeabi-v7a -libsds-android-arm: ARCH_DIRNAME=arm-linux-androideabi -libsds-android-arm: | libsds-android-precheck -# cross-rs target architecture name does not match the one used in android - $(MAKE) build-libsds-for-android-arch ANDROID_ARCH=$(ANDROID_ARCH) \ - ARCH=$(ARCH) ABIDIR=$(ABIDIR) ARCH_DIRNAME=$(ARCH_DIRNAME) \ - -libsds-android: -ifeq ($(ARCH),arm64) - $(MAKE) libsds-android-arm64 -else ifeq ($(ARCH),amd64) - $(MAKE) libsds-android-amd64 -else ifeq ($(ARCH),x86) - $(MAKE) libsds-android-x86 -# else ifeq ($(ARCH),arm) -# $(MAKE) libsds-android-arm -# This target is disabled because on recent versions of cross-rs complain with the following error -# relocation R_ARM_THM_ALU_PREL_11_0 cannot be used against symbol 'stack_init_trampoline_return'; recompile with -fPIC -# It's likely this architecture is not used so we might just not support it. -else - $(error Unsupported ARCH '$(ARCH)'. Please set ARCH to one of: arm64, arm, amd64, x86) -endif - -# Target iOS - -libsds-ios: | - nimble libsdsIOS $(NIM_PARAMS) sds.nimble - diff --git a/flake.nix b/flake.nix index 5b964fe..a1f4b49 100644 --- a/flake.nix +++ b/flake.nix @@ -47,14 +47,14 @@ src = self; }; - # All potential targets + # All potential targets — must match nimble task names in sds.nimble. allTargets = [ "libsds" - "libsds-android-arm64" - "libsds-android-amd64" - "libsds-android-x86" - "libsds-android-arm" - "libsds-ios" + "libsdsAndroidArm64" + "libsdsAndroidAmd64" + "libsdsAndroidX86" + "libsdsAndroidArm" + "libsdsIOS" ]; # Create a package for each target @@ -65,6 +65,12 @@ in allPackages // { default = allPackages.libsds; + # Convenience aliases matching old hyphenated names. + libsds-android-arm64 = allPackages.libsdsAndroidArm64; + libsds-android-amd64 = allPackages.libsdsAndroidAmd64; + libsds-android-x86 = allPackages.libsdsAndroidX86; + libsds-android-arm = allPackages.libsdsAndroidArm; + libsds-ios = allPackages.libsdsIOS; } ); diff --git a/nix/default.nix b/nix/default.nix index ec3326d..1ac95f1 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -1,10 +1,8 @@ { pkgs, src ? ../., - # Options: 0,1,2 - verbosity ? 2, - # Make targets - targets ? ["libsds-android-arm64"], + # Nimble targets to build (task names from sds.nimble). + targets ? ["libsdsAndroidArm64"], # These are the only platforms tested in CI and considered stable. stableSystems ? ["x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" "x86_64-windows"], }: @@ -14,7 +12,7 @@ let inherit (lib) any match substring optionals optionalString; # Check if build is for android platform. - containsAndroid = s: (match ".*android.*" s) != null; + containsAndroid = s: (match ".*[Aa]ndroid.*" s) != null; isAndroidBuild = any containsAndroid targets; tools = callPackage ./tools.nix {}; @@ -99,10 +97,6 @@ in stdenv.mkDerivation { pkgs.lsb-release ]; - makeFlags = targets ++ [ - "V=${toString verbosity}" - ]; - configurePhase = '' export NIMBLE_DIR=$NIX_BUILD_TOP/nimbledeps mkdir -p $NIMBLE_DIR/pkgs2 @@ -115,6 +109,10 @@ in stdenv.mkDerivation { cp ${nimblePaths} ./nimble.paths ''; + buildPhase = lib.concatMapStringsSep "\n" (target: '' + nimble --verbose ${target} + '') targets; + installPhase = let androidManifest = '' diff --git a/sds.nimble b/sds.nimble index 0feed4f..fb5f43d 100644 --- a/sds.nimble +++ b/sds.nimble @@ -206,21 +206,108 @@ task libsdsIOS, "Build the mobile bindings for iOS": buildMobileIOS srcDir, sdkPath ### Mobile Android +proc checkAndroidNdk() = + let ndkRoot = getEnv("ANDROID_NDK_ROOT") + if ndkRoot.len == 0: + quit """Error: ANDROID_NDK_ROOT is not set.""" + if not dirExists(ndkRoot): + quit "Error: ANDROID_NDK_ROOT points to a non-existent directory: " & ndkRoot + # source.properties contains Pkg.Revision — present in every NDK since r10. + let propsFile = ndkRoot / "source.properties" + if not fileExists(propsFile): + quit "Error: " & ndkRoot & " does not look like a valid NDK (source.properties not found)." + let (props, _) = gorgeEx("cat " & propsFile) + var revision = "" + for line in props.splitLines(): + if line.startsWith("Pkg.Revision"): + let parts = line.split('=') + if parts.len == 2: + revision = parts[1].strip() + if revision.len == 0: + quit "Error: Could not read NDK version from " & propsFile + echo "Android NDK version: " & revision + proc buildMobileAndroid(srcDir = ".", extra_params = "") = let cpu = getMyCpu() + let ndkRoot = getEnv("ANDROID_NDK_ROOT") + let androidTarget = "30" - let outDir = "build/" + # Map Nim CPU name → NDK target triple and include dirname. + let (androidArch, archDirname) = + if cpu == "arm64": ("aarch64-linux-android", "aarch64-linux-android") + elif cpu == "amd64": ("x86_64-linux-android", "x86_64-linux-android") + elif cpu == "i386": ("i686-linux-android", "i686-linux-android") + else: ("armv7a-linux-androideabi","arm-linux-androideabi") + + # NDK prebuilt toolchain — location differs by host OS. + let (hostOS, _) = gorgeEx("uname -s") + let ndkHostTag = + if hostOS.strip() == "Darwin": "darwin-x86_64" + else: "linux-x86_64" + let toolchainDir = ndkRoot / "toolchains/llvm/prebuilt" / ndkHostTag + let sysroot = toolchainDir / "sysroot" + let ndkClang = toolchainDir / "bin" / (androidArch & androidTarget & "-clang") + + let outDir = "build" if not dirExists outDir: mkDir outDir - exec "nim c" & " --out:" & outDir & - "/libsds.so --threads:on --app:lib --opt:size --noMain --mm:refc --nimMainPrefix:libsds " & - "-d:chronicles_sinks=textlines[dynamic] --header --passL:-L" & outdir & - " --passL:-llog --cpu:" & cpu & - " --os:android -d:androidNDK -d:chronosEventEngine=epoll " & extra_params & " " & - srcDir & "/libsds.nim" + exec "nim c" & + " --out:" & outDir & "/libsds.so" & + " --threads:on --app:lib --opt:size --noMain --mm:refc --nimMainPrefix:libsds" & + " --cc:clang" & + " --clang.exe:\"" & ndkClang & "\"" & + " --clang.linkerexe:\"" & ndkClang & "\"" & + " --cpu:" & cpu & + " --os:android" & + " -d:androidNDK" & + " -d:chronosEventEngine=epoll" & + " --passC:\"--sysroot=" & sysroot & "\"" & + " --passL:\"--sysroot=" & sysroot & "\"" & + " --passC:\"--target=" & androidArch & androidTarget & "\"" & + " --passL:\"--target=" & androidArch & androidTarget & "\"" & + " --passC:\"-I" & sysroot & "/usr/include\"" & + " --passC:\"-I" & sysroot & "/usr/include/" & archDirname & "\"" & + " --passL:\"-L" & sysroot & "/usr/lib/" & archDirname & "/" & androidTarget & "\"" & + " --passL:-llog" & + " -d:chronicles_sinks=textlines[dynamic]" & + " --header" & + " " & extra_params & + " " & srcDir & "/libsds.nim" -task libsdsAndroid, "Build the mobile bindings for Android": +task libsdsAndroid, "Build the mobile bindings for Android (uses ARCH env var)": + checkAndroidNdk() let srcDir = "./library" - let extraParams = "-d:chronicles_log_level=ERROR" - buildMobileAndroid srcDir, extraParams + buildMobileAndroid srcDir, "-d:chronicles_log_level=ERROR" + +task libsdsAndroidArm64, "Build Android arm64 bindings": + checkAndroidNdk() + putEnv("ARCH", "arm64") + buildMobileAndroid "./library", "-d:chronicles_log_level=ERROR" + +task libsdsAndroidAmd64, "Build Android amd64 bindings": + checkAndroidNdk() + putEnv("ARCH", "amd64") + buildMobileAndroid "./library", "-d:chronicles_log_level=ERROR" + +task libsdsAndroidX86, "Build Android x86 bindings": + checkAndroidNdk() + putEnv("ARCH", "i386") + buildMobileAndroid "./library", "-d:chronicles_log_level=ERROR" + +task libsdsAndroidArm, "Build Android arm bindings": + checkAndroidNdk() + putEnv("ARCH", "arm") + buildMobileAndroid "./library", "-d:chronicles_log_level=ERROR" + +task libsds, "Build the shared library for the current platform": + when defined(macosx): + exec "nimble libsdsDynamicMac" + elif defined(windows): + exec "nimble libsdsDynamicWindows" + else: + exec "nimble libsdsDynamicLinux" + +task clean, "Remove build artifacts": + if dirExists "build": + rmDir "build"