diff --git a/docs/cmake-settings.md b/docs/cmake-settings.md index 09e49f14e7..3896edc9b4 100644 --- a/docs/cmake-settings.md +++ b/docs/cmake-settings.md @@ -43,6 +43,12 @@ Some options support the replacement of special values in their string value by |`${workspaceRootFolderName}`| The name of the leaf directory in the workspace directory path.| |`${buildType}`|The current CMake build type. For example: `Debug`, `Release`, `MinSizeRel`| |`${buildKit}`| The current CMake kit name. For example: `GCC 7.3.0`| +|`${buildKitVendor}`| The current CMake kit vendor. Possible values: `gnu|msvc|llvm` and so on, all lowercase| +|`${buildKitHostOs}`| The current CMake kit host OS. Possible values: `win32|osx|linux` and so on, all lowercase| +|`${buildKitTargetOs}`| The current CMake kit target OS. Possible values: `win32|osx|linux` and so on, all lowercase| +|`${buildKitTargetArch}`| The current CMake kit target architecture. Possible values `x86,x64,arm,aarch64` and so on, all lowercase| +|`${buildKitVersionMajor}`| The current CMake kit major version. For example: `7`| +|`${buildKitVersionMinor}`| The current CMake kit minor version. For example: `3`| |`${generator}`| The name of the CMake generator. For example: `Ninja`| |`${projectName}`|**DEPRECATED**. Expands to the constant string `"ProjectName"` CMake does not consider there to be just one project name to use. The concept of a single project does not work in CMake. Use `${workspaceRootFolderName}`, instead.| |`${userHome}`| The full path to the current user's home directory. | diff --git a/schemas/kits-schema.json b/schemas/kits-schema.json index 9d18aa00b6..21d65a301b 100644 --- a/schemas/kits-schema.json +++ b/schemas/kits-schema.json @@ -10,6 +10,30 @@ "type": "string", "description": "Name of this kit" }, + "vendor": { + "type": "string", + "description": "The vendor of this kit" + }, + "hostOs": { + "type": "string", + "description": "The host OS of this kit" + }, + "targetOs": { + "type": "string", + "description": "The target OS of this kit" + }, + "targetArch": { + "type": "string", + "description": "The target architecture of this kit" + }, + "versionMajor": { + "type": "string", + "description": "The major version of this kit" + }, + "versionMinor": { + "type": "string", + "description": "The minor version of this kit" + }, "keep": { "type": "boolean", "description": "If `true`, this kit will be kept even if it appears out-of-date." diff --git a/src/drivers/driver.ts b/src/drivers/driver.ts index 8f6e2594b1..9d362dfb97 100644 --- a/src/drivers/driver.ts +++ b/src/drivers/driver.ts @@ -218,6 +218,12 @@ export abstract class CMakeDriver implements vscode.Disposable { generator: this.generatorName || 'null', userHome: paths.userHome, buildKit: this._kit ? this._kit.name : '__unknownkit__', + buildKitVendor: this?._kit?.vendor ?? '__unknow_vendor__', + buildKitHostOs: this?._kit?.hostOs ?? '__unknow_host_os__', + buildKitTargetOs: this?._kit?.targetOs ?? '__unknow_target_os__', + buildKitTargetArch: this?._kit?.targetArch ?? '__unknow_target_arch__', + buildKitVersionMajor: this?._kit?.versionMajor ?? '__unknow_version_major__', + buildKitVersionMinor: this?._kit?.versionMinor ?? '__unknow_version_minor__', // DEPRECATED EXPANSION: Remove this in the future: projectName: 'ProjectName', }; diff --git a/src/expand.ts b/src/expand.ts index 9a5c8e6e4e..f63c010a94 100644 --- a/src/expand.ts +++ b/src/expand.ts @@ -27,6 +27,12 @@ export interface RequiredExpansionContextVars { workspaceFolder: string; buildType: string; buildKit: string; + buildKitVendor: string; + buildKitHostOs: string; + buildKitTargetOs: string; + buildKitTargetArch: string; + buildKitVersionMajor: string; + buildKitVersionMinor: string; workspaceRootFolderName: string; workspaceFolderBasename: string; generator: string; diff --git a/src/kit.ts b/src/kit.ts index 2de9ff2a72..bc7fd3a520 100644 --- a/src/kit.ts +++ b/src/kit.ts @@ -17,6 +17,7 @@ import {fs} from './pr'; import * as proc from './proc'; import {loadSchema} from './schema'; import {compare, dropNulls, objectPairs, Ordering} from './util'; +import { findTargetTriple, parseTargetTriple, TargetTriple } from './triple'; import * as nls from 'vscode-nls'; nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })(); @@ -62,6 +63,36 @@ export interface Kit { */ name: string; + /** + * The vendor of the kit + */ + vendor?: string; + + /** + * The host OS of the kit + */ + hostOs?: string; + + /** + * The target OS of the kit + */ + targetOs?: string; + + /** + * The target architecture of the kit + */ + targetArch?: string; + + /** + * The major version of the kit + */ + versionMajor?: string; + + /** + * The minor version of the kit + */ + versionMinor?: string; + /** * The preferred CMake generator for this kit */ @@ -116,14 +147,15 @@ export interface Kit { interface ClangVersion { fullVersion: string; version: string; - target?: string; + target: TargetTriple; threadModel?: string; installedDir?: string; } async function getClangVersion(binPath: string): Promise { log.debug(localize('testing.clang.binary', 'Testing Clang binary: {0}', binPath)); - const exec = await proc.execute(binPath, ['-v']).result; + const execOption: proc.ExecutionOptions = { environment: {LC_ALL : 'C' } }; + const exec = await proc.execute(binPath, ['-v'], null, execOption).result; if (exec.retc !== 0) { log.debug(localize('bad.clang.binary', 'Bad Clang binary ("-v" returns non-zero): {0}', binPath)); return null; @@ -132,23 +164,22 @@ async function getClangVersion(binPath: string): Promise { const version_re = /^(?:Apple LLVM|Apple clang|clang) version ([^\s-]+)[\s-]/; let version: string = ""; let fullVersion: string = ""; + let target: TargetTriple | undefined; for (const line of lines) { const version_match = version_re.exec(line); if (version_match !== null) { version = version_match[1]; fullVersion = line; - break; + } + const target_triple_match = findTargetTriple(line); + if (target_triple_match !== null) { + target = parseTargetTriple(target_triple_match); } } - if (!version) { + if (!version || !target) { log.debug(localize('bad.clang.binary.output', 'Bad Clang binary {0} -v output: {1}', binPath, exec.stderr)); return null; } - const target_mat = /Target:\s+(.*)/.exec(exec.stderr); - let target: string|undefined; - if (target_mat) { - target = target_mat[1]; - } const thread_model_mat = /Thread model:\s+(.*)/.exec(exec.stderr); let threadModel: string|undefined; if (thread_model_mat) { @@ -168,6 +199,24 @@ async function getClangVersion(binPath: string): Promise { }; } +function majorVersionSemver(semver: string) : string { + const major_version_re = /^(\d+)./; + const major_version_match = major_version_re.exec(semver); + if (Array.isArray(major_version_match)) { + return major_version_match[1] ?? ''; + } + return ''; +} + +function minorVersionSemver(semver: string) : string { + const minor_version_re = /^(\d+).(\d+)/; + const minor_version_match = minor_version_re.exec(semver); + if (Array.isArray(minor_version_match)) { + return minor_version_match[2] ?? ''; + } + return ''; +} + /** * Convert a binary (by path) to a CompilerKit. This checks if the named binary * is a GCC or Clang compiler and gets its version. If it is not a compiler, @@ -186,7 +235,8 @@ export async function kitIfCompiler(bin: string, pr?: ProgressReporter): Promise log.debug(localize('testing.gcc.binary', 'Testing GCC binary: {0}', bin)); if (pr) pr.report({message: localize('getting.gcc.version', 'Getting GCC version for {0}', bin)}); - const exec = await proc.execute(bin, ['-v']).result; + const execOption: proc.ExecutionOptions = { environment: {LC_ALL : 'C' } }; + const exec = await proc.execute(bin, ['-v'], null, execOption).result; if (exec.retc !== 0) { log.debug(localize('bad.gcc.binary', 'Bad GCC binary ("-v" returns non-zero): {0}', bin)); return null; @@ -195,39 +245,43 @@ export async function kitIfCompiler(bin: string, pr?: ProgressReporter): Promise const compiler_version_output = exec.stderr.trim().split('\n'); const version_re = /^gcc version (.*?) .*/; let version: string = ""; + let target: TargetTriple | undefined; + for (const line of compiler_version_output) { const version_match = version_re.exec(line); if (version_match !== null) { version = version_match[1]; - break; + } + const target_triple_match = findTargetTriple(line); + if (target_triple_match !== null) { + target = parseTargetTriple(target_triple_match); } } - if (!version) { + if (!version || !target) { log.debug(localize('bad.gcc.binary.output', 'Bad GCC binary {0} -v output: {1}', bin, exec.stderr)); return null; } const gxx_fname = fname.replace(/gcc/, 'g++'); const gxx_bin = path.join(path.dirname(bin), gxx_fname); - const target_triple_re = /((\w+-)+)gcc.*/; - const target_triple_match = target_triple_re.exec(fname); - let description = ''; - if (target_triple_match !== null) { - description += `for ${target_triple_match[1].slice(0, -1)} `; - } - const name = `GCC ${description}${version}`; + const name = `GCC for ${target.triple} ${version}`; log.debug(localize('detected.gcc.compiler', 'Detected GCC compiler: {0}', bin)); - let gccKit: Kit = { - name, - compilers: { - C: bin, - } - }; - + const gccCompilers: {[lang: string]: string} = { C: bin }; if (await fs.exists(gxx_bin)) { - gccKit = {name, compilers: {C: bin, CXX: gxx_bin}}; + gccCompilers.CXX = gxx_bin; } + const gccKit: Kit = { + name, + vendor: `gnu-${target.vendor}`, + hostOs: process.platform, + targetOs: target.targetOs, + targetArch: target.targetArch, + versionMajor: majorVersionSemver(version), + versionMinor: minorVersionSemver(version), + compilers: gccCompilers + }; const isWin32 = process.platform === 'win32'; + if (isWin32 && bin.toLowerCase().includes('mingw')) { const binParentPath = path.dirname(bin); const mingwMakePath = path.join(binParentPath, 'mingw32-make.exe'); @@ -274,7 +328,7 @@ export async function kitIfCompiler(bin: string, pr?: ProgressReporter): Promise if (version === null) { return null; } - if (version.target && version.target.includes('msvc')) { + if (version.target && version.target.triple.includes('msvc')) { // DO NOT include Clang's that target MSVC but don't present the MSVC // command-line interface. CMake does not support them properly. return null; @@ -283,22 +337,20 @@ export async function kitIfCompiler(bin: string, pr?: ProgressReporter): Promise const clangxx_bin = path.join(path.dirname(bin), clangxx_fname); const name = `Clang ${version.version}`; log.debug(localize('detected.clang.compiler', 'Detected Clang compiler: {0}', bin)); + const clangCompilers: {[lang: string]: string} = { C: bin }; if (await fs.exists(clangxx_bin)) { - return { - name, - compilers: { - C: bin, - CXX: clangxx_bin, - }, - }; - } else { - return { - name, - compilers: { - C: bin, - }, - }; + clangCompilers.CXX = clangxx_bin; } + return { + name, + vendor: `clang-${version.target.vendor}}`, + hostOs: process.platform, + targetOs: version.target.targetOs, + targetArch: version.target.targetArch, + versionMajor: majorVersionSemver(version.version), + versionMinor: minorVersionSemver(version.version), + compilers: clangCompilers, + }; } else { return null; } @@ -739,6 +791,12 @@ async function tryCreateNewVCEnvironment(inst: VSInstallation, hostArch: string, const kit: Kit = { name, + vendor: 'msvc', + hostOs: process.platform, + targetOs: process.platform, + targetArch, + versionMajor: majorVersionSemver(inst.installationVersion), + versionMinor: minorVersionSemver(inst.installationVersion), visualStudio: kitVSName(inst), visualStudioArchitecture: hostArch }; @@ -805,9 +863,15 @@ async function scanDirForClangCLKits(dir: string, vsInstalls: VSInstallation[]): } return vsInstalls.map((vs): Kit => { const installName = vsDisplayName(vs); - const vs_arch = (version.target && version.target.includes('i686-pc')) ? 'x86' : 'amd64'; + const vs_arch = version.target.targetArch === 'x86_64' ? 'amd64' : 'x86' ; return { name: localize('clang.for.msvc', 'Clang {0} for MSVC with {1} ({2})', version.version, installName, vs_arch), + vendor: 'clang_msvc' + majorVersionSemver(vs.installationVersion), + hostOs: process.platform, + targetOs: version.target.targetOs, + targetArch: version.target.targetArch, + versionMajor: majorVersionSemver(version.version), + versionMinor: minorVersionSemver(version.version), visualStudio: kitVSName(vs), visualStudioArchitecture: vs_arch, compilers: { @@ -881,11 +945,29 @@ export async function effectiveKitEnvironment(kit: Kit, opts?: expand.ExpansionO } } const env = new Map(util.chain(host_env, kit_env)); - if (env.has("CMT_MINGW_PATH")) { + const isWin32 = process.platform === 'win32'; + if (isWin32) + { + const path_list: string[] = []; + if (kit.vendor?.startsWith('gnu-')) { + const cCompiler = kit.compilers?.C; + if (cCompiler) { + path_list.push(path.dirname(cCompiler)); + } + } + const cmt_mingw_path = env.get("CMT_MINGW_PATH"); + if (cmt_mingw_path) { + path_list.push(cmt_mingw_path); + } + let path_key : string | undefined = undefined; if (env.has("PATH")) { - env.set("PATH", env.get("PATH")!.concat(`;${env.get("CMT_MINGW_PATH")}`)); + path_key = "PATH"; } else if (env.has("Path")) { - env.set("Path", env.get("Path")!.concat(`;${env.get("CMT_MINGW_PATH")}`)); + path_key = "Path"; + } + if (path_key) { + path_list.unshift(env.get(path_key) ?? ''); + env.set(path_key, path_list.join(';')); } } return env; diff --git a/src/paths.ts b/src/paths.ts index 7df930286f..2f2ed4935e 100644 --- a/src/paths.ts +++ b/src/paths.ts @@ -137,6 +137,12 @@ class Paths { workspaceFolder: wsc.folder.uri.fsPath, userHome: this.userHome, buildKit: '', + buildKitVendor: '', + buildKitHostOs: '', + buildKitTargetOs: '', + buildKitTargetArch: '', + buildKitVersionMajor: '', + buildKitVersionMinor: '', buildType: '', generator: '', workspaceRootFolderName: path.basename(wsc.folder.uri.fsPath), diff --git a/src/triple.ts b/src/triple.ts new file mode 100644 index 0000000000..1fe523f503 --- /dev/null +++ b/src/triple.ts @@ -0,0 +1,180 @@ + +export interface TargetTriple { + triple: string; + targetOs: string; + targetArch: string; + vendor: string; + abi: string; + libc: string; +} + +export function findTargetTriple(line: string): string | null { + const target_triple_re = /^Target:\s+(.*)/; + const target_triple_match = target_triple_re.exec(line); + if (target_triple_match !== null) { + return target_triple_match[1]; + } + const target_triple_re_old = /.*gcc-lib[/\\]([^/\\]*)[/\\]/; + const target_triple_match_old = target_triple_re_old.exec(line); + if (target_triple_match_old !== null) { + return target_triple_match_old[1]; + } + return null; +} + +/* https://gcc.gnu.org/install/specific.html */ +const TriplePossibleArch: {[index: string]: RegExp} = { + x86: /^i[3456]86/, + aarch64: /^aarch64.*/, + amdgcn: /^amdgcn/, + arc: /^arc/, + arm: /^arm/, + avr: /^avr/, + blackfin: /^blackfin/, /* https://sourceforge.net/projects/adi-toolchain/files/2014R1/ */ + cr16: /^cr16/, + cris: /^cris/, + epiphany: /^epiphany/, + h8300: /^h8300/, + hppa: /^hppa.*/, + ia64: /^ia64/, + iq2000: /^iq2000/, + lm32: /^lm32/, + m32c: /^m32c/, + m32r: /^m32r/, + m68k: /^m68k/, + microblaze: /^microblaze/, + mips: /^mips/, + moxie: /^moxie/, + msp430: /^msp430/, + nds32le: /^nds32le/, + nds32be: /^nds32be/, + nvptx: /^nvptx/, + or1k: /^or1k/, + powerpc: /^powerpc$/, + powerpcle: /^powerpcle$/, + rl78: /^rl78/, + riscv32: /^riscv32/, + riscv64: /^riscv64/, + rx: /^rx/, + s390: /^s390$/, + s390x: /^s390x$/, + sparc: /^sparc$/, + sparc64: /^(sparc64|sparcv9)$/, + c6x: /^c6x$/, + tilegx: /^tilegx$/, + tilegxbe: /^tilegxbe$/, + tilepro: /^tilepro$/, + visium: /^visium/, + x64: /^(x86_64|amd64)/, + xtensa: /^xtensa.*/ +}; + +const TriplePossibleOS: {[index: string]: RegExp} = { + win32: /^(mingw32|mingw|mingw64|w64|msvc|windows)/, + cygwin: /^cygwin/, + msys: /^msys/, + linux: /^linux.*/, + solaris: /^solaris.*/, + darwin: /^darwin.*/, + uclinux: /^uclinux.*/, + bsd: /^(netbsd|openbsd)/, + vxworks: /^(vxworks|vxworksae)$/, + none: /^none$/, +}; + +const TriplePossibleABI: {[index: string]: RegExp} = { + elf: /^(linux.*|uclinux.*|elf|netbsd|openbsd|aix|solaris.*|gnueabi|gnueabihf)/, + marcho: /^darwin.*/, + pe: /^(mingw32|mingw|mingw64|w64|msvc|windows|cygwin|msys)/, + eabi: /^eabi$/, + eabisim: /^eabisim$/, +}; + +const TriplePossibleLibC: {[index: string]: RegExp} = { + musl: /^musl$/, + glibc: /^(gnu|msys|cygwin)$/, + msvcrt: /^msvc$/, + mingw: /^(mingw32|mingw|mingw64|w64)/, +// 'llvm': /^llvm$/, TODO:https://github.com/llvm/llvm-project/tree/master/libc/src/stdio +// otherwise system libc +}; + +export function parseTargetTriple(triple: string): TargetTriple | undefined { + const triples = triple.split("-"); + let foundArch = "unknow"; + let foundOs = 'unknow'; + let foundAbi = 'unknow'; + let foundLibc = 'unknow'; + const elementToSkip: string[] = []; + for (const tripleElement of triples) { + for (const key of Object.keys(TriplePossibleArch)) { + const archReg = TriplePossibleArch[key]; + if (archReg.exec(tripleElement) !== null) { + elementToSkip.push(tripleElement); + if (foundArch === "unknow") { + foundArch = key; + } else if (foundArch !== key) { + return undefined; + } + } + } + + for (const key of Object.keys(TriplePossibleOS)) { + const osReg = TriplePossibleOS[key]; + if (osReg.exec(tripleElement) !== null) { + elementToSkip.push(tripleElement); + if (foundOs === "unknow" || foundOs === 'none') { + foundOs = key; + } else if (foundOs !== key && key !== 'none') { + return undefined; + } + } + } + + for (const key of Object.keys(TriplePossibleABI)) { + const abiReg = TriplePossibleABI[key]; + if (abiReg.exec(tripleElement) !== null) { + elementToSkip.push(tripleElement); + if (foundAbi === "unknow") { + foundAbi = key; + } else if (foundAbi !== key) { + return undefined; + } + } + } + + for (const key of Object.keys(TriplePossibleLibC)) { + const libcReg = TriplePossibleLibC[key]; + if (libcReg.exec(tripleElement) !== null) { + elementToSkip.push(tripleElement); + if (foundLibc === "unknow") { + foundLibc = key; + } else if (foundLibc !== key) { + return undefined; + } + } + } + } + const vendors: string[] = []; + for (const tripleElement of triples) { + if (elementToSkip.indexOf(tripleElement) < 0) { + vendors.push(tripleElement); + } + } + if (vendors.length === 0) { + if (foundAbi === 'eabi') { + vendors.push('embbed'); + } else { + vendors.push('pc'); + } + } + + return { + triple, + targetOs: foundOs, + targetArch: foundArch, + vendor: vendors.join('-'), + abi: foundAbi, + libc: foundLibc === 'unknow' ? 'system' : foundLibc, + }; +} diff --git a/test/extension-tests/successful-build/test/variable-substitution.test.ts b/test/extension-tests/successful-build/test/variable-substitution.test.ts index 7b3efa6d30..025ac1bfc6 100644 --- a/test/extension-tests/successful-build/test/variable-substitution.test.ts +++ b/test/extension-tests/successful-build/test/variable-substitution.test.ts @@ -98,6 +98,23 @@ suite('[Variable Substitution]', async () => { expect(typeof cacheEntry.value).to.eq('string', '[buildKit] unexpected cache entry value type'); }).timeout(100000); + test('Check substitution for "buildKitVendor"', async () => { + // Set fake settings + testEnv.config.updatePartial({configureSettings: {buildKitVendor: '${buildKitVendor}'}}); + + // Configure + expect(await cmt.configure()).to.be.eq(0, '[buildKitVendor] configure failed'); + expect(testEnv.projectFolder.buildDirectory.isCMakeCachePresent).to.eql(true, 'expected cache not present'); + const cache = await CMakeCache.fromPath(await cmt.cachePath); + + const cacheEntry = cache.get('buildKitVendor') as api.CacheEntry; + expect(cacheEntry.type).to.eq(api.CacheEntryType.String, '[buildKitVendor] unexpected cache entry type'); + expect(cacheEntry.key).to.eq('buildKitVendor', '[buildKitVendor] unexpected cache entry key name'); + const kit = cmt.activeKit; + expect(cacheEntry.as()).to.eq(kit!.vendor, '[buildKitVendor] substitution incorrect'); + expect(typeof cacheEntry.value).to.eq('string', '[buildKitVendor] unexpected cache entry value type'); + }).timeout(100000); + test('Check substitution for "workspaceRootFolderName"', async () => { // Set fake settings testEnv.config.updatePartial({configureSettings: {workspaceRootFolderName: '${workspaceRootFolderName}'}}); diff --git a/test/unit-tests/kit-scan.test.ts b/test/unit-tests/kit-scan.test.ts index 52baf839cf..bf7723cfd4 100644 --- a/test/unit-tests/kit-scan.test.ts +++ b/test/unit-tests/kit-scan.test.ts @@ -6,6 +6,7 @@ chai.use(chaiAsPromised); import {expect} from 'chai'; import * as kit from '../../src/kit'; +import * as triple from '../../src/triple'; import {fs} from '../../src/pr'; // tslint:disable:no-unused-expression @@ -36,6 +37,33 @@ suite('Kits scan test', async () => { } }); + test('gcc target triple match', () => { + expect(triple.findTargetTriple('Reading specs from ../lib/gcc-lib/powerpc-wrs-vxworks/gcc-2.96/specs')) + .to.equal('powerpc-wrs-vxworks'); + expect(triple.findTargetTriple(`Reading specs from C:\\WindRiver-VxWorks653-2.2.0.0\\gnu\\3.3.2-vxworks653\\x86-win32\\bin\..\\lib\\gcc-lib\\powerpc-wrs-vxworksae\\3.3.2\\specs`)) + .to.equal('powerpc-wrs-vxworksae'); + expect(triple.findTargetTriple('Target: x86_64-linux-gnu')) + .to.equal('x86_64-linux-gnu'); + expect(triple.findTargetTriple('Target: x86_64-alpine-linux-musl')) + .to.equal('x86_64-alpine-linux-musl'); + expect(triple.findTargetTriple('Target: powerpc-wrs-vxworks')) + .to.equal('powerpc-wrs-vxworks'); + expect(triple.findTargetTriple('Target: x86_64-w64-mingw32')) + .to.equal('x86_64-w64-mingw32'); + expect(triple.findTargetTriple('Target: i686-w64-mingw32')) + .to.equal('i686-w64-mingw32'); + expect(triple.findTargetTriple('Target: x86_64-pc-msys')) + .to.equal('x86_64-pc-msys'); + expect(triple.findTargetTriple('Target: x86_64-pc-windows-msvc')) + .to.equal('x86_64-pc-windows-msvc'); + expect(triple.findTargetTriple('Target: arm-none-eabi')) + .to.equal('arm-none-eabi'); + expect(triple.findTargetTriple('Target: arm-none-linux-gnueabi')) + .to.equal('arm-none-linux-gnueabi'); + expect(triple.findTargetTriple('Target: arm-linux-gnueabihf')) + .to.equal('arm-linux-gnueabihf'); + }); + test('Detect system kits never throws', async () => { // Don't care about the result, just check that we don't throw during the test