diff --git a/.github/workflows/linux-aarch64-zig.yaml b/.github/workflows/linux-aarch64-zig.yaml new file mode 100644 index 00000000..d33577af --- /dev/null +++ b/.github/workflows/linux-aarch64-zig.yaml @@ -0,0 +1,69 @@ +name: Linux-aarch64-zig + +env: + DEBUG: 'napi:*' + +on: + push: + branches: + - main + pull_request: + +jobs: + build: + name: stable - aarch64-unknown-linux-gnu - node@16 + runs-on: ubuntu-latest + + steps: + - run: docker run --rm --privileged multiarch/qemu-user-static:register --reset + + - uses: actions/checkout@v2 + + - name: Setup node + uses: actions/setup-node@v2 + with: + node-version: 16 + check-latest: true + cache: 'yarn' + + - name: Install + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + override: true + target: aarch64-unknown-linux-musl + + - name: Install aarch64 toolchain + run: rustup target add aarch64-unknown-linux-gnu + + - name: Install ziglang + uses: goto-bus-stop/setup-zig@v1 + with: + version: 0.9.0 + + - name: Cache NPM dependencies + uses: actions/cache@v2 + with: + path: node_modules + key: npm-cache-linux-aarch64-gnu-node@16-${{ hashFiles('yarn.lock') }} + + - name: Install dependencies + run: yarn install --frozen-lockfile --ignore-platform --registry https://registry.npmjs.org --network-timeout 300000 + + - name: 'Build TypeScript' + run: yarn build + + - name: Cross build native tests + run: | + yarn --cwd ./examples/napi-compat-mode build --target aarch64-unknown-linux-musl --zig + yarn --cwd ./examples/napi build --target aarch64-unknown-linux-musl --zig + + - name: Setup and run tests + uses: docker://multiarch/alpine:aarch64-latest-stable + with: + args: > + sh -c " + apk add nodejs yarn && \ + yarn test + " diff --git a/cli/package.json b/cli/package.json index a388be94..00b255fe 100644 --- a/cli/package.json +++ b/cli/package.json @@ -47,6 +47,7 @@ "chalk": "4", "clipanion": "^3.1.0", "debug": "^4.3.3", + "env-paths": "^3.0.0", "fdir": "^5.1.0", "inquirer": "^8.2.0", "js-yaml": "^4.1.0", diff --git a/cli/src/build.ts b/cli/src/build.ts index 0aea66f4..7f1c6710 100644 --- a/cli/src/build.ts +++ b/cli/src/build.ts @@ -1,16 +1,17 @@ import { execSync } from 'child_process' -import { existsSync } from 'fs' +import { existsSync, mkdirSync, writeFileSync } from 'fs' import { join, parse, sep } from 'path' import { Instance } from 'chalk' import { Command, Option } from 'clipanion' +import envPaths from 'env-paths' import { groupBy } from 'lodash-es' import toml from 'toml' import { getNapiConfig } from './consts' import { debugFactory } from './debug' import { createJsBinding } from './js-binding-template' -import { getDefaultTargetTriple, parseTriple } from './parse-triple' +import { getCpuArch, getDefaultTargetTriple, parseTriple } from './parse-triple' import { copyFileAsync, mkdirAsync, @@ -117,6 +118,10 @@ export class BuildCommand extends Command { required: false, }) + useZig = Option.Boolean(`--zig`, false, { + description: `Use ${chalk.green('zig')} as linker`, + }) + async execute() { const cwd = this.cargoCwd ? join(process.cwd(), this.cargoCwd) @@ -160,6 +165,34 @@ export class BuildCommand extends Command { CARGO_PROFILE_RELEASE_LTO: false, }) } + + if (triple.raw.includes('musl')) { + let rustflags = process.env.RUSTFLAGS ?? '' + if (!rustflags.includes('target-feature=-crt-static')) { + rustflags += '-C target-feature=-crt-static' + additionalEnv['RUSTFLAGS'] = rustflags + } + } + + if (this.useZig && triple.platform === 'linux') { + const paths = envPaths('napi-rs') + const cpuArch = getCpuArch(triple.arch) + const linkerWrapper = join(paths.cache, `zig-cc-${triple.abi}.sh`) + const zigTarget = `${cpuArch}-linux-${triple.abi}` + mkdirSync(paths.cache, { recursive: true }) + writeFileSync( + linkerWrapper, + `#!/bin/bash\nzig cc \${@/-lgcc_s/-lunwind} -target ${zigTarget}\n`, + { mode: 0o700 }, + ) + const envTarget = triple.raw.replaceAll('-', '_').toUpperCase() + Object.assign(additionalEnv, { + TARGET_CC: linkerWrapper, + TARGET_CXX: `zig c++ -target ${zigTarget}`, + }) + additionalEnv[`CARGO_TARGET_${envTarget}_LINKER`] = linkerWrapper + } + execSync(cargoCommand, { env: { ...process.env, diff --git a/cli/src/parse-triple.ts b/cli/src/parse-triple.ts index c09e40bd..8d824509 100644 --- a/cli/src/parse-triple.ts +++ b/cli/src/parse-triple.ts @@ -21,6 +21,14 @@ const CpuToNodeArch: { [index: string]: NodeJSArch } = { armv7: 'arm', } +const NodeArchToCpu: { [index: string]: string } = { + arm64: 'aarch64', + ppc: 'powerpc', + ppc64: 'powerpc64', + x32: 'i686', + x64: 'x86_64', +} + const SysToNodePlatform: { [index: string]: NodeJS.Platform } = { linux: 'linux', freebsd: 'freebsd', @@ -125,3 +133,7 @@ export function getDefaultTargetTriple(rustcfg: string): PlatformDetail { } return parseTriple(triple) } + +export function getCpuArch(nodeArch: NodeJSArch): string { + return NodeArchToCpu[nodeArch] || nodeArch +} diff --git a/yarn.lock b/yarn.lock index 036fa3ee..fb79d492 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2636,6 +2636,11 @@ env-paths@^2.2.0: resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== +env-paths@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz#2f1e89c2f6dbd3408e1b1711dd82d62e317f58da" + integrity sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A== + envinfo@^7.7.4: version "7.8.1" resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475"