mirror of https://github.com/aminya/setup-cpp.git
180 lines
6.4 KiB
TypeScript
180 lines
6.4 KiB
TypeScript
import { execRoot } from "admina"
|
|
import { GITHUB_ACTIONS } from "ci-info"
|
|
import { info, warning } from "ci-log"
|
|
import { execa } from "execa"
|
|
import { promises } from "fs"
|
|
const { readFile, writeFile, chmod } = promises
|
|
import memoize from "micro-memoize"
|
|
import { delimiter } from "path"
|
|
import { pathExists } from "path-exists"
|
|
import { addExeExt, join } from "patha"
|
|
import { setupGcc } from "../gcc/gcc"
|
|
import { setupMacOSSDK } from "../macos-sdk/macos-sdk"
|
|
import { addEnv, addPath } from "../utils/env/addEnv"
|
|
import { isUbuntu } from "../utils/env/isUbuntu"
|
|
import { ubuntuVersion } from "../utils/env/ubuntu_version"
|
|
import { hasNala, setupAptPack, updateAptAlternatives } from "../utils/setup/setupAptPack"
|
|
import { InstallationInfo, setupBin } from "../utils/setup/setupBin"
|
|
import { semverCoerceIfInvalid } from "../utils/setup/version"
|
|
import { getVersion } from "../versions/versions"
|
|
import { getLLVMPackageInfo } from "./llvm_url"
|
|
|
|
export async function setupLLVM(version: string, setupDir: string, arch: string): Promise<InstallationInfo> {
|
|
const installationInfo = await setupLLVMWithoutActivation(version, setupDir, arch)
|
|
await activateLLVM(installationInfo.installDir ?? setupDir)
|
|
return installationInfo
|
|
}
|
|
|
|
/** 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) {
|
|
const coeredVersion = semverCoerceIfInvalid(version)
|
|
const majorVersion = parseInt(coeredVersion.split(".")[0], 10)
|
|
try {
|
|
if (isUbuntu()) {
|
|
return setupLLVMApt(majorVersion)
|
|
}
|
|
} catch (err) {
|
|
info(`Failed to install llvm via system package manager ${err}`)
|
|
}
|
|
|
|
const installationInfo = await setupBin("llvm", version, getLLVMPackageInfo, setupDir, arch)
|
|
await llvmBinaryDeps(majorVersion)
|
|
return installationInfo
|
|
}
|
|
|
|
async function setupLLVMApt(majorVersion: number): Promise<InstallationInfo> {
|
|
// 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.toString()], {
|
|
stdio: "inherit",
|
|
shell: true,
|
|
})
|
|
|
|
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")
|
|
// 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)
|
|
|
|
// the packages needed by the script
|
|
return [{ name: "lsb-release" }, { name: "wget" }, { name: "software-properties-common" }, { name: "gnupg" }]
|
|
}
|
|
|
|
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 ld = process.env.LD_LIBRARY_PATH ?? ""
|
|
const dyld = process.env.DYLD_LIBRARY_PATH ?? ""
|
|
|
|
const actPromises: Promise<any>[] = [
|
|
// the output of this action
|
|
addEnv("LLVM_PATH", directory),
|
|
|
|
// Setup LLVM as the compiler
|
|
addEnv("LD_LIBRARY_PATH", `${directory}/lib${delimiter}${ld}`),
|
|
addEnv("DYLD_LIBRARY_PATH", `${directory}/lib${delimiter}${dyld}`),
|
|
|
|
// compiler flags
|
|
addEnv("LDFLAGS", `-L"${directory}/lib"`),
|
|
addEnv("CPPFLAGS", `-I"${directory}/include"`),
|
|
|
|
// compiler paths
|
|
addEnv("CC", addExeExt(`${directory}/bin/clang`)),
|
|
addEnv("CXX", addExeExt(`${directory}/bin/clang++`)),
|
|
|
|
addEnv("LIBRARY_PATH", `${directory}/lib`),
|
|
|
|
// os sdks
|
|
setupMacOSSDK(),
|
|
]
|
|
|
|
// TODO Causes issues with clangd
|
|
// TODO Windows builds fail with llvm's CPATH
|
|
// if (process.platform !== "win32") {
|
|
// if (await pathExists(`${directory}/lib/clang/${version}/include`)) {
|
|
// promises.push(addEnv("CPATH", `${directory}/lib/clang/${version}/include`))
|
|
// } else if (await pathExists(`${directory}/lib/clang/${llvmMajor}/include`)) {
|
|
// promises.push(addEnv("CPATH", `${directory}/lib/clang/${llvmMajor}/include`))
|
|
// }
|
|
// }
|
|
|
|
if (isUbuntu()) {
|
|
actPromises.push(
|
|
updateAptAlternatives("cc", `${directory}/bin/clang`),
|
|
updateAptAlternatives("cxx", `${directory}/bin/clang++`),
|
|
updateAptAlternatives("clang", `${directory}/bin/clang`),
|
|
updateAptAlternatives("clang++", `${directory}/bin/clang++`),
|
|
updateAptAlternatives("lld", `${directory}/bin/lld`),
|
|
updateAptAlternatives("ld.lld", `${directory}/bin/ld.lld`),
|
|
updateAptAlternatives("llvm-ar", `${directory}/bin/llvm-ar`)
|
|
)
|
|
}
|
|
|
|
await Promise.all(actPromises)
|
|
}
|
|
|
|
async function addLLVMLoggingMatcher() {
|
|
if (GITHUB_ACTIONS) {
|
|
const matcherPath = join(__dirname, "llvm_matcher.json")
|
|
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}`)
|
|
}
|
|
}
|