feat(cli): auto choose the tooling for cross compiling (#1367)
This commit is contained in:
parent
035def0600
commit
696c2ddcd8
4 changed files with 233 additions and 22 deletions
|
@ -3,6 +3,36 @@
|
|||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.13.0-alpha.6](https://github.com/napi-rs/napi-rs/compare/@napi-rs/cli@2.13.0-alpha.5...@napi-rs/cli@2.13.0-alpha.6) (2022-11-20)
|
||||
|
||||
**Note:** Version bump only for package @napi-rs/cli
|
||||
|
||||
# [2.13.0-alpha.5](https://github.com/napi-rs/napi-rs/compare/@napi-rs/cli@2.13.0-alpha.4...@napi-rs/cli@2.13.0-alpha.5) (2022-11-20)
|
||||
|
||||
**Note:** Version bump only for package @napi-rs/cli
|
||||
|
||||
# [2.13.0-alpha.4](https://github.com/napi-rs/napi-rs/compare/@napi-rs/cli@2.13.0-alpha.3...@napi-rs/cli@2.13.0-alpha.4) (2022-11-20)
|
||||
|
||||
**Note:** Version bump only for package @napi-rs/cli
|
||||
|
||||
# [2.13.0-alpha.3](https://github.com/napi-rs/napi-rs/compare/@napi-rs/cli@2.13.0-alpha.2...@napi-rs/cli@2.13.0-alpha.3) (2022-11-20)
|
||||
|
||||
**Note:** Version bump only for package @napi-rs/cli
|
||||
|
||||
# [2.13.0-alpha.2](https://github.com/napi-rs/napi-rs/compare/@napi-rs/cli@2.13.0-alpha.1...@napi-rs/cli@2.13.0-alpha.2) (2022-11-17)
|
||||
|
||||
**Note:** Version bump only for package @napi-rs/cli
|
||||
|
||||
# [2.13.0-alpha.1](https://github.com/napi-rs/napi-rs/compare/@napi-rs/cli@2.13.0-alpha.0...@napi-rs/cli@2.13.0-alpha.1) (2022-11-17)
|
||||
|
||||
**Note:** Version bump only for package @napi-rs/cli
|
||||
|
||||
# [2.13.0-alpha.0](https://github.com/napi-rs/napi-rs/compare/@napi-rs/cli@2.12.1...@napi-rs/cli@2.13.0-alpha.0) (2022-11-17)
|
||||
|
||||
### Features
|
||||
|
||||
- **cli:** auto choose the tooling for cross compiling ([7faf4fc](https://github.com/napi-rs/napi-rs/commit/7faf4fc4cc3b2e9dc47c892a9acf9bcf7e0571ad))
|
||||
|
||||
## [2.12.1](https://github.com/napi-rs/napi-rs/compare/@napi-rs/cli@2.12.0...@napi-rs/cli@2.12.1) (2022-11-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@napi-rs/cli",
|
||||
"version": "2.12.1",
|
||||
"version": "2.13.0-alpha.6",
|
||||
"description": "Cli tools for napi-rs",
|
||||
"keywords": [
|
||||
"cli",
|
||||
|
|
56
cli/src/arm-features.h.ts
Normal file
56
cli/src/arm-features.h.ts
Normal file
|
@ -0,0 +1,56 @@
|
|||
export const ARM_FEATURES_H = `/* Macros to test for CPU features on ARM. Generic ARM version.
|
||||
Copyright (C) 2012-2022 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library. If not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _ARM_ARM_FEATURES_H
|
||||
#define _ARM_ARM_FEATURES_H 1
|
||||
|
||||
/* An OS-specific arm-features.h file should define ARM_HAVE_VFP to
|
||||
an appropriate expression for testing at runtime whether the VFP
|
||||
hardware is present. We'll then redefine it to a constant if we
|
||||
know at compile time that we can assume VFP. */
|
||||
|
||||
#ifndef __SOFTFP__
|
||||
/* The compiler is generating VFP instructions, so we're already
|
||||
assuming the hardware exists. */
|
||||
# undef ARM_HAVE_VFP
|
||||
# define ARM_HAVE_VFP 1
|
||||
#endif
|
||||
|
||||
/* An OS-specific arm-features.h file may define ARM_ASSUME_NO_IWMMXT
|
||||
to indicate at compile time that iWMMXt hardware is never present
|
||||
at runtime (or that we never care about its state) and so need not
|
||||
be checked for. */
|
||||
|
||||
/* A more-specific arm-features.h file may define ARM_ALWAYS_BX to indicate
|
||||
that instructions using pc as a destination register must never be used,
|
||||
so a "bx" (or "blx") instruction is always required. */
|
||||
|
||||
/* The log2 of the minimum alignment required for an address that
|
||||
is the target of a computed branch (i.e. a "bx" instruction).
|
||||
A more-specific arm-features.h file may define this to set a more
|
||||
stringent requirement.
|
||||
Using this only makes sense for code in ARM mode (where instructions
|
||||
always have a fixed size of four bytes), or for Thumb-mode code that is
|
||||
specifically aligning all the related branch targets to match (since
|
||||
Thumb instructions might be either two or four bytes). */
|
||||
#ifndef ARM_BX_ALIGN_LOG2
|
||||
# define ARM_BX_ALIGN_LOG2 2
|
||||
#endif
|
||||
|
||||
/* An OS-specific arm-features.h file may define ARM_NO_INDEX_REGISTER to
|
||||
indicate that the two-register addressing modes must never be used. */
|
||||
|
||||
#endif /* arm-features.h */
|
||||
`
|
167
cli/src/build.ts
167
cli/src/build.ts
|
@ -8,6 +8,7 @@ import { Command, Option } from 'clipanion'
|
|||
import envPaths from 'env-paths'
|
||||
import { groupBy } from 'lodash-es'
|
||||
|
||||
import { ARM_FEATURES_H } from './arm-features.h'
|
||||
import { getNapiConfig } from './consts'
|
||||
import { debugFactory } from './debug'
|
||||
import { createJsBinding } from './js-binding-template'
|
||||
|
@ -34,8 +35,14 @@ const ZIG_PLATFORM_TARGET_MAP = {
|
|||
'aarch64-apple-darwin': 'aarch64-macos',
|
||||
'aarch64-unknown-linux-gnu': 'aarch64-linux-gnu',
|
||||
'aarch64-unknown-linux-musl': 'aarch64-linux-musl',
|
||||
'armv7-unknown-linux-gnueabihf': 'arm-linux-gnueabihf',
|
||||
}
|
||||
|
||||
const DEFAULT_GLIBC_TARGET = process.env.GLIBC_ABI_TARGET ?? '2.17'
|
||||
|
||||
const SHEBANG_NODE = process.platform === 'win32' ? '' : '#!/usr/bin/env node\n'
|
||||
const SHEBANG_SH = process.platform === 'win32' ? '' : '#!/usr/bin/env sh\n'
|
||||
|
||||
function processZigLinkerArgs(platform: string, args: string[]) {
|
||||
if (platform.includes('apple')) {
|
||||
const newArgs = args.filter(
|
||||
|
@ -158,7 +165,7 @@ export class BuildCommand extends Command {
|
|||
'lto',
|
||||
)} and increase ${chalk.green(
|
||||
'codegen-units',
|
||||
)}. Enabled by default. See ${chalk.underline.blue(
|
||||
)}. Disabled by default. See ${chalk.underline.blue(
|
||||
'https://github.com/napi-rs/napi-rs/issues/297',
|
||||
)}`,
|
||||
},
|
||||
|
@ -256,11 +263,29 @@ export class BuildCommand extends Command {
|
|||
]
|
||||
.filter((flag) => Boolean(flag))
|
||||
.join(' ')
|
||||
const cargo = process.env.CARGO ?? 'cargo'
|
||||
const additionalEnv = {}
|
||||
const isCrossForWin =
|
||||
triple.platform === 'win32' && process.platform !== 'win32'
|
||||
const isCrossForLinux =
|
||||
triple.platform === 'linux' &&
|
||||
(process.platform !== 'linux' ||
|
||||
triple.arch !== process.arch ||
|
||||
(function () {
|
||||
const glibcVersionRuntime =
|
||||
// @ts-expect-error
|
||||
process.report?.getReport()?.header?.glibcVersionRuntime
|
||||
const libc = glibcVersionRuntime ? 'gnu' : 'musl'
|
||||
return triple.abi !== libc
|
||||
})())
|
||||
const isCrossForMacOS =
|
||||
triple.platform === 'darwin' && process.platform !== 'darwin'
|
||||
const cargo = process.env.CARGO ?? isCrossForWin ? 'cargo-xwin' : 'cargo'
|
||||
if (isCrossForWin && triple.arch === 'ia32') {
|
||||
additionalEnv['XWIN_ARCH'] = 'x86'
|
||||
}
|
||||
const cargoCommand = `${cargo} build ${externalFlags}`
|
||||
const intermediateTypeFile = join(tmpdir(), `type_def.${Date.now()}.tmp`)
|
||||
debug(`Run ${chalk.green(cargoCommand)}`)
|
||||
const additionalEnv = {}
|
||||
|
||||
const rustflags = process.env.RUSTFLAGS
|
||||
? process.env.RUSTFLAGS.split(' ')
|
||||
|
@ -278,16 +303,39 @@ export class BuildCommand extends Command {
|
|||
if (rustflags.length > 0) {
|
||||
additionalEnv['RUSTFLAGS'] = rustflags.join(' ')
|
||||
}
|
||||
let isZigExisted = false
|
||||
if (isCrossForLinux || isCrossForMacOS) {
|
||||
try {
|
||||
execSync('zig version')
|
||||
isZigExisted = true
|
||||
} catch (e) {
|
||||
if (this.useZig) {
|
||||
throw new TypeError(
|
||||
`Could not find ${chalk.green('zig')} on the PATH`,
|
||||
)
|
||||
} else {
|
||||
debug(
|
||||
`Could not find ${chalk.green(
|
||||
'zig',
|
||||
)} on the PATH, fallback to normal linker`,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.useZig) {
|
||||
if ((this.useZig || isCrossForLinux || isCrossForMacOS) && isZigExisted) {
|
||||
const zigABIVersion =
|
||||
this.zigABIVersion ?? (isCrossForLinux && triple.abi === 'gnu')
|
||||
? DEFAULT_GLIBC_TARGET
|
||||
: null
|
||||
const zigTarget = `${ZIG_PLATFORM_TARGET_MAP[triple.raw]}${
|
||||
this.zigABIVersion ? `.${this.zigABIVersion}` : ''
|
||||
zigABIVersion ? `.${zigABIVersion}` : ''
|
||||
}`
|
||||
if (!zigTarget) {
|
||||
throw new Error(`${triple.raw} can not be cross compiled by zig`)
|
||||
}
|
||||
const paths = envPaths('napi-rs')
|
||||
const shellFileExt = process.platform === 'win32' ? 'bat' : 'sh'
|
||||
const shellFileExt = process.platform === 'win32' ? 'cmd' : 'sh'
|
||||
const linkerWrapperShell = join(
|
||||
paths.cache,
|
||||
`zig-linker-${triple.raw}.${shellFileExt}`,
|
||||
|
@ -302,31 +350,43 @@ export class BuildCommand extends Command {
|
|||
)
|
||||
const linkerWrapper = join(paths.cache, `zig-cc-${triple.raw}.js`)
|
||||
mkdirSync(paths.cache, { recursive: true })
|
||||
const forwardArgs = process.platform === 'win32' ? '%*' : '$@'
|
||||
const forwardArgs = process.platform === 'win32' ? '"%*"' : '$@'
|
||||
if (triple.arch === 'arm') {
|
||||
await patchArmFeaturesHForArmTargets()
|
||||
}
|
||||
await writeFileAsync(
|
||||
linkerWrapperShell,
|
||||
`#!/bin/sh\nnode ${linkerWrapper} ${forwardArgs}`,
|
||||
process.platform === 'win32'
|
||||
? `@IF EXIST "%~dp0\\node.exe" (
|
||||
"%~dp0\\node.exe" "${linkerWrapper}" %*
|
||||
) ELSE (
|
||||
@SETLOCAL
|
||||
@SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
node "${linkerWrapper}" %*
|
||||
)`
|
||||
: `${SHEBANG_SH}node ${linkerWrapper} ${forwardArgs}`,
|
||||
{
|
||||
mode: '777',
|
||||
},
|
||||
)
|
||||
await writeFileAsync(
|
||||
CCWrapperShell,
|
||||
`#!/bin/sh\nzig cc -target ${zigTarget} ${forwardArgs}`,
|
||||
`${SHEBANG_SH}zig cc -target ${zigTarget} ${forwardArgs}`,
|
||||
{
|
||||
mode: '777',
|
||||
},
|
||||
)
|
||||
await writeFileAsync(
|
||||
CXXWrapperShell,
|
||||
`#!/bin/sh\nzig c++ -target ${zigTarget} ${forwardArgs}`,
|
||||
`${SHEBANG_SH}zig c++ -target ${zigTarget} ${forwardArgs}`,
|
||||
{
|
||||
mode: '777',
|
||||
},
|
||||
)
|
||||
|
||||
await writeFileAsync(
|
||||
linkerWrapper,
|
||||
`#!/usr/bin/env node\nconst{writeFileSync} = require('fs')\n${processZigLinkerArgs.toString()}\nconst {status} = require('child_process').spawnSync('zig', ['${
|
||||
`${SHEBANG_NODE}const{writeFileSync} = require('fs')\n${processZigLinkerArgs.toString()}\nconst {status} = require('child_process').spawnSync('zig', ['${
|
||||
triple.platform === 'win32' ? 'c++' : 'cc'
|
||||
}', ...processZigLinkerArgs('${
|
||||
triple.raw
|
||||
|
@ -349,16 +409,54 @@ export class BuildCommand extends Command {
|
|||
})
|
||||
additionalEnv[`CARGO_TARGET_${envTarget}_LINKER`] = linkerWrapperShell
|
||||
}
|
||||
|
||||
execSync(cargoCommand, {
|
||||
env: {
|
||||
...process.env,
|
||||
...additionalEnv,
|
||||
TYPE_DEF_TMP_PATH: intermediateTypeFile,
|
||||
},
|
||||
stdio: 'inherit',
|
||||
cwd,
|
||||
})
|
||||
debug(`Platform: ${JSON.stringify(triple, null, 2)}`)
|
||||
if (triple.platform === 'android') {
|
||||
const { ANDROID_NDK_LATEST_HOME } = process.env
|
||||
if (!ANDROID_NDK_LATEST_HOME) {
|
||||
console.info(
|
||||
`${chalk.yellow(
|
||||
'ANDROID_NDK_LATEST_HOME',
|
||||
)} environment variable is missing`,
|
||||
)
|
||||
}
|
||||
const targetArch = triple.arch === 'arm' ? 'armv7a' : 'aarch64'
|
||||
const targetPlatform =
|
||||
triple.arch === 'arm' ? 'androideabi24' : 'android24'
|
||||
Object.assign(additionalEnv, {
|
||||
CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-android24-clang`,
|
||||
CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-androideabi24-clang`,
|
||||
CC: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-${targetPlatform}-clang`,
|
||||
CXX: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-${targetPlatform}-clang++`,
|
||||
AR: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar`,
|
||||
PATH: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin:${process.env.PATH}`,
|
||||
})
|
||||
}
|
||||
try {
|
||||
execSync(cargoCommand, {
|
||||
env: {
|
||||
...process.env,
|
||||
...additionalEnv,
|
||||
TYPE_DEF_TMP_PATH: intermediateTypeFile,
|
||||
},
|
||||
stdio: 'inherit',
|
||||
cwd,
|
||||
})
|
||||
} catch (e) {
|
||||
if (cargo === 'cargo-xwin') {
|
||||
console.warn(
|
||||
`You are cross compiling ${chalk.underline(
|
||||
triple.raw,
|
||||
)} target on ${chalk.green(process.platform)} host`,
|
||||
)
|
||||
} else if (isCrossForLinux || isCrossForMacOS) {
|
||||
console.warn(
|
||||
`You are cross compiling ${chalk.underline(
|
||||
triple.raw,
|
||||
)} on ${chalk.green(process.platform)} host`,
|
||||
)
|
||||
}
|
||||
throw e
|
||||
}
|
||||
const { binaryName, packageName } = getNapiConfig(this.configFileName)
|
||||
let cargoArtifactName = this.cargoName
|
||||
if (!cargoArtifactName) {
|
||||
|
@ -721,3 +819,30 @@ async function writeJsBinding(
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function patchArmFeaturesHForArmTargets() {
|
||||
let zigExePath: string
|
||||
try {
|
||||
const zigEnv = JSON.parse(execSync(`zig env`, { encoding: 'utf8' }).trim())
|
||||
zigExePath = zigEnv['zig_exe']
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
'Cannot get zig env correctly, please ensure the zig is installed correctly on your system',
|
||||
)
|
||||
}
|
||||
try {
|
||||
await writeFileAsync(
|
||||
join(zigExePath, '../lib/libc/glibc/sysdeps/arm/arm-features.h'),
|
||||
ARM_FEATURES_H,
|
||||
{
|
||||
mode: 0o644,
|
||||
},
|
||||
)
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Cannot patch arm-features.h, error: ${
|
||||
(e as Error).message || e
|
||||
}. See: https://github.com/ziglang/zig/issues/3287`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue