Skip to content

Commit 53257a9

Browse files
authored
new CI (#339)
* new CI without macos support - can't figure out the issue for macos
1 parent fd2d95b commit 53257a9

File tree

4 files changed

+151
-92
lines changed

4 files changed

+151
-92
lines changed

.github/workflows/ci.yml

Lines changed: 104 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ name: CI
22

33
on:
44
push:
5-
branches:
6-
- master
7-
tags:
8-
- v*
5+
branches: [master]
6+
tags: [v*]
97
pull_request:
10-
branches:
11-
- master
8+
branches: [master]
129

1310
jobs:
11+
# ------------------------------------------------------------
12+
# C++ library build & test (fast sanity check)
13+
# ------------------------------------------------------------
1414
build-and-test-cpp:
1515
name: Build and test C++ library on ${{ matrix.os }}
1616
runs-on: ${{ matrix.os }}
@@ -20,139 +20,178 @@ jobs:
2020
os: [ubuntu-latest, macos-latest]
2121

2222
steps:
23-
- uses: actions/checkout@v3
23+
- uses: actions/checkout@v4
2424
with:
2525
submodules: recursive
2626

27-
- name: Install ICU libraries on macOS
28-
if: startsWith(matrix.os, 'macos')
27+
- name: Install ICU (macOS)
28+
if: runner.os == 'macOS'
2929
run: brew install icu4c
3030

31-
- name: Build and install
32-
if: startsWith(matrix.os, 'ubuntu')
31+
- name: Build & install (Linux)
32+
if: runner.os == 'Linux'
3333
run: |
3434
cmake -DBUILD_TESTS=ON -DCMAKE_INSTALL_PREFIX=$PWD/install .
35-
make install
35+
make -j2 install
3636
37-
- name: Build and install (macOS)
38-
if: startsWith(matrix.os, 'macos')
37+
- name: Build & install (macOS)
38+
if: runner.os == 'macOS'
3939
run: |
4040
ICU_PREFIX=$(brew --prefix icu4c)
4141
cmake \
4242
-DBUILD_TESTS=ON \
4343
-DCMAKE_INSTALL_PREFIX=$PWD/install \
4444
-DICU_ROOT=$ICU_PREFIX \
4545
.
46-
make install
46+
make -j2 install
4747
48-
49-
- name: Test
50-
run: |
51-
test/onmt_tokenizer_test test/data
48+
- name: Run C++ tests
49+
run: test/onmt_tokenizer_test test/data
5250

5351

52+
# ------------------------------------------------------------
53+
# Python style checks
54+
# ------------------------------------------------------------
5455
check-python-style:
5556
runs-on: ubuntu-latest
5657

5758
steps:
58-
- uses: actions/checkout@v3
59+
- uses: actions/checkout@v4
5960

60-
- name: Set up Python
61-
uses: actions/setup-python@v4
61+
- uses: actions/setup-python@v5
6262
with:
6363
python-version: "3.11"
6464

65-
- name: Install dependencies
65+
- name: Install linters
6666
run: |
67-
python -m pip install black==22.* flake8==3.9.* isort==5.*
67+
pip install black==22.* flake8==3.9.* isort==5.*
6868
69-
- name: Check code format with Black
69+
- name: Black
7070
working-directory: bindings/python
71-
run: |
72-
black --check .
71+
run: black --check .
7372

74-
- name: Check imports order with isort
73+
- name: isort
7574
working-directory: bindings/python
76-
run: |
77-
isort --check-only .
75+
run: isort --check-only .
7876

79-
- name: Check code style with Flake8
77+
- name: Flake8
8078
working-directory: bindings/python
81-
if: ${{ always() }}
82-
run: |
83-
flake8 .
79+
run: flake8 .
8480

8581

86-
build-and-test-python-wheels:
87-
name: Build and test wheels on ${{ matrix.os }} for ${{ matrix.arch }}
82+
# ------------------------------------------------------------
83+
# Build Python wheels
84+
# ------------------------------------------------------------
85+
build-python-wheels:
86+
name: Wheels – ${{ matrix.os }} / ${{ matrix.arch }}
8887
runs-on: ${{ matrix.os }}
8988
strategy:
9089
fail-fast: false
9190
matrix:
92-
os: [ubuntu-latest, macos-latest, windows-2019]
93-
arch: [auto64]
9491
include:
95-
- os: ubuntu-latest
96-
arch: aarch64
97-
- os: macos-latest
98-
arch: arm64
92+
# Linux
93+
- os: ubuntu-latest
94+
arch: x86_64
95+
- os: ubuntu-latest
96+
arch: aarch64
97+
98+
# macOS
99+
#- os: macos-latest
100+
# arch: x86_64
101+
#- os: macos-latest
102+
# arch: arm64
103+
104+
# Windows
105+
- os: windows-2019
106+
arch: AMD64
99107

100108
steps:
101-
- uses: actions/checkout@v3
109+
- uses: actions/checkout@v4
102110
with:
103111
submodules: recursive
104112

105-
- uses: docker/setup-qemu-action@v2
106-
if: ${{ matrix.arch == 'aarch64' }}
107-
name: Set up QEMU
113+
- name: Enable QEMU (Linux ARM)
114+
if: matrix.arch == 'aarch64'
115+
uses: docker/setup-qemu-action@v3
108116

109117
- name: Build wheels
110118
uses: pypa/[email protected]
111119
with:
112120
package-dir: bindings/python
113121
output-dir: wheelhouse
114122
env:
115-
CIBW_ENVIRONMENT_LINUX: TOKENIZER_ROOT=/project/build/install ICU_ROOT=/project/icu
116-
CIBW_ENVIRONMENT_MACOS: TOKENIZER_ROOT=${GITHUB_WORKSPACE}/build/install
117-
CIBW_ENVIRONMENT_WINDOWS: TOKENIZER_ROOT=${GITHUB_WORKSPACE}/build/install
118-
CIBW_BEFORE_ALL_LINUX: bindings/python/tools/prepare_build_environment_linux.sh
119-
CIBW_BEFORE_ALL_MACOS: bindings/python/tools/prepare_build_environment_macos.sh
120-
CIBW_BEFORE_ALL_WINDOWS: bash bindings/python/tools/prepare_build_environment_windows.sh
123+
# ---- Build selection ----
124+
CIBW_BUILD: "cp310-* cp311-* cp312-*"
125+
CIBW_SKIP: "pp* *-musllinux_*"
126+
CIBW_ARCHS: ${{ matrix.arch }}
127+
128+
# ---- Dependencies ----
121129
CIBW_BEFORE_BUILD: pip install pybind11==2.10.1
130+
131+
# ---- Linux ----
132+
CIBW_BEFORE_ALL_LINUX: bindings/python/tools/prepare_build_environment_linux.sh
133+
CIBW_ENVIRONMENT_LINUX: |
134+
TOKENIZER_ROOT=/project/build/install
135+
ICU_ROOT=/project/icu
122136
CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
123137
CIBW_MANYLINUX_AARCH64_IMAGE: manylinux2014
124-
CIBW_BUILD: "cp310-* cp311-* cp312-*"
138+
139+
# ---- macOS ----
140+
CIBW_BEFORE_ALL_MACOS: bindings/python/tools/prepare_build_environment_macos.sh
141+
CIBW_ENVIRONMENT_MACOS: |
142+
TOKENIZER_ROOT=${GITHUB_WORKSPACE}/build/install
143+
ICU_ROOT=${GITHUB_WORKSPACE}/icu
144+
DYLD_LIBRARY_PATH=${GITHUB_WORKSPACE}/icu/lib:${DYLD_LIBRARY_PATH}
145+
CIBW_REPAIR_WHEEL_COMMAND_MACOS: |
146+
set -e
147+
echo "=== Bundling ICU into wheel ==="
148+
delocate-wheel -v -w {dest_dir} {wheel}
149+
150+
REPAIRED_WHEEL=$(ls {dest_dir}/*.whl)
151+
echo "=== Inspecting repaired wheel ==="
152+
unzip -q "$REPAIRED_WHEEL" -d /tmp/wheel_check
153+
SO_FILE=$(find /tmp/wheel_check -name "_ext*.so" | head -n1)
154+
otool -L "$SO_FILE"
155+
rm -rf /tmp/wheel_check
156+
echo "=== Wheel ready: $REPAIRED_WHEEL ==="
157+
158+
159+
160+
# ---- Windows ----
161+
CIBW_BEFORE_ALL_WINDOWS: bash bindings/python/tools/prepare_build_environment_windows.sh
162+
CIBW_ENVIRONMENT_WINDOWS: TOKENIZER_ROOT=${GITHUB_WORKSPACE}/build/install
163+
164+
# ---- Tests ----
125165
CIBW_TEST_COMMAND: pytest {project}/bindings/python/test/test.py
126166
CIBW_TEST_REQUIRES: pytest
127-
CIBW_ARCHS: ${{ matrix.arch }}
128-
CIBW_SKIP: pp* *-musllinux_*
129-
CIBW_TEST_SKIP: "*-macosx_arm64"
130-
CIBW_REPAIR_WHEEL_COMMAND_MACOS: ""
131167

132-
- name: Upload Python wheels
168+
- name: Upload wheels
133169
uses: actions/upload-artifact@v4
134170
with:
135171
name: python-wheels-${{ matrix.os }}-${{ matrix.arch }}
136172
path: wheelhouse
137173

138174

139-
publish-python-wheels-on-pypi:
140-
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
141-
needs: [build-and-test-cpp, build-and-test-python-wheels]
175+
# ------------------------------------------------------------
176+
# Publish to PyPI (API token, no Trusted Publishing)
177+
# ------------------------------------------------------------
178+
publish-python-wheels:
179+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
180+
needs: [build-and-test-cpp, build-python-wheels]
142181
runs-on: ubuntu-latest
143182

144183
steps:
145-
- name: Download all wheels
184+
- name: Download wheels
146185
uses: actions/download-artifact@v4
147186
with:
148187
path: artifacts
149188

150189
- name: Collect wheels
151190
run: |
152-
mkdir -p dist
191+
mkdir dist
153192
find artifacts -name "*.whl" -exec cp {} dist/ \;
154-
155-
- name: Publish Python wheels to PyPI
193+
194+
- name: Publish to PyPI
156195
uses: pypa/gh-action-pypi-publish@release/v1
157196
with:
158197
packages-dir: dist

bindings/python/pyproject.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[build-system]
2+
requires = [
3+
"setuptools>=61",
4+
"wheel",
5+
"pybind11==2.10.1"
6+
]
7+
build-backend = "setuptools.build_meta"
8+

bindings/python/setup.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
include_dirs = [pybind11.get_include()]
99
library_dirs = []
10+
libraries = ["OpenNMTTokenizer"]
1011

1112

1213
def _get_long_description():
@@ -25,26 +26,41 @@ def _get_project_version():
2526

2627

2728
def _maybe_add_library_root(lib_name, header_only=False):
28-
root = os.environ.get("%s_ROOT" % lib_name)
29+
root = os.environ.get(f"{lib_name}_ROOT")
2930
if root is None:
30-
return
31+
return None
3132
include_dirs.append(os.path.join(root, "include"))
3233
if not header_only:
3334
for lib_subdir in ("lib64", "lib"):
3435
lib_dir = os.path.join(root, lib_subdir)
3536
if os.path.isdir(lib_dir):
3637
library_dirs.append(lib_dir)
37-
break
38+
return lib_dir
39+
return None
3840

3941

4042
_maybe_add_library_root("TOKENIZER")
43+
icu_lib_dir = _maybe_add_library_root("ICU")
4144

4245
cflags = ["-std=c++17", "-fvisibility=hidden"]
4346
ldflags = []
4447
package_data = {}
48+
4549
if sys.platform == "darwin":
4650
cflags.append("-mmacosx-version-min=10.14")
47-
ldflags.append("-Wl,-rpath,/usr/local/lib")
51+
if icu_lib_dir:
52+
icu_libs = ["icuuc", "icudata", "icui18n", "icuio"]
53+
libraries.extend(icu_libs)
54+
55+
# Link ICU with full paths so delocate can see it
56+
for lib in icu_libs:
57+
full_lib_path = os.path.join(icu_lib_dir, f"lib{lib}.dylib")
58+
if os.path.isfile(full_lib_path):
59+
ldflags.append(full_lib_path)
60+
61+
# rpath for runtime
62+
ldflags.append("-Wl,-rpath,@loader_path/../icu/lib")
63+
4864
elif sys.platform == "win32":
4965
cflags = ["/std:c++17", "/d2FH4-"]
5066
package_data["pyonmttok"] = ["*.dll"]
@@ -56,7 +72,7 @@ def _maybe_add_library_root(lib_name, header_only=False):
5672
extra_link_args=ldflags,
5773
include_dirs=include_dirs,
5874
library_dirs=library_dirs,
59-
libraries=["OpenNMTTokenizer"],
75+
libraries=libraries,
6076
)
6177

6278
setup(
@@ -92,7 +108,5 @@ def _maybe_add_library_root(lib_name, header_only=False):
92108
packages=find_packages(),
93109
package_data=package_data,
94110
python_requires=">=3.10",
95-
setup_requires=["pytest-runner"],
96-
tests_require=["pytest"],
97111
ext_modules=[tokenizer_module],
98112
)
Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
#! /bin/bash
2-
1+
#!/bin/bash
32
set -e
43
set -x
54

@@ -9,35 +8,34 @@ CMAKE_EXTRA_ARGS=""
98

109
mkdir -p "$ICU_ROOT"
1110

12-
# Install ICU via Homebrew
13-
brew install icu4c
14-
ICU_PREFIX="$(brew --prefix icu4c)"
15-
16-
# Copy ICU into local prefix
17-
rsync -a "$ICU_PREFIX/" "$ICU_ROOT/"
11+
# Copy ICU only if not present
12+
if [ ! -d "$ICU_ROOT/lib" ]; then
13+
brew install icu4c
14+
ICU_PREFIX="$(brew --prefix icu4c)"
15+
rsync -a "$ICU_PREFIX/" "$ICU_ROOT/"
16+
fi
1817

19-
# Remove dynamic libraries to force static linking
20-
rm -f "$ICU_ROOT/lib/"*.dylib || true
18+
export DYLD_LIBRARY_PATH="$ICU_ROOT/lib:$DYLD_LIBRARY_PATH"
2119

22-
# Explicit Apple Silicon handling
2320
if [[ "$(uname -m)" == "arm64" ]]; then
2421
CMAKE_EXTRA_ARGS="-DCMAKE_OSX_ARCHITECTURES=arm64"
2522
fi
2623

27-
# Install cmake
2824
pip install cmake
2925

30-
# Build Tokenizer
31-
rm -rf build
32-
mkdir build
33-
cd build
26+
rm -rf "$ROOT_DIR/build"
27+
mkdir -p "$ROOT_DIR/build"
28+
3429
cmake \
30+
-S "$ROOT_DIR" \
31+
-B "$ROOT_DIR/build" \
3532
-DLIB_ONLY=ON \
33+
-DBUILD_SHARED_LIBS=OFF \
3634
-DICU_ROOT="$ICU_ROOT" \
3735
-DCMAKE_INSTALL_PREFIX="$ROOT_DIR/build/install" \
38-
$CMAKE_EXTRA_ARGS \
39-
..
36+
-DCMAKE_MACOSX_RPATH=ON \
37+
-DCMAKE_INSTALL_RPATH="$ICU_ROOT/lib" \
38+
$CMAKE_EXTRA_ARGS
4039

41-
VERBOSE=1 make -j2 install
42-
cd "$ROOT_DIR"
40+
cmake --build "$ROOT_DIR/build" --target install -j2
4341

0 commit comments

Comments
 (0)