Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: sync LLVM compiler version and clang tools version #298

Merged
merged 2 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dist/legacy/setup-cpp.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/legacy/setup-cpp.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/modern/setup-cpp.mjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/modern/setup-cpp.mjs.map

Large diffs are not rendered by default.

40 changes: 38 additions & 2 deletions src/__tests__/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,39 @@ describe("getCompilerInfo", () => {
})
})

describe("getCompilerInfo", () => {
it("getCompilerInfo with semver", () => {
const { compiler, version } = getCompilerInfo("llvm-12.0.0")
expect(compiler).toBe("llvm")
expect(version).toBe("12.0.0")
})

it("getCompilerInfo with major version", () => {
const { compiler, version } = getCompilerInfo("llvm-12")
expect(compiler).toBe("llvm")
expect(version).toBe("12")
})

it("getCompilerInfo without version", () => {
const { compiler, version } = getCompilerInfo("llvm")
expect(compiler).toBe("llvm")
expect(version).toBeUndefined()
})
})

describe("syncVersion", () => {
it("Syncs llvm tools versions", () => {
expect(syncVersions(parseArgs(["--llvm", "14.0.0", "--clangtidy", "true"]), llvmTools as Inputs[])).toBe(true)
expect(syncVersions(parseArgs(["--llvm", "13.0.0", "--clangtidy", "true"]), llvmTools as Inputs[])).toBe(true)
expect(syncVersions(parseArgs(["--compiler", "llvm-13.0.0", "--clangtidy", "true"]), llvmTools as Inputs[])).toBe(
true,
)
expect(syncVersions(parseArgs(["--llvm", "13.0.0", "--clangtidy", "12.0.0"]), llvmTools as Inputs[])).toBe(false)

const opts1 = parseArgs(["--llvm", "14.0.0", "--clangtidy", "true"])
expect(syncVersions(opts1, llvmTools as Inputs[])).toBe(true)
expect(opts1.llvm).toBe(opts1.clangtidy)
expect(opts1.llvm).toBe("14.0.0")
expect(opts1.clangtidy).toBe("14.0.0")
expect(opts1.clangformat).toBe(undefined)

const opts2 = parseArgs(["--clangtidy", "15.0.0", "--clangformat", "true"])
Expand All @@ -45,8 +69,20 @@ describe("syncVersion", () => {
const opts3 = parseArgs(["--llvm", "true", "--clangformat", "true"])
expect(syncVersions(opts3, llvmTools as Inputs[])).toBe(true)
expect(opts3.llvm).toBe("true")
expect(opts3.clangtidy).toBe(undefined)
expect(opts3.clangformat).toBe("true")
expect(opts3.clangtidy).toBe(undefined)

const opts4 = parseArgs(["--compiler", "llvm-13.0.0", "--clangtidy", "true"])
expect(syncVersions(opts4, [...llvmTools, "compiler"] as Inputs[], getCompilerInfo("llvm-13.0.0"))).toBe(true)
expect(opts4.compiler).toBe("llvm-13.0.0")
expect(opts4.clangtidy).toBe("13.0.0")
expect(opts4.clangformat).toBe(undefined)

const opts5 = parseArgs(["--compiler", "gcc-13", "--clangtidy", "true"])
expect(syncVersions(opts5, [...llvmTools, "compiler"] as Inputs[], getCompilerInfo("gcc-13"))).toBe(true)
expect(opts5.compiler).toBe("gcc-13")
expect(opts5.clangtidy).toBe("true")
expect(opts5.clangformat).toBe(undefined)
})
})

Expand Down
14 changes: 9 additions & 5 deletions src/compilers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ import { appleClangSetups, gccSetups, llvmSetups, mingwSetups, msvcSetups } from
import type { InstallationInfo } from "./utils/setup/setupBin.js"
import { getVersion } from "./versions/versions.js"

export type CompilerInfo = {
compiler: string
version: string | undefined
}

/**
* Detecting the compiler version. Divide the given string by `-` and use the second element as the version
*
Expand All @@ -20,7 +25,7 @@ import { getVersion } from "./versions/versions.js"
*
* @nothrow It doesn't throw any error, but it logs the error if it fails to parse the compiler info
*/
export function getCompilerInfo(compilerAndVersion: string) {
export function getCompilerInfo(compilerAndVersion: string): CompilerInfo {
try {
const compilerAndMaybeVersion = compilerAndVersion.split("-")
const compiler = compilerAndMaybeVersion[0]
Expand All @@ -40,15 +45,14 @@ export function getCompilerInfo(compilerAndVersion: string) {

/** Installing the specified compiler */
export async function installCompiler(
compilerAndVersion: string,
compiler: string,
version: string | undefined,
osVersion: number[] | null,
setupCppDir: string,
arch: string,
successMessages: string[],
errorMessages: string[],
) {
const { compiler, version } = getCompilerInfo(compilerAndVersion)

let installationInfo: InstallationInfo | undefined | void | null // null means the compiler is not supported
try {
// install the compiler. We allow some aliases for the compiler name
Expand Down Expand Up @@ -82,7 +86,7 @@ export async function installCompiler(
}
} catch (err) {
error(err as string | Error)
errorMessages.push(`Failed to install the ${compilerAndVersion}`)
errorMessages.push(`Failed to install the ${compiler} ${version}`)
}

if (installationInfo !== null) {
Expand Down
37 changes: 21 additions & 16 deletions src/setup-cpp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import timeDeltaLocale from "time-delta/locales/en.js"
import { untildifyUser } from "untildify-user"
import { checkUpdates } from "./check-updates.js"
import { parseArgs, printHelp, rcOptions } from "./cli-options.js"
import { installCompiler } from "./compilers.js"
import { getCompilerInfo, installCompiler } from "./compilers.js"
import { installTool } from "./installTool.js"
import { type Inputs, llvmTools, tools } from "./tool.js"
import { isArch } from "./utils/env/isArch.js"
Expand Down Expand Up @@ -55,8 +55,10 @@ async function main(args: string[]): Promise<number> {

const osVersion = await ubuntuVersion()

const compilerInfo = opts.compiler !== undefined ? getCompilerInfo(opts.compiler) : undefined

// sync the version for the llvm tools
if (!syncVersions(opts, llvmTools as Inputs[])) {
if (!syncVersions(opts, [...llvmTools, "compiler"] as Inputs[], compilerInfo)) {
error("The same version must be used for llvm, clang-format and clang-tidy")
return 1
}
Expand All @@ -71,11 +73,9 @@ async function main(args: string[]): Promise<number> {
let failedFast = false
for (const tool of tools) {
// fail fast inside CI when any tool fails
if (isCI) {
if (errorMessages.length !== 0) {
failedFast = true
break
}
if (isCI && errorMessages.length !== 0) {
failedFast = true
break
}

// get the version or "true" or undefined for this tool from the options
Expand All @@ -101,15 +101,20 @@ async function main(args: string[]): Promise<number> {
}
}

if (!failedFast) {
// installing the specified compiler
const maybeCompiler = opts.compiler
if (maybeCompiler !== undefined) {
const time1Compiler = Date.now()
await installCompiler(maybeCompiler, osVersion, setupCppDir, arch, successMessages, errorMessages)
const time2Compiler = Date.now()
info(`took ${timeFormatter.format(time1Compiler, time2Compiler) || "0 seconds"}`)
}
if (!failedFast && compilerInfo !== undefined) {
// install the specified compiler
const time1Compiler = Date.now()
await installCompiler(
compilerInfo.compiler,
compilerInfo.version,
osVersion,
setupCppDir,
arch,
successMessages,
errorMessages,
)
const time2Compiler = Date.now()
info(`took ${timeFormatter.format(time1Compiler, time2Compiler) || "0 seconds"}`)
}

await finalizeRC(rcOptions)
Expand Down
51 changes: 45 additions & 6 deletions src/versions/versions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Opts } from "../cli-options.js"
import type { CompilerInfo } from "../compilers.js"
import type { Inputs } from "../tool.js"
import { DefaultUbuntuVersion, DefaultVersions } from "./default_versions.js"

Expand Down Expand Up @@ -34,21 +35,59 @@ function getDefaultLinuxVersion(osVersion: number[], toolLinuxVersions: Record<n
/**
* Sync the versions for the given inputs
*
* It modifies the opts object to have the same version for all the tools
* If the return is false, it means that versions don't match the target version
* @param opts - The options object (modified in place)
* @param tools - The tools to sync the versions for (it can include `compiler`)
* @param compilerInfo - The compiler info to sync the versions for (if any)
*/
export function syncVersions(opts: Opts, tools: Inputs[]): boolean {
export function syncVersions(
opts: Opts,
toolsGiven: Inputs[],
compilerInfo: CompilerInfo | undefined = undefined,
): boolean {
// check if compiler version should be synced
const syncCompiler = compilerInfo === undefined ? false : toolsGiven.includes(compilerInfo.compiler as Inputs)

// remove the compiler from the tools if it should not be synced
const tools = syncCompiler ? toolsGiven : toolsGiven.filter((tool) => tool !== "compiler")

// filter out the tools that are in use in the options
const toolsInUse = tools.filter((tool) => opts[tool] !== undefined)
const toolsNonDefaultVersion = toolsInUse.filter((tool) => !isVersionDefault(opts[tool]))

const targetVersion = toolsNonDefaultVersion.length >= 1 ? opts[toolsNonDefaultVersion[0]] : "true"
// filter out the tools that are not default
const toolsNonDefaultVersion = toolsInUse.filter((tool) => {
const version = (syncCompiler && tool === "compiler" && compilerInfo !== undefined)
? compilerInfo.version
: opts[tool]
return !isVersionDefault(version)
})

// find the target version to sync to
const targetVersion: string = (toolsNonDefaultVersion.length !== 0)
? (syncCompiler && toolsNonDefaultVersion[0] === "compiler" && compilerInfo !== undefined)
? compilerInfo.version ?? "true"
: opts[toolsNonDefaultVersion[0]] ?? "true"
: "true"

// error if any explicit versions don't match the target version
if (
toolsNonDefaultVersion.some((tool) => {
if (syncCompiler && tool === "compiler" && compilerInfo !== undefined) {
return opts.compiler !== `${compilerInfo.compiler}-${targetVersion}`
}

if (toolsNonDefaultVersion.some((tool) => opts[tool] !== targetVersion)) {
// error if any explicit versions don't match the target version
return opts[tool] !== targetVersion
})
) {
return false
}

// update the version of all the tools to the target version
for (const tool of toolsInUse) {
opts[tool] = targetVersion
opts[tool] = (syncCompiler && tool === "compiler" && compilerInfo !== undefined)
? `${compilerInfo.compiler}-${targetVersion}`
: targetVersion
}

return true
Expand Down
Loading