Merge pull request #182 from aminya/llvm-system [skip ci]

This commit is contained in:
Amin Yahyaabadi 2023-07-17 21:33:29 -07:00 committed by GitHub
commit 0c3b851567
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 345 additions and 214 deletions

3
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"recommendations": ["orta.vscode-jest", "esbenp.prettier-vscode", "dbaeumer.vscode-eslint"]
}

15
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,15 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Jest Current File",
"runtimeExecutable": "sh",
"program": "node_modules/.bin/jest",
"args": ["${relativeFile}"],
"console": "integratedTerminal",
"internalConsoleOptions": "openOnFirstSessionStart"
}
]
}

View File

@ -209,7 +209,7 @@ If you want to build the ones included, then run:
```shell ```shell
git clone --recurse-submodules https://github.com/aminya/setup-cpp git clone --recurse-submodules https://github.com/aminya/setup-cpp
cd ./setup-cpp cd ./setup-cpp
docker build -f ./dev/docker/setup-cpp-ubuntu.dockerfile -t setup-cpp . docker build -f ./dev/docker/setup-cpp-ubuntu.dockerfile -t setup-cpp-ubuntu .
``` ```
Where you should use the path to the dockerfile after `-f`. Where you should use the path to the dockerfile after `-f`.

View File

@ -9,6 +9,7 @@ ignorePaths:
- dist/ - dist/
- dev/cpp_vcpkg_project - dev/cpp_vcpkg_project
- "**/node_modules/" - "**/node_modules/"
- .vscode/extensions.json
words: words:
- aarch - aarch
- clangd - clangd

View File

@ -7,8 +7,7 @@ RUN pacman -Syuu --noconfirm && \
pacman-db-upgrade && \ pacman-db-upgrade && \
# install nodejs # install nodejs
pacman -S --noconfirm --needed nodejs npm && \ pacman -S --noconfirm --needed nodejs npm && \
# install setup-cpp
npm install -g setup-cpp@v0.31.1 && \
# install the compiler and tools # install the compiler and tools
node /usr/lib/setup-cpp/setup-cpp.js \ node /usr/lib/setup-cpp/setup-cpp.js \
--compiler llvm \ --compiler llvm \

View File

@ -5,8 +5,7 @@ COPY "./dist/legacy" "/usr/lib/setup-cpp/"
# install nodejs # install nodejs
RUN dnf -y install nodejs npm && \ RUN dnf -y install nodejs npm && \
# install setup-cpp
npm install -g setup-cpp@v0.31.1 && \
# install the compiler and tools # install the compiler and tools
node /usr/lib/setup-cpp/setup-cpp.js \ node /usr/lib/setup-cpp/setup-cpp.js \
--compiler llvm \ --compiler llvm \

View File

@ -6,8 +6,7 @@ COPY "./dist/legacy" "/usr/lib/setup-cpp/"
RUN apt-get update -qq && \ RUN apt-get update -qq && \
# install nodejs # install nodejs
apt-get install -y --no-install-recommends nodejs npm && \ apt-get install -y --no-install-recommends nodejs npm && \
# install setup-cpp
npm install -g setup-cpp@v0.31.1 && \
# install the compiler and tools # install the compiler and tools
node /usr/lib/setup-cpp/setup-cpp.js \ node /usr/lib/setup-cpp/setup-cpp.js \
--nala true \ --nala true \

View File

@ -7,10 +7,12 @@ async function main() {
const dockerFileContent = await readFile(`./dev/docker/setup-cpp-${dockerFile}.dockerfile`, "utf-8") const dockerFileContent = await readFile(`./dev/docker/setup-cpp-${dockerFile}.dockerfile`, "utf-8")
const builderExample = await readFile(`./dev/docker/${dockerFile}.dockerfile`, "utf-8") const builderExample = await readFile(`./dev/docker/${dockerFile}.dockerfile`, "utf-8")
// after the first FROM, add COPY "./dist/legacy" "/"
const modifiedDockerFile = dockerFileContent const modifiedDockerFile = dockerFileContent
// load the externally built setup-cpp
.replace(/FROM (.*)/g, `FROM $1\n\nCOPY "./dist/legacy" "/usr/lib/setup-cpp/"`) .replace(/FROM (.*)/g, `FROM $1\n\nCOPY "./dist/legacy" "/usr/lib/setup-cpp/"`)
.replace("setup-cpp ", "node /usr/lib/setup-cpp/setup-cpp.js ") .replace("setup-cpp ", "node /usr/lib/setup-cpp/setup-cpp.js ")
// remove the npm install line
.replace(/# install setup-cpp\n\s*npm install -g setup-cpp.*/, "")
// concat the two files // concat the two files
const newDockerFileContent = `${modifiedDockerFile}\n${builderExample}` const newDockerFileContent = `${modifiedDockerFile}\n${builderExample}`

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -151,7 +151,8 @@
], ],
"alias": { "alias": {
"electron": false, "electron": false,
"patha": "patha/dist/index.node.mjs" "patha": "patha/dist/index.node.mjs",
"admina": "admina/dist/index.mjs"
}, },
"pnpm": { "pnpm": {
"overrides": { "overrides": {

View File

@ -23,9 +23,9 @@ export async function setupBazel(version: string, _setupDir: string, _arch: stri
throw new Error("installing bazel on Arch linux is not supported yet") throw new Error("installing bazel on Arch linux is not supported yet")
} else if (hasDnf()) { } else if (hasDnf()) {
// https://bazel.build/install/redhat // https://bazel.build/install/redhat
setupDnfPack("dnf-plugins-core", undefined) await setupDnfPack([{ name: "dnf-plugins-core" }])
execRootSync("dnf", ["copr", "enable", "vbatts/bazel"]) execRootSync("dnf", ["copr", "enable", "vbatts/bazel"])
return setupDnfPack("bazel4", undefined) return setupDnfPack([{ name: "bazel4" }])
} else if (isUbuntu()) { } else if (isUbuntu()) {
// https://bazel.build/install/ubuntu // https://bazel.build/install/ubuntu
const keyFileName = await addAptKeyViaDownload( const keyFileName = await addAptKeyViaDownload(

View File

@ -20,7 +20,7 @@ export function setupCcache(version: string, _setupDir: string, _arch: string) {
if (isArch()) { if (isArch()) {
return setupPacmanPack("ccache", version) return setupPacmanPack("ccache", version)
} else if (hasDnf()) { } else if (hasDnf()) {
return setupDnfPack("ccache", version) return setupDnfPack([{ name: "ccache", version }])
} else if (isUbuntu()) { } else if (isUbuntu()) {
return setupAptPack([{ name: "ccache", version }]) return setupAptPack([{ name: "ccache", version }])
} }

View File

@ -23,7 +23,7 @@ export async function setupCppcheck(version: string | undefined, _setupDir: stri
if (isArch()) { if (isArch()) {
return setupPacmanPack("cppcheck", version) return setupPacmanPack("cppcheck", version)
} else if (hasDnf()) { } else if (hasDnf()) {
return setupDnfPack("ccache", version) return setupDnfPack([{ name: "ccache", version }])
} else if (isUbuntu()) { } else if (isUbuntu()) {
return setupAptPack([{ name: "cppcheck", version }]) return setupAptPack([{ name: "cppcheck", version }])
} }

View File

@ -73,7 +73,7 @@ export async function setupDoxygen(version: string, setupDir: string, arch: stri
if (isArch()) { if (isArch()) {
installationInfo = await setupPacmanPack("doxygen", version) installationInfo = await setupPacmanPack("doxygen", version)
} else if (hasDnf()) { } else if (hasDnf()) {
return setupDnfPack("doxygen", version) return setupDnfPack([{ name: "doxygen", version }])
} else if (isUbuntu()) { } else if (isUbuntu()) {
installationInfo = await setupAptPack([{ name: "doxygen", version }]) installationInfo = await setupAptPack([{ name: "doxygen", version }])
} else { } else {

View File

@ -98,9 +98,11 @@ export async function setupGcc(version: string, setupDir: string, arch: string)
if (isArch()) { if (isArch()) {
installationInfo = await setupPacmanPack("gcc", version) installationInfo = await setupPacmanPack("gcc", version)
} else if (hasDnf()) { } else if (hasDnf()) {
installationInfo = setupDnfPack("gcc", version) installationInfo = await setupDnfPack([
setupDnfPack("gcc-c++", version) { name: "gcc", version },
setupDnfPack("libstdc++-devel", undefined) { name: "gcc-c++", version },
{ name: "libstdc++-devel" },
])
} else if (isUbuntu()) { } else if (isUbuntu()) {
installationInfo = await setupAptPack([ installationInfo = await setupAptPack([
{ name: "gcc", version, repositories: ["ppa:ubuntu-toolchain-r/test"] }, { name: "gcc", version, repositories: ["ppa:ubuntu-toolchain-r/test"] },

View File

@ -23,7 +23,7 @@ export async function setupGraphviz(version: string, _setupDir: string, _arch: s
if (isArch()) { if (isArch()) {
return setupPacmanPack("graphviz", version) return setupPacmanPack("graphviz", version)
} else if (hasDnf()) { } else if (hasDnf()) {
return setupDnfPack("graphviz", version) return setupDnfPack([{ name: "graphviz", version }])
} else if (isUbuntu()) { } else if (isUbuntu()) {
return setupAptPack([{ name: "graphviz", version }]) return setupAptPack([{ name: "graphviz", version }])
} }

View File

@ -47,8 +47,7 @@ async function buildKcov(file: string, dest: string) {
if (isArch()) { if (isArch()) {
await Promise.all([setupPacmanPack("libdwarf"), setupPacmanPack("libcurl-openssl")]) await Promise.all([setupPacmanPack("libdwarf"), setupPacmanPack("libcurl-openssl")])
} else if (hasDnf()) { } else if (hasDnf()) {
setupDnfPack("libdwarf-devel") await setupDnfPack([{ name: "libdwarf-devel" }, { name: "libcurl-devel" }])
setupDnfPack("libcurl-devel")
} else if (isUbuntu()) { } else if (isUbuntu()) {
await setupAptPack([{ name: "libdw-dev" }, { name: "libcurl4-openssl-dev" }]) await setupAptPack([{ name: "libdw-dev" }, { name: "libcurl4-openssl-dev" }])
} }
@ -116,7 +115,7 @@ export async function setupKcov(versionGiven: string, setupDir: string, arch: st
if (isArch()) { if (isArch()) {
await setupPacmanPack("binutils") await setupPacmanPack("binutils")
} else if (hasDnf()) { } else if (hasDnf()) {
setupDnfPack("binutils") await setupDnfPack([{ name: "binutils" }])
} else if (isUbuntu()) { } else if (isUbuntu()) {
await setupAptPack([{ name: "libbinutils" }]) await setupAptPack([{ name: "libbinutils" }])
} }

View File

@ -1,81 +1,134 @@
import { join, addExeExt } from "patha" import { execRoot } from "admina"
import { GITHUB_ACTIONS } from "ci-info"
import { info, warning } from "ci-log"
import { execa } from "execa"
import { promises } from "fs"
const { readFile, writeFile, chmod } = promises
import memoize from "micro-memoize"
import { delimiter } from "path" import { delimiter } from "path"
import { pathExists } from "path-exists"
import { addExeExt, join } from "patha"
import { setupGcc } from "../gcc/gcc"
import { setupMacOSSDK } from "../macos-sdk/macos-sdk"
import { addEnv, addPath } from "../utils/env/addEnv"
import { isUbuntu } from "../utils/env/isUbuntu"
import { ubuntuVersion } from "../utils/env/ubuntu_version"
import { hasNala, setupAptPack, updateAptAlternatives } from "../utils/setup/setupAptPack"
import { InstallationInfo, setupBin } from "../utils/setup/setupBin" import { InstallationInfo, setupBin } from "../utils/setup/setupBin"
import { semverCoerceIfInvalid } from "../utils/setup/version" import { semverCoerceIfInvalid } from "../utils/setup/version"
import { setupMacOSSDK } from "../macos-sdk/macos-sdk"
import { addEnv } from "../utils/env/addEnv"
import { setupAptPack, updateAptAlternatives } from "../utils/setup/setupAptPack"
import { info, warning } from "ci-log"
import { GITHUB_ACTIONS } from "ci-info"
import { setupGcc } from "../gcc/gcc"
import { getVersion } from "../versions/versions" import { getVersion } from "../versions/versions"
import { isUbuntu } from "../utils/env/isUbuntu"
import { getLLVMPackageInfo } from "./llvm_url" import { getLLVMPackageInfo } from "./llvm_url"
import { ubuntuVersion } from "../utils/env/ubuntu_version"
import { pathExists } from "path-exists"
import { ExecaReturnValue } from "execa"
export async function setupLLVM(version: string, setupDir: string, arch: string): Promise<InstallationInfo> { export async function setupLLVM(version: string, setupDir: string, arch: string): Promise<InstallationInfo> {
const installationInfo = await setupLLVMWithoutActivation(version, setupDir, arch) const installationInfo = await setupLLVMWithoutActivation(version, setupDir, arch)
await activateLLVM(installationInfo.installDir ?? setupDir, version) await activateLLVM(installationInfo.installDir ?? setupDir)
return installationInfo return installationInfo
} }
let installedDeps = false /** Setup llvm tools (clang tidy, clang format, etc) without activating llvm and using it as the compiler */
export const setupClangTools = setupLLVMWithoutActivation
async function setupLLVMWithoutActivation(version: string, setupDir: string, arch: string) { async function setupLLVMWithoutActivation(version: string, setupDir: string, arch: string) {
const installationInfoPromise = setupBin("llvm", version, getLLVMPackageInfo, setupDir, arch)
let depsPromise: Promise<void>
if (!installedDeps) {
depsPromise = setupLLVMDeps(arch, version)
// eslint-disable-next-line require-atomic-updates
installedDeps = true
} else {
depsPromise = Promise.resolve()
}
// install LLVM and its dependencies in parallel // install LLVM and its dependencies in parallel
// eslint-disable-next-line @typescript-eslint/no-unused-vars const [installationInfo, _1, _2] = await Promise.all([
const [installationInfo, _] = await Promise.all([installationInfoPromise, depsPromise]) setupLLVMOnly(version, setupDir, arch),
setupLLVMDeps(arch),
addLLVMLoggingMatcher(),
])
return installationInfo return installationInfo
} }
async function setupLLVMDeps(arch: string, version: string) { async function setupLLVMOnly(version: string, setupDir: string, arch: string) {
if (process.platform === "linux") { const coeredVersion = semverCoerceIfInvalid(version)
// install llvm build dependencies const majorVersion = parseInt(coeredVersion.split(".")[0], 10)
await setupGcc(getVersion("gcc", undefined, await ubuntuVersion()), "", arch) // using llvm requires ld, an up to date libstdc++, etc. So, install gcc first try {
if (isUbuntu()) { if (isUbuntu()) {
const majorVersion = parseInt(version.split(".")[0], 10) return await setupLLVMApt(majorVersion)
if (majorVersion <= 10) {
await setupAptPack([{ name: "libtinfo5" }])
} else {
await setupAptPack([{ name: "libtinfo-dev" }])
}
} }
// TODO: install libtinfo on other distros } catch (err) {
// await setupPacmanPack("ncurses") info(`Failed to install llvm via system package manager ${err}`)
}
const installationInfo = await setupBin("llvm", version, getLLVMPackageInfo, setupDir, arch)
await llvmBinaryDeps(majorVersion)
return installationInfo
}
async function setupLLVMApt(majorVersion: number): Promise<InstallationInfo> {
// TODO for older versions, this also includes the minor version
const installationFolder = `/usr/lib/llvm-${majorVersion}`
await setupAptPack([{ name: "curl" }])
await execa("curl", ["-LJO", "https://apt.llvm.org/llvm.sh"], { cwd: "/tmp" })
const neededPackages = await patchAptLLVMScript("/tmp/llvm.sh", "/tmp/llvm-setup-cpp.sh")
await setupAptPack(neededPackages)
await chmod("/tmp/llvm-setup-cpp.sh", "755")
await execRoot("bash", ["/tmp/llvm-setup-cpp.sh", `${majorVersion}`, "all"], {
stdio: "inherit",
shell: true,
})
await addPath(`${installationFolder}/bin`)
return {
installDir: `${installationFolder}`,
binDir: `${installationFolder}/bin`,
bin: `${installationFolder}/bin/clang++`,
} }
} }
export async function activateLLVM(directory: string, versionGiven: string) { async function patchAptLLVMScript(path: string, target_path: string) {
const _version = semverCoerceIfInvalid(versionGiven) let script = await readFile(path, "utf-8")
// make the scirpt non-interactive and fix broken packages
script = script
.replace(
/add-apt-repository "\${REPO_NAME}"/g,
// eslint-disable-next-line no-template-curly-in-string
'add-apt-repository -y "${REPO_NAME}"'
)
// fix conflicts between libclang-rt and libclang
.replace(/apt-get install -y/g, 'apt-get install -o Dpkg::Options::="--force-overwrite" -y --fix-broken')
// use nala if it is available
if (hasNala()) {
script = script.replace(/apt-get/g, "nala")
}
await writeFile(target_path, script)
const lib = join(directory, "lib") // the packages needed by the script
return [{ name: "lsb-release" }, { name: "wget" }, { name: "software-properties-common" }, { name: "gnupg" }]
}
async function llvmBinaryDeps_raw(majorVersion: number) {
if (isUbuntu()) {
if (majorVersion <= 10) {
await setupAptPack([{ name: "libtinfo5" }])
} else {
await setupAptPack([{ name: "libtinfo-dev" }])
}
}
}
const llvmBinaryDeps = memoize(llvmBinaryDeps_raw, { isPromise: true })
async function setupLLVMDeps_raw(arch: string) {
if (process.platform === "linux") {
// using llvm requires ld, an up to date libstdc++, etc. So, install gcc first
await setupGcc(getVersion("gcc", undefined, await ubuntuVersion()), "", arch)
}
}
const setupLLVMDeps = memoize(setupLLVMDeps_raw, { isPromise: true })
export async function activateLLVM(directory: string) {
const ld = process.env.LD_LIBRARY_PATH ?? "" const ld = process.env.LD_LIBRARY_PATH ?? ""
const dyld = process.env.DYLD_LIBRARY_PATH ?? "" const dyld = process.env.DYLD_LIBRARY_PATH ?? ""
const promises: Promise<void | ExecaReturnValue<string>>[] = [ const actPromises: Promise<any>[] = [
// the output of this action // the output of this action
addEnv("LLVM_PATH", directory), addEnv("LLVM_PATH", directory),
// Setup LLVM as the compiler // Setup LLVM as the compiler
addEnv("LD_LIBRARY_PATH", `${lib}${delimiter}${ld}`), addEnv("LD_LIBRARY_PATH", `${directory}/lib${delimiter}${ld}`),
addEnv("DYLD_LIBRARY_PATH", `${lib}${delimiter}${dyld}`), addEnv("DYLD_LIBRARY_PATH", `${directory}/lib${delimiter}${dyld}`),
// compiler flags // compiler flags
addEnv("LDFLAGS", `-L"${directory}/lib"`), addEnv("LDFLAGS", `-L"${directory}/lib"`),
@ -94,7 +147,6 @@ export async function activateLLVM(directory: string, versionGiven: string) {
// TODO Causes issues with clangd // TODO Causes issues with clangd
// TODO Windows builds fail with llvm's CPATH // TODO Windows builds fail with llvm's CPATH
// if (process.platform !== "win32") { // if (process.platform !== "win32") {
// const llvmMajor = semverMajor(version)
// if (await pathExists(`${directory}/lib/clang/${version}/include`)) { // if (await pathExists(`${directory}/lib/clang/${version}/include`)) {
// promises.push(addEnv("CPATH", `${directory}/lib/clang/${version}/include`)) // promises.push(addEnv("CPATH", `${directory}/lib/clang/${version}/include`))
// } else if (await pathExists(`${directory}/lib/clang/${llvmMajor}/include`)) { // } else if (await pathExists(`${directory}/lib/clang/${llvmMajor}/include`)) {
@ -103,7 +155,7 @@ export async function activateLLVM(directory: string, versionGiven: string) {
// } // }
if (isUbuntu()) { if (isUbuntu()) {
promises.push( actPromises.push(
updateAptAlternatives("cc", `${directory}/bin/clang`), updateAptAlternatives("cc", `${directory}/bin/clang`),
updateAptAlternatives("cxx", `${directory}/bin/clang++`), updateAptAlternatives("cxx", `${directory}/bin/clang++`),
updateAptAlternatives("clang", `${directory}/bin/clang`), updateAptAlternatives("clang", `${directory}/bin/clang`),
@ -114,25 +166,15 @@ export async function activateLLVM(directory: string, versionGiven: string) {
) )
} }
if (GITHUB_ACTIONS) { await Promise.all(actPromises)
await addLLVMLoggingMatcher()
}
await Promise.all(promises)
}
/** Setup llvm tools (clang tidy, clang format, etc) without activating llvm and using it as the compiler */
export async function setupClangTools(version: string, setupDir: string, arch: string): Promise<InstallationInfo> {
if (GITHUB_ACTIONS) {
await addLLVMLoggingMatcher()
}
return setupLLVMWithoutActivation(version, setupDir, arch)
} }
async function addLLVMLoggingMatcher() { async function addLLVMLoggingMatcher() {
const matcherPath = join(__dirname, "llvm_matcher.json") if (GITHUB_ACTIONS) {
if (!(await pathExists(matcherPath))) { const matcherPath = join(__dirname, "llvm_matcher.json")
return warning("the llvm_matcher.json file does not exist in the same folder as setup-cpp.js") if (!(await pathExists(matcherPath))) {
return warning("the llvm_matcher.json file does not exist in the same folder as setup-cpp.js")
}
info(`::add-matcher::${matcherPath}`)
} }
info(`::add-matcher::${matcherPath}`)
} }

View File

@ -67,6 +67,8 @@ export const VERSIONS: Set<string> = getVersions([
"16.0.2", "16.0.2",
"16.0.3", "16.0.3",
"16.0.4", "16.0.4",
"16.0.5",
"16.0.6",
]) ])
/** The LLVM versions that were never released for the Windows platform. */ /** The LLVM versions that were never released for the Windows platform. */
@ -97,6 +99,8 @@ const DARWIN_MISSING = new Set([
"16.0.2", "16.0.2",
"16.0.3", "16.0.3",
"16.0.4", "16.0.4",
"16.0.5",
"16.0.6",
]) ])
/** /**
@ -161,7 +165,7 @@ const UBUNTU_SUFFIX_MAP: { [key: string]: string } = {
} }
/** The latest supported LLVM version for the Linux (Ubuntu) platform. */ /** The latest supported LLVM version for the Linux (Ubuntu) platform. */
const MAX_UBUNTU: string = "15.0.6" const MAX_UBUNTU: string = "16.0.4"
//================================================ //================================================
// URL // URL

View File

@ -23,7 +23,7 @@ export async function setupMake(version: string, _setupDir: string, _arch: strin
if (isArch()) { if (isArch()) {
return setupPacmanPack("make", version) return setupPacmanPack("make", version)
} else if (hasDnf()) { } else if (hasDnf()) {
return setupDnfPack("make", version) return setupDnfPack([{ name: "make", version }])
} else if (isUbuntu()) { } else if (isUbuntu()) {
return setupAptPack([{ name: "make", version }]) return setupAptPack([{ name: "make", version }])
} }

View File

@ -2,7 +2,7 @@ import { dirname } from "patha"
import which from "which" import which from "which"
import { isUbuntu } from "../utils/env/isUbuntu" import { isUbuntu } from "../utils/env/isUbuntu"
import { execRootSync } from "admina" import { execRootSync } from "admina"
import { addAptKeyViaDownload, setupAptPack } from "../utils/setup/setupAptPack" import { addAptKeyViaDownload, hasNala, setupAptPack } from "../utils/setup/setupAptPack"
let binDir: string | undefined let binDir: string | undefined
@ -45,3 +45,10 @@ export async function setupNala(version: string, _setupDir: string, _arch: strin
return { binDir } return { binDir }
} }
export function bashWithNala(script: string) {
if (hasNala()) {
return `apt-get() { nala $@; }; export -f apt-get; ${script}; unset -f apt-get`
}
return script
}

View File

@ -26,12 +26,12 @@ export async function setupPowershell(version: string | undefined, _setupDir: st
if (isArch()) { if (isArch()) {
return setupPacmanPack("powershell-bin", version, "yay") return setupPacmanPack("powershell-bin", version, "yay")
} else if (hasDnf()) { } else if (hasDnf()) {
setupDnfPack("curl") setupDnfPack([{ name: "curl" }])
execRootSync("/bin/bash", [ execRootSync("/bin/bash", [
"-c", "-c",
`curl https://packages.microsoft.com/config/rhel/8/prod.repo | sudo tee /etc/yum.repos.d/microsoft.repo`, `curl https://packages.microsoft.com/config/rhel/8/prod.repo | sudo tee /etc/yum.repos.d/microsoft.repo`,
]) ])
return setupDnfPack("powershell", version) return setupDnfPack([{ name: "powershell", version }])
} else if (isUbuntu()) { } else if (isUbuntu()) {
await setupAptPack([{ name: "curl" }]) await setupAptPack([{ name: "curl" }])
const ubuntuVerSplitted = (await ubuntuVersion())! const ubuntuVerSplitted = (await ubuntuVersion())!

View File

@ -112,7 +112,7 @@ async function setupPythonSystem(setupDir: string, version: string) {
if (isArch()) { if (isArch()) {
installInfo = await setupPacmanPack("python", version) installInfo = await setupPacmanPack("python", version)
} else if (hasDnf()) { } else if (hasDnf()) {
installInfo = setupDnfPack("python3", version) installInfo = await setupDnfPack([{ name: "python3", version }])
} else if (isUbuntu()) { } else if (isUbuntu()) {
installInfo = await setupAptPack([{ name: "python3", version }, { name: "python-is-python3" }]) installInfo = await setupAptPack([{ name: "python3", version }, { name: "python-is-python3" }])
} else { } else {
@ -234,7 +234,7 @@ function setupPipSystem() {
if (isArch()) { if (isArch()) {
return setupPacmanPack("python-pip") return setupPacmanPack("python-pip")
} else if (hasDnf()) { } else if (hasDnf()) {
return setupDnfPack("python3-pip") return setupDnfPack([{ name: "python3-pip" }])
} else if (isUbuntu()) { } else if (isUbuntu()) {
return setupAptPack([{ name: "python3-pip" }]) return setupAptPack([{ name: "python3-pip" }])
} }

View File

@ -20,8 +20,10 @@ export function setupSevenZip(version: string, _setupDir: string, _arch: string)
if (isArch()) { if (isArch()) {
return setupPacmanPack("p7zip", version) return setupPacmanPack("p7zip", version)
} else if (hasDnf()) { } else if (hasDnf()) {
setupDnfPack("p7zip", version) return setupDnfPack([
return setupDnfPack("p7zip-plugins", version) { name: "p7zip", version },
{ name: "p7zip-plugins", version },
])
} else if (isUbuntu()) { } else if (isUbuntu()) {
return setupAptPack([{ name: "p7zip-full", version }]) return setupAptPack([{ name: "p7zip-full", version }])
} }

View File

@ -2,13 +2,13 @@ import { InstallationInfo } from "./setupBin"
import { execRoot, execRootSync } from "admina" import { execRoot, execRootSync } from "admina"
import { GITHUB_ACTIONS } from "ci-info" import { GITHUB_ACTIONS } from "ci-info"
import { addEnv, cpprc_path, setupCppInProfile } from "../env/addEnv" import { addEnv, cpprc_path, setupCppInProfile } from "../env/addEnv"
import which from "which"
import { pathExists } from "path-exists" import { pathExists } from "path-exists"
import { promises as fsPromises } from "fs" import { promises as fsPromises } from "fs"
const { appendFile } = fsPromises const { appendFile } = fsPromises
import { execa } from "execa" import { execa, ExecaError } from "execa"
import escapeRegex from "escape-string-regexp" import escapeRegex from "escape-string-regexp"
import { warning, info } from "ci-log" import { warning, info } from "ci-log"
import which from "which"
/* eslint-disable require-atomic-updates */ /* eslint-disable require-atomic-updates */
let didUpdate: boolean = false let didUpdate: boolean = false
@ -52,12 +52,31 @@ export async function setupAptPack(packages: AptPackage[], update = false): Prom
} }
const aptArgs = await Promise.all(packages.map((pack) => getAptArg(pack.name, pack.version))) const aptArgs = await Promise.all(packages.map((pack) => getAptArg(pack.name, pack.version)))
execRootSync(apt, ["install", "--fix-broken", "-y", ...aptArgs]) try {
execRootSync(apt, ["install", "--fix-broken", "-y", ...aptArgs])
} catch (err) {
if ("stderr" in (err as ExecaError)) {
const stderr = (err as ExecaError).stderr
if (stderr.includes("E: Could not get lock") || stderr.includes("dpkg: error processing archive")) {
warning(`Failed to install packages ${aptArgs}. Retrying...`)
execRootSync(apt, ["install", "--fix-broken", "-y", ...aptArgs])
}
} else {
throw err
}
}
return { binDir: "/usr/bin/" } return { binDir: "/usr/bin/" }
} }
async function getAptArg(name: string, version: string | undefined) { export enum AptPackageType {
NameDashVersion,
NameEqualsVersion,
Name,
None,
}
export async function aptPackageType(name: string, version: string | undefined): Promise<AptPackageType> {
if (version !== undefined && version !== "") { if (version !== undefined && version !== "") {
const { stdout } = await execa("apt-cache", [ const { stdout } = await execa("apt-cache", [
"search", "search",
@ -65,26 +84,55 @@ async function getAptArg(name: string, version: string | undefined) {
`^${escapeRegex(name)}-${escapeRegex(version)}$`, `^${escapeRegex(name)}-${escapeRegex(version)}$`,
]) ])
if (stdout.trim() !== "") { if (stdout.trim() !== "") {
return `${name}-${version}` return AptPackageType.NameDashVersion
} else { }
try {
// check if apt-get show can find the version try {
const { stdout: showStdout } = await execa("apt-cache", ["show", `${name}=${version}`]) // check if apt-get show can find the version
if (showStdout.trim() === "") { // eslint-disable-next-line @typescript-eslint/no-shadow
return `${name}=${version}` const { stdout } = await execa("apt-cache", ["show", `${name}=${version}`])
} if (stdout.trim() === "") {
} catch { return AptPackageType.NameEqualsVersion
// ignore
} }
warning(`Failed to install ${name} ${version} via apt, trying without version`) } catch {
// ignore
} }
} }
return name
try {
const { stdout: showStdout } = await execa("apt-cache", ["show", name])
if (showStdout.trim() !== "") {
return AptPackageType.Name
}
} catch {
// ignore
}
return AptPackageType.None
}
async function getAptArg(name: string, version: string | undefined) {
const package_type = await aptPackageType(name, version)
switch (package_type) {
case AptPackageType.NameDashVersion:
return `${name}-${version}`
case AptPackageType.NameEqualsVersion:
return `${name}=${version}`
case AptPackageType.Name:
return name
case AptPackageType.None:
default:
throw new Error(`Could not find package ${name} ${version ?? ""}`)
}
}
export function hasNala() {
return which.sync("nala", { nothrow: true }) !== null
} }
function getApt() { function getApt() {
let apt: string let apt: string
if (which.sync("nala", { nothrow: true }) !== null) { if (hasNala()) {
apt = "nala" apt = "nala"
} else { } else {
apt = "apt-get" apt = "apt-get"

View File

@ -107,9 +107,7 @@ export async function setupBin(
if (isArch()) { if (isArch()) {
await Promise.all([setupPacmanPack("unzip"), setupPacmanPack("tar"), setupPacmanPack("xz")]) await Promise.all([setupPacmanPack("unzip"), setupPacmanPack("tar"), setupPacmanPack("xz")])
} else if (hasDnf()) { } else if (hasDnf()) {
setupDnfPack("unzip") await setupDnfPack([{ name: "unzip" }, { name: "tar" }, { name: "xz" }])
setupDnfPack("tar")
setupDnfPack("xz")
} else if (isUbuntu()) { } else if (isUbuntu()) {
await setupAptPack([{ name: "unzip" }, { name: "tar" }, { name: "xz-utils" }]) await setupAptPack([{ name: "unzip" }, { name: "tar" }, { name: "xz-utils" }])
} }

View File

@ -1,31 +1,42 @@
/* eslint-disable require-atomic-updates */
import { InstallationInfo } from "./setupBin" import { InstallationInfo } from "./setupBin"
import { execRootSync } from "admina" import { execRootSync } from "admina"
import { info, warning } from "ci-log" import { info, warning } from "ci-log"
import { execa } from "execa"
// let didUpdate: boolean = false type DnfPackage = {
name: string
version?: string
}
/** A function that installs a package using dnf */ /** A function that installs a package using dnf */
export function setupDnfPack(name: string, version?: string): InstallationInfo { export async function setupDnfPack(packages: DnfPackage[]): Promise<InstallationInfo> {
info(`Installing ${name} ${version ?? ""} via dnf`) for (const { name, version } of packages) {
info(`Installing ${name} ${version ?? ""} via dnf`)
const dnf = "dnf"
// if (!didUpdate) {
// execRootSync(dnf, ["-y", "check-update"])
// didUpdate = true
// }
if (version !== undefined && version !== "") {
try {
execRootSync(dnf, ["-y", "install", `${name}-${version}`])
} catch (err) {
warning(`${(err as Error).toString()}\nInstalling the default version available via dnf`)
execRootSync(dnf, ["-y", "install", name])
}
} else {
execRootSync(dnf, ["-y", "install", name])
} }
const dnfArgs = await Promise.all(packages.map((pack) => getDnfArg(pack.name, pack.version)))
execRootSync("dnf", ["-y", "install", ...dnfArgs])
return { binDir: "/usr/bin/" } return { binDir: "/usr/bin/" }
} }
async function getDnfArg(name: string, version: string | undefined) {
if (version !== undefined && version !== "") {
// check if name-version is available
const { stdout } = await execa("dnf", ["search", "-q", `${name}-${version}`])
if (stdout.trim() !== "") {
return `${name}-${version}`
} else {
// try with ${name}${version}
// eslint-disable-next-line @typescript-eslint/no-shadow
const { stdout } = await execa("dnf", ["search", "-q", `${name}${version}`])
if (stdout.trim() !== "") {
return `${name}${version}`
}
warning(`Failed to install ${name} ${version} via dnf, trying without version`)
}
}
return name
}

View File

@ -35,20 +35,24 @@ export async function testBin(
args: string[] | null = ["--version"], args: string[] | null = ["--version"],
binDir: string | undefined = undefined binDir: string | undefined = undefined
) { ) {
let bin = name try {
if (typeof binDir === "string") { let bin = name
console.log(`Testing the existence of ${binDir}`) if (typeof binDir === "string") {
expect(binDir).toBeDefined() console.log(`Testing the existence of ${binDir}`)
expect(binDir).not.toHaveLength(0) expect(binDir).toBeDefined()
expect(await pathExists(binDir)).toBeTruthy() expect(binDir).not.toHaveLength(0)
bin = join(binDir, addExeExt(name)) expect(await pathExists(binDir)).toBeTruthy()
} bin = join(binDir, addExeExt(name))
}
if (args !== null) { if (args !== null) {
console.log(`Running ${bin} ${args.join(" ")}`) console.log(`Running ${bin} ${args.join(" ")}`)
const { status } = spawn.sync(bin, args, { stdio: "inherit" }) const { status } = spawn.sync(bin, args, { stdio: "inherit" })
expect(status).toBe(0) expect(status).toBe(0)
} }
expect((await io.which(name, true)).includes(bin)) expect((await io.which(name, true)).includes(bin))
} catch (err) {
throw new Error(`Failed to test bin ${name}: ${err}`)
}
} }

View File

@ -30,12 +30,14 @@ export async function setupVcpkg(_version: string, setupDir: string, _arch: stri
setupPacmanPack("pkg-config"), setupPacmanPack("pkg-config"),
]) ])
} else if (hasDnf()) { } else if (hasDnf()) {
setupDnfPack("curl") await setupDnfPack([
setupDnfPack("zip") { name: "curl" },
setupDnfPack("unzip") { name: "zip" },
setupDnfPack("tar") { name: "unzip" },
setupDnfPack("git") { name: "tar" },
setupDnfPack("pkg-config") { name: "git" },
{ name: "pkg-config" },
])
} else if (isUbuntu()) { } else if (isUbuntu()) {
await setupAptPack([ await setupAptPack([
{ name: "curl" }, { name: "curl" },

View File

@ -4,7 +4,6 @@ import { DefaultLinuxVersion, DefaultVersions } from "./default_versions"
/** 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) {
console.log("isDefault", version, name, isVersionDefault(version))
if (isVersionDefault(version) && process.platform === "linux" && osVersion !== null && name in DefaultLinuxVersion) { if (isVersionDefault(version) && process.platform === "linux" && osVersion !== null && name in DefaultLinuxVersion) {
return getDefaultLinuxVersion(osVersion, DefaultLinuxVersion[name]!) return getDefaultLinuxVersion(osVersion, DefaultLinuxVersion[name]!)
} else if (isVersionDefault(version) && name in DefaultVersions) { } else if (isVersionDefault(version) && name in DefaultVersions) {