diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml new file mode 100644 index 0000000..0b85b60 --- /dev/null +++ b/.gitea/workflows/build.yml @@ -0,0 +1,195 @@ +name: Build + +on: + push: + branches: + - master + - main + pull_request: + workflow_dispatch: + +env: + BUILD_JOBS: 4 + +jobs: + linux: + name: Linux ${{ matrix.target }} + runs-on: ubuntu-latest + container: + image: catthehacker/ubuntu:act-24.04 + strategy: + fail-fast: false + matrix: + include: + - target: x64 + extra_arch: "" + packages: "build-essential binutils cmake file git libgl1-mesa-dev libsdl2-dev make pkg-config python3 zip" + - target: x32 + extra_arch: "i386" + packages: "binutils cmake file gcc-multilib g++-multilib git libc6-dev-i386 libgl1-mesa-dev:i386 libsdl2-dev:i386 make pkg-config python3 zip" + - target: arm64 + extra_arch: "arm64" + packages: "binutils cmake file gcc-aarch64-linux-gnu g++-aarch64-linux-gnu git libgl1-mesa-dev:arm64 libsdl2-dev:arm64 make pkg-config python3 zip" + - target: arm32 + extra_arch: "armhf" + packages: "binutils cmake file gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf git libgl1-mesa-dev:armhf libsdl2-dev:armhf make pkg-config python3 zip" + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Linux dependencies + shell: bash + run: | + set -euo pipefail + if [ -n "${{ matrix.extra_arch }}" ]; then + dpkg --add-architecture "${{ matrix.extra_arch }}" + fi + apt-get update + DEBIAN_FRONTEND=noninteractive apt-get install -y ${{ matrix.packages }} + + - name: Build Linux package + shell: bash + run: ./scripts/ci/build-linux-target.sh "${{ matrix.target }}" + + - uses: actions/upload-artifact@v4 + with: + name: armsx-linux-${{ matrix.target }} + path: artifacts/linux/${{ matrix.target }}/armsx-linux-${{ matrix.target }}.zip + + windows: + name: Windows ${{ matrix.target }} + runs-on: ubuntu-latest + container: + image: catthehacker/ubuntu:act-24.04 + strategy: + fail-fast: false + matrix: + target: [x64, x32] + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install MinGW toolchains + shell: bash + run: | + set -euo pipefail + apt-get update + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + binutils-mingw-w64 \ + cmake \ + git \ + gcc-mingw-w64 \ + g++-mingw-w64 \ + make \ + python3 \ + zip + + - name: Build Windows package + shell: bash + run: ./scripts/ci/build-windows-cross.sh "${{ matrix.target }}" + + - uses: actions/upload-artifact@v4 + with: + name: armsx-windows-${{ matrix.target }} + path: artifacts/windows/${{ matrix.target }}/armsx-windows-${{ matrix.target }}.zip + + android: + name: Android Debug APK + runs-on: ubuntu-latest + container: + image: catthehacker/ubuntu:act-24.04 + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Android build dependencies + shell: bash + run: | + set -euo pipefail + apt-get update + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + cmake \ + curl \ + git \ + make \ + ninja-build \ + openjdk-17-jdk \ + pkg-config \ + python3 \ + unzip \ + zip + + - name: Build Android APK + shell: bash + run: | + set -euo pipefail + export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 + export PATH="$JAVA_HOME/bin:$PATH" + + export ANDROID_HOME="$PWD/.android-sdk" + export ANDROID_SDK_ROOT="$ANDROID_HOME" + mkdir -p "$ANDROID_HOME/cmdline-tools" + + curl -fsSL https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -o /tmp/android-cmdline-tools.zip + unzip -q /tmp/android-cmdline-tools.zip -d "$ANDROID_HOME/cmdline-tools" + mv "$ANDROID_HOME/cmdline-tools/cmdline-tools" "$ANDROID_HOME/cmdline-tools/latest" + + export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$PATH" + yes | sdkmanager --sdk_root="$ANDROID_HOME" --licenses >/dev/null + sdkmanager --sdk_root="$ANDROID_HOME" \ + "build-tools;34.0.0" \ + "ndk;27.1.12297006" \ + "platform-tools" \ + "platforms;android-34" + + export ANDROID_NDK_ROOT="$ANDROID_HOME/ndk/27.1.12297006" + export ANDROID_NDK_HOME="$ANDROID_NDK_ROOT" + export NDK_HOME="$ANDROID_NDK_ROOT" + export GRADLE_USER_HOME="$PWD/.gradle-home" + + curl -fsSL https://services.gradle.org/distributions/gradle-8.14.3-bin.zip -o /tmp/gradle.zip + unzip -q /tmp/gradle.zip -d "$PWD/.gradle-dist" + export GRADLE_BIN="$PWD/.gradle-dist/gradle-8.14.3/bin/gradle" + + ./scripts/ci/build-android-all.sh + + - uses: actions/upload-artifact@v4 + with: + name: armsx-android-debug + path: android/app/build/outputs/apk/debug/app-debug.apk + + wasm: + name: WASM + runs-on: ubuntu-latest + container: + image: emscripten/emsdk:3.1.72 + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install WASM packaging tools + shell: bash + run: | + set -euo pipefail + apt-get update + DEBIAN_FRONTEND=noninteractive apt-get install -y zip + + - name: Build WebAssembly package + shell: bash + run: | + set -euo pipefail + ./build.sh wasm + mkdir -p artifacts/wasm + ( + cd bin + zip -r ../artifacts/wasm/armsx-wasm.zip wasm + ) + + - uses: actions/upload-artifact@v4 + with: + name: armsx-wasm + path: artifacts/wasm/armsx-wasm.zip diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml deleted file mode 100644 index 10f2145..0000000 --- a/.github/workflows/macos.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: macOS CI - -on: - push: - branches: [ "master" ] - -jobs: - build: - runs-on: macos-latest - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Install SDL2 and dylibbundler - run: | - brew install sdl2 - brew install dylibbundler - - name: Build and pack PSXE - run: | - git fetch --all --tags - ./build.sh - tar -czf psxe-macos-latest.tar.gz psxe.app - - uses: actions/upload-artifact@v4 - with: - name: psxe-macos-latest - path: ./psxe-macos-latest.tar.gz diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml deleted file mode 100644 index 61ce78c..0000000 --- a/.github/workflows/ubuntu.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Ubuntu CI - -on: - push: - branches: [ "master" ] - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Install SDL2 - run: | - sudo apt update - sudo apt install libsdl2-dev - - name: Build PSXE - run: | - git fetch --all --tags - make - - name: Pack executable - run: | - chmod +x ./bin/psxe - mv ./bin/psxe ./ - tar -czf psxe-ubuntu-latest.tar.gz ./psxe - - uses: actions/upload-artifact@v4 - with: - name: psxe-ubuntu-latest - path: ./psxe-ubuntu-latest.tar.gz diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml deleted file mode 100644 index bb8165d..0000000 --- a/.github/workflows/windows.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Windows CI - -on: - push: - branches: [ "master" ] - -jobs: - build: - runs-on: windows-latest - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Run build-deps - run: ./build-deps.ps1 - - name: Run build-win64 - run: | - ./build-win64.ps1 - New-Item -Path "psxe" -ItemType Directory - Copy-Item -Recurse "bin" -Destination "psxe" - - uses: actions/upload-artifact@v4 - with: - name: psxe-win64-latest - path: psxe/ \ No newline at end of file diff --git a/Makefile b/Makefile index 8552111..1b77c1e 100644 --- a/Makefile +++ b/Makefile @@ -245,6 +245,8 @@ ALL_OBJS_SHARED := $(C_OBJS_SHARED) $(CPP_OBJS) # Force dynamic SDL when building the shared library ifneq (,$(filter shared,$(MAKECMDGOALS))) override SDL_STATIC := 0 +BASE_CFLAGS += -D__DLL_BUILD -fPIC +BASE_CXXFLAGS += -D__DLL_BUILD -fPIC endif ifeq ($(SDL_STATIC),1) diff --git a/README.md b/README.md index 065d852..e703c97 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,11 @@ A simple and portable Sony PlayStation emulator and emulation library written in ## Screenshots | Windows | Ubuntu | macOS | | ------------- | ------------- | ------------- -| ![Mega Man X6 (USA)](https://github.com/allkern/psxe/assets/15825466/34dde8f9-eedb-4b44-a08d-c17026df2ff2) | ![Bloody Roar 2 - Bringer of the New Age (Europe)](https://github.com/allkern/psxe/assets/15825466/41a6dc67-b0ba-442f-bed6-7b207c0db4dd) | ![Parodius (Europe)](https://github.com/allkern/psxe/assets/15825466/9ab291d9-ec47-4997-92d3-23e38982ae45) | -| ![Spyro 2 - Ripto's Rage (USA)](https://github.com/allkern/psxe/assets/15825466/e161ab66-af57-4327-9a94-8b2591a0012a) | ![Namco Museum Vol. 1 (USA)](https://github.com/allkern/psxe/assets/15825466/67ea61e4-5f30-470c-a978-23e0755850b6) | ![Darius Gaiden (Japan)](https://github.com/allkern/psxe/assets/15825466/0c55118c-ab42-40e5-b34a-7594528080bf) | +| ![Mega Man X6 (USA)](https://git.nanodata.cloud/moonpower/ARMSX/assets/15825466/34dde8f9-eedb-4b44-a08d-c17026df2ff2) | ![Bloody Roar 2 - Bringer of the New Age (Europe)](https://git.nanodata.cloud/moonpower/ARMSX/assets/15825466/41a6dc67-b0ba-442f-bed6-7b207c0db4dd) | ![Parodius (Europe)](https://git.nanodata.cloud/moonpower/ARMSX/assets/15825466/9ab291d9-ec47-4997-92d3-23e38982ae45) | +| ![Spyro 2 - Ripto's Rage (USA)](https://git.nanodata.cloud/moonpower/ARMSX/assets/15825466/e161ab66-af57-4327-9a94-8b2591a0012a) | ![Namco Museum Vol. 1 (USA)](https://git.nanodata.cloud/moonpower/ARMSX/assets/15825466/67ea61e4-5f30-470c-a978-23e0755850b6) | ![Darius Gaiden (Japan)](https://git.nanodata.cloud/moonpower/ARMSX/assets/15825466/0c55118c-ab42-40e5-b34a-7594528080bf) | -### CI status -![Windows](https://github.com/allkern/psx/actions/workflows/windows.yml/badge.svg) -![macOS](https://github.com/allkern/psx/actions/workflows/macos.yml/badge.svg) -![Ubuntu](https://github.com/allkern/psx/actions/workflows/ubuntu.yml/badge.svg) +### CI status +CI now lives in `.gitea/workflows/build.yml`. ## Running You can download the latest automated build for your platform on Releases. If your system isn't supported, you can easily build the emulator from source, instructions on "Building" below. @@ -24,15 +22,15 @@ Use the `-b` or `--bios` setting to configure the BIOS file. ## Progress All major components have been implemented, including memory card support. - CPU
- DMA
- GPU
- SPU
- MDEC
- GTE
- Timers
- CDROM
- Memory cards
+ CPU
+ DMA
+ GPU
+ SPU
+ MDEC
+ GTE
+ Timers
+ CDROM
+ Memory cards
## Building Building the emulator should be easy, just use the scripts provided in this repo. @@ -58,8 +56,8 @@ Assuming you did everything described above, you should be able to build the emu ### Windows ``` -git clone https://github.com/allkern/psxe -cd psxe +git clone https://git.nanodata.cloud/moonpower/ARMSX.git +cd ARMSX ./build-deps ./build-win64.ps1 ``` @@ -67,15 +65,15 @@ On rare cases these scripts might not work (PowerShell/Windows bugs). If so, ple ### Ubuntu ``` -git clone https://github.com/allkern/psxe -cd psxe +git clone https://git.nanodata.cloud/moonpower/ARMSX.git +cd ARMSX make clean && make ``` ### macOS ``` -git clone https://github.com/allkern/psxe -cd psxe +git clone https://git.nanodata.cloud/moonpower/ARMSX.git +cd ARMSX ./build.sh ``` diff --git a/android/app/build.gradle b/android/app/build.gradle index 57f561b..40b8fd2 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -67,12 +67,14 @@ dependencies { def repoRootDir = rootProject.projectDir.parentFile def nativeOutput = file("$projectDir/src/main/jniLibs/arm64-v8a/libarmsx.so") +def skipNativePrepare = System.getenv("ARMSX_SKIP_NATIVE_PREPARE") == "1" tasks.register("prepareNativeBinaries") { group = "native" description = "Build SDL2/libarmsx via build.sh android." inputs.file(new File(repoRootDir, "build.sh")) outputs.file(nativeOutput) + onlyIf { !skipNativePrepare } doLast { exec { workingDir repoRootDir diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 01ec193..41aef35 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,4 @@ - + + android:theme="@style/AppTheme"> diff --git a/android/build.gradle b/android/build.gradle index c56152f..fc32c6f 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -30,7 +30,7 @@ allprojects { project -> configurations.all { config -> config.resolutionStrategy.eachDependency { details -> - if (details.requested.group == 'org.jetbrains.kotlin' && details.requested.name == 'kotlin-gradle-plugin') { + if (details.requested.group == 'org.jetbrains.kotlin') { details.useVersion(rootProject.ext.kotlinVersion) } } diff --git a/build.sh b/build.sh index 42f6149..35c751a 100755 --- a/build.sh +++ b/build.sh @@ -163,12 +163,19 @@ elif [ "$MODE" = "android" ]; then case "${ANDROID_ABI}" in arm64-v8a) ANDROID_TRIPLE="aarch64-linux-android" + ANDROID_CXX_RUNTIME_TRIPLE="aarch64-linux-android" ;; armeabi-v7a) ANDROID_TRIPLE="armv7a-linux-androideabi" + ANDROID_CXX_RUNTIME_TRIPLE="arm-linux-androideabi" + ;; + x86) + ANDROID_TRIPLE="i686-linux-android" + ANDROID_CXX_RUNTIME_TRIPLE="i686-linux-android" ;; x86_64) ANDROID_TRIPLE="x86_64-linux-android" + ANDROID_CXX_RUNTIME_TRIPLE="x86_64-linux-android" ;; *) echo "Unsupported ANDROID_ABI '${ANDROID_ABI}'." @@ -204,17 +211,23 @@ elif [ "$MODE" = "android" ]; then echo "Using Android NDK at ${ANDROID_NDK_ROOT} (toolchain ${HOST_TAG}, ABI ${ANDROID_ABI}, API ${ANDROID_API})" - SDL_BUILD_ROOT="build/android/sdl" + ANDROID_TOOLCHAIN_FILE="${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake" + if [ ! -f "${ANDROID_TOOLCHAIN_FILE}" ]; then + echo "Android toolchain file not found at ${ANDROID_TOOLCHAIN_FILE}" + exit 1 + fi + + REPO_ROOT="$(pwd)" + + SDL_BUILD_ROOT="${REPO_ROOT}/build/android/sdl" SDL_INSTALL_DIR="${SDL_BUILD_ROOT}/install" mkdir -p "${SDL_BUILD_ROOT}" cmake -S third_party/SDL -B "${SDL_BUILD_ROOT}" \ - -DANDROID=ON \ + -DCMAKE_TOOLCHAIN_FILE="${ANDROID_TOOLCHAIN_FILE}" \ -DANDROID_ABI="${ANDROID_ABI}" \ -DANDROID_PLATFORM="${ANDROID_PLATFORM}" \ -DANDROID_STL=c++_shared \ - -DCMAKE_SYSTEM_NAME=Android \ - -DCMAKE_ANDROID_NDK="${ANDROID_NDK_ROOT}" \ -DCMAKE_BUILD_TYPE=Release \ -DBUILD_SHARED_LIBS=ON \ -DSDL_STATIC=OFF \ @@ -227,13 +240,18 @@ elif [ "$MODE" = "android" ]; then SDL_LIB_PATH="${SDL_INSTALL_DIR}/lib" SDL_INCLUDE_PATH="${SDL_INSTALL_DIR}/include/SDL2" SDL_SHARED_LIB="${SDL_LIB_PATH}/libSDL2.so" + CXX_SHARED_RUNTIME="${TOOLCHAIN_DIR}/sysroot/usr/lib/${ANDROID_CXX_RUNTIME_TRIPLE}/libc++_shared.so" if [ ! -f "${SDL_SHARED_LIB}" ]; then echo "SDL shared library not found at ${SDL_SHARED_LIB}" exit 1 fi + if [ ! -f "${CXX_SHARED_RUNTIME}" ]; then + echo "Android C++ shared runtime not found at ${CXX_SHARED_RUNTIME}" + exit 1 + fi - JNI_LIB_DIR="android/app/src/main/jniLibs/${ANDROID_ABI}" - SDL_HEADER_STAGE="android/native-deps/SDL2/include" + JNI_LIB_DIR="${REPO_ROOT}/android/app/src/main/jniLibs/${ANDROID_ABI}" + SDL_HEADER_STAGE="${REPO_ROOT}/android/native-deps/SDL2/include" mkdir -p "${JNI_LIB_DIR}" rm -rf "${SDL_HEADER_STAGE}" mkdir -p "${SDL_HEADER_STAGE}" @@ -248,25 +266,34 @@ elif [ "$MODE" = "android" ]; then SDL_CFLAGS="-D_REENTRANT -DANDROID -I${SDL_INCLUDE_PATH}" SDL_LIBS="-L${SDL_LIB_PATH} -lSDL2 -llog -landroid -lGLESv3 -lEGL -lOpenSLES -lm -lc++_shared" - FSUI_BUILD_ROOT="build/fsui/android/${ANDROID_ABI}" + FSUI_BUILD_ROOT="${REPO_ROOT}/build/fsui/android/${ANDROID_ABI}" cmake -S third_party/fsui-lib -B "${FSUI_BUILD_ROOT}" \ -DFSUI_BUILD_SAMPLES=OFF \ -DFSUI_PLATFORM_BACKEND=SDL2 \ -DFSUI_USE_SYSTEM_SDL2=ON \ - -DANDROID=ON \ + -DCMAKE_TOOLCHAIN_FILE="${ANDROID_TOOLCHAIN_FILE}" \ -DANDROID_ABI="${ANDROID_ABI}" \ -DANDROID_PLATFORM="${ANDROID_PLATFORM}" \ -DANDROID_STL=c++_shared \ - -DCMAKE_SYSTEM_NAME=Android \ - -DCMAKE_ANDROID_NDK="${ANDROID_NDK_ROOT}" \ -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_PREFIX_PATH="${SDL_INSTALL_DIR}" + -DCMAKE_PREFIX_PATH="${SDL_INSTALL_DIR}" \ + -DSDL2_DIR="${SDL_INSTALL_DIR}/lib/cmake/SDL2" cmake --build "${FSUI_BUILD_ROOT}" --config Release -j"${BUILD_JOBS}" make clean - SDL_STATIC=0 SDL_CFLAGS="${SDL_CFLAGS}" SDL_LIBS_DYNAMIC="${SDL_LIBS}" FSUI_BUILD_DIR="$(pwd)/${FSUI_BUILD_ROOT}" make shared + make \ + SDL_STATIC=0 \ + SDL_CFLAGS="${SDL_CFLAGS}" \ + SDL_LIBS_DYNAMIC="${SDL_LIBS}" \ + FSUI_BUILD_DIR="${FSUI_BUILD_ROOT}" \ + PLATFORM=Android \ + OS_INFO=Android \ + PLATFORM_EXTRA_LDFLAGS= \ + PLATFORM_EXTRA_LIBS= \ + shared cp "${SDL_SHARED_LIB}" "${JNI_LIB_DIR}/" + cp "${CXX_SHARED_RUNTIME}" "${JNI_LIB_DIR}/" cp bin/libarmsx.so "${JNI_LIB_DIR}/" elif [ "$MODE" = "psvita" ]; then diff --git a/scripts/ci/build-android-all.sh b/scripts/ci/build-android-all.sh new file mode 100755 index 0000000..7a7566c --- /dev/null +++ b/scripts/ci/build-android-all.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +abis="${ANDROID_ABIS:-x86 x86_64 armeabi-v7a arm64-v8a}" + +if [ -z "${ANDROID_NDK_ROOT:-}" ]; then + echo "ANDROID_NDK_ROOT must be set." >&2 + exit 1 +fi + +if [ -z "${GRADLE_BIN:-}" ]; then + echo "GRADLE_BIN must be set to a Gradle executable." >&2 + exit 1 +fi + +rm -rf "$repo_root/android/app/src/main/jniLibs" \ + "$repo_root/android/native-deps" + +for abi in $abis; do + echo "Building Android ABI: $abi" + ( + cd "$repo_root" + ANDROID_ABI="$abi" ./build.sh android + ) +done + +( + cd "$repo_root" + export ARMSX_SKIP_NATIVE_PREPARE=1 + "$GRADLE_BIN" -p android --no-daemon assembleDebug +) diff --git a/scripts/ci/build-linux-target.sh b/scripts/ci/build-linux-target.sh new file mode 100755 index 0000000..b4767e0 --- /dev/null +++ b/scripts/ci/build-linux-target.sh @@ -0,0 +1,134 @@ +#!/usr/bin/env bash + +set -euo pipefail + +target="${1:-}" +build_jobs="${BUILD_JOBS:-4}" + +if [ -z "$target" ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +fsui_build_dir="$repo_root/build/fsui/ci-linux-$target" +artifact_root="$repo_root/artifacts/linux/$target" +package_root="$artifact_root/armsx-linux-$target" +package_zip="$artifact_root/armsx-linux-$target.zip" + +cmake_args=( + -S "$repo_root/third_party/fsui-lib" + -B "$fsui_build_dir" + -DFSUI_BUILD_SAMPLES=OFF + -DFSUI_PLATFORM_BACKEND=SDL2 + -DFSUI_USE_SYSTEM_SDL2=ON + -DCMAKE_BUILD_TYPE=Release +) + +extra_cflags="" +extra_ldflags="" +pkg_config_libdir="" +dep_search_dirs="" + +case "$target" in + x64) + cc="gcc" + cxx="g++" + pkg_config_libdir="/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/share/pkgconfig" + dep_search_dirs="/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/lib64:/usr/lib64" + ;; + x32|x86) + target="x32" + cc="gcc" + cxx="g++" + extra_cflags="-m32" + extra_ldflags="-m32" + pkg_config_libdir="/usr/lib/i386-linux-gnu/pkgconfig:/usr/share/pkgconfig" + dep_search_dirs="/lib/i386-linux-gnu:/usr/lib/i386-linux-gnu" + ;; + arm64) + cc="aarch64-linux-gnu-gcc" + cxx="aarch64-linux-gnu-g++" + pkg_config_libdir="/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig" + dep_search_dirs="/lib/aarch64-linux-gnu:/usr/lib/aarch64-linux-gnu:/usr/aarch64-linux-gnu/lib" + cmake_args+=( + -DCMAKE_SYSTEM_NAME=Linux + -DCMAKE_SYSTEM_PROCESSOR=aarch64 + ) + ;; + arm32) + cc="arm-linux-gnueabihf-gcc" + cxx="arm-linux-gnueabihf-g++" + pkg_config_libdir="/usr/lib/arm-linux-gnueabihf/pkgconfig:/usr/share/pkgconfig" + dep_search_dirs="/lib/arm-linux-gnueabihf:/usr/lib/arm-linux-gnueabihf:/usr/arm-linux-gnueabihf/lib" + cmake_args+=( + -DCMAKE_SYSTEM_NAME=Linux + -DCMAKE_SYSTEM_PROCESSOR=arm + ) + ;; + *) + echo "Unsupported Linux target '$target'." >&2 + exit 1 + ;; +esac + +export PKG_CONFIG_LIBDIR="$pkg_config_libdir" +unset PKG_CONFIG_PATH + +if [ -n "$extra_cflags" ]; then + cmake_args+=( + -DCMAKE_C_FLAGS="$extra_cflags" + -DCMAKE_CXX_FLAGS="$extra_cflags" + -DCMAKE_EXE_LINKER_FLAGS="$extra_ldflags" + ) +fi + +cmake_args+=( + -DCMAKE_C_COMPILER="$cc" + -DCMAKE_CXX_COMPILER="$cxx" +) + +sdl_cflags="$(pkg-config --cflags sdl2)" +sdl_libs="$(pkg-config --libs sdl2)" + +if [ -n "$extra_cflags" ]; then + sdl_cflags="$sdl_cflags $extra_cflags" + sdl_libs="$sdl_libs $extra_ldflags" +fi + +rm -rf "$fsui_build_dir" "$artifact_root" +mkdir -p "$artifact_root" + +cmake "${cmake_args[@]}" +cmake --build "$fsui_build_dir" -j"$build_jobs" + +cd "$repo_root" +make clean +make \ + CC="$cc" \ + CXX="$cxx" \ + FSUI_BUILD_DIR="$fsui_build_dir" \ + SDL_STATIC=0 \ + SDL_CFLAGS="$sdl_cflags" \ + SDL_LIBS_DYNAMIC="$sdl_libs" \ + PLATFORM_EXTRA_LDFLAGS="-static-libstdc++ -static-libgcc" + +mkdir -p "$package_root/lib" "$package_root/icons" +cp "$repo_root/bin/armsx" "$package_root/armsx.bin" +cp -R "$repo_root/icons/." "$package_root/icons/" +"$repo_root/scripts/ci/copy-linux-runtime-deps.sh" "$repo_root/bin/armsx" "$package_root/lib" "$dep_search_dirs" + +cat > "$package_root/armsx" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail +app_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +export LD_LIBRARY_PATH="$app_dir/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" +exec "$app_dir/armsx.bin" "$@" +EOF + +chmod +x "$package_root/armsx" + +( + cd "$artifact_root" + zip -r "$(basename "$package_zip")" "$(basename "$package_root")" +) diff --git a/scripts/ci/build-windows-cross.sh b/scripts/ci/build-windows-cross.sh new file mode 100755 index 0000000..b39b646 --- /dev/null +++ b/scripts/ci/build-windows-cross.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env bash + +set -euo pipefail + +target="${1:-}" +build_jobs="${BUILD_JOBS:-4}" + +if [ -z "$target" ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +artifact_root="$repo_root/artifacts/windows/$target" +package_root="$artifact_root/armsx-windows-$target" +package_zip="$artifact_root/armsx-windows-$target.zip" +sdl_build_dir="$repo_root/build/sdl/windows-$target" +sdl_install_dir="$sdl_build_dir/install" +fsui_build_dir="$repo_root/build/fsui/ci-windows-$target" + +case "$target" in + x64) + triplet="x86_64-w64-mingw32" + toolchain_file="$repo_root/third_party/SDL/build-scripts/cmake-toolchain-mingw64-x86_64.cmake" + ;; + x32|x86) + target="x32" + triplet="i686-w64-mingw32" + toolchain_file="$repo_root/third_party/SDL/build-scripts/cmake-toolchain-mingw64-i686.cmake" + ;; + *) + echo "Unsupported Windows target '$target'." >&2 + exit 1 + ;; +esac + +rm -rf "$artifact_root" "$sdl_build_dir" "$fsui_build_dir" +mkdir -p "$artifact_root" + +cmake -S "$repo_root/third_party/SDL" -B "$sdl_build_dir" \ + -DCMAKE_TOOLCHAIN_FILE="$toolchain_file" \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=ON \ + -DSDL_SHARED=ON \ + -DSDL_STATIC=OFF \ + -DSDL_TEST=OFF \ + -DCMAKE_INSTALL_PREFIX="$sdl_install_dir" +cmake --build "$sdl_build_dir" -j"$build_jobs" +cmake --install "$sdl_build_dir" + +sdl_include="$sdl_install_dir/include/SDL2" +sdl_lib="$sdl_install_dir/lib" +sdl_dll="$(find "$sdl_install_dir" -type f \( -name 'SDL2.dll' -o -name 'libSDL2.dll' \) | head -n 1)" +if [ -z "$sdl_dll" ]; then + echo "Unable to locate SDL2.dll in $sdl_install_dir" >&2 + exit 1 +fi + +cmake -S "$repo_root/third_party/fsui-lib" -B "$fsui_build_dir" \ + -DCMAKE_TOOLCHAIN_FILE="$toolchain_file" \ + -DFSUI_BUILD_SAMPLES=OFF \ + -DFSUI_PLATFORM_BACKEND=SDL2 \ + -DFSUI_USE_SYSTEM_SDL2=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH="$sdl_install_dir" +cmake --build "$fsui_build_dir" -j"$build_jobs" + +cd "$repo_root" +make clean +make \ + CC="$triplet-gcc" \ + CXX="$triplet-g++" \ + WINDRES="$triplet-windres" \ + WINDOWS_TARGET=1 \ + SDL_STATIC=0 \ + FSUI_BUILD_DIR="$fsui_build_dir" \ + SDL_CFLAGS="-I$sdl_include" \ + SDL_LIBS_DYNAMIC="-L$sdl_lib -lSDL2 -lsetupapi -limm32 -lversion -lwinmm -lgdi32 -lole32 -loleaut32 -lshell32 -luuid -lopengl32" \ + PLATFORM_EXTRA_LDFLAGS="-static-libstdc++ -static-libgcc" + +mkdir -p "$package_root/icons" +cp "$repo_root/bin/armsx.exe" "$package_root/" +cp "$sdl_dll" "$package_root/SDL2.dll" +cp -R "$repo_root/icons/." "$package_root/icons/" + +for runtime_dll in \ + "$("$triplet-g++" -print-file-name=libstdc++-6.dll)" \ + "$("$triplet-g++" -print-file-name=libwinpthread-1.dll)"; do + if [ -n "$runtime_dll" ] && [ -f "$runtime_dll" ]; then + cp "$runtime_dll" "$package_root/" + fi +done + +gcc_dir="$(dirname "$("$triplet-gcc" -print-libgcc-file-name)")" +for libgcc_dll in "$gcc_dir"/libgcc_s_*.dll; do + if [ -f "$libgcc_dll" ]; then + cp "$libgcc_dll" "$package_root/" + fi +done + +( + cd "$artifact_root" + zip -r "$(basename "$package_zip")" "$(basename "$package_root")" +) diff --git a/scripts/ci/copy-linux-runtime-deps.sh b/scripts/ci/copy-linux-runtime-deps.sh new file mode 100755 index 0000000..dbb42d7 --- /dev/null +++ b/scripts/ci/copy-linux-runtime-deps.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash + +set -euo pipefail + +if [ "$#" -ne 3 ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +binary="$1" +dest_dir="$2" +search_dirs="$3" + +mkdir -p "$dest_dir" + +declare -A seen=() +queue=("$binary") + +resolve_library() { + local lib_name="$1" + local dir= + local old_ifs="$IFS" + IFS=':' + for dir in $search_dirs; do + if [ -f "$dir/$lib_name" ]; then + printf '%s\n' "$dir/$lib_name" + IFS="$old_ifs" + return 0 + fi + done + IFS="$old_ifs" + return 1 +} + +while [ "${#queue[@]}" -gt 0 ]; do + current="${queue[0]}" + queue=("${queue[@]:1}") + + while IFS= read -r needed; do + [ -n "$needed" ] || continue + + case "$needed" in + linux-vdso.so.*|ld-linux*.so.*) + continue + ;; + esac + + if [ -n "${seen[$needed]:-}" ]; then + continue + fi + + resolved="$(resolve_library "$needed" || true)" + if [ -z "$resolved" ]; then + echo "Unable to resolve runtime dependency '$needed' for '$current'." >&2 + exit 1 + fi + + cp -L "$resolved" "$dest_dir/$needed" + seen["$needed"]=1 + queue+=("$resolved") + done < <(readelf -d "$current" 2>/dev/null | sed -n 's/.*Shared library: \[\(.*\)\]/\1/p') +done