Skip to content

Commit 8c9d48b

Browse files
authored
Merge pull request #141 from aminya/faster-llvm [skip ci]
2 parents 96bc4cd + a1958c0 commit 8c9d48b

File tree

11 files changed

+314
-299
lines changed

11 files changed

+314
-299
lines changed

cspell.config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ ignorePaths:
88
- .git/
99
- dist/
1010
- dev/cpp_vcpkg_project
11+
- "**/node_modules/"
1112
words:
1213
- aarch
1314
- aminya

dev/scripts/pack-exe.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable import/no-extraneous-dependencies */
12
import { node } from "execa"
23

34
function getPlatformName() {

dist/node12/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/node12/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/node16/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/node16/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.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@
2929
"docs": "shx rm -rf packages/*/README.md && pnpm -r exec readme --path ../../dev/readme/template.md -y && pnpm -r exec ts-readme",
3030
"format": "run-s lint.prettier",
3131
"lint": "run-p --aggregate-output --continue-on-error lint.cspell lint.eslint lint.prettier lint.tsc",
32-
"lint.cspell": "cspell lint --no-progress --show-suggestions",
32+
"lint.cspell": "cspell lint --no-progress --show-suggestions --cache --cache-location ./.cache/cspell/.cspellcache",
3333
"lint.eslint": "eslint **/*.{ts,tsx,js,jsx,cjs,mjs,json,yaml} --no-error-on-unmatched-pattern --cache --cache-location ./.cache/eslint/ --fix",
34-
"lint.prettier": "prettier --write .",
34+
"lint.prettier": "prettier --list-different --write .",
3535
"lint.tsc": "tsc --noEmit",
3636
"pack.exe": "shx rm -rf ./dist/tsconfig.tsbuildinfo && ts-node --esm ./dev/scripts/pack-exe.ts",
3737
"prepare": "pnpm run -r build && pnpm run -w build",

src/llvm/__tests__/llvm.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { setupLLVM, VERSIONS, getUrl, setupClangTools, getLinuxUrl } from "../llvm"
1+
import { setupLLVM, setupClangTools } from "../llvm"
22
import { getSpecificVersionAndUrl } from "../../utils/setup/version"
33
import { isUrlOnline } from "is-url-online"
44
import { setupTmpDir, testBin } from "../../utils/tests/test-helpers"
@@ -9,6 +9,7 @@ import { chmodSync } from "fs"
99
import { getVersion } from "../../versions/versions"
1010
import { ubuntuVersion } from "../../utils/env/ubuntu_version"
1111
import * as io from "@actions/io"
12+
import { getLinuxUrl, getUrl, VERSIONS } from "../llvm_url"
1213

1314
jest.setTimeout(400000)
1415
async function testUrl(version: string) {

src/llvm/llvm.ts

Lines changed: 29 additions & 291 deletions
Original file line numberDiff line numberDiff line change
@@ -1,319 +1,57 @@
11
import { join, addExeExt } from "patha"
22
import { delimiter } from "path"
3-
import semverLte from "semver/functions/lte"
43
import semverMajor from "semver/functions/major"
5-
import { isUrlOnline } from "is-url-online"
6-
import { InstallationInfo, PackageInfo, setupBin } from "../utils/setup/setupBin"
7-
import { extractExe, extractTarByExe } from "../utils/setup/extract"
8-
import {
9-
getSpecificVersionAndUrl,
10-
getSpecificVersions,
11-
getVersions,
12-
semverCoerceIfInvalid,
13-
} from "../utils/setup/version"
4+
import { InstallationInfo, setupBin } from "../utils/setup/setupBin"
5+
import { semverCoerceIfInvalid } from "../utils/setup/version"
146
import { setupMacOSSDK } from "../macos-sdk/macos-sdk"
157
import { addEnv } from "../utils/env/addEnv"
16-
import { setOutput } from "@actions/core"
178
import { setupAptPack, updateAptAlternatives } from "../utils/setup/setupAptPack"
189
import { info, warning } from "ci-log"
1910
import { existsSync } from "fs"
2011
import ciDetect from "@npmcli/ci-detect"
2112
import { setupGcc } from "../gcc/gcc"
2213
import { getVersion } from "../versions/versions"
23-
import { isArch } from "../utils/env/isArch"
2414
import { isUbuntu } from "../utils/env/isUbuntu"
15+
import { getLLVMPackageInfo } from "./llvm_url"
2516

26-
//================================================
27-
// Version
28-
//================================================
29-
30-
/** The specific and minimum LLVM versions supported by this action. */
31-
export const VERSIONS: Set<string> = getVersions([
32-
"3.5.0",
33-
"3.5.1",
34-
"3.5.2",
35-
"3.6.0",
36-
"3.6.1",
37-
"3.6.2",
38-
"3.7.0",
39-
"3.7.1",
40-
"3.8.0",
41-
"3.8.1",
42-
"3.9.0",
43-
"3.9.1",
44-
"4.0.0",
45-
"4.0.1",
46-
"5.0.0",
47-
"5.0.1",
48-
"5.0.2",
49-
"6.0.0",
50-
"6.0.1",
51-
"7.0.0",
52-
"7.0.1",
53-
"7.1.0",
54-
"8.0.0",
55-
"8.0.1",
56-
"9.0.0",
57-
"9.0.1",
58-
"10.0.0",
59-
"10.0.1",
60-
"11.0.0",
61-
"11.0.1",
62-
"11.1.0",
63-
"12.0.0",
64-
"12.0.1",
65-
"13.0.0",
66-
"13.0.1",
67-
"14.0.0",
68-
"14.0.1",
69-
"14.0.2",
70-
"14.0.3",
71-
"14.0.4",
72-
"14.0.5",
73-
"14.0.6",
74-
"15.0.0",
75-
"15.0.1",
76-
"15.0.2",
77-
])
78-
79-
//================================================
80-
// URL
81-
//================================================
82-
83-
/** Gets a LLVM download URL for GitHub. */
84-
function getGitHubUrl(version: string, prefix: string, suffix: string): string {
85-
const file = `${prefix}${version}${suffix}`
86-
return `https://github.com/llvm/llvm-project/releases/download/llvmorg-${version}/${file}`
87-
}
88-
89-
/** Gets a LLVM download URL for https://releases.llvm.org. */
90-
function getReleaseUrl(version: string, prefix: string, suffix: string): string {
91-
const file = `${prefix}${version}${suffix}`
92-
return `https://releases.llvm.org/${version}/${file}`
93-
}
94-
95-
/** The LLVM versions that were never released for the Darwin platform. */
96-
const DARWIN_MISSING: Set<string> = new Set([
97-
"3.5.1",
98-
"3.6.1",
99-
"3.6.2",
100-
"3.7.1",
101-
"3.8.1",
102-
"3.9.1",
103-
"6.0.1",
104-
"7.0.1",
105-
"7.1.0",
106-
"8.0.1",
107-
"11.0.1",
108-
"11.1.0",
109-
"12.0.1",
110-
])
111-
112-
/** Gets an LLVM download URL for the Darwin platform. */
113-
function getDarwinUrl(version: string): string | null {
114-
if (DARWIN_MISSING.has(version)) {
115-
return null
116-
}
117-
118-
const darwin = version === "9.0.0" ? "-darwin-apple" : "-apple-darwin"
119-
const prefix = "clang+llvm-"
120-
const suffix = `-x86_64${darwin}.tar.xz`
121-
if (semverLte(version, "9.0.1")) {
122-
return getReleaseUrl(version, prefix, suffix)
123-
} else {
124-
return getGitHubUrl(version, prefix, suffix)
125-
}
126-
}
127-
128-
/**
129-
* The LLVM versions that should use the last RC version instead of the release version for the Linux (Ubuntu) platform.
130-
* This is useful when there were binaries released for the Linux (Ubuntu) platform for the last RC version but not for
131-
* the actual release version.
132-
*/
133-
const UBUNTU_RC: Map<string, string> = new Map()
134-
135-
/**
136-
* The (latest) Ubuntu versions for each LLVM version.
137-
*
138-
* https://github.com/llvm/llvm-project/releases/tag/llvmorg-14.0.1 or https://releases.llvm.org/14.0.1
139-
*/
140-
// TODO change based on ubuntu version
141-
const UBUNTU_SUFFIX_MAP: { [key: string]: string } = {
142-
"3.5.0": "-ubuntu-14.04",
143-
"3.5.1": "",
144-
"3.5.2": "-ubuntu-14.04",
145-
"3.6.0": "-ubuntu-14.04",
146-
"3.6.1": "-ubuntu-14.04",
147-
"3.6.2": "-ubuntu-14.04",
148-
"3.7.0": "-ubuntu-14.04",
149-
"3.7.1": "-ubuntu-14.04",
150-
"3.8.0": "-ubuntu-16.04",
151-
"3.8.1": "-ubuntu-16.04",
152-
"3.9.0": "-ubuntu-16.04",
153-
"3.9.1": "-ubuntu-16.04",
154-
"4.0.0": "-ubuntu-16.04",
155-
"5.0.0": "-ubuntu16.04",
156-
"5.0.1": "-ubuntu-16.04",
157-
"5.0.2": "-ubuntu-16.04",
158-
"6.0.0": "-ubuntu-16.04",
159-
"6.0.1": "-ubuntu-16.04",
160-
"7.0.0": "-ubuntu-16.04",
161-
"7.0.1": "-ubuntu-18.04",
162-
"7.1.0": "-ubuntu-14.04",
163-
"8.0.0": "-ubuntu-18.04",
164-
"9.0.0": "-ubuntu-18.04",
165-
"9.0.1": "-ubuntu-16.04",
166-
"10.0.0": "-ubuntu-18.04",
167-
"10.0.1": "-ubuntu-16.04",
168-
"11.0.0": "-ubuntu-20.04",
169-
"11.0.1": "-ubuntu-16.04",
170-
"11.1.0": "-ubuntu-16.04",
171-
"12.0.0": "-ubuntu-20.04",
172-
"12.0.1": "-ubuntu-16.04",
173-
"13.0.0": "-ubuntu-20.04",
174-
"13.0.0-ubuntu-16.04": "-ubuntu-16.04",
175-
"13.0.0-ubuntu-20.04": "-ubuntu-20.04",
176-
"13.0.1": "-ubuntu-18.04",
177-
"13.0.1-ubuntu-18.04": "-ubuntu-18.04",
178-
"14.0.0": "-ubuntu-18.04",
179-
// "14.0.1": "-ubuntu-18.04", // only available for powerpc64le
180-
"15.0.2": "-rhel86",
17+
export async function setupLLVM(version: string, setupDir: string, arch: string): Promise<InstallationInfo> {
18+
const installationInfo = await setupLLVMWithoutActivation(version, setupDir, arch)
19+
await activateLLVM(installationInfo.installDir ?? setupDir, version)
20+
return installationInfo
18121
}
18222

183-
/** The latest supported LLVM version for the Linux (Ubuntu) platform. */
184-
const MAX_UBUNTU: string = "15.0.2"
185-
186-
/** Gets an LLVM download URL for the Linux (Ubuntu) platform. */
187-
export function getLinuxUrl(versionGiven: string): string {
188-
let version = versionGiven
189-
190-
const rc = UBUNTU_RC.get(version)
191-
if (rc !== undefined) {
192-
version = rc
193-
}
194-
195-
let linuxVersion: string
196-
// ubuntu-version is specified
197-
if (version.includes("ubuntu")) {
198-
const givenUbuntuVersion = version.replace(/-ubuntu-.*/, "")
199-
if (!VERSIONS.has(givenUbuntuVersion)) {
200-
throw new Error(`Unsupported Ubuntu version: ${givenUbuntuVersion}`)
201-
}
202-
linuxVersion = version.replace(givenUbuntuVersion, "")
203-
version = getSpecificVersions(VERSIONS, givenUbuntuVersion)[0]
204-
} else if (version !== "" && version in UBUNTU_SUFFIX_MAP) {
205-
linuxVersion = UBUNTU_SUFFIX_MAP[version]
206-
} else {
207-
// default to the maximum version
208-
linuxVersion = UBUNTU_SUFFIX_MAP[MAX_UBUNTU]
209-
warning(`Falling back to LLVM version ${MAX_UBUNTU} ${linuxVersion} for the Ubuntu.`)
210-
}
211-
212-
const prefix = "clang+llvm-"
23+
let installedDeps = false
21324

214-
let suffix: string
215-
if (version === "5.0.0") {
216-
suffix = `-linux-x86_64${linuxVersion}.tar.xz`
217-
} else if (linuxVersion.includes("-rhel86")) {
218-
suffix = `-x86_64-unknown-linux-gnu${linuxVersion}.tar.xz`
219-
} else {
220-
suffix = `-x86_64-linux-gnu${linuxVersion}.tar.xz`
221-
}
25+
async function setupLLVMWithoutActivation(version: string, setupDir: string, arch: string) {
26+
const installationInfoPromise = setupBin("llvm", version, getLLVMPackageInfo, setupDir, arch)
22227

223-
if (semverLte(version, "9.0.1")) {
224-
return getReleaseUrl(version, prefix, suffix)
28+
let depsPromise: Promise<void>
29+
if (!installedDeps) {
30+
depsPromise = setupLLVMDeps(arch)
31+
// eslint-disable-next-line require-atomic-updates
32+
installedDeps = true
22533
} else {
226-
return getGitHubUrl(version, prefix, suffix)
34+
depsPromise = Promise.resolve()
22735
}
228-
}
229-
230-
/** The LLVM versions that were never released for the Windows platform. */
231-
const WIN32_MISSING: Set<string> = new Set(["10.0.1"])
23236

233-
/** Gets an LLVM download URL for the Windows platform. */
234-
async function getWin32Url(version: string): Promise<string | null> {
235-
if (WIN32_MISSING.has(version)) {
236-
return null
237-
}
37+
// install LLVM and its dependencies in parallel
38+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
39+
const [installationInfo, _] = await Promise.all([installationInfoPromise, depsPromise])
23840

239-
const prefix = "LLVM-"
240-
const suffix = semverLte(version, "3.7.0") ? "-win32.exe" : "-win64.exe"
241-
242-
const olderThan9_1 = semverLte(version, "9.0.1")
243-
let url: string
244-
let fallback = false
245-
if (olderThan9_1) {
246-
url = getReleaseUrl(version, prefix, suffix)
247-
if (!(await isUrlOnline(url))) {
248-
fallback = true // fallback to github
249-
}
250-
}
251-
if (fallback || !olderThan9_1) {
252-
url = getGitHubUrl(version, prefix, suffix)
253-
}
254-
255-
return url!
256-
}
257-
258-
/** Gets an LLVM download URL. */
259-
export function getUrl(platform: string, version: string): string | null | Promise<string | null> {
260-
switch (platform) {
261-
case "darwin":
262-
return getDarwinUrl(version)
263-
case "linux":
264-
return getLinuxUrl(version)
265-
case "win32":
266-
return getWin32Url(version)
267-
default:
268-
return null
269-
}
270-
}
271-
272-
//================================================
273-
// Exports
274-
//================================================
275-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
276-
async function getLLVMPackageInfo(version: string, platform: NodeJS.Platform, _arch: string): Promise<PackageInfo> {
277-
const [specificVersion, url] = await getSpecificVersionAndUrl(VERSIONS, platform, version, getUrl)
278-
setOutput("version", specificVersion)
279-
return {
280-
url,
281-
extractedFolderName: "",
282-
binRelativeDir: "bin",
283-
binFileName: addExeExt("clang"),
284-
extractFunction:
285-
platform === "win32"
286-
? extractExe
287-
: (file: string, dest: string) => {
288-
return extractTarByExe(file, dest, ["--strip-components=1"])
289-
},
290-
}
291-
}
292-
293-
export async function setupLLVM(version: string, setupDir: string, arch: string): Promise<InstallationInfo> {
294-
const installationInfo = await _setupLLVM(version, setupDir, arch)
295-
await activateLLVM(installationInfo.installDir ?? setupDir, version)
29641
return installationInfo
29742
}
29843

299-
let didInit = false
300-
async function _setupLLVM(version: string, setupDir: string, arch: string) {
301-
const installationInfo = await setupBin("llvm", version, getLLVMPackageInfo, setupDir, arch)
302-
if (!didInit) {
303-
if (process.platform === "linux") {
304-
// install llvm build dependencies
305-
await setupGcc(getVersion("gcc", undefined), "", arch) // using llvm requires ld, an up to date libstdc++, etc. So, install gcc first
306-
if (isArch()) {
307-
// setupPacmanPack("ncurses")
308-
// TODO: install libtinfo ?
309-
} else if (isUbuntu()) {
310-
await setupAptPack("libtinfo-dev")
311-
}
44+
async function setupLLVMDeps(arch: string) {
45+
if (process.platform === "linux") {
46+
// install llvm build dependencies
47+
await setupGcc(getVersion("gcc", undefined), "", arch) // using llvm requires ld, an up to date libstdc++, etc. So, install gcc first
48+
49+
if (isUbuntu()) {
50+
await setupAptPack("libtinfo-dev")
31251
}
313-
// eslint-disable-next-line require-atomic-updates
314-
didInit = true
52+
// TODO: install libtinfo on other distros
53+
// setupPacmanPack("ncurses")
31554
}
316-
return installationInfo
31755
}
31856

31957
export async function activateLLVM(directory: string, versionGiven: string) {
@@ -378,7 +116,7 @@ export function setupClangTools(version: string, setupDir: string, arch: string)
378116
if (ciDetect() === "github-actions") {
379117
addLLVMLoggingMatcher()
380118
}
381-
return _setupLLVM(version, setupDir, arch)
119+
return setupLLVMWithoutActivation(version, setupDir, arch)
382120
}
383121

384122
function addLLVMLoggingMatcher() {

0 commit comments

Comments
 (0)