diff --git a/Dockerfile b/Dockerfile index 6d32b3a..5bb3542 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,6 +14,7 @@ RUN apt-get update \ ca-certificates \ wget \ git \ + unzip \ && rm -rf /var/lib/apt/lists/* # Install Go (latest stable version) @@ -39,6 +40,19 @@ RUN --mount=type=cache,target=/root/.cache/go-build \ --mount=type=cache,target=/go/pkg/mod \ go install mvdan.cc/garble@latest +# Install Android NDK for Android cross-compilation +ENV ANDROID_NDK_VERSION=r27c +ENV ANDROID_NDK_HOME=/opt/android-ndk +RUN case "${TARGETARCH}" in \ + amd64) NDK_HOST="linux-x86_64" ;; \ + arm64) NDK_HOST="linux-aarch64" ;; \ + *) echo "Unsupported architecture for Android NDK: ${TARGETARCH}" >&2; exit 1 ;; \ + esac \ + && wget -q "https://dl.google.com/android/repository/android-ndk-${ANDROID_NDK_VERSION}-linux.zip" \ + && unzip -q android-ndk-${ANDROID_NDK_VERSION}-linux.zip \ + && mv android-ndk-${ANDROID_NDK_VERSION} ${ANDROID_NDK_HOME} \ + && rm android-ndk-${ANDROID_NDK_VERSION}-linux.zip + # Copy package files and lockfile COPY Overlord-Server/package.json Overlord-Server/bun.lock* ./ diff --git a/Overlord-Server/src/server/build-process.ts b/Overlord-Server/src/server/build-process.ts index feeb194..dc2cbc2 100644 --- a/Overlord-Server/src/server/build-process.ts +++ b/Overlord-Server/src/server/build-process.ts @@ -44,6 +44,13 @@ function resolveClientBuildCacheRoot(): string { return path.resolve(ensureDataDir(), "client-build-cache"); } +function resolveAndroidNdkToolchainBin(): string | null { + const ndkHome = (process.env.ANDROID_NDK_HOME || "/opt/android-ndk").trim(); + const hostArch = process.arch === "arm64" ? "linux-aarch64" : "linux-x86_64"; + const toolchainBin = path.join(ndkHome, "toolchains", "llvm", "prebuilt", hostArch, "bin"); + return fs.existsSync(toolchainBin) ? toolchainBin : null; +} + type BuildProcessConfig = { platforms: string[]; serverUrl?: string; @@ -160,6 +167,15 @@ export async function startBuildProcess( throw new Error("One or more requested platforms are not allowed"); } + const ndkBin = resolveAndroidNdkToolchainBin(); + if (!ndkBin && platformsToBuild.some((p) => p.startsWith("android-"))) { + sendToStream({ + type: "output", + text: "Warning: Android NDK not found. Android builds require the NDK. Install it to /opt/android-ndk or set the ANDROID_NDK_HOME environment variable.\n", + level: "warn", + }); + } + let buildMutex = ""; if (!config.disableMutex) { buildMutex = config.mutex || deps.generateBuildMutex(); @@ -196,11 +212,21 @@ export async function startBuildProcess( "linux/amd64": "gcc", "windows/amd64": "x86_64-w64-mingw32-gcc", "windows/386": "i686-w64-mingw32-gcc", + ...(ndkBin ? { + "android/amd64": path.join(ndkBin, "x86_64-linux-android21-clang"), + "android/arm64": path.join(ndkBin, "aarch64-linux-android21-clang"), + "android/arm/v7": path.join(ndkBin, "armv7a-linux-androideabi21-clang"), + } : {}), }; const cxxCompilerByTarget: Record = { "linux/amd64": "g++", "windows/amd64": "x86_64-w64-mingw32-g++", "windows/386": "i686-w64-mingw32-g++", + ...(ndkBin ? { + "android/amd64": path.join(ndkBin, "x86_64-linux-android21-clang++"), + "android/arm64": path.join(ndkBin, "aarch64-linux-android21-clang++"), + "android/arm/v7": path.join(ndkBin, "armv7a-linux-androideabi21-clang++"), + } : {}), }; const cc = cCompilerByTarget[targetKey]; @@ -218,6 +244,9 @@ export async function startBuildProcess( if (cxx) { env.CXX = cxx; } + if (os === "android" && ndkBin) { + env.AR = path.join(ndkBin, "llvm-ar"); + } } let ldflags = config.stripDebug !== false ? "-s -w" : "";