mirror of https://github.com/aminya/setup-cpp
fix: refactor LLVM installation
This commit is contained in:
parent
70a091c663
commit
dd9ff769c8
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
197
src/llvm/llvm.ts
197
src/llvm/llvm.ts
|
@ -1,119 +1,123 @@
|
||||||
import { join, addExeExt } from "patha"
|
import { execRoot } from "admina"
|
||||||
|
import { GITHUB_ACTIONS } from "ci-info"
|
||||||
|
import { info, warning } from "ci-log"
|
||||||
|
import { ExecaReturnValue, execa } from "execa"
|
||||||
|
import { promises } from "fs"
|
||||||
|
const { readFile, writeFile, chmod } = promises
|
||||||
|
import memoize from "micro-memoize"
|
||||||
import { delimiter } from "path"
|
import { delimiter } from "path"
|
||||||
import { InstallationInfo, setupBin } from "../utils/setup/setupBin"
|
import { pathExists } from "path-exists"
|
||||||
import { semverCoerceIfInvalid } from "../utils/setup/version"
|
import { addExeExt, join } from "patha"
|
||||||
|
import { setupGcc } from "../gcc/gcc"
|
||||||
import { setupMacOSSDK } from "../macos-sdk/macos-sdk"
|
import { setupMacOSSDK } from "../macos-sdk/macos-sdk"
|
||||||
import { addEnv } from "../utils/env/addEnv"
|
import { addEnv } from "../utils/env/addEnv"
|
||||||
import { hasNala, setupAptPack, updateAptAlternatives } from "../utils/setup/setupAptPack"
|
|
||||||
import { info, warning } from "ci-log"
|
|
||||||
|
|
||||||
import { GITHUB_ACTIONS } from "ci-info"
|
|
||||||
import { setupGcc } from "../gcc/gcc"
|
|
||||||
import { getVersion } from "../versions/versions"
|
|
||||||
import { isUbuntu } from "../utils/env/isUbuntu"
|
import { isUbuntu } from "../utils/env/isUbuntu"
|
||||||
import { getLLVMPackageInfo } from "./llvm_url"
|
|
||||||
import { ubuntuVersion } from "../utils/env/ubuntu_version"
|
import { ubuntuVersion } from "../utils/env/ubuntu_version"
|
||||||
import { pathExists } from "path-exists"
|
import { hasNala, setupAptPack, updateAptAlternatives } from "../utils/setup/setupAptPack"
|
||||||
import { ExecaReturnValue, execa } from "execa"
|
import { InstallationInfo, setupBin } from "../utils/setup/setupBin"
|
||||||
import { readFileSync, writeFileSync } from "fs"
|
import { semverCoerceIfInvalid } from "../utils/setup/version"
|
||||||
import { execRootSync } from "admina"
|
import { getVersion } from "../versions/versions"
|
||||||
|
import { getLLVMPackageInfo } from "./llvm_url"
|
||||||
|
|
||||||
export async function setupLLVM(version: string, setupDir: string, arch: string): Promise<InstallationInfo> {
|
export async function setupLLVM(version: string, setupDir: string, arch: string): Promise<InstallationInfo> {
|
||||||
const installationInfo = await setupLLVMWithoutActivation(version, setupDir, arch)
|
const installationInfo = await setupLLVMWithoutActivation(version, setupDir, arch)
|
||||||
await activateLLVM(installationInfo.installDir ?? setupDir, version)
|
await activateLLVM(installationInfo.installDir ?? setupDir)
|
||||||
return installationInfo
|
return installationInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
let installedDeps = false
|
/** Setup llvm tools (clang tidy, clang format, etc) without activating llvm and using it as the compiler */
|
||||||
|
export const setupClangTools = setupLLVMWithoutActivation
|
||||||
|
|
||||||
|
async function setupLLVMWithoutActivation(version: string, setupDir: string, arch: string) {
|
||||||
|
// install LLVM and its dependencies in parallel
|
||||||
|
const [installationInfo, _1, _2] = await Promise.all([
|
||||||
|
setupLLVMOnly(version, setupDir, arch),
|
||||||
|
setupLLVMDeps(arch),
|
||||||
|
addLLVMLoggingMatcher(),
|
||||||
|
])
|
||||||
|
|
||||||
|
return installationInfo
|
||||||
|
}
|
||||||
|
|
||||||
async function setupLLVMOnly(version: string, setupDir: string, arch: string) {
|
async function setupLLVMOnly(version: string, setupDir: string, arch: string) {
|
||||||
|
const coeredVersion = semverCoerceIfInvalid(version)
|
||||||
|
const majorVersion = parseInt(coeredVersion.split(".")[0], 10)
|
||||||
try {
|
try {
|
||||||
if (isUbuntu()) {
|
if (isUbuntu()) {
|
||||||
const coeredVersion = semverCoerceIfInvalid(version)
|
return setupLLVMApt(majorVersion)
|
||||||
const majorVersion = parseInt(coeredVersion.split(".")[0], 10)
|
|
||||||
const installationFolder = `/usr/lib/llvm-${majorVersion}` // TODO for older versions, this also includes the minor version
|
|
||||||
|
|
||||||
await setupAptPack([{ name: "curl" }])
|
|
||||||
await execa("curl", ["-LJO", "https://apt.llvm.org/llvm.sh"], { cwd: "/tmp" })
|
|
||||||
|
|
||||||
let script = readFileSync("/tmp/llvm.sh", "utf-8")
|
|
||||||
// make the scirpt non-interactive and fix broken packages
|
|
||||||
script = script
|
|
||||||
.replace(
|
|
||||||
/add-apt-repository "\${REPO_NAME}"/g,
|
|
||||||
// eslint-disable-next-line no-template-curly-in-string
|
|
||||||
'add-apt-repository -y "${REPO_NAME}"'
|
|
||||||
)
|
|
||||||
.replace(/apt-get install -y/g, "apt-get install -y --fix-broken")
|
|
||||||
// use nala if it is available
|
|
||||||
if (hasNala()) {
|
|
||||||
script = script.replace(/apt-get/g, "nala")
|
|
||||||
}
|
|
||||||
writeFileSync("/tmp/llvm-setup-cpp.sh", script)
|
|
||||||
|
|
||||||
execRootSync("chmod", ["+x", "/tmp/llvm-setup-cpp.sh"])
|
|
||||||
execRootSync("bash", ["/tmp/setup-cpp-llvm.sh"], {
|
|
||||||
stdio: "inherit",
|
|
||||||
shell: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
installDir: `/usr/lib/${installationFolder}`,
|
|
||||||
binDir: `/usr/bin`,
|
|
||||||
version,
|
|
||||||
} as InstallationInfo
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
info(`Failed to install llvm via system package manager ${err}`)
|
info(`Failed to install llvm via system package manager ${err}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return setupBin("llvm", version, getLLVMPackageInfo, setupDir, arch)
|
const installationInfo = await setupBin("llvm", version, getLLVMPackageInfo, setupDir, arch)
|
||||||
}
|
await llvmBinaryDeps(majorVersion)
|
||||||
|
|
||||||
async function setupLLVMWithoutActivation(version: string, setupDir: string, arch: string) {
|
|
||||||
const installationInfoPromise = setupLLVMOnly(version, setupDir, arch)
|
|
||||||
|
|
||||||
let depsPromise: Promise<void> = Promise.resolve()
|
|
||||||
if (!installedDeps) {
|
|
||||||
depsPromise = setupLLVMDeps(arch, version)
|
|
||||||
// eslint-disable-next-line require-atomic-updates
|
|
||||||
installedDeps = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// install LLVM and its dependencies in parallel
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const [installationInfo, _] = await Promise.all([installationInfoPromise, depsPromise])
|
|
||||||
|
|
||||||
return installationInfo
|
return installationInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setupLLVMDeps(arch: string, version: string) {
|
async function setupLLVMApt(majorVersion: number): Promise<InstallationInfo> {
|
||||||
if (process.platform === "linux") {
|
// TODO for older versions, this also includes the minor version
|
||||||
// install llvm build dependencies
|
const installationFolder = `/usr/lib/llvm-${majorVersion}`
|
||||||
await setupGcc(getVersion("gcc", undefined, await ubuntuVersion()), "", arch) // using llvm requires ld, an up to date libstdc++, etc. So, install gcc first
|
|
||||||
|
|
||||||
if (isUbuntu()) {
|
await setupAptPack([{ name: "curl" }])
|
||||||
const majorVersion = parseInt(version.split(".")[0], 10)
|
await execa("curl", ["-LJO", "https://apt.llvm.org/llvm.sh"], { cwd: "/tmp" })
|
||||||
if (majorVersion <= 10) {
|
await patchAptLLVMScript("/tmp/llvm.sh", "/tmp/llvm-setup-cpp.sh")
|
||||||
await setupAptPack([{ name: "libtinfo5" }])
|
await chmod("/tmp/llvm-setup-cpp.sh", "755")
|
||||||
} else {
|
await execRoot("bash", ["/tmp/setup-cpp-llvm.sh"], {
|
||||||
await setupAptPack([{ name: "libtinfo-dev" }])
|
stdio: "inherit",
|
||||||
}
|
shell: true,
|
||||||
}
|
})
|
||||||
// TODO: install libtinfo on other distros
|
|
||||||
// await setupPacmanPack("ncurses")
|
return {
|
||||||
|
installDir: `${installationFolder}`,
|
||||||
|
binDir: `${installationFolder}/bin`,
|
||||||
|
bin: `${installationFolder}/bin/clang++`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function activateLLVM(directory: string, versionGiven: string) {
|
async function patchAptLLVMScript(path: string, target_path: string) {
|
||||||
const _version = semverCoerceIfInvalid(versionGiven)
|
let script = await readFile(path, "utf-8")
|
||||||
|
// make the scirpt non-interactive and fix broken packages
|
||||||
|
script = script
|
||||||
|
.replace(
|
||||||
|
/add-apt-repository "\${REPO_NAME}"/g,
|
||||||
|
// eslint-disable-next-line no-template-curly-in-string
|
||||||
|
'add-apt-repository -y "${REPO_NAME}"'
|
||||||
|
)
|
||||||
|
.replace(/apt-get install -y/g, "apt-get install -y --fix-broken")
|
||||||
|
// use nala if it is available
|
||||||
|
if (hasNala()) {
|
||||||
|
script = script.replace(/apt-get/g, "nala")
|
||||||
|
}
|
||||||
|
await writeFile(target_path, script)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function llvmBinaryDeps_raw(majorVersion: number) {
|
||||||
|
if (isUbuntu()) {
|
||||||
|
if (majorVersion <= 10) {
|
||||||
|
await setupAptPack([{ name: "libtinfo5" }])
|
||||||
|
} else {
|
||||||
|
await setupAptPack([{ name: "libtinfo-dev" }])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const llvmBinaryDeps = memoize(llvmBinaryDeps_raw, { isPromise: true })
|
||||||
|
|
||||||
|
async function setupLLVMDeps_raw(arch: string) {
|
||||||
|
if (process.platform === "linux") {
|
||||||
|
// using llvm requires ld, an up to date libstdc++, etc. So, install gcc first
|
||||||
|
await setupGcc(getVersion("gcc", undefined, await ubuntuVersion()), "", arch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const setupLLVMDeps = memoize(setupLLVMDeps_raw, { isPromise: true })
|
||||||
|
|
||||||
|
export async function activateLLVM(directory: string) {
|
||||||
const lib = join(directory, "lib")
|
const lib = join(directory, "lib")
|
||||||
|
|
||||||
const ld = process.env.LD_LIBRARY_PATH ?? ""
|
const ld = process.env.LD_LIBRARY_PATH ?? ""
|
||||||
const dyld = process.env.DYLD_LIBRARY_PATH ?? ""
|
const dyld = process.env.DYLD_LIBRARY_PATH ?? ""
|
||||||
|
|
||||||
const promises: Promise<void | ExecaReturnValue<string>>[] = [
|
const actPromises: Promise<void | ExecaReturnValue<string>>[] = [
|
||||||
// the output of this action
|
// the output of this action
|
||||||
addEnv("LLVM_PATH", directory),
|
addEnv("LLVM_PATH", directory),
|
||||||
|
|
||||||
|
@ -138,7 +142,6 @@ export async function activateLLVM(directory: string, versionGiven: string) {
|
||||||
// TODO Causes issues with clangd
|
// TODO Causes issues with clangd
|
||||||
// TODO Windows builds fail with llvm's CPATH
|
// TODO Windows builds fail with llvm's CPATH
|
||||||
// if (process.platform !== "win32") {
|
// if (process.platform !== "win32") {
|
||||||
// const llvmMajor = semverMajor(version)
|
|
||||||
// if (await pathExists(`${directory}/lib/clang/${version}/include`)) {
|
// if (await pathExists(`${directory}/lib/clang/${version}/include`)) {
|
||||||
// promises.push(addEnv("CPATH", `${directory}/lib/clang/${version}/include`))
|
// promises.push(addEnv("CPATH", `${directory}/lib/clang/${version}/include`))
|
||||||
// } else if (await pathExists(`${directory}/lib/clang/${llvmMajor}/include`)) {
|
// } else if (await pathExists(`${directory}/lib/clang/${llvmMajor}/include`)) {
|
||||||
|
@ -147,7 +150,7 @@ export async function activateLLVM(directory: string, versionGiven: string) {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if (isUbuntu()) {
|
if (isUbuntu()) {
|
||||||
promises.push(
|
actPromises.push(
|
||||||
updateAptAlternatives("cc", `${directory}/bin/clang`),
|
updateAptAlternatives("cc", `${directory}/bin/clang`),
|
||||||
updateAptAlternatives("cxx", `${directory}/bin/clang++`),
|
updateAptAlternatives("cxx", `${directory}/bin/clang++`),
|
||||||
updateAptAlternatives("clang", `${directory}/bin/clang`),
|
updateAptAlternatives("clang", `${directory}/bin/clang`),
|
||||||
|
@ -158,25 +161,15 @@ export async function activateLLVM(directory: string, versionGiven: string) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GITHUB_ACTIONS) {
|
await Promise.all(actPromises)
|
||||||
await addLLVMLoggingMatcher()
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all(promises)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Setup llvm tools (clang tidy, clang format, etc) without activating llvm and using it as the compiler */
|
|
||||||
export async function setupClangTools(version: string, setupDir: string, arch: string): Promise<InstallationInfo> {
|
|
||||||
if (GITHUB_ACTIONS) {
|
|
||||||
await addLLVMLoggingMatcher()
|
|
||||||
}
|
|
||||||
return setupLLVMWithoutActivation(version, setupDir, arch)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addLLVMLoggingMatcher() {
|
async function addLLVMLoggingMatcher() {
|
||||||
const matcherPath = join(__dirname, "llvm_matcher.json")
|
if (GITHUB_ACTIONS) {
|
||||||
if (!(await pathExists(matcherPath))) {
|
const matcherPath = join(__dirname, "llvm_matcher.json")
|
||||||
return warning("the llvm_matcher.json file does not exist in the same folder as setup-cpp.js")
|
if (!(await pathExists(matcherPath))) {
|
||||||
|
return warning("the llvm_matcher.json file does not exist in the same folder as setup-cpp.js")
|
||||||
|
}
|
||||||
|
info(`::add-matcher::${matcherPath}`)
|
||||||
}
|
}
|
||||||
info(`::add-matcher::${matcherPath}`)
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue