mirror of https://github.com/aminya/setup-cpp.git
317 lines
8.9 KiB
TypeScript
317 lines
8.9 KiB
TypeScript
import * as core from "@actions/core"
|
|
import * as exec from "@actions/exec"
|
|
import * as io from "@actions/io"
|
|
import * as tc from "@actions/tool-cache"
|
|
import * as path from "path"
|
|
import semverLte from "semver/functions/lte"
|
|
import { isValidUrl } from "../utils/http/validate_url"
|
|
|
|
//================================================
|
|
// Version
|
|
//================================================
|
|
|
|
/**
|
|
* Gets the specific and minimum LLVM versions that can be used to refer to the supplied specific LLVM versions (e.g.,
|
|
* `3`, `3.5`, `3.5.2` for `3.5.2`).
|
|
*/
|
|
function getVersions(specific: string[]): Set<string> {
|
|
const versions = new Set(specific)
|
|
|
|
for (const version of specific) {
|
|
versions.add(/^\d+/.exec(version)![0])
|
|
versions.add(/^\d+\.\d+/.exec(version)![0])
|
|
}
|
|
|
|
return versions
|
|
}
|
|
|
|
/** The specific and minimum LLVM versions supported by this action. */
|
|
const VERSIONS: Set<string> = getVersions([
|
|
"3.5.0",
|
|
"3.5.1",
|
|
"3.5.2",
|
|
"3.6.0",
|
|
"3.6.1",
|
|
"3.6.2",
|
|
"3.7.0",
|
|
"3.7.1",
|
|
"3.8.0",
|
|
"3.8.1",
|
|
"3.9.0",
|
|
"3.9.1",
|
|
"4.0.0",
|
|
"4.0.1",
|
|
"5.0.0",
|
|
"5.0.1",
|
|
"5.0.2",
|
|
"6.0.0",
|
|
"6.0.1",
|
|
"7.0.0",
|
|
"7.0.1",
|
|
"7.1.0",
|
|
"8.0.0",
|
|
"8.0.1",
|
|
"9.0.0",
|
|
"9.0.1",
|
|
"10.0.0",
|
|
"10.0.1",
|
|
"11.0.0",
|
|
"11.0.1",
|
|
"11.1.0",
|
|
"12.0.0",
|
|
"12.0.1",
|
|
])
|
|
|
|
/**
|
|
* Gets the specific LLVM versions supported by this action compatible with the supplied (specific or minimum) LLVM
|
|
* version in descending order of release (e.g., `5.0.2`, `5.0.1`, and `5.0.0` for `5`).
|
|
*/
|
|
function getSpecificVersions(version: string): string[] {
|
|
return Array.from(VERSIONS)
|
|
.filter((v) => /^\d+\.\d+\.\d+$/.test(v) && v.startsWith(version))
|
|
.sort()
|
|
.reverse()
|
|
}
|
|
|
|
//================================================
|
|
// URL
|
|
//================================================
|
|
|
|
/** Gets a LLVM download URL for GitHub. */
|
|
function getGitHubUrl(version: string, prefix: string, suffix: string): string {
|
|
const file = `${prefix}${version}${suffix}`
|
|
return `https://github.com/llvm/llvm-project/releases/download/llvmorg-${version}/${file}`
|
|
}
|
|
|
|
/** Gets a LLVM download URL for https://releases.llvm.org. */
|
|
function getReleaseUrl(version: string, prefix: string, suffix: string): string {
|
|
const file = `${prefix}${version}${suffix}`
|
|
return `https://releases.llvm.org/${version}/${file}`
|
|
}
|
|
|
|
/** The LLVM versions that were never released for the Darwin platform. */
|
|
const DARWIN_MISSING: Set<string> = new Set([
|
|
"3.5.1",
|
|
"3.6.1",
|
|
"3.6.2",
|
|
"3.7.1",
|
|
"3.8.1",
|
|
"3.9.1",
|
|
"6.0.1",
|
|
"7.0.1",
|
|
"7.1.0",
|
|
"8.0.1",
|
|
"11.0.1",
|
|
"11.1.0",
|
|
"12.0.1",
|
|
])
|
|
|
|
/** Gets an LLVM download URL for the Darwin platform. */
|
|
function getDarwinUrl(version: string): string | null {
|
|
if (DARWIN_MISSING.has(version)) {
|
|
return null
|
|
}
|
|
|
|
const darwin = version === "9.0.0" ? "-darwin-apple" : "-apple-darwin"
|
|
const prefix = "clang+llvm-"
|
|
const suffix = `-x86_64${darwin}.tar.xz`
|
|
if (semverLte(version, "9.0.1")) {
|
|
return getReleaseUrl(version, prefix, suffix)
|
|
} else {
|
|
return getGitHubUrl(version, prefix, suffix)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The LLVM versions that should use the last RC version instead of the release version for the Linux (Ubuntu) platform.
|
|
* This is useful when there were binaries released for the Linux (Ubuntu) platform for the last RC version but not for
|
|
* the actual release version.
|
|
*/
|
|
const UBUNTU_RC: Map<string, string> = new Map([["12.0.1", "12.0.1-rc4"]])
|
|
|
|
/** The (latest) Ubuntu versions for each LLVM version. */
|
|
const UBUNTU: { [key: string]: string } = {
|
|
"3.5.0": "-ubuntu-14.04",
|
|
"3.5.1": "",
|
|
"3.5.2": "-ubuntu-14.04",
|
|
"3.6.0": "-ubuntu-14.04",
|
|
"3.6.1": "-ubuntu-14.04",
|
|
"3.6.2": "-ubuntu-14.04",
|
|
"3.7.0": "-ubuntu-14.04",
|
|
"3.7.1": "-ubuntu-14.04",
|
|
"3.8.0": "-ubuntu-16.04",
|
|
"3.8.1": "-ubuntu-16.04",
|
|
"3.9.0": "-ubuntu-16.04",
|
|
"3.9.1": "-ubuntu-16.04",
|
|
"4.0.0": "-ubuntu-16.04",
|
|
"5.0.0": "-ubuntu16.04",
|
|
"5.0.1": "-ubuntu-16.04",
|
|
"5.0.2": "-ubuntu-16.04",
|
|
"6.0.0": "-ubuntu-16.04",
|
|
"6.0.1": "-ubuntu-16.04",
|
|
"7.0.0": "-ubuntu-16.04",
|
|
"7.0.1": "-ubuntu-18.04",
|
|
"7.1.0": "-ubuntu-14.04",
|
|
"8.0.0": "-ubuntu-18.04",
|
|
"9.0.0": "-ubuntu-18.04",
|
|
"9.0.1": "-ubuntu-16.04",
|
|
"10.0.0": "-ubuntu-18.04",
|
|
"10.0.1": "-ubuntu-16.04",
|
|
"11.0.0": "-ubuntu-20.04",
|
|
"11.0.1": "-ubuntu-16.04",
|
|
"11.1.0": "-ubuntu-16.04",
|
|
"12.0.0": "-ubuntu-20.04",
|
|
"12.0.1-rc4": "-ubuntu-21.04",
|
|
}
|
|
|
|
/** The latest supported LLVM version for the Linux (Ubuntu) platform. */
|
|
const MAX_UBUNTU: string = "12.0.1-rc4"
|
|
|
|
/** Gets an LLVM download URL for the Linux (Ubuntu) platform. */
|
|
function getLinuxUrl(versionGiven: string): string {
|
|
let version = versionGiven
|
|
|
|
const rc = UBUNTU_RC.get(version)
|
|
if (rc !== undefined) {
|
|
version = rc
|
|
}
|
|
|
|
let ubuntu: string
|
|
// ubuntu-version is specified
|
|
if (version.includes("ubuntu")) {
|
|
ubuntu = version
|
|
} else if (version !== "" && version in UBUNTU) {
|
|
ubuntu = UBUNTU[version]
|
|
} else {
|
|
// default to the maximum vresion
|
|
ubuntu = UBUNTU[MAX_UBUNTU]
|
|
}
|
|
|
|
const prefix = "clang+llvm-"
|
|
const suffix = version === "5.0.0" ? `-linux-x86_64${ubuntu}.tar.xz` : `-x86_64-linux-gnu${ubuntu}.tar.xz`
|
|
if (semverLte(version, "9.0.1")) {
|
|
return getReleaseUrl(version, prefix, suffix)
|
|
} else {
|
|
return getGitHubUrl(version, prefix, suffix)
|
|
}
|
|
}
|
|
|
|
/** The LLVM versions that were never released for the Windows platform. */
|
|
const WIN32_MISSING: Set<string> = new Set(["10.0.1"])
|
|
|
|
/** Gets an LLVM download URL for the Windows platform. */
|
|
async function getWin32Url(version: string): Promise<string | null> {
|
|
if (WIN32_MISSING.has(version)) {
|
|
return null
|
|
}
|
|
|
|
const prefix = "LLVM-"
|
|
const suffix = semverLte(version, "3.7.0") ? "-win32.exe" : "-win64.exe"
|
|
|
|
const olderThan9_1 = semverLte(version, "9.0.1")
|
|
let url: string
|
|
let fallback = false
|
|
if (olderThan9_1) {
|
|
url = getReleaseUrl(version, prefix, suffix)
|
|
if (!(await isValidUrl(url))) {
|
|
fallback = true // fallback to github
|
|
}
|
|
}
|
|
if (fallback || !olderThan9_1) {
|
|
url = getGitHubUrl(version, prefix, suffix)
|
|
}
|
|
|
|
return url!
|
|
}
|
|
|
|
/** Gets an LLVM download URL. */
|
|
function getUrl(platform: string, version: string): string | null | Promise<string | null> {
|
|
switch (platform) {
|
|
case "darwin":
|
|
return getDarwinUrl(version)
|
|
case "linux":
|
|
return getLinuxUrl(version)
|
|
case "win32":
|
|
return getWin32Url(version)
|
|
default:
|
|
return null
|
|
}
|
|
}
|
|
|
|
/** Gets the most recent specific LLVM version for which there is a valid download URL. */
|
|
export async function getSpecificVersionAndUrl(platform: string, version: string): Promise<[string, string]> {
|
|
if (!VERSIONS.has(version)) {
|
|
throw new Error(`Unsupported target! (platform='${platform}', version='${version}')`)
|
|
}
|
|
|
|
for (const specificVersion of getSpecificVersions(version)) {
|
|
// eslint-disable-next-line no-await-in-loop
|
|
const url = await getUrl(platform, specificVersion)
|
|
// eslint-disable-next-line no-await-in-loop
|
|
if (url !== null && (await isValidUrl(url))) {
|
|
return [specificVersion, url]
|
|
}
|
|
}
|
|
|
|
throw new Error(`Unsupported target! (platform='${platform}', version='${version}')`)
|
|
}
|
|
|
|
//================================================
|
|
// Action
|
|
//================================================
|
|
|
|
const DEFAULT_NIX_DIRECTORY = "./llvm"
|
|
const DEFAULT_WIN32_DIRECTORY = "C:/Program Files/LLVM"
|
|
|
|
async function install(version: string, directory: string): Promise<void> {
|
|
const platform = process.platform
|
|
const [specificVersion, url] = await getSpecificVersionAndUrl(platform, version)
|
|
core.setOutput("version", specificVersion)
|
|
|
|
core.info(`Installing LLVM and Clang ${version} (${specificVersion})...`)
|
|
core.info(`Downloading and extracting '${url}'...`)
|
|
const archive = await tc.downloadTool(url)
|
|
|
|
let exit
|
|
if (platform === "win32") {
|
|
exit = await exec.exec("7z", ["x", archive, `-o${directory}`])
|
|
} else {
|
|
await io.mkdirP(directory)
|
|
exit = await exec.exec("tar", ["xf", archive, "-C", directory, "--strip-components=1"])
|
|
}
|
|
|
|
if (exit !== 0) {
|
|
throw new Error("Could not extract LLVM and Clang binaries.")
|
|
}
|
|
|
|
core.info(`Installed LLVM and Clang ${version} (${specificVersion})!`)
|
|
core.info(`Install location: ${directory}`)
|
|
}
|
|
|
|
export async function setupLLVM(version: string, directoryGiven?: string, cached: boolean = false): Promise<void> {
|
|
let directory = directoryGiven
|
|
if (directory === "" || directory === undefined) {
|
|
directory = process.platform === "win32" ? DEFAULT_WIN32_DIRECTORY : DEFAULT_NIX_DIRECTORY
|
|
}
|
|
|
|
directory = path.resolve(directory)
|
|
|
|
if (cached) {
|
|
core.info(`Using cached LLVM and Clang ${version}...`)
|
|
} else {
|
|
await install(version, directory)
|
|
}
|
|
|
|
const bin = path.join(directory, "bin")
|
|
const lib = path.join(directory, "lib")
|
|
|
|
core.addPath(bin)
|
|
|
|
const ld = process.env.LD_LIBRARY_PATH ?? ""
|
|
const dyld = process.env.DYLD_LIBRARY_PATH ?? ""
|
|
|
|
core.exportVariable("LLVM_PATH", directory)
|
|
core.exportVariable("LD_LIBRARY_PATH", `${lib}${path.delimiter}${ld}`)
|
|
core.exportVariable("DYLD_LIBRARY_PATH", `${lib}${path.delimiter}${dyld}`)
|
|
}
|