Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .github/workflows/release-npm-binaries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
9 changes: 7 additions & 2 deletions .github/workflows/release-npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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'
Expand Down
14 changes: 8 additions & 6 deletions node/PUBLISHING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -60,9 +61,10 @@ prebuilds/assets/*.ocd2
prebuilds/<current-platform>-<current-arch>/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

Expand Down
91 changes: 70 additions & 21 deletions scripts/build-node-prebuild-bazel.sh
Original file line number Diff line number Diff line change
@@ -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 <target>

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 <target>

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 <<EOF
Target $target does not match host $host_target.
Run this script on a matching runner, or use win32-x64 from macOS/Linux via the
existing Bazel + Zig cross-compile path.
EOF
exit 1
fi

echo "Preparing shared Node.js prebuild assets..."
node scripts/prepare-node-prebuild-artifacts.js

dest_dir="prebuilds/$target"
dest="$dest_dir/opencc.node"
mkdir -p "$dest_dir"
cp -fL "$addon" "$dest"

echo "Copied Bazel-built addon to $dest"
ls -lh "$dest"
9 changes: 1 addition & 8 deletions scripts/build-node-windows-prebuild-zig.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,4 @@ set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT_DIR"

echo "Building Windows x64 (node-windows-x64-zig)..."
bazel build --config=node-windows-x64-zig //node:opencc_node_windows_zig

mkdir -p prebuilds/win32-x64
cp -fL bazel-bin/node/windows-x64/opencc.node prebuilds/win32-x64/
echo "Copied to prebuilds/win32-x64/opencc.node"

ls -lh prebuilds/win32-x64/opencc.node
exec ./scripts/build-node-prebuild-bazel.sh win32-x64
54 changes: 47 additions & 7 deletions scripts/prepare-node-prebuild-artifacts.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,62 @@ const path = require('path');

const runtimeAssetPattern = /\.(json|ocd2)$/;

function prepareArtifacts(root = path.join(__dirname, '..')) {
const sourceDir = path.join(root, 'build', 'Release');
const outputDir = path.join(root, 'prebuilds', 'assets');

fs.rmSync(outputDir, { recursive: true, force: true });
fs.mkdirSync(outputDir, { recursive: true });
function copyMatchingFiles(sourceDir, outputDir, predicate) {
if (!fs.existsSync(sourceDir)) {
return 0;
}

let count = 0;
for (const entry of fs.readdirSync(sourceDir, { withFileTypes: true })) {
if (!entry.isFile() || !runtimeAssetPattern.test(entry.name)) {
if (!entry.isFile() || !predicate(entry.name)) {
continue;
}

fs.copyFileSync(
path.join(sourceDir, entry.name),
path.join(outputDir, entry.name)
);
count += 1;
}
return count;
}

function prepareArtifacts(root = path.join(__dirname, '..')) {
const legacySourceDir = path.join(root, 'build', 'Release');
const bazelDictionaryDir = path.join(root, 'bazel-bin', 'data', 'dictionary');
const configDir = path.join(root, 'data', 'config');
const outputDir = path.join(root, 'prebuilds', 'assets');

fs.rmSync(outputDir, { recursive: true, force: true });
fs.mkdirSync(outputDir, { recursive: true });

if (fs.existsSync(legacySourceDir)) {
const copied = copyMatchingFiles(
legacySourceDir,
outputDir,
(name) => 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.'
);
}
}

Expand Down
Loading