From 508f692f90cea417a011aff8020ddd5dbd7b4b44 Mon Sep 17 00:00:00 2001 From: Carbo Date: Wed, 8 Jul 2026 21:44:05 -0700 Subject: [PATCH] Build Node release binaries with Bazel Use the Bazel Node addon target to produce npm scoped binary package artifacts while keeping the existing node-gyp source fallback for unsupported platforms. --- .github/workflows/release-npm-binaries.yml | 9 ++- .github/workflows/release-npm.yml | 9 ++- node/PUBLISHING.md | 14 ++-- scripts/build-node-prebuild-bazel.sh | 91 +++++++++++++++++----- scripts/build-node-windows-prebuild-zig.sh | 9 +-- scripts/prepare-node-prebuild-artifacts.js | 54 +++++++++++-- 6 files changed, 140 insertions(+), 46 deletions(-) diff --git a/.github/workflows/release-npm-binaries.yml b/.github/workflows/release-npm-binaries.yml index 9fa6494ca..a41a7c897 100644 --- a/.github/workflows/release-npm-binaries.yml +++ b/.github/workflows/release-npm-binaries.yml @@ -217,8 +217,13 @@ jobs: node-version: 24 registry-url: https://registry.npmjs.org/ + - uses: bazel-contrib/setup-bazel@0.19.0 + with: + bazelisk-cache: true + repository-cache: true + - name: Install dependencies - run: npm install --omit=optional + run: npm install --omit=optional --ignore-scripts - name: Prepare package versions shell: bash @@ -260,7 +265,7 @@ jobs: if: steps.publish_check.outputs.published != 'true' shell: bash run: | - npm run prebuild + ./scripts/build-node-prebuild-bazel.sh "${{ matrix.target }}" test -f "prebuilds/${{ matrix.target }}/opencc.node" - name: Prepare scoped package diff --git a/.github/workflows/release-npm.yml b/.github/workflows/release-npm.yml index 74ae6865c..aa78d8173 100644 --- a/.github/workflows/release-npm.yml +++ b/.github/workflows/release-npm.yml @@ -177,8 +177,13 @@ jobs: node-version: 24 registry-url: https://registry.npmjs.org/ + - uses: bazel-contrib/setup-bazel@0.19.0 + with: + bazelisk-cache: true + repository-cache: true + - name: Install dependencies - run: npm ci --omit=optional + run: npm ci --omit=optional --ignore-scripts - name: Prepare package versions shell: bash @@ -214,7 +219,7 @@ jobs: - name: Build opencc package assets if: steps.publish_check.outputs.published != 'true' - run: npm run prebuild + run: ./scripts/build-node-prebuild-bazel.sh darwin-arm64 - name: Test if: steps.publish_check.outputs.published != 'true' diff --git a/node/PUBLISHING.md b/node/PUBLISHING.md index 1f138465a..82ccfb213 100644 --- a/node/PUBLISHING.md +++ b/node/PUBLISHING.md @@ -45,11 +45,12 @@ scoped package for its own platform. ## Build -From the repository root: +From the repository root, build the scoped binary for the current platform with +Bazel: ```sh -npm install -npm run prebuild +npm install --omit=optional --ignore-scripts +./scripts/build-node-prebuild-bazel.sh "$(node -p 'process.platform + "-" + process.arch')" ``` This creates: @@ -60,9 +61,10 @@ prebuilds/assets/*.ocd2 prebuilds/-/opencc.node ``` -The release workflow runs this command separately on macOS ARM64, Linux ARM64, -Linux x64, and Windows x64 runners. It no longer depends on Bazel remote -execution to cross-build npm binaries. +The release workflow runs the Bazel build script separately on the runner for +each scoped binary target. The main `opencc` package still keeps `binding.gyp` +and source files so installs on platforms without a scoped binary package can +fall back to `node-gyp-build`. ## Prepare scoped binary packages diff --git a/scripts/build-node-prebuild-bazel.sh b/scripts/build-node-prebuild-bazel.sh index 34d85a43a..ec18421d7 100755 --- a/scripts/build-node-prebuild-bazel.sh +++ b/scripts/build-node-prebuild-bazel.sh @@ -1,28 +1,77 @@ #!/usr/bin/env bash # -# Build opencc.node via Bazel remote execution and copy to the prebuilds directory. -# Usage: ./scripts/build-node-prebuild-bazel.sh +# Build opencc.node with Bazel and copy it to the npm prebuilds directory. +# Usage: ./scripts/build-node-prebuild-bazel.sh -set -e +set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$ROOT_DIR" -echo "Building Linux x64 (node-linux-x64)..." -bazel build --config=node-linux-x64 -c opt --remote_download_toplevel //node:opencc -mkdir -p prebuilds/linux-x64 -cp -fL bazel-bin/node/opencc.node prebuilds/linux-x64/ -echo "Copied to prebuilds/linux-x64/opencc.node" - -echo "" -echo "Building Linux arm64 (node-linux-arm64)..." -bazel build --config=node-linux-arm64 -c opt --remote_download_toplevel //node:opencc -mkdir -p prebuilds/linux-arm64 -cp -fL bazel-bin/node/opencc.node prebuilds/linux-arm64/ -echo "Copied to prebuilds/linux-arm64/opencc.node" - -# 资产文件仍然依赖于 prepare-node-prebuild-artifacts.js, -# 但那是 npm run prebuild 的一部分。如果需要,也可以在这里复制。 -echo "" -echo "Prebuild files collected successfully." -ls -lh prebuilds/linux-x64/opencc.node prebuilds/linux-arm64/opencc.node +# Git Bash/MSYS rewrites Bazel labels such as //node:opencc as paths on Windows. +case "$(uname -s 2>/dev/null || true)" in + MINGW*|MSYS*|CYGWIN*) + export MSYS2_ARG_CONV_EXCL='*' + export MSYS_NO_PATHCONV=1 + ;; +esac + +usage() { + cat >&2 <<'EOF' +Usage: ./scripts/build-node-prebuild-bazel.sh + +Supported targets: + darwin-arm64 + darwin-x64 + linux-arm64 + linux-x64 + win32-x64 +EOF +} + +if [[ $# -ne 1 ]]; then + usage + exit 2 +fi + +target="$1" +case "$target" in + darwin-arm64|darwin-x64|linux-arm64|linux-x64|win32-x64) + ;; + *) + echo "Unsupported target: $target" >&2 + usage + exit 2 + ;; +esac + +host_target="$(node -p "process.platform + '-' + process.arch")" + +if [[ "$target" == "win32-x64" && "$host_target" != "$target" ]]; then + echo "Building $target with Bazel + Zig cross-compile..." + bazel build -c opt //data/dictionary:binary_dictionaries + bazel build -c opt --config=node-windows-x64-zig //node:opencc_node_windows_zig + addon="bazel-bin/node/windows-x64/opencc.node" +elif [[ "$target" == "$host_target" ]]; then + echo "Building $target with native Bazel toolchain..." + bazel build -c opt //data/dictionary:binary_dictionaries //node:opencc + addon="bazel-bin/node/opencc.node" +else + cat >&2 < runtimeAssetPattern.test(name) + ); + if (copied > 0) { + return; + } + } + + const jsonCount = copyMatchingFiles( + configDir, + outputDir, + (name) => name.endsWith('.json') && !name.endsWith('.schema.json') + ); + const dictionaryCount = copyMatchingFiles( + bazelDictionaryDir, + outputDir, + (name) => name.endsWith('.ocd2') + ); + + if (jsonCount === 0 || dictionaryCount === 0) { + throw new Error( + 'Could not prepare Node.js prebuild assets. Run node-gyp prebuild first, ' + + 'or run `bazel build -c opt //data/dictionary:binary_dictionaries` before this script.' + ); } }