import { execRoot } from "admina" import { execa } from "execa" import { addPath } from "../utils/env/addEnv" import { hasNala, isPackageInstalled, setupAptPack } from "../utils/setup/setupAptPack" import { InstallationInfo } from "../utils/setup/setupBin" import { promises } from "fs" import { info } from "console" import { DEFAULT_TIMEOUT } from "../installTool" const { readFile, writeFile, chmod } = promises export async function setupLLVMApt(majorVersion: number): Promise { // TODO for older versions, this also includes the minor version const installationFolder = `/usr/lib/llvm-${majorVersion}` await setupAptPack([{ name: "curl" }]) await execa("curl", ["-LJO", "https://apt.llvm.org/llvm.sh"], { cwd: "/tmp" }) const neededPackages = await patchAptLLVMScript("/tmp/llvm.sh", "/tmp/llvm-setup-cpp.sh") await setupAptPack(neededPackages) await chmod("/tmp/llvm-setup-cpp.sh", "755") await execRoot("bash", ["/tmp/llvm-setup-cpp.sh", `${majorVersion}`, "all"], { stdio: "inherit", shell: true, timeout: DEFAULT_TIMEOUT, }) await addPath(`${installationFolder}/bin`) return { installDir: `${installationFolder}`, binDir: `${installationFolder}/bin`, bin: `${installationFolder}/bin/clang++`, } } async function patchAptLLVMScript(path: string, target_path: string) { let script = await readFile(path, "utf-8") script = debugScript(script) script = nonInteractiveScript(script) script = await removeConflictingPAckages(script) script = useNalaScript(script) await writeFile(target_path, script) // the packages needed by the script return [{ name: "lsb-release" }, { name: "wget" }, { name: "software-properties-common" }, { name: "gnupg" }] } function debugScript(script: string) { if (!process.env.NODE_DEBUG) { return script.replace(/set -eux/g, "set -eu") } return script } function nonInteractiveScript(script: string) { // make the scirpt non-interactive and fix broken packages return script.replace( /add-apt-repository "\${REPO_NAME}"/g, // eslint-disable-next-line no-template-curly-in-string 'add-apt-repository -y "${REPO_NAME}"', ) } async function removeConflictingPAckages(givenScript: string) { // fix conflicts between libclang-rt and libclang let script = givenScript.replace( /apt-get install -y/g, 'apt-get install -o Dpkg::Options::="--force-overwrite" -y --fix-broken', ) // check if these are installed and if so, remove them from the script as they conflict const conflictingPackages = ["libc++-$LLVM_VERSION-dev", "libc++abi-$LLVM_VERSION-dev", "libunwind-$LLVM_VERSION-dev"] await Promise.all( conflictingPackages.map(async (pack) => { const installingPack = pack.replace("$LLVM_VERSION", "*") if (await isPackageInstalled(installingPack)) { info(`Removing conflicting package ${installingPack}`) script = script.replace(pack, "") } }), ) return script } function useNalaScript(script: string) { // use nala if it is available if (hasNala()) { return script.replace(/apt-get/g, "nala") } return script }