From ab59e0be37712a36448ea4f037826514a63d7b3f Mon Sep 17 00:00:00 2001 From: Emin Date: Fri, 17 Apr 2026 15:37:02 +0800 Subject: [PATCH 01/18] feat: add pyproject.toml and ecc_tools_bin package for wheel packaging --- ecc_tools_bin/__init__.py | 2 ++ pyproject.toml | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 ecc_tools_bin/__init__.py create mode 100644 pyproject.toml diff --git a/ecc_tools_bin/__init__.py b/ecc_tools_bin/__init__.py new file mode 100644 index 000000000..502076f96 --- /dev/null +++ b/ecc_tools_bin/__init__.py @@ -0,0 +1,2 @@ +# Intentionally minimal — consumers import ecc_py directly: +# from ecc_tools_bin import ecc_py diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..1a6e02258 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,22 @@ +[build-system] +requires = ["uv_build>=0.8.5"] +build-backend = "uv_build" + +[project] +name = "ecc-tools" +version = "0.1.0-alpha.1" +requires-python = ">=3.11" +dependencies = [] + +[tool.uv.build-backend] +module-name = ["ecc_tools_bin"] +module-root = "" + +[tool.uv.build-backend.source-exclude] +patterns = ["src/**", "build/**", "scripts/**", ".github/**"] + +[tool.uv.build-backend.wheel-exclude] +patterns = ["src/**", "build/**", "scripts/**", ".github/**"] + +[dependency-groups] +dev = ["auditwheel"] From 159bcc9df8463359161866d5b4f2e75c065808a5 Mon Sep 17 00:00:00 2001 From: Emin Date: Fri, 17 Apr 2026 15:38:47 +0800 Subject: [PATCH 02/18] feat: add build-wheel.sh for manylinux wheel packaging --- scripts/build-wheel.sh | 146 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100755 scripts/build-wheel.sh diff --git a/scripts/build-wheel.sh b/scripts/build-wheel.sh new file mode 100755 index 000000000..f11c08df8 --- /dev/null +++ b/scripts/build-wheel.sh @@ -0,0 +1,146 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Build ecc-tools wheel: CMake build → collect .so → uv build → auditwheel repair → smoke test +# Intended to run inside manylinux_2_34_x86_64 container or equivalent. + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +cd "$REPO_ROOT" + +# ── 0. Validate environment ────────────────────────────────────────────── + +if [[ "${OSTYPE:-}" != linux* ]]; then + echo "ERROR: wheel build is only supported on Linux." >&2 + exit 1 +fi + +PYTHON3="${PYTHON3:-$(command -v python3)}" +if [[ ! -x "$PYTHON3" ]]; then + echo "ERROR: python3 not found." >&2 + exit 1 +fi + +for cmd in cmake ninja auditwheel uv sha256sum; do + if ! command -v "$cmd" >/dev/null 2>&1; then + echo "ERROR: required command not found: $cmd" >&2 + exit 1 + fi +done + +# ── 1. CMake configure ─────────────────────────────────────────────────── + +echo "[build-wheel] CMake configure" +cmake_launcher_args=() +if command -v sccache >/dev/null 2>&1; then + cmake_launcher_args+=( + -DCMAKE_CXX_COMPILER_LAUNCHER=sccache + -DCMAKE_C_COMPILER_LAUNCHER=sccache + ) + echo "[build-wheel] sccache enabled" +fi + +cmake -B build -G Ninja \ + -DBUILD_ECOS=ON \ + -DBUILD_PYTHON=ON \ + -DBUILD_STATIC_LIB=OFF \ + -DCOMPATIBILITY_MODE=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DPython3_EXECUTABLE="$PYTHON3" \ + -DPython3_ROOT_DIR="$("$PYTHON3" -c "import sys; print(sys.prefix)")" \ + -DPython3_FIND_STRATEGY=LOCATION \ + "${cmake_launcher_args[@]}" + +# ── 2. CMake build ─────────────────────────────────────────────────────── + +echo "[build-wheel] CMake build ($(nproc) jobs)" +cmake --build build --target ecc_py -j"$(nproc)" + +# ── 3. Collect artifacts ───────────────────────────────────────────────── + +echo "[build-wheel] Collecting .so artifacts" +ECC_PY_ABI="$("$PYTHON3" -c "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))")" + +# Find the ecc_py shared object +ecc_py_so="$(find build -name "ecc_py${ECC_PY_ABI}" -print -quit)" +if [[ -z "$ecc_py_so" ]]; then + echo "ERROR: ecc_py${ECC_PY_ABI} not found in build/" >&2 + exit 1 +fi +echo "[build-wheel] Found: $ecc_py_so" + +# Copy into the Python package directory +cp -f "$ecc_py_so" "ecc_tools_bin/" +echo "[build-wheel] Copied ecc_py to ecc_tools_bin/" + +# ── 4. Build raw wheel ────────────────────────────────────────────────── + +echo "[build-wheel] Building raw wheel" +raw_out="dist/wheel/raw" +mkdir -p "$raw_out" +uv build --wheel --out-dir "$raw_out" + +raw_whl="$(find "$raw_out" -name 'ecc_tools-*.whl' -print -quit)" +if [[ -z "$raw_whl" ]]; then + echo "ERROR: raw wheel not found in $raw_out" >&2 + exit 1 +fi +echo "[build-wheel] Raw wheel: $raw_whl" + +# ── 5. auditwheel repair ──────────────────────────────────────────────── + +echo "[build-wheel] auditwheel show/repair" +repair_out="dist/wheel/repaired" +report_out="dist/wheel/reports" +mkdir -p "$repair_out" "$report_out" + +show_report="$report_out/show.txt" +{ + echo "=== $(basename "$raw_whl") ===" + auditwheel show "$raw_whl" + echo +} > "$show_report" + +auditwheel repair "$raw_whl" -w "$repair_out" + +shopt -s nullglob +repaired_wheels=("$repair_out"/ecc_tools-*.whl) +if [[ ${#repaired_wheels[@]} -eq 0 ]]; then + echo "ERROR: no repaired ecc_tools wheel found in $repair_out" >&2 + exit 1 +fi +shopt -u nullglob + +# ── 6. Smoke test ──────────────────────────────────────────────────────── + +echo "[build-wheel] Running smoke test" +smoke_dir="$(mktemp -d)" +cleanup() { rm -rf "$smoke_dir"; } +trap cleanup EXIT + +"$PYTHON3" -m pip install --target "$smoke_dir/site" "${repaired_wheels[@]}" +PYTHONPATH="$smoke_dir/site" "$PYTHON3" -c " +from ecc_tools_bin import ecc_py + +required = ['flow_init', 'flow_exit', 'db_init', 'def_init', 'lef_init', + 'def_save', 'run_placer', 'run_cts', 'run_rt', 'run_drc', + 'run_filler', 'init_floorplan', 'report_db', 'feature_summary'] +missing = [f for f in required if not callable(getattr(ecc_py, f, None))] +assert not missing, f'missing or non-callable bindings: {missing}' + +print(f'ecc_py smoke test passed: {len(required)} bindings verified') +" + +# ── 7. Checksums ───────────────────────────────────────────────────────── + +( + cd "$repair_out" + sha256sum ./*.whl > "$REPO_ROOT/dist/wheel/SHA256SUMS" +) + +echo "[build-wheel] done" +echo "[build-wheel] raw wheels: $raw_out" +echo "[build-wheel] repaired wheels: $repair_out" +echo "[build-wheel] report: $show_report" +echo "[build-wheel] checksums: dist/wheel/SHA256SUMS" From aa52be44c697a6dc0bf0a2ea1bcf880e85a8b3ba Mon Sep 17 00:00:00 2001 From: Emin Date: Fri, 17 Apr 2026 15:39:12 +0800 Subject: [PATCH 03/18] feat: add build-wheel composite action for GitHub Actions --- .github/actions/build-wheel/action.yml | 49 ++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/actions/build-wheel/action.yml diff --git a/.github/actions/build-wheel/action.yml b/.github/actions/build-wheel/action.yml new file mode 100644 index 000000000..1bf98196d --- /dev/null +++ b/.github/actions/build-wheel/action.yml @@ -0,0 +1,49 @@ +name: Build ECC-Tools Wheel +description: CMake build + auditwheel repair + smoke test. Requires manylinux_2_34_x86_64 container. + +runs: + using: composite + steps: + - name: Install system dependencies + shell: bash + run: | + dnf install -y epel-release + dnf config-manager --set-enabled crb || true + dnf install -y \ + cmake ninja-build pkgconfig patchelf \ + boost-devel cairo-devel gflags-devel glog-devel \ + flex flex-devel bison eigen3-devel gtest-devel \ + tbb-devel hwloc-devel libcurl-devel libunwind-devel \ + metis-devel gmp-devel tcl-devel \ + unzip zip + + - name: Setup Python 3.11 + shell: bash + run: echo "/opt/python/cp311-cp311/bin" >> "$GITHUB_PATH" + + - name: Setup sccache + uses: mozilla-actions/sccache-action@v0.0.9 + + - name: Setup uv + uses: astral-sh/setup-uv@v5 + with: + version: latest + enable-cache: true + + - name: Install Python dev dependencies + shell: bash + run: uv sync --frozen --all-groups --python 3.11 + + - name: Restore CMake build cache + uses: actions/cache@v4 + with: + path: build + key: cmake-${{ runner.os }}-${{ hashFiles('CMakeLists.txt', 'src/**/CMakeLists.txt') }} + restore-keys: | + cmake-${{ runner.os }}- + + - name: Build repaired wheel + shell: bash + env: + SCCACHE_GHA_ENABLED: "true" + run: scripts/build-wheel.sh From c9047cbda64e03e383dc8024e41ed8b62d619ac4 Mon Sep 17 00:00:00 2001 From: Emin Date: Fri, 17 Apr 2026 15:39:36 +0800 Subject: [PATCH 04/18] feat: add CI workflow for wheel build verification --- .github/workflows/ci.yml | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..716490025 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,39 @@ +name: CI + +on: + pull_request: + push: + branches: [main] + +concurrency: + group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + build-wheel: + name: Build Wheel + runs-on: ubuntu-latest + container: quay.io/pypa/manylinux_2_34_x86_64 + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Build wheel + uses: ./.github/actions/build-wheel + + - name: Upload repaired wheel + uses: actions/upload-artifact@v4 + with: + name: ecc-tools-wheel + path: dist/wheel/repaired/ecc_tools-*.whl + retention-days: 7 + if-no-files-found: error + + - name: Upload checksums + uses: actions/upload-artifact@v4 + with: + name: checksums + path: dist/wheel/SHA256SUMS + retention-days: 7 From 5c9f5e5d8d36ef556b57ba47fe5ee2e04af9a5a8 Mon Sep 17 00:00:00 2001 From: Emin Date: Fri, 17 Apr 2026 15:40:21 +0800 Subject: [PATCH 05/18] feat: add release workflow for GitHub Releases --- .github/workflows/release.yml | 98 +++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..fc267bcb0 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,98 @@ +name: Release + +on: + push: + tags: ['v*'] + workflow_dispatch: + inputs: + tag_name: + description: 'Tag to release (e.g., v0.1.0-alpha.1)' + required: true + default: 'v0.1.0-alpha.1' + +permissions: + contents: write + +jobs: + build: + name: Build Wheel + runs-on: ubuntu-latest + container: quay.io/pypa/manylinux_2_34_x86_64 + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 + + - name: Build wheel + uses: ./.github/actions/build-wheel + + - name: Upload wheel artifact + uses: actions/upload-artifact@v4 + with: + name: ecc-tools-wheel + path: | + dist/wheel/repaired/ecc_tools-*.whl + dist/wheel/SHA256SUMS + + release: + name: Create Release + needs: build + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Download wheel artifact + uses: actions/download-artifact@v4 + with: + name: ecc-tools-wheel + path: dist/wheel + + - name: Determine tag version + id: version + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + FULL_TAG="${{ github.event.inputs.tag_name }}" + TAG_VERSION="${FULL_TAG#v}" + else + FULL_TAG="${GITHUB_REF_NAME}" + TAG_VERSION="${GITHUB_REF_NAME#v}" + fi + echo "version=$TAG_VERSION" >> "$GITHUB_OUTPUT" + echo "full_tag=$FULL_TAG" >> "$GITHUB_OUTPUT" + + - name: Generate release notes + id: notes + run: | + PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "") + if [[ -n "$PREV_TAG" ]]; then + RANGE="${PREV_TAG}..HEAD" + else + RANGE="HEAD" + fi + { + echo "## Changes" + echo "" + git log --oneline --no-merges "$RANGE" | sed 's/^/- /' + echo "" + echo "## Checksums" + echo "" + echo '```' + cat dist/wheel/SHA256SUMS + echo '```' + } > release-notes.md + cat release-notes.md + + - name: Create GitHub Release + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release create "${{ steps.version.outputs.full_tag }}" \ + --title "ecc-tools ${{ steps.version.outputs.full_tag }}" \ + --notes-file release-notes.md \ + dist/wheel/repaired/ecc_tools-*.whl \ + dist/wheel/SHA256SUMS From a430f7d972d8fb3c7db40680ab72d8ea14f11d81 Mon Sep 17 00:00:00 2001 From: Emin Date: Fri, 17 Apr 2026 15:42:17 +0800 Subject: [PATCH 06/18] fix: use flat array format for uv build-backend exclude config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Match the format used in the ecc main repo pyproject.toml — source-exclude and wheel-exclude are arrays, not sub-tables with a patterns key. --- pyproject.toml | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1a6e02258..8532998e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,15 +8,21 @@ version = "0.1.0-alpha.1" requires-python = ">=3.11" dependencies = [] -[tool.uv.build-backend] -module-name = ["ecc_tools_bin"] -module-root = "" - -[tool.uv.build-backend.source-exclude] -patterns = ["src/**", "build/**", "scripts/**", ".github/**"] - -[tool.uv.build-backend.wheel-exclude] -patterns = ["src/**", "build/**", "scripts/**", ".github/**"] +[tool.uv] +build-backend.module-name = ["ecc_tools_bin"] +build-backend.module-root = "" +build-backend.source-exclude = [ + "src/**", + "build/**", + "scripts/**", + ".github/**", +] +build-backend.wheel-exclude = [ + "src/**", + "build/**", + "scripts/**", + ".github/**", +] [dependency-groups] dev = ["auditwheel"] From 0789f5a28064690016b9b0e72086efc1fc826e10 Mon Sep 17 00:00:00 2001 From: Emin Date: Fri, 17 Apr 2026 15:58:03 +0800 Subject: [PATCH 07/18] fix: remove git recursive init Signed-off-by: Emin --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 716490025..8d96f2eaf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,8 +17,6 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - with: - submodules: recursive - name: Build wheel uses: ./.github/actions/build-wheel From ceaf9e29dfcee7db9f1f35c028a565012433c4b3 Mon Sep 17 00:00:00 2001 From: Emin Date: Fri, 17 Apr 2026 15:59:07 +0800 Subject: [PATCH 08/18] fix: drop --frozen from uv sync (no lockfile needed) --- .github/actions/build-wheel/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/build-wheel/action.yml b/.github/actions/build-wheel/action.yml index 1bf98196d..cdfd6fa27 100644 --- a/.github/actions/build-wheel/action.yml +++ b/.github/actions/build-wheel/action.yml @@ -32,7 +32,7 @@ runs: - name: Install Python dev dependencies shell: bash - run: uv sync --frozen --all-groups --python 3.11 + run: uv sync --all-groups --python 3.11 - name: Restore CMake build cache uses: actions/cache@v4 From f607e3f27789af14dd8b276ddc42ff5548fd00bb Mon Sep 17 00:00:00 2001 From: Emin Date: Fri, 17 Apr 2026 16:04:27 +0800 Subject: [PATCH 09/18] fix: add upper bound on uv_build version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8532998e8..767f86a49 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["uv_build>=0.8.5"] +requires = ["uv_build>=0.8.5,<0.12"] build-backend = "uv_build" [project] From 092adbccdb65bb8c7b4b57adf291e5d7a27fae57 Mon Sep 17 00:00:00 2001 From: Emin Date: Fri, 17 Apr 2026 16:05:01 +0800 Subject: [PATCH 10/18] fix: add Rust toolchain for verilog parser build --- .github/actions/build-wheel/action.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/actions/build-wheel/action.yml b/.github/actions/build-wheel/action.yml index cdfd6fa27..50a1aecae 100644 --- a/.github/actions/build-wheel/action.yml +++ b/.github/actions/build-wheel/action.yml @@ -21,6 +21,9 @@ runs: shell: bash run: echo "/opt/python/cp311-cp311/bin" >> "$GITHUB_PATH" + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + - name: Setup sccache uses: mozilla-actions/sccache-action@v0.0.9 From 9e8f7c89424eacad71e5b125abeb46f58f4e2da6 Mon Sep 17 00:00:00 2001 From: Emin Date: Fri, 17 Apr 2026 16:38:58 +0800 Subject: [PATCH 11/18] fix: search bin/ in addition to build/ for ecc_py .so CMakeLists.txt sets CMAKE_RUNTIME_OUTPUT_DIRECTORY to ${PROJECT_SOURCE_DIR}/bin, so the linked ecc_py .so ends up in bin/ rather than build/. --- scripts/build-wheel.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/build-wheel.sh b/scripts/build-wheel.sh index f11c08df8..175f7fe25 100755 --- a/scripts/build-wheel.sh +++ b/scripts/build-wheel.sh @@ -62,10 +62,10 @@ cmake --build build --target ecc_py -j"$(nproc)" echo "[build-wheel] Collecting .so artifacts" ECC_PY_ABI="$("$PYTHON3" -c "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))")" -# Find the ecc_py shared object -ecc_py_so="$(find build -name "ecc_py${ECC_PY_ABI}" -print -quit)" +# Find the ecc_py shared object (CMake outputs to bin/ or build/ depending on config) +ecc_py_so="$(find build bin -name "ecc_py${ECC_PY_ABI}" -print -quit 2>/dev/null)" if [[ -z "$ecc_py_so" ]]; then - echo "ERROR: ecc_py${ECC_PY_ABI} not found in build/" >&2 + echo "ERROR: ecc_py${ECC_PY_ABI} not found in build/ or bin/" >&2 exit 1 fi echo "[build-wheel] Found: $ecc_py_so" From de9578fec0f1fb8611d1b38030d2e5bf9bf67123 Mon Sep 17 00:00:00 2001 From: Emin Date: Fri, 17 Apr 2026 17:26:58 +0800 Subject: [PATCH 12/18] feat(pyproject): exclude .gitee dir Signed-off-by: Emin --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 767f86a49..b87dfe0ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,12 +16,14 @@ build-backend.source-exclude = [ "build/**", "scripts/**", ".github/**", + ".gitee/**", ] build-backend.wheel-exclude = [ "src/**", "build/**", "scripts/**", ".github/**", + ".gitee/**", ] [dependency-groups] From a3486dc52748f164e073aef44fa10006ccaa123a Mon Sep 17 00:00:00 2001 From: Emin Date: Fri, 17 Apr 2026 17:32:10 +0800 Subject: [PATCH 13/18] refactor: simplify build-wheel.sh Extract die/require_cmd helpers, remove decorative banners, use idiomatic bash patterns for error handling. --- scripts/build-wheel.sh | 82 +++++++++++++----------------------------- 1 file changed, 25 insertions(+), 57 deletions(-) diff --git a/scripts/build-wheel.sh b/scripts/build-wheel.sh index 175f7fe25..8af6895fa 100755 --- a/scripts/build-wheel.sh +++ b/scripts/build-wheel.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -# Build ecc-tools wheel: CMake build → collect .so → uv build → auditwheel repair → smoke test +# Build ecc-tools wheel: CMake build -> collect .so -> uv build -> auditwheel repair -> smoke test # Intended to run inside manylinux_2_34_x86_64 container or equivalent. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -9,28 +9,23 @@ REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" cd "$REPO_ROOT" -# ── 0. Validate environment ────────────────────────────────────────────── +die() { echo "ERROR: $1" >&2; exit 1; } -if [[ "${OSTYPE:-}" != linux* ]]; then - echo "ERROR: wheel build is only supported on Linux." >&2 - exit 1 -fi +require_cmd() { + command -v "$1" >/dev/null 2>&1 || die "required command not found: $1" +} -PYTHON3="${PYTHON3:-$(command -v python3)}" -if [[ ! -x "$PYTHON3" ]]; then - echo "ERROR: python3 not found." >&2 - exit 1 -fi +# Validate environment +[[ "${OSTYPE:-}" == linux* ]] || die "wheel build is only supported on Linux" + +PYTHON3="${PYTHON3:-$(command -v python3 || true)}" +[[ -x "${PYTHON3:-}" ]] || die "python3 not found" for cmd in cmake ninja auditwheel uv sha256sum; do - if ! command -v "$cmd" >/dev/null 2>&1; then - echo "ERROR: required command not found: $cmd" >&2 - exit 1 - fi + require_cmd "$cmd" done -# ── 1. CMake configure ─────────────────────────────────────────────────── - +# CMake configure echo "[build-wheel] CMake configure" cmake_launcher_args=() if command -v sccache >/dev/null 2>&1; then @@ -52,44 +47,28 @@ cmake -B build -G Ninja \ -DPython3_FIND_STRATEGY=LOCATION \ "${cmake_launcher_args[@]}" -# ── 2. CMake build ─────────────────────────────────────────────────────── - +# CMake build echo "[build-wheel] CMake build ($(nproc) jobs)" cmake --build build --target ecc_py -j"$(nproc)" -# ── 3. Collect artifacts ───────────────────────────────────────────────── - -echo "[build-wheel] Collecting .so artifacts" +# Collect artifacts ECC_PY_ABI="$("$PYTHON3" -c "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))")" - -# Find the ecc_py shared object (CMake outputs to bin/ or build/ depending on config) ecc_py_so="$(find build bin -name "ecc_py${ECC_PY_ABI}" -print -quit 2>/dev/null)" -if [[ -z "$ecc_py_so" ]]; then - echo "ERROR: ecc_py${ECC_PY_ABI} not found in build/ or bin/" >&2 - exit 1 -fi +[[ -n "$ecc_py_so" ]] || die "ecc_py${ECC_PY_ABI} not found in build/ or bin/" echo "[build-wheel] Found: $ecc_py_so" - -# Copy into the Python package directory cp -f "$ecc_py_so" "ecc_tools_bin/" -echo "[build-wheel] Copied ecc_py to ecc_tools_bin/" - -# ── 4. Build raw wheel ────────────────────────────────────────────────── +# Build raw wheel echo "[build-wheel] Building raw wheel" raw_out="dist/wheel/raw" mkdir -p "$raw_out" uv build --wheel --out-dir "$raw_out" raw_whl="$(find "$raw_out" -name 'ecc_tools-*.whl' -print -quit)" -if [[ -z "$raw_whl" ]]; then - echo "ERROR: raw wheel not found in $raw_out" >&2 - exit 1 -fi +[[ -n "$raw_whl" ]] || die "raw wheel not found in $raw_out" echo "[build-wheel] Raw wheel: $raw_whl" -# ── 5. auditwheel repair ──────────────────────────────────────────────── - +# auditwheel repair echo "[build-wheel] auditwheel show/repair" repair_out="dist/wheel/repaired" report_out="dist/wheel/reports" @@ -104,22 +83,15 @@ show_report="$report_out/show.txt" auditwheel repair "$raw_whl" -w "$repair_out" -shopt -s nullglob -repaired_wheels=("$repair_out"/ecc_tools-*.whl) -if [[ ${#repaired_wheels[@]} -eq 0 ]]; then - echo "ERROR: no repaired ecc_tools wheel found in $repair_out" >&2 - exit 1 -fi -shopt -u nullglob - -# ── 6. Smoke test ──────────────────────────────────────────────────────── +repaired_whl="$(find "$repair_out" -name 'ecc_tools-*.whl' -print -quit)" +[[ -n "$repaired_whl" ]] || die "no repaired ecc_tools wheel found in $repair_out" +# Smoke test echo "[build-wheel] Running smoke test" smoke_dir="$(mktemp -d)" -cleanup() { rm -rf "$smoke_dir"; } -trap cleanup EXIT +trap 'rm -rf "$smoke_dir"' EXIT -"$PYTHON3" -m pip install --target "$smoke_dir/site" "${repaired_wheels[@]}" +"$PYTHON3" -m pip install --target "$smoke_dir/site" "$repair_out"/ecc_tools-*.whl PYTHONPATH="$smoke_dir/site" "$PYTHON3" -c " from ecc_tools_bin import ecc_py @@ -132,12 +104,8 @@ assert not missing, f'missing or non-callable bindings: {missing}' print(f'ecc_py smoke test passed: {len(required)} bindings verified') " -# ── 7. Checksums ───────────────────────────────────────────────────────── - -( - cd "$repair_out" - sha256sum ./*.whl > "$REPO_ROOT/dist/wheel/SHA256SUMS" -) +# Checksums +sha256sum "$repair_out"/*.whl > dist/wheel/SHA256SUMS echo "[build-wheel] done" echo "[build-wheel] raw wheels: $raw_out" From 9e8b24d1a17af6ea34e2f9b50753165ae7a4936f Mon Sep 17 00:00:00 2001 From: Emin Date: Mon, 20 Apr 2026 10:36:18 +0800 Subject: [PATCH 14/18] chore: downgrade version to 0.1.0-alpha Signed-off-by: Emin --- .github/workflows/release.yml | 4 ++-- pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fc267bcb0..6bf0cedf0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,9 +6,9 @@ on: workflow_dispatch: inputs: tag_name: - description: 'Tag to release (e.g., v0.1.0-alpha.1)' + description: 'Tag to release (e.g., v0.1.0-alpha)' required: true - default: 'v0.1.0-alpha.1' + default: 'v0.1.0-alpha' permissions: contents: write diff --git a/pyproject.toml b/pyproject.toml index b87dfe0ee..f320872a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "uv_build" [project] name = "ecc-tools" -version = "0.1.0-alpha.1" +version = "0.1.0-alpha" requires-python = ">=3.11" dependencies = [] From 779473567d9cf953cda822058ab4e080feadd03e Mon Sep 17 00:00:00 2001 From: Emin Date: Mon, 20 Apr 2026 10:43:00 +0800 Subject: [PATCH 15/18] refactor: move build-wheel.sh from scripts/ to .github/scripts/ scripts/ is gitignored (upstream iEDA runtime data), so CI scripts don't belong there. Also narrow .gitignore rule from `scripts` to `/scripts` to avoid ignoring .github/scripts/. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/actions/build-wheel/action.yml | 2 +- {scripts => .github/scripts}/build-wheel.sh | 0 .gitignore | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename {scripts => .github/scripts}/build-wheel.sh (100%) diff --git a/.github/actions/build-wheel/action.yml b/.github/actions/build-wheel/action.yml index 50a1aecae..7d3437e24 100644 --- a/.github/actions/build-wheel/action.yml +++ b/.github/actions/build-wheel/action.yml @@ -49,4 +49,4 @@ runs: shell: bash env: SCCACHE_GHA_ENABLED: "true" - run: scripts/build-wheel.sh + run: .github/scripts/build-wheel.sh diff --git a/scripts/build-wheel.sh b/.github/scripts/build-wheel.sh similarity index 100% rename from scripts/build-wheel.sh rename to .github/scripts/build-wheel.sh diff --git a/.gitignore b/.gitignore index 27fb474a9..09f3e91f2 100755 --- a/.gitignore +++ b/.gitignore @@ -25,7 +25,7 @@ rpt*/ target/ *-stamp/ tmp/ -scripts +/scripts *.patch CMakePresets.json .venv/ From e8126d5d85aab19739d22348b3f41786c10390b9 Mon Sep 17 00:00:00 2001 From: Emin Date: Mon, 20 Apr 2026 10:46:25 +0800 Subject: [PATCH 16/18] fix: correct REPO_ROOT path after script relocation .github/scripts/ is two levels below repo root, not one. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/scripts/build-wheel.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/build-wheel.sh b/.github/scripts/build-wheel.sh index 8af6895fa..689c91151 100755 --- a/.github/scripts/build-wheel.sh +++ b/.github/scripts/build-wheel.sh @@ -5,7 +5,7 @@ set -euo pipefail # Intended to run inside manylinux_2_34_x86_64 container or equivalent. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" cd "$REPO_ROOT" From 856b97ec38b6d9f36ade5f2d863a38c044d6496d Mon Sep 17 00:00:00 2001 From: Emin Date: Mon, 20 Apr 2026 11:07:55 +0800 Subject: [PATCH 17/18] fix(ci): add .venv/bin to PATH so auditwheel is discoverable uv sync installs auditwheel into .venv/bin/ which is not on the default PATH. Append it via $GITHUB_PATH after uv sync. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/actions/build-wheel/action.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/actions/build-wheel/action.yml b/.github/actions/build-wheel/action.yml index 7d3437e24..9977b3124 100644 --- a/.github/actions/build-wheel/action.yml +++ b/.github/actions/build-wheel/action.yml @@ -35,7 +35,9 @@ runs: - name: Install Python dev dependencies shell: bash - run: uv sync --all-groups --python 3.11 + run: | + uv sync --all-groups --python 3.11 + echo "${{ github.workspace }}/.venv/bin" >> "$GITHUB_PATH" - name: Restore CMake build cache uses: actions/cache@v4 From 7c873f235c13ef7a2e41dc624cc58384c839c915 Mon Sep 17 00:00:00 2001 From: Emin Date: Mon, 20 Apr 2026 11:09:55 +0800 Subject: [PATCH 18/18] fix(ci): checkout dispatch tag ref in release workflow workflow_dispatch provides a tag_name but checkout defaulted to the repo's default branch, risking a mismatch between built wheel and published tag. Use inputs.tag_name when available, fall back to github.ref for tag-push triggers. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6bf0cedf0..2eab8015a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,6 +22,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: + ref: ${{ inputs.tag_name || github.ref }} submodules: recursive fetch-depth: 0 @@ -44,6 +45,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: + ref: ${{ inputs.tag_name || github.ref }} fetch-depth: 0 - name: Download wheel artifact