Files
forma-engine/native/setup-android.mjs
T

110 lines
3.1 KiB
JavaScript

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.");