Skip to content

Commit 799b040

Browse files
authored
Merge pull request #364 from aminya/llvm-deps
refactor: refactor llvm install into modules
2 parents e4a5426 + 935a8bc commit 799b040

File tree

8 files changed

+162
-109
lines changed

8 files changed

+162
-109
lines changed

dist/legacy/setup-cpp.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/legacy/setup-cpp.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/modern/setup-cpp.mjs

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

dist/modern/setup-cpp.mjs.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/llvm/llvm.ts

Lines changed: 33 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,24 @@
1-
import { tmpdir } from "os"
21
import path, { delimiter, join } from "path"
32
import { fileURLToPath } from "url"
4-
import { execRootSync } from "admina"
53
import ciInfo from "ci-info"
64
const { GITHUB_ACTIONS } = ciInfo
75
import { info, warning } from "ci-log"
86
import { addEnv } from "envosman"
97
import memoize from "memoizee"
10-
import { DownloaderHelper } from "node-downloader-helper"
118
import { pathExists } from "path-exists"
129
import { addExeExt } from "patha"
13-
import { addUpdateAlternativesToRc, installAptPack } from "setup-apt"
10+
import { addUpdateAlternativesToRc } from "setup-apt"
1411
import { rcOptions } from "../cli-options.js"
1512
import { setupGcc } from "../gcc/gcc.js"
1613
import { setupMacOSSDK } from "../macos-sdk/macos-sdk.js"
17-
import { arm64, x86_64 } from "../utils/env/arch.js"
18-
import { hasDnf } from "../utils/env/hasDnf.js"
19-
import { isArch } from "../utils/env/isArch.js"
2014
import { isUbuntu } from "../utils/env/isUbuntu.js"
2115
import { ubuntuVersion } from "../utils/env/ubuntu_version.js"
22-
import { type InstallationInfo, setupBin } from "../utils/setup/setupBin.js"
23-
import { setupDnfPack } from "../utils/setup/setupDnfPack.js"
24-
import { setupPacmanPack } from "../utils/setup/setupPacmanPack.js"
25-
import { semverCoerceIfInvalid } from "../utils/setup/version.js"
16+
import type { InstallationInfo } from "../utils/setup/setupBin.js"
2617
import { quoteIfHasSpace } from "../utils/std/index.js"
2718
import { getVersion } from "../versions/versions.js"
28-
import { LLVMPackages, setupLLVMApt } from "./llvm_installer.js"
29-
import { getLLVMPackageInfo } from "./llvm_url.js"
19+
import { LLVMPackages, trySetupLLVMApt } from "./llvm_apt_installer.js"
20+
import { setupLLVMBin } from "./llvm_bin.js"
21+
import { majorLLVMVersion } from "./utils.js"
3022

3123
const dirname = typeof __dirname === "string" ? __dirname : path.dirname(fileURLToPath(import.meta.url))
3224

@@ -44,110 +36,34 @@ async function setupLLVMWithoutActivation_(version: string, setupDir: string, ar
4436
])
4537

4638
// install LLVM dependencies
47-
await setupLLVMDeps(arch)
39+
await setupGccForLLVM(arch)
4840

4941
return installationInfo
5042
}
5143
const setupLLVMWithoutActivation = memoize(setupLLVMWithoutActivation_, { promise: true })
5244

53-
/**
54-
* Setup clang-format
55-
*
56-
* This uses the LLVM installer on Ubuntu, and the LLVM binaries on other platforms
57-
*/
58-
export function setupClangFormat(version: string, setupDir: string, arch: string) {
59-
return setupLLVMOnly(version, setupDir, arch, LLVMPackages.ClangFormat)
60-
}
61-
62-
/** Setup llvm tools (clang tidy, etc.) without activating llvm and using it as the compiler */
63-
export function setupClangTools(version: string, setupDir: string, arch: string) {
64-
return setupLLVMOnly(version, setupDir, arch)
65-
}
66-
6745
async function setupLLVMOnly(
6846
version: string,
6947
setupDir: string,
7048
arch: string,
7149
packages: LLVMPackages = LLVMPackages.All,
7250
) {
73-
const majorVersion = majorLLVMVersion(version)
74-
if (isUbuntu()) {
75-
try {
76-
return await setupLLVMApt(majorVersion, packages)
77-
} catch (err) {
78-
info(`Failed to install llvm via system package manager ${err}. Trying to remove the repository`)
79-
try {
80-
execRootSync(join(dirname, "llvm_repo_remove.bash"), [`${majorVersion}`])
81-
} catch (removeErr) {
82-
info(`Failed to remove llvm repository ${removeErr}`)
83-
}
84-
}
51+
const aptInstallInfo = await trySetupLLVMApt(version, packages)
52+
if (aptInstallInfo !== undefined) {
53+
return aptInstallInfo
8554
}
8655

87-
const installationInfo = await setupBin("llvm", version, getLLVMPackageInfo, setupDir, arch)
88-
await llvmBinaryDeps(majorVersion)
89-
return installationInfo
90-
}
91-
92-
function majorLLVMVersion(version: string) {
93-
const coeredVersion = semverCoerceIfInvalid(version)
94-
return Number.parseInt(coeredVersion.split(".")[0], 10)
56+
return setupLLVMBin(version, setupDir, arch)
9557
}
9658

97-
async function llvmBinaryDeps_(_majorVersion: number) {
98-
if (isUbuntu()) {
99-
for (const dep of ["libtinfo5", "libtinfo6"]) {
100-
/* eslint-disable no-await-in-loop */
101-
try {
102-
await installAptPack([{ name: dep }])
103-
} catch (_err) {
104-
try {
105-
if (dep === "libtinfo5") {
106-
// Manually install libtinfo5 if the package is not available
107-
info(`Failed to install ${dep}\nManually installing the package`)
108-
const arch = x86_64.includes(process.arch)
109-
? "amd64"
110-
: arm64.includes(process.arch)
111-
? "arm64"
112-
: process.arch
113-
114-
const fileName = `libtinfo5_6.3-2ubuntu0.1_${arch}.deb`
115-
const url = `http://launchpadlibrarian.net/666971015/${fileName}`
116-
const dl = new DownloaderHelper(url, tmpdir(), { fileName })
117-
dl.on("error", async (dlErr) => {
118-
info(`Failed to download ${url}: ${dlErr}`)
119-
await dl.stop()
120-
})
121-
await dl.start()
122-
// Install the downloaded package via dpkg
123-
execRootSync("dpkg", ["-i", join(tmpdir(), fileName)])
124-
}
125-
} catch (_errFallback) {
126-
info(`Failed to install ${dep}. Ignoring`)
127-
}
128-
}
129-
/* eslint-enable no-await-in-loop */
130-
}
131-
} else if (isArch()) {
132-
// https://aur.archlinux.org/packages/ncurses5-compat-libs
133-
await setupPacmanPack("ncurses5-compat-libs", undefined, "yay")
134-
} else if (hasDnf()) {
135-
// https://packages.fedoraproject.org/pkgs/ncurses/ncurses-compat-libs/index.html
136-
await setupDnfPack([
137-
{ name: "ncurses-compat-libs" },
138-
])
139-
}
140-
}
141-
const llvmBinaryDeps = memoize(llvmBinaryDeps_, { promise: true })
142-
143-
async function setupLLVMDeps_(arch: string) {
59+
async function setupGccForLLVM_(arch: string) {
14460
if (process.platform === "linux") {
14561
// using llvm requires ld, an up to date libstdc++, etc. So, install gcc first,
14662
// but with a lower priority than the one used by activateLLVM()
14763
await setupGcc(getVersion("gcc", undefined, await ubuntuVersion()), "", arch, 40)
14864
}
14965
}
150-
const setupLLVMDeps = memoize(setupLLVMDeps_, { promise: true })
66+
const setupGccForLLVM = memoize(setupGccForLLVM_, { promise: true })
15167

15268
export async function activateLLVM(directory: string, version: string) {
15369
const ld = process.env.LD_LIBRARY_PATH ?? ""
@@ -201,11 +117,26 @@ export async function activateLLVM(directory: string, version: string) {
201117
}
202118

203119
async function addLLVMLoggingMatcher() {
204-
if (GITHUB_ACTIONS) {
205-
const matcherPath = join(dirname, "llvm_matcher.json")
206-
if (!(await pathExists(matcherPath))) {
207-
return warning("the llvm_matcher.json file does not exist in the same folder as setup-cpp.js")
208-
}
209-
info(`::add-matcher::${matcherPath}`)
120+
if (!GITHUB_ACTIONS) {
121+
return
210122
}
123+
const matcherPath = join(dirname, "llvm_matcher.json")
124+
if (!(await pathExists(matcherPath))) {
125+
return warning("the llvm_matcher.json file does not exist in the same folder as setup-cpp.js")
126+
}
127+
info(`::add-matcher::${matcherPath}`)
128+
}
129+
130+
/**
131+
* Setup clang-format
132+
*
133+
* This uses the LLVM installer on Ubuntu, and the LLVM binaries on other platforms
134+
*/
135+
export function setupClangFormat(version: string, setupDir: string, arch: string) {
136+
return setupLLVMOnly(version, setupDir, arch, LLVMPackages.ClangFormat)
137+
}
138+
139+
/** Setup llvm tools (clang tidy, etc.) without activating llvm and using it as the compiler */
140+
export function setupClangTools(version: string, setupDir: string, arch: string) {
141+
return setupLLVMOnly(version, setupDir, arch)
211142
}

src/llvm/llvm_installer.ts renamed to src/llvm/llvm_apt_installer.ts

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,71 @@
11
import { info } from "console"
22
import { tmpdir } from "os"
3-
import { join } from "path"
4-
import { execRoot } from "admina"
3+
import path, { join } from "path"
4+
import { fileURLToPath } from "url"
5+
import { execRoot, execRootSync } from "admina"
56
import { addPath } from "envosman"
67
import { chmod, readFile, writeFile } from "fs/promises"
78
import { DownloaderHelper } from "node-downloader-helper"
89
import { aptTimeout, hasNala, installAddAptRepo, installAptPack, isAptPackRegexInstalled } from "setup-apt"
910
import { rcOptions } from "../cli-options.js"
1011
import { DEFAULT_TIMEOUT } from "../installTool.js"
12+
import { isUbuntu } from "../utils/env/isUbuntu.js"
1113
import type { InstallationInfo } from "../utils/setup/setupBin.js"
14+
import { majorLLVMVersion } from "./utils.js"
15+
const dirname = typeof __dirname === "string" ? __dirname : path.dirname(fileURLToPath(import.meta.url))
1216

1317
export enum LLVMPackages {
1418
All = 0,
1519
ClangFormat = 1,
1620
Core = 2,
1721
}
1822

23+
/**
24+
* Try to setup LLVM via the apt package manager
25+
*
26+
* @param {string} version - The version of LLVM to install
27+
* @param {LLVMPackages} packages - The packages to install
28+
*
29+
* @returns {InstallationInfo} The installation info if the installation was successful
30+
* @returns {undefined} If the installation fails, it will try to remove the repository and will return undefined
31+
*/
32+
export async function trySetupLLVMApt(
33+
version: string,
34+
packages: LLVMPackages = LLVMPackages.All,
35+
): Promise<InstallationInfo | undefined> {
36+
if (!isUbuntu()) {
37+
return undefined
38+
}
39+
40+
try {
41+
return await setupLLVMApt(version, packages)
42+
} catch (err) {
43+
info(`Failed to install llvm via system package manager ${err}. Trying to remove the repository`)
44+
try {
45+
execRootSync(join(dirname, "llvm_repo_remove.bash"), [`${majorLLVMVersion(version)}`])
46+
} catch (removeErr) {
47+
info(`Failed to remove llvm repository ${removeErr}`)
48+
}
49+
}
50+
return undefined
51+
}
52+
53+
/**
54+
* Setup LLVM via the apt package manager
55+
*
56+
* @note assumes this is running on an Ubuntu/Debian system
57+
*
58+
* @param {string} version - The version of LLVM to install
59+
* @param {LLVMPackages} packages - The packages to install
60+
*
61+
* @returns {InstallationInfo} The installation info if the installation was successful
62+
*/
1963
export async function setupLLVMApt(
20-
majorVersion: number,
64+
version: string,
2165
packages: LLVMPackages = LLVMPackages.All,
2266
): Promise<InstallationInfo> {
67+
const majorVersion = majorLLVMVersion(version)
68+
2369
// TODO for older versions, this also includes the minor version
2470
const installationFolder = `/usr/lib/llvm-${majorVersion}`
2571

src/llvm/llvm_bin.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { tmpdir } from "os"
2+
import { join } from "path"
3+
import { execRootSync } from "admina"
4+
import { info } from "ci-log"
5+
import memoize from "memoizee"
6+
import { DownloaderHelper } from "node-downloader-helper"
7+
import { installAptPack } from "setup-apt"
8+
import { arm64, x86_64 } from "../utils/env/arch.js"
9+
import { hasDnf } from "../utils/env/hasDnf.js"
10+
import { isArch } from "../utils/env/isArch.js"
11+
import { isUbuntu } from "../utils/env/isUbuntu.js"
12+
import { setupBin } from "../utils/setup/setupBin.js"
13+
import { setupDnfPack } from "../utils/setup/setupDnfPack.js"
14+
import { setupPacmanPack } from "../utils/setup/setupPacmanPack.js"
15+
import { getLLVMPackageInfo } from "./llvm_url.js"
16+
import { majorLLVMVersion } from "./utils.js"
17+
18+
export async function setupLLVMBin(version: string, setupDir: string, arch: string) {
19+
const installInfo = await setupBin("llvm", version, getLLVMPackageInfo, setupDir, arch)
20+
await llvmBinaryDeps(majorLLVMVersion(version))
21+
return installInfo
22+
}
23+
24+
async function llvmBinaryDeps_(_majorVersion: number) {
25+
if (isUbuntu()) {
26+
for (const dep of ["libtinfo5", "libtinfo6"]) {
27+
/* eslint-disable no-await-in-loop */
28+
try {
29+
await installAptPack([{ name: dep }])
30+
} catch (_err) {
31+
try {
32+
if (dep === "libtinfo5") {
33+
// Manually install libtinfo5 if the package is not available
34+
info(`Failed to install ${dep}\nManually installing the package`)
35+
const arch = x86_64.includes(process.arch)
36+
? "amd64"
37+
: arm64.includes(process.arch)
38+
? "arm64"
39+
: process.arch
40+
41+
const fileName = `libtinfo5_6.3-2ubuntu0.1_${arch}.deb`
42+
const url = `http://launchpadlibrarian.net/666971015/${fileName}`
43+
const dl = new DownloaderHelper(url, tmpdir(), { fileName })
44+
dl.on("error", async (dlErr) => {
45+
info(`Failed to download ${url}: ${dlErr}`)
46+
await dl.stop()
47+
})
48+
await dl.start()
49+
// Install the downloaded package via dpkg
50+
execRootSync("dpkg", ["-i", join(tmpdir(), fileName)])
51+
}
52+
} catch (_errFallback) {
53+
info(`Failed to install ${dep}. Ignoring`)
54+
}
55+
}
56+
/* eslint-enable no-await-in-loop */
57+
}
58+
} else if (isArch()) {
59+
// https://aur.archlinux.org/packages/ncurses5-compat-libs
60+
await setupPacmanPack("ncurses5-compat-libs", undefined, "yay")
61+
} else if (hasDnf()) {
62+
// https://packages.fedoraproject.org/pkgs/ncurses/ncurses-compat-libs/index.html
63+
await setupDnfPack([
64+
{ name: "ncurses-compat-libs" },
65+
])
66+
}
67+
}
68+
const llvmBinaryDeps = memoize(llvmBinaryDeps_, { promise: true })

src/llvm/utils.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import memoize from "memoizee"
2+
import { semverCoerceIfInvalid } from "../utils/setup/version.ts"
3+
function majorLLVMVersion_(version: string) {
4+
const coeredVersion = semverCoerceIfInvalid(version)
5+
return Number.parseInt(coeredVersion.split(".")[0], 10)
6+
}
7+
8+
export const majorLLVMVersion = memoize(majorLLVMVersion_)

0 commit comments

Comments
 (0)