import { promises as fs } from "node:fs"; import { spawn } from "node:child_process"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { createHash } from "node:crypto"; const root = path.dirname(fileURLToPath(import.meta.url)); const dir = path.join(root, ".toolchains"); const proxy = process.env.HTTPS_PROXY || process.env.https_proxy; const proxyArgs = proxy ? (() => { const u = new URL(proxy); return [ "--proxy=http", "--proxy_host=" + u.hostname, "--proxy_port=" + (u.port || "80"), ]; })() : []; const run = (c, a) => new Promise((resolve, reject) => { const p = spawn(c, a, { stdio: "inherit" }); p.on("error", reject); p.on("exit", (n) => n === 0 ? resolve() : reject(Error(c + " exited " + n)), ); }); if (process.platform !== "linux") throw Error( "Automatic setup supports Linux. On other hosts install Android SDK, JDK 17 and Gradle 8.13; set ANDROID_HOME, JAVA_HOME and FORMA_GRADLE.", ); await run( process.env.JAVA_HOME ? path.join(process.env.JAVA_HOME, "bin", "javac") : "javac", ["-version"], ); await fs.mkdir(dir, { recursive: true }); const gradleZip = path.join(dir, "gradle.zip"); if ( !(await fs.access(path.join(dir, "gradle-8.13", "bin", "gradle")).then( () => true, () => false, )) ) { await run("curl", [ "-fL", "--connect-timeout", "20", "--max-time", "300", "--retry", "2", "-o", gradleZip, "https://services.gradle.org/distributions/gradle-8.13-bin.zip", ]); const shaFile = path.join(dir, "gradle.sha256"); await run("curl", [ "-fL", "-o", shaFile, "https://services.gradle.org/distributions/gradle-8.13-bin.zip.sha256", ]); if ( createHash("sha256") .update(await fs.readFile(gradleZip)) .digest("hex") !== (await fs.readFile(shaFile, "utf8")).trim() ) throw Error("Gradle checksum mismatch"); await run("unzip", ["-q", "-o", gradleZip, "-d", dir]); } const sdk = process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT || path.join(dir, "android-sdk"); const manager = path.join(sdk, "cmdline-tools", "latest", "bin", "sdkmanager"); try { await fs.access(manager); } catch { const zip = path.join(dir, "android-tools.zip"), staging = path.join(dir, "sdk-tools"); await run("curl", [ "-fL", "--connect-timeout", "20", "--max-time", "300", "--retry", "2", "-o", zip, "https://dl.google.com/android/repository/commandlinetools-linux-13114758_latest.zip", ]); await run("unzip", ["-q", "-o", zip, "-d", staging]); await fs.mkdir(path.dirname(path.dirname(manager)), { recursive: true }); await fs.cp( path.join(staging, "cmdline-tools"), path.join(sdk, "cmdline-tools", "latest"), { recursive: true }, ); } // sdkmanager presents licenses to the human running setup; never silently accepts them. await run(manager, [ "--sdk_root=" + sdk, ...proxyArgs, "platforms;android-36", "build-tools;35.0.0", ]); console.log("Android toolchain ready. Run node native/build.mjs --doctor.");