setup-cpp
Version:
Install all the tools required for building and testing C++/C projects.
151 lines (126 loc) • 5.57 kB
text/typescript
import path, { delimiter, join } from "path"
import { fileURLToPath } from "url"
import ciInfo from "ci-info"
const { GITHUB_ACTIONS } = ciInfo
import { info, warning } from "ci-log"
import { addEnv } from "envosman"
import memoize from "memoizee"
import { pathExists } from "path-exists"
import { addExeExt } from "patha"
import { addUpdateAlternativesToRc } from "setup-apt"
import { setupGcc } from "../gcc/gcc.js"
import { setupMacOSSDK } from "../macos-sdk/macos-sdk.js"
import { rcOptions } from "../options.js"
import { isUbuntu } from "../utils/env/isUbuntu.js"
import { ubuntuVersion } from "../utils/env/ubuntu_version.js"
import type { InstallationInfo } from "../utils/setup/setupBin.js"
import { quoteIfHasSpace } from "../utils/std/index.js"
import { getVersion } from "../versions/versions.js"
import { trySetupLLVMApk } from "./llvm_apk_installer.js"
import { LLVMPackages, trySetupLLVMApt } from "./llvm_apt_installer.js"
import { setupLLVMBin } from "./llvm_bin.js"
import { trySetupLLVMBrew } from "./llvm_brew_installer.js"
import { majorLLVMVersion } from "./utils.js"
const dirname = typeof __dirname === "string" ? __dirname : path.dirname(fileURLToPath(import.meta.url))
export async function setupLLVM(version: string, setupDir: string, arch: string): Promise<InstallationInfo> {
const installationInfo = await setupLLVMOnly(version, setupDir, arch)
// install gcc for LLVM (for ld, libstdc++, etc.)
await setupGccForLLVM(arch)
// add the logging matcher
await addLLVMLoggingMatcher()
// activate LLVM in the end
if (installationInfo.installDir !== undefined) {
await activateLLVM(installationInfo.installDir, version)
}
return installationInfo
}
async function setupLLVMOnly(
version: string,
setupDir: string,
arch: string,
packages: LLVMPackages = LLVMPackages.All,
) {
const aptInstallInfo = await trySetupLLVMApt(version, packages)
if (aptInstallInfo !== undefined) {
return aptInstallInfo
}
const apkInstallInfo = await trySetupLLVMApk(version)
if (apkInstallInfo !== undefined) {
return apkInstallInfo
}
const brewInstallInfo = await trySetupLLVMBrew(version, setupDir, arch)
if (brewInstallInfo !== undefined) {
return brewInstallInfo
}
return setupLLVMBin(version, setupDir, arch)
}
async function setupGccForLLVM_(arch: string) {
if (process.platform === "linux") {
// using llvm requires ld, an up to date libstdc++, etc. So, install gcc first,
// but with a lower priority than the one used by activateLLVM()
await setupGcc(getVersion("gcc", undefined, await ubuntuVersion()), "", arch, 40)
}
}
const setupGccForLLVM = memoize(setupGccForLLVM_, { promise: true })
export async function activateLLVM(directory: string, version: string) {
const ld = process.env.LD_LIBRARY_PATH ?? ""
const dyld = process.env.DYLD_LIBRARY_PATH ?? ""
const llvmMajor = majorLLVMVersion(version)
const actPromises: Promise<void>[] = [
// compiler paths
addEnv("CC", addExeExt(`${directory}/bin/clang`), rcOptions),
addEnv("CXX", addExeExt(`${directory}/bin/clang++`), rcOptions),
// the output of this action
addEnv("LLVM_PATH", directory, rcOptions),
// Setup LLVM as the compiler
addEnv("LLVM_LD_LIBRARY_PATH", `${ld}${delimiter}${directory}/lib`, rcOptions),
addEnv("LLVM_DYLD_LIBRARY_PATH", `${dyld}${delimiter}${directory}/lib`, rcOptions),
// compiler flags
addEnv("LLVM_LDFLAGS", `-L${quoteIfHasSpace(`${directory}/lib`)}`, rcOptions),
addEnv("LLVM_CPPFLAGS", `-I${quoteIfHasSpace(`${directory}/include`)}`, rcOptions),
// CPATH
await pathExists(`${directory}/lib/clang/${version}/include`)
? addEnv("LLVM_CPATH", `${directory}/lib/clang/${version}/include`, rcOptions)
: await pathExists(`${directory}/lib/clang/${llvmMajor}/include`)
? addEnv("LLVM_CPATH", `${directory}/lib/clang/${llvmMajor}/include`, rcOptions)
: Promise.resolve(),
addEnv("LLVM_LIBRARY_PATH", `${directory}/lib`, rcOptions),
// os sdks
setupMacOSSDK(),
]
if (isUbuntu()) {
const priority = 60
actPromises.push(
addUpdateAlternativesToRc("cc", `${directory}/bin/clang`, rcOptions, priority),
addUpdateAlternativesToRc("cxx", `${directory}/bin/clang++`, rcOptions, priority),
addUpdateAlternativesToRc("clang", `${directory}/bin/clang`, rcOptions),
addUpdateAlternativesToRc("clang++", `${directory}/bin/clang++`, rcOptions),
addUpdateAlternativesToRc("lld", `${directory}/bin/lld`, rcOptions),
addUpdateAlternativesToRc("ld.lld", `${directory}/bin/ld.lld`, rcOptions),
addUpdateAlternativesToRc("llvm-ar", `${directory}/bin/llvm-ar`, rcOptions),
)
}
await Promise.all(actPromises)
}
async function addLLVMLoggingMatcher() {
if (!GITHUB_ACTIONS) {
return
}
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}`)
}
/**
* Setup clang-format
*
* This uses the LLVM installer on Ubuntu, and the LLVM binaries on other platforms
*/
export function setupClangFormat(version: string, setupDir: string, arch: string) {
return setupLLVMOnly(version, setupDir, arch, LLVMPackages.ClangFormat)
}
/** Setup llvm tools (clang tidy, etc.) without activating llvm and using it as the compiler */
export function setupClangTools(version: string, setupDir: string, arch: string) {
return setupLLVMOnly(version, setupDir, arch)
}