feat: find LLVM assets based on platform/arch/version heuristics

This commit is contained in:
Amin Yahyaabadi 2024-09-07 17:30:18 -07:00
parent 22bfbec1e0
commit e003dfdeac
No known key found for this signature in database
GPG Key ID: F52AF77F636088F0
13 changed files with 362 additions and 435 deletions

View File

@ -13,11 +13,11 @@ ignorePaths:
- patches/*.patch - patches/*.patch
- "**/github_*.json" - "**/github_*.json"
- "**/llvm_org_releases.json" - "**/llvm_org_releases.json"
words: words:
- aarch - aarch
- aminya - aminya
- applellvm - applellvm
- armv
- bazel - bazel
- bazelisk - bazelisk
- biome - biome

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

View File

@ -1,7 +1,7 @@
import { parseArgs } from "../cli-options.js" import { parseArgs } from "../cli-options.js"
import { getCompilerInfo } from "../compilers.js" import { getCompilerInfo } from "../compilers.js"
import type { Inputs } from "../tool.js" import type { Inputs } from "../tool.js"
import { DefaultLinuxVersion } from "../versions/default_versions.js" import { DefaultUbuntuVersion } from "../versions/default_versions.js"
import { getVersion, syncVersions } from "../versions/versions.js" import { getVersion, syncVersions } from "../versions/versions.js"
jest.setTimeout(300000) jest.setTimeout(300000)
@ -55,18 +55,18 @@ describe("getVersion", () => {
it("gcovr", () => { it("gcovr", () => {
expect(getVersion("gcovr", "5.0")).toBe("5.0") expect(getVersion("gcovr", "5.0")).toBe("5.0")
if (process.platform === "linux") { if (process.platform === "linux") {
expect(getVersion("gcovr", "true", [22, 4])).toBe(DefaultLinuxVersion.gcovr![22]) expect(getVersion("gcovr", "true", [22, 4])).toBe(DefaultUbuntuVersion.gcovr![22])
expect(getVersion("gcovr", "true", [20, 4])).toBe(DefaultLinuxVersion.gcovr![20]) expect(getVersion("gcovr", "true", [20, 4])).toBe(DefaultUbuntuVersion.gcovr![20])
expect(getVersion("gcovr", "true", [18, 4])).toBe(DefaultLinuxVersion.gcovr![18]) expect(getVersion("gcovr", "true", [18, 4])).toBe(DefaultUbuntuVersion.gcovr![18])
} }
}) })
it("llvm", () => { it("llvm", () => {
expect(getVersion("llvm", "13.0.0")).toBe("13.0.0") expect(getVersion("llvm", "13.0.0")).toBe("13.0.0")
if (process.platform === "linux") { if (process.platform === "linux") {
expect(getVersion("llvm", "true", [20, 4])).toBe(DefaultLinuxVersion.llvm![20]) expect(getVersion("llvm", "true", [20, 4])).toBe(DefaultUbuntuVersion.llvm![20])
expect(getVersion("llvm", "true", [18, 4])).toBe(DefaultLinuxVersion.llvm![18]) expect(getVersion("llvm", "true", [18, 4])).toBe(DefaultUbuntuVersion.llvm![18])
expect(getVersion("llvm", "true", [16, 4])).toBe(DefaultLinuxVersion.llvm![16]) expect(getVersion("llvm", "true", [16, 4])).toBe(DefaultUbuntuVersion.llvm![16])
} }
}) })
}) })

View File

@ -48,6 +48,10 @@ async function getGccPackageInfo(version: string, platform: NodeJS.Platform, arc
}, },
) )
if (asset === undefined) {
throw new Error(`No asset found for version ${version} and arch ${arch}`)
}
return { return {
binRelativeDir: "bin/", binRelativeDir: "bin/",
binFileName: addExeExt("g++"), binFileName: addExeExt("g++"),

View File

@ -3,25 +3,16 @@ import { fileURLToPath } from "url"
import * as io from "@actions/io" import * as io from "@actions/io"
import { execaSync } from "execa" import { execaSync } from "execa"
import { chmod } from "fs/promises" import { chmod } from "fs/promises"
import { isUrlOnline } from "is-url-online"
import { addExeExt } from "patha" import { addExeExt } from "patha"
import { ubuntuVersion } from "../../utils/env/ubuntu_version.js" import { ubuntuVersion } from "../../utils/env/ubuntu_version.js"
import { getSpecificVersionAndUrl } from "../../utils/setup/version.js"
import { setupTmpDir, testBin } from "../../utils/tests/test-helpers.js" import { setupTmpDir, testBin } from "../../utils/tests/test-helpers.js"
import { getVersion } from "../../versions/versions.js" import { getVersion } from "../../versions/versions.js"
import { setupClangFormat, setupClangTools, setupLLVM } from "../llvm.js" import { setupClangFormat, setupClangTools, setupLLVM } from "../llvm.js"
import { VERSIONS, getLinuxUrl, getUrl } from "../llvm_url.js" import { getLLVMAssetURL } from "../llvm_url.js"
const dirname = typeof __dirname === "string" ? __dirname : path.dirname(fileURLToPath(import.meta.url)) const dirname = typeof __dirname === "string" ? __dirname : path.dirname(fileURLToPath(import.meta.url))
jest.setTimeout(400000) jest.setTimeout(400000)
async function testUrl(version: string) {
const [specificVersion, url] = await getSpecificVersionAndUrl(VERSIONS, process.platform, version, getUrl)
if (!(await isUrlOnline(url))) {
throw new Error(`Failed to install Version: ${version} => ${specificVersion} \n URL: ${url}`)
}
}
describe("setup-llvm", () => { describe("setup-llvm", () => {
let directory: string let directory: string
@ -31,38 +22,15 @@ describe("setup-llvm", () => {
it("Finds URL for ubuntu version", async () => { it("Finds URL for ubuntu version", async () => {
expect( expect(
await getSpecificVersionAndUrl( await getLLVMAssetURL("linux", "x86_64", "13.0.0"),
VERSIONS, ).toStrictEqual(
"linux",
"13.0.0-ubuntu-16.04",
(_plantform, version) => getLinuxUrl(version),
),
).toStrictEqual([
"13.0.0-ubuntu-16.04",
"https://github.com/llvm/llvm-project/releases/download/llvmorg-13.0.0/clang+llvm-13.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz", "https://github.com/llvm/llvm-project/releases/download/llvmorg-13.0.0/clang+llvm-13.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz",
]) )
expect( expect(
await getSpecificVersionAndUrl( await getLLVMAssetURL("linux", "x86_64", "13.0.1"),
VERSIONS, ).toStrictEqual(
"linux",
"13.0.1-ubuntu-18.04",
(_plantform, version) => getLinuxUrl(version),
),
).toStrictEqual([
"13.0.1-ubuntu-18.04",
"https://github.com/llvm/llvm-project/releases/download/llvmorg-13.0.1/clang+llvm-13.0.1-x86_64-linux-gnu-ubuntu-18.04.tar.xz", "https://github.com/llvm/llvm-project/releases/download/llvmorg-13.0.1/clang+llvm-13.0.1-x86_64-linux-gnu-ubuntu-18.04.tar.xz",
]) )
expect(
await getSpecificVersionAndUrl(
VERSIONS,
"linux",
"13.0.0-ubuntu-20.04",
(_plantform, version) => getLinuxUrl(version),
),
).toStrictEqual([
"13.0.0-ubuntu-20.04",
"https://github.com/llvm/llvm-project/releases/download/llvmorg-13.0.0/clang+llvm-13.0.0-x86_64-linux-gnu-ubuntu-20.04.tar.xz",
])
}) })
it("Finds valid LLVM URLs", async () => { it("Finds valid LLVM URLs", async () => {
@ -89,7 +57,7 @@ describe("setup-llvm", () => {
"5", "5",
"5.0.0", "5.0.0",
"4", "4",
].map((version) => testUrl(version)), ].map((version) => getLLVMAssetURL(process.platform, process.arch, version)),
) )
}) })

View File

@ -1,329 +1,24 @@
import { info, warning } from "ci-log" import path, { join } from "path"
import { isUrlOnline } from "is-url-online" import { fileURLToPath } from "url"
import { info } from "ci-log"
import { addExeExt } from "patha" import { addExeExt } from "patha"
import semverLte from "semver/functions/lte" import { loadAssetList, matchAsset } from "../utils/asset/load-assets.js"
import { hasDnf } from "../utils/env/hasDnf.js"
import { isUbuntu } from "../utils/env/isUbuntu.js"
import { ubuntuVersion } from "../utils/env/ubuntu_version.js"
import { extractExe, extractTarByExe } from "../utils/setup/extract.js" import { extractExe, extractTarByExe } from "../utils/setup/extract.js"
import type { PackageInfo } from "../utils/setup/setupBin.js" import type { PackageInfo } from "../utils/setup/setupBin.js"
import { getSpecificVersionAndUrl, getSpecificVersions, getVersions } from "../utils/setup/version.js"
// ================================================ const dirname = typeof __dirname === "string" ? __dirname : path.dirname(fileURLToPath(import.meta.url))
// Version
// ================================================
/** The specific and minimum LLVM versions supported by this action. */
export 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",
"13.0.0",
"13.0.1",
"14.0.0",
"14.0.1",
"14.0.2",
"14.0.3",
"14.0.4",
"14.0.5",
"14.0.6",
"15.0.0",
"15.0.1",
"15.0.2",
"15.0.3",
"15.0.4",
"15.0.5",
"15.0.6",
"15.0.7",
"16.0.0",
"16.0.1",
"16.0.2",
"16.0.3",
"16.0.4",
"16.0.5",
"16.0.6",
"17.0.1",
"17.0.2",
"17.0.3",
"17.0.4",
"17.0.5",
"17.0.6",
"18.1.0",
"18.1.1",
"18.1.2",
"18.1.3",
"18.1.4",
"18.1.5",
"18.1.6",
"18.1.7",
"18.1.8",
])
/** The LLVM versions that were never released for the Windows platform. */
const WIN32_MISSING: Set<string> = new Set(["10.0.1", "15.0.5", "15.0.6", "17.0.5"])
/** The LLVM versions that were never released for the Darwin platform. */
const DARWIN_X64_MISSING = 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",
"15.0.4",
"15.0.5",
"15.0.6",
"16.0.0",
"16.0.1",
"16.0.2",
"16.0.3",
"16.0.4",
"16.0.5",
"16.0.6",
"17.0.1",
"17.0.2",
"17.0.3",
"17.0.4",
"17.0.5",
"17.0.6",
"18.1.0",
"18.1.1",
"18.1.2",
"18.1.3",
"18.1.4",
"18.1.5",
"18.1.6",
"18.1.7",
"18.1.8",
])
/**
* 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()
/**
* The (latest) Ubuntu versions for each LLVM version.
*
* https://github.com/llvm/llvm-project/releases/tag/llvmorg-14.0.1 or https://releases.llvm.org/6.0.1
*/
// TODO change based on ubuntu version
const UBUNTU_X64_SUFFIX_MAP: { [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": "-ubuntu-16.04",
"13.0.0": "-ubuntu-20.04",
"13.0.0-ubuntu-16.04": "-ubuntu-16.04",
"13.0.0-ubuntu-20.04": "-ubuntu-20.04",
"13.0.1": "-ubuntu-18.04",
"13.0.1-ubuntu-18.04": "-ubuntu-18.04",
"14.0.0": "-ubuntu-18.04",
"15.0.2": "-rhel86",
"15.0.5": "-ubuntu-18.04",
"15.0.6": "-ubuntu-18.04",
"16.0.0": "-ubuntu-18.04",
"16.0.2": "-ubuntu-22.04",
"16.0.3": "-ubuntu-22.04",
"16.0.4": "-ubuntu-22.04",
"17.0.2": "-ubuntu-22.04",
"17.0.4": "-ubuntu-22.04",
"17.0.5": "-ubuntu-22.04",
"17.0.6": "-ubuntu-22.04",
"18.1.4": "-ubuntu-18.04",
"18.1.7": "-ubuntu-18.04",
"18.1.8": "-ubuntu-18.04",
}
/** The latest supported LLVM version for the Linux (Ubuntu) platform. */
const MAX_UBUNTU: string = "18.1.8"
// ================================================
// 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}`
}
/** Gets an LLVM download URL for the Darwin platform. */
function getDarwinUrl(version: string): string | null {
if (DARWIN_X64_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)
}
}
/** Gets an LLVM download URL for the Linux (Ubuntu) platform. */
export function getLinuxUrl(versionGiven: string): string {
let version = versionGiven
const rc = UBUNTU_RC.get(version)
if (rc !== undefined) {
version = rc
}
let linuxVersion: string
// ubuntu-version is specified
if (version.includes("ubuntu")) {
const givenUbuntuVersion = version.replace(/-ubuntu-.*/, "")
if (!VERSIONS.has(givenUbuntuVersion)) {
throw new Error(`Unsupported Ubuntu version: ${givenUbuntuVersion}`)
}
linuxVersion = version.replace(givenUbuntuVersion, "")
version = getSpecificVersions(VERSIONS, givenUbuntuVersion)[0]
} else if (version !== "" && version in UBUNTU_X64_SUFFIX_MAP) {
linuxVersion = UBUNTU_X64_SUFFIX_MAP[version]
} else {
// default to the maximum version
linuxVersion = UBUNTU_X64_SUFFIX_MAP[MAX_UBUNTU]
warning(`Falling back to LLVM version ${MAX_UBUNTU} ${linuxVersion} for the Ubuntu.`)
}
const prefix = "clang+llvm-"
let suffix: string
if (version === "5.0.0") {
suffix = `-linux-x86_64${linuxVersion}.tar.xz`
} else if (linuxVersion.includes("-rhel86")) {
suffix = `-x86_64-unknown-linux-gnu${linuxVersion}.tar.xz`
} else {
suffix = `-x86_64-linux-gnu${linuxVersion}.tar.xz`
}
if (semverLte(version, "9.0.1")) {
return getReleaseUrl(version, prefix, suffix)
} else {
return getGitHubUrl(version, prefix, suffix)
}
}
/** 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 isUrlOnline(url))) {
fallback = true // fallback to github
}
}
if (fallback || !olderThan9_1) {
url = getGitHubUrl(version, prefix, suffix)
}
return url!
}
/** Gets an LLVM download URL. */
export 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
}
}
export async function getLLVMPackageInfo( export async function getLLVMPackageInfo(
version: string, version: string,
platform: NodeJS.Platform, platform: NodeJS.Platform,
// eslint-disable-next-line @typescript-eslint/no-unused-vars arch: string,
_arch: string,
): Promise<PackageInfo> { ): Promise<PackageInfo> {
const [specificVersion, url] = await getSpecificVersionAndUrl(VERSIONS, platform, version, getUrl) const url = await getLLVMAssetURL(platform, arch, version)
info(`specific llvm version: ${specificVersion}`) info(`Downloading LLVM from ${url}`)
return { return {
url, url,
extractedFolderName: "", extractedFolderName: "",
@ -336,3 +31,195 @@ export async function getLLVMPackageInfo(
}, },
} }
} }
export async function getLLVMAssetURL(platform: string, arch: string, version: string) {
const { keywords, optionalKeywords } = await getAssetKeywords(platform, arch)
// first check the github releases
const llvmGitHubAssets = await loadAssetList(
join(dirname, "github_llvm_llvm-project.json"),
)
const ghAsset = matchAsset(
llvmGitHubAssets,
{
version,
keywords,
optionalKeywords,
filterMapTag(tag) {
return tag.replace(/^llvmorg-/, "")
},
filterName(name) {
return name.startsWith("LLVM-") || name.startsWith("clang+llvm-")
},
},
)
if (ghAsset !== undefined) {
return `https://github.com/llvm/llvm-project/releases/download/${ghAsset.tag}/${ghAsset.name}`
}
// check the llvm website
const llvmWebsiteAssets = await loadAssetList(
join(dirname, "llvm_org_releases.json"),
)
const websiteAsset = matchAsset(
llvmWebsiteAssets,
{
version,
keywords,
optionalKeywords,
},
)
if (websiteAsset !== undefined) {
return `https://llvm.org/releases/${websiteAsset.tag}/${websiteAsset.name}`
}
throw new Error(`No asset found for version ${version} matching ${keywords} and ${optionalKeywords}`)
}
async function getAssetKeywords(platform: string, arch: string) {
const keywords: string[] = []
const optionalKeywords: string[] = []
switch (platform) {
case "win32": {
switch (arch) {
case "win64":
case "x64":
case "amd64":
case "x86_64":
case "64":
keywords.push("win64")
break
case "win32":
case "x86":
case "i386":
case "ia32":
case "32":
keywords.push("win32")
break
case "woa64":
case "aarch64":
case "arm64":
case "arm": {
keywords.push("woa64")
break
}
default:
info(`Using arch ${arch} for LLVM`)
keywords.push(arch)
break
}
break
}
case "linux": {
keywords.push("linux")
if (isUbuntu()) {
optionalKeywords.push("ubuntu")
const ubuntuVer = await ubuntuVersion()
if (ubuntuVer !== null) {
optionalKeywords.push(`${ubuntuVer[0]}`)
const ubuntuMin = ubuntuVer[1] < 10 ? `0${ubuntuVer[1]}` : `${ubuntuVer[1]}`
optionalKeywords.push(`${ubuntuVer[0]}.${ubuntuMin}`)
optionalKeywords.push(`${ubuntuVer[0]}.${ubuntuMin}.${ubuntuVer[2]}`)
}
} else if (hasDnf()) {
optionalKeywords.push("rhel")
}
switch (arch) {
case "x86_64":
case "x64":
case "amd64":
case "64":
keywords.push("x86_64")
break
case "x86":
case "i386":
case "ia32":
case "32":
keywords.push("x86")
break
case "aarch64":
case "arm64":
case "arm":
keywords.push("aarch64")
break
case "armv7a":
case "armv7":
keywords.push("armv7a")
break
case "powerpc64le":
case "ppc64le":
keywords.push("powerpc64le")
break
case "sparc64":
keywords.push("sparc64")
break
default:
info(`Using arch ${arch} for LLVM`)
keywords.push(arch)
break
}
break
}
case "darwin": {
keywords.push("darwin")
switch (arch) {
case "x86_64":
case "x64":
case "amd64":
case "64":
keywords.push("x86_64")
break
case "arm64":
case "arm":
case "aarch64":
// allow falling back to x86_64 if arm64 is not available
optionalKeywords.push("arm64")
break
default:
info(`Using arch ${arch} for LLVM`)
keywords.push(arch)
break
}
break
}
case "freebsd": {
keywords.push("freebsd")
switch (arch) {
case "x86_64":
case "x64":
case "amd64":
case "64":
keywords.push("amd64")
break
case "x86":
case "i386":
case "ia32":
case "32":
keywords.push("i386")
break
default:
info(`Using arch ${arch} for LLVM`)
keywords.push(arch)
break
}
break
}
default:
info(`Using ${platform} ${arch} for LLVM`)
optionalKeywords.push(platform, arch)
break
}
return { keywords, optionalKeywords }
}

View File

@ -1,3 +1,4 @@
import { info } from "ci-log"
import { readFile } from "fs/promises" import { readFile } from "fs/promises"
/** /**
@ -18,7 +19,8 @@ export async function loadAssetList(path: string): Promise<Assets> {
type MatchAssetOpts = { type MatchAssetOpts = {
version: string version: string
keywords?: string[] keywords?: string[]
filterTag?: (version: string) => boolean optionalKeywords?: string[]
filterMapTag?: (tag: string) => string | undefined
filterName?: (asset: string) => boolean filterName?: (asset: string) => boolean
} }
@ -28,58 +30,119 @@ type MatchAssetOpts = {
export function matchAsset( export function matchAsset(
assets: Assets, assets: Assets,
opts: MatchAssetOpts, opts: MatchAssetOpts,
): { tag: string; name: string } { ): { tag: string; name: string } | undefined {
// get the list of versions // match the tag
let tags = Object.keys(assets) const assetVersion = matchAssetVersion(assets, opts)
if (assetVersion === undefined) {
return undefined
}
const { tag, assetNames } = assetVersion
// filter the versions // if no keywords are given, return the first asset
if (opts.filterTag !== undefined) { if (!opts.keywords?.length && !opts.optionalKeywords?.length) {
tags = tags.filter(opts.filterTag) return { tag, name: assetNames[0] }
} }
if (tags.length === 0) { // check if the asset contains all the keywords
throw new Error(`no tag found for version ${opts.version}`) let candidates: string[] = []
if (opts.keywords?.length) {
for (const name of assetNames) {
if (opts.keywords!.every((keyword) => name.includes(keyword))) {
candidates.push(name)
}
}
} else {
candidates = assetNames
}
if (candidates.length === 0) {
info(`no asset found for version ${opts.version} and keywords ${opts.keywords}`)
return undefined
}
// prefer the candidates that contain more optional keywords
if (opts.optionalKeywords?.length) {
// rate the candidates based on the number of optional keywords they contain
const candidateScores = candidates.map((name) => {
let score = 0
for (const keyword of opts.optionalKeywords!) {
if (name.includes(keyword)) {
score++
}
}
return score
})
// find the candidate with the highest score
const maxScore = Math.max(...candidateScores)
const maxIndex = candidateScores.indexOf(maxScore)
return { tag, name: candidates[maxIndex] }
}
// return the first candidate if no optional keywords are given
return { tag, name: candidates[0] }
}
function matchAssetVersion(assets: Assets, opts: MatchAssetOpts) {
// get the list of versions
const origTags = Object.keys(assets)
// filter/map the tags
const versionMap: Map<string, string> = new Map()
if (opts.filterMapTag === undefined) {
for (const origTag of origTags) {
versionMap.set(origTag, origTag)
}
} else {
for (const origTag of origTags) {
const mappedTag = opts.filterMapTag(origTag)
if (mappedTag !== undefined) {
versionMap.set(mappedTag, origTag)
}
}
}
if (versionMap.size === 0) {
info(`no tag found for version ${opts.version}`)
return undefined
} }
// find the first tag that starts with the version // find the first tag that starts with the version
// loop over the versions starting with the latest // loop over the versions starting with the latest
let tag: string | undefined let foundVersion: string | undefined
for (const mingwVersion of tags) { let foundOrigTag: string | undefined
if (mingwVersion.startsWith(opts.version)) { for (const [version, origTag] of versionMap.entries()) {
tag = mingwVersion if (
version === opts.version
|| version.startsWith(opts.version)
) {
foundVersion = version
foundOrigTag = origTag
break break
} }
} }
if (tag === undefined) {
throw new Error(`version ${opts.version} is not supported`) if (foundVersion === undefined || foundOrigTag === undefined) {
info(`version ${opts.version} is not supported`)
return undefined
} }
// get the list of assets // get the list of assets
let matchedNames = assets[tag] let assetNames = assets[foundOrigTag]
if (assetNames === undefined) {
info(`no asset found for version ${opts.version}`)
return undefined
}
// filter the assets // filter the assets
if (opts.filterName !== undefined) { if (opts.filterName !== undefined) {
matchedNames = matchedNames.filter(opts.filterName) assetNames = assetNames.filter(opts.filterName)
} }
if (matchedNames.length === 0) { if (assetNames.length === 0) {
throw new Error(`no asset found for version ${opts.version}`) info(`no asset found for version ${opts.version}`)
return undefined
} }
if (opts.keywords?.length === 0) { return { tag: foundOrigTag, assetNames }
return { tag, name: matchedNames[0] }
}
// find the first asset that matches the keywords
for (const name of matchedNames) {
if (opts.keywords!.every((keyword) => name.includes(keyword))) {
return { tag, name }
}
}
throw new Error(
`Could not find a matching asset for version ${opts.version} and keywords ${JSON.stringify(opts.keywords)} among ${
JSON.stringify(matchedNames)
}`,
)
} }

View File

@ -42,12 +42,17 @@ function detectUsingOsVersion() {
return null return null
} }
// #46~22.04.1-Ubuntu SMP ... // #40~22.04.3-Ubuntu SMP PREEMPT_DYNAMIC Tue Jul 30 17:30:19 UTC 2
const osVersion: string = os.version() const osVersion: string = os.version()
const versionSplitted = osVersion.split(".") // parse the version
const majorVersion = Number.parseInt(versionSplitted[0].replace("#", ""), 10) const versionMatch = osVersion.match(/(\d+)\.(\d+)\.(\d+)/)
const minorVersion = Number.parseInt(versionSplitted[1].replace("~", ""), 10) if (versionMatch === null) {
const patchVersion = Number.parseInt(versionSplitted[2].split("-")[0], 10) return null
}
const majorVersion = Number.parseInt(versionMatch[1], 10)
const minorVersion = Number.parseInt(versionMatch[2], 10)
const patchVersion = Number.parseInt(versionMatch[3], 10)
return [majorVersion, minorVersion, patchVersion] return [majorVersion, minorVersion, patchVersion]
} }

View File

@ -3,7 +3,7 @@ import { isArch } from "../utils/env/isArch.js"
// passing "" to a tool installed by a package manager (apt, brew, choco) will result in the default version of that package manager. // passing "" to a tool installed by a package manager (apt, brew, choco) will result in the default version of that package manager.
// the directly downloaded tools require a given version ("" doesn't work). // the directly downloaded tools require a given version ("" doesn't work).
function getLLVMDefault() { function getNonUbuntuLLVMDefault() {
switch (process.platform) { switch (process.platform) {
case "win32": case "win32":
return "17.0.6" return "17.0.6"
@ -19,9 +19,9 @@ function getLLVMDefault() {
} }
export const DefaultVersions: Record<string, string | undefined> = { export const DefaultVersions: Record<string, string | undefined> = {
llvm: getLLVMDefault(), // https://github.com/llvm/llvm-project/releases llvm: getNonUbuntuLLVMDefault(), // https://github.com/llvm/llvm-project/releases
clangtidy: getLLVMDefault(), clangtidy: getNonUbuntuLLVMDefault(),
clangformat: getLLVMDefault(), clangformat: getNonUbuntuLLVMDefault(),
ninja: "1.12.1", // https://github.com/ninja-build/ninja/releases ninja: "1.12.1", // https://github.com/ninja-build/ninja/releases
cmake: "3.30.2", // https://github.com/Kitware/CMake/releases cmake: "3.30.2", // https://github.com/Kitware/CMake/releases
gcovr: "5.2", // "6.0", // https://pypi.org/project/gcovr/ gcovr: "5.2", // "6.0", // https://pypi.org/project/gcovr/
@ -45,7 +45,7 @@ export const MinVersions: Record<string, string | undefined> = {
/// If an ubuntu versions is not in this map: /// If an ubuntu versions is not in this map:
// - the newer ubuntu versions use the first entry (e.g. v20), // - the newer ubuntu versions use the first entry (e.g. v20),
// - the older ones use "" // - the older ones use ""
export const DefaultLinuxVersion: Record<string, Record<number, string> | undefined> = { export const DefaultUbuntuVersion: Record<string, Record<number, string> | undefined> = {
// https://packages.ubuntu.com/search?suite=all&arch=any&searchon=names&keywords=mingw-w64 // https://packages.ubuntu.com/search?suite=all&arch=any&searchon=names&keywords=mingw-w64
mingw: { mingw: {
24: "8.0.0-1", 24: "8.0.0-1",
@ -54,28 +54,28 @@ export const DefaultLinuxVersion: Record<string, Record<number, string> | undefi
}, },
// the suffixes relate to the suffix in the llvm releases // the suffixes relate to the suffix in the llvm releases
llvm: { llvm: {
24: "17.0.6-ubuntu-22.04", 24: "17.0.6",
22: "17.0.6-ubuntu-22.04", 22: "17.0.6",
20: "17.0.6-ubuntu-22.04", 20: "17.0.6",
18: "15.0.6-ubuntu-18.04", 18: "15.0.6",
16: "15.0.6-ubuntu-18.04", 16: "15.0.6",
14: "13.0.0-ubuntu-16.04", 14: "13.0.0",
}, },
clangtidy: { clangtidy: {
24: "17.0.6-ubuntu-22.04", 24: "17.0.6",
22: "17.0.6-ubuntu-22.04", 22: "17.0.6",
20: "17.0.6-ubuntu-22.04", 20: "17.0.6",
18: "15.0.6-ubuntu-18.04", 18: "15.0.6",
16: "15.0.6-ubuntu-18.04", 16: "15.0.6",
14: "13.0.0-ubuntu-16.04", 14: "13.0.0",
}, },
clangformat: { clangformat: {
24: "17.0.6-ubuntu-22.04", 24: "17.0.6",
22: "17.0.6-ubuntu-22.04", 22: "17.0.6",
20: "17.0.6-ubuntu-22.04", 20: "17.0.6",
18: "15.0.6-ubuntu-18.04", 18: "15.0.6",
16: "15.0.6-ubuntu-18.04", 16: "15.0.6",
14: "13.0.0-ubuntu-16.04", 14: "13.0.0",
}, },
gcovr: { gcovr: {
24: "6.0", 24: "6.0",

View File

@ -1,11 +1,11 @@
import type { Opts } from "../cli-options.js" import type { Opts } from "../cli-options.js"
import type { Inputs } from "../tool.js" import type { Inputs } from "../tool.js"
import { DefaultLinuxVersion, DefaultVersions } from "./default_versions.js" import { DefaultUbuntuVersion, DefaultVersions } from "./default_versions.js"
/** Get the default version if passed true or undefined, otherwise return the version itself */ /** Get the default version if passed true or undefined, otherwise return the version itself */
export function getVersion(name: string, version: string | undefined, osVersion: number[] | null = null) { export function getVersion(name: string, version: string | undefined, osVersion: number[] | null = null) {
if (isVersionDefault(version) && process.platform === "linux" && osVersion !== null && name in DefaultLinuxVersion) { if (isVersionDefault(version) && process.platform === "linux" && osVersion !== null && name in DefaultUbuntuVersion) {
return getDefaultLinuxVersion(osVersion, DefaultLinuxVersion[name]!) return getDefaultLinuxVersion(osVersion, DefaultUbuntuVersion[name]!)
} else if (isVersionDefault(version) && name in DefaultVersions) { } else if (isVersionDefault(version) && name in DefaultVersions) {
return DefaultVersions[name] ?? "" return DefaultVersions[name] ?? ""
} else if (version === "true") { } else if (version === "true") {