diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e955c1..b3c2736 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,76 +2,37 @@ name: CI on: workflow_dispatch: - # push: - # branches: - # - 'main' - # pull_request: + push: + branches: + - main + pull_request: concurrency: group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true +permissions: + contents: read + env: - GH_TOKEN: ${{ secrets.GH_TOKEN }} - SCCACHE_GHA_ENABLED: "true" - RUSTC_WRAPPER: "sccache" UV_PYTHON_PREFERENCE: only-managed + ECC_DREAMPLACE_WHEEL_URL: https://github.com/openecos-projects/ecc-dreamplace/releases/download/v0.1.0-alpha.1/ecc_dreamplace-0.1.0a1-py3-none-manylinux_2_34_x86_64.whl + ECC_TOOLS_WHEEL_URL: https://github.com/openecos-projects/ecc-tools/releases/download/v0.1.0-alpha.1/ecc_tools-0.1.0a1-py3-none-manylinux_2_34_x86_64.whl jobs: - build-package: - name: Build Package + ci: + name: Checks And Build runs-on: ubuntu-latest steps: - - name: Free Disk Space (Ubuntu) - uses: jlumbroso/free-disk-space@main - with: - # this might remove tools that are actually needed, - # if set to "true" but frees about 6 GB - tool-cache: true - - # feel free to set to "false" if necessary for your workflow - android: true - dotnet: true - haskell: true - large-packages: true - docker-images: true - swap-storage: false - name: Checkout uses: actions/checkout@v4 with: - token: ${{ env.GH_TOKEN }} - - name: Run sccache-cache - uses: mozilla-actions/sccache-action@v0.0.9 - - - name: Convert SSH to HTTPS in gitmodules - run: sed -i 's/git@github.com:/https:\/\/github.com\//' .gitmodules - - - name: Configure git credentials - run: | - git config --global url."https://${{ env.GH_TOKEN }}@github.com/".insteadOf "https://github.com/" - - - name: Checkout submodules - run: git submodule update --init --recursive + submodules: recursive - name: Setup Python uses: actions/setup-python@v5 with: - python-version: '3.11' - - - name: Setup Rust - uses: dtolnay/rust-toolchain@stable - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: latest - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: 'pnpm' - cache-dependency-path: gui/pnpm-lock.yaml + python-version: "3.11" - name: Setup uv uses: astral-sh/setup-uv@v3 @@ -79,31 +40,6 @@ jobs: version: latest enable-cache: "true" - - name: Install Python dependencies (uv) - run: uv sync --frozen --all-groups --python 3.11 - - - name: Install ecc-dreamplace from release wheel - run: > - uv pip install --no-deps - https://github.com/openecos-projects/ecc-dreamplace/releases/download/v0.1.0-alpha.1/ecc_dreamplace-0.1.0a1-py3-none-manylinux_2_34_x86_64.whl - - - name: Install frontend dependencies - working-directory: gui - run: pnpm install - - - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install -y cmake ninja-build build-essential unzip pkg-config \ - libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev libsoup-3.0-dev libfuse2 \ - patchelf squashfs-tools desktop-file-utils libwebkit2gtk-4.1-dev - - sudo apt-get install -y g++-10 \ - tcl-dev libgflags-dev libgoogle-glog-dev libboost-all-dev libgtest-dev \ - flex libeigen3-dev libunwind-dev libmetis-dev libgmp-dev bison \ - libhwloc-dev libcairo2-dev libcurl4-openssl-dev libtbb-dev git \ - libftdi1-2 libhidapi-hidraw0 librsvg2-dev - - name: Setup Bazel uses: bazel-contrib/setup-bazel@0.14.0 with: @@ -111,28 +47,45 @@ jobs: disk-cache: ${{ github.workflow }} repository-cache: true - - name: Build release (packaging test) - env: - ENABLE_OSS_CAD_SUITE: "true" - CARGO_PROFILE_RELEASE_STRIP: "false" - APPIMAGE_EXTRACT_AND_RUN: "1" + - name: Install Python dependencies + run: uv sync --frozen --all-groups --python 3.11 + + - name: Install release wheels for native toolchains run: | - bazel build //:release_bundle - mkdir -p _bundle_out - tar -xf bazel-bin/tauri_bundle/tauri_bundle.tar -C _bundle_out + uv pip install --python .venv/bin/python --no-deps "${ECC_DREAMPLACE_WHEEL_URL}" + uv pip install --python .venv/bin/python --no-deps "${ECC_TOOLS_WHEEL_URL}" - - name: Upload AppImage artifacts - if: always() - uses: actions/upload-artifact@v4 - with: - name: appimage - if-no-files-found: ignore - path: _bundle_out/**/*.AppImage + - name: Smoke test native toolchain wheels + run: | + .venv/bin/python - <<'PY' + import ecc_tools_bin.ecc_py # noqa: F401 + from dreamplace.Params import Params # noqa: F401 + from dreamplace.Placer import PlacementEngine # noqa: F401 + + print("ecc-tools and ecc-dreamplace imports passed") + PY + + # - name: Ruff format check + # run: uv run ruff format --check chipcompiler test + + # - name: Ruff lint + # run: uv run ruff check chipcompiler test + + # - name: Pyright + # run: uv run pyright chipcompiler + + # - name: Pytest + # run: uv run pytest test/ --cov=chipcompiler --cov-report=term-missing + + - name: Build distributable wheel + run: bazel run //:build_wheel - - name: Upload deb artifacts + - name: Upload wheel artifacts if: always() uses: actions/upload-artifact@v4 with: - name: deb + name: ecc-wheel if-no-files-found: ignore - path: _bundle_out/**/*.deb + path: | + dist/wheel/repaired/*.whl + dist/wheel/SHA256SUMS diff --git a/BUILD.bazel b/BUILD.bazel index f7623ce..2fcedd9 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1,27 +1,5 @@ -load("@ecos-bazel//rules:python_packaging.bzl", "python_pyinstaller_bundle") - package(default_visibility = ["//visibility:public"]) -exports_files([ - "ecc.spec", -]) - -python_pyinstaller_bundle( - name = "server_bundle", - spec_file = "ecc.spec", - srcs = [ - "README.md", - "pyproject.toml", - "uv.lock", - "//chipcompiler:chipcompiler_python_sources", - "//chipcompiler:chipcompiler_runtime_data", - ], - runtime_bundle = "//chipcompiler/thirdparty:ecc_bundle", - output_name = "chipcompiler", - pyinstaller = "//bazel:pyinstaller", -) - - alias( name = "prepare_dev", actual = "//bazel/scripts:prepare_dev", @@ -34,14 +12,12 @@ genrule( "README.md", "//chipcompiler:chipcompiler_python_sources", "//chipcompiler:chipcompiler_runtime_data", - "//chipcompiler/thirdparty:ecc_bundle", ], outs = ["raw_wheel/ecc-0.1.0-py3-none-any.whl"], tools = ["@multitool//tools/uv"], cmd = """ set -euo pipefail UV="$(execpath @multitool//tools/uv)" - BUNDLE="$(location //chipcompiler/thirdparty:ecc_bundle)" STAGE=$$(mktemp -d) trap 'rm -rf "$$STAGE"' EXIT for src in $(locations pyproject.toml) \ @@ -51,7 +27,6 @@ genrule( mkdir -p "$$STAGE/$$(dirname "$$src")" cp "$$src" "$$STAGE/$$src" done - tar -xf "$$BUNDLE" -C "$$STAGE" "$$UV" build --wheel --directory "$$STAGE" --out-dir "$$STAGE/_dist" --cache-dir "$$STAGE/.uv-cache" cp "$$STAGE"/_dist/*.whl "$@" """, diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 442cd0f..30eb3f8 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -182,7 +182,7 @@ }, "@@buildifier_prebuilt+//:defs.bzl%buildifier_prebuilt_deps_extension": { "general": { - "bzlTransitiveDigest": "AiLhk8M3bYsCKureJxwsWsnViRPlVmt1v8pnOgBAUzw=", + "bzlTransitiveDigest": "RLgcFR7CQZcdQY0jjtq8gpqxhHTnuZL3H7smk0RNeyI=", "usagesDigest": "m+RORtK3MOrJs2auGj/7mY7N11R7swVsHYHg1jls5hs=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -527,7 +527,7 @@ }, "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { "general": { - "bzlTransitiveDigest": "rL/34P1aFDq2GqVC2zCFgQ8nTuOC6ziogocpvG50Qz8=", + "bzlTransitiveDigest": "nvW/NrBXlAmiQw99EMGKkLaD2KbNp2mQDlxdfpr+0Ls=", "usagesDigest": "QI2z8ZUR+mqtbwsf2fLqYdJAkPOHdOV+tF2yVAUgRzw=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -591,7 +591,7 @@ }, "@@rules_multitool+//multitool:extension.bzl%multitool": { "general": { - "bzlTransitiveDigest": "OmyUzwsloBp1uNITj7Bu1ytDkaGAuF0eMC4A0GZV/Kg=", + "bzlTransitiveDigest": "4sgFCqOYi5PKLD3JgbkSx9RNVoOeRM4EFrTJ16K/9iU=", "usagesDigest": "MReaPRik0/8UKLZa91fyNWJFYAkjl4qR3j46gmIFYKA=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -687,7 +687,7 @@ }, "@@rules_python+//python/extensions:config.bzl%config": { "general": { - "bzlTransitiveDigest": "W97kKxM+lW7l/kO0rQa7Jm31CA1j+W1bNHGKjwX5xMg=", + "bzlTransitiveDigest": "9cZw51LLMu2V/jKcxvnA1pBg58ZgQEDuMni3CbNZSRg=", "usagesDigest": "ZVSXMAGpD+xzVNPuvF1IoLBkty7TROO0+akMapt1pAg=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -922,7 +922,7 @@ }, "@@rules_python+//python/uv:uv.bzl%uv": { "general": { - "bzlTransitiveDigest": "zyNsrbgVKwpA0B3zI84imAfuC424VSzYNPgjr/HJy5M=", + "bzlTransitiveDigest": "ijW9KS7qsIY+yBVvJ+Nr1mzwQox09j13DnE3iIwaeTM=", "usagesDigest": "H8dQoNZcoqP+Mu0tHZTi4KHATzvNkM5ePuEqoQdklIU=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, diff --git a/bazel/BUILD.bazel b/bazel/BUILD.bazel index 692c8ae..ffd0fb0 100644 --- a/bazel/BUILD.bazel +++ b/bazel/BUILD.bazel @@ -1,9 +1 @@ -load("@rules_shell//shell:sh_binary.bzl", "sh_binary") - package(default_visibility = ["//visibility:public"]) - -# Alias to ecos-bazel-rules pyinstaller tool -alias( - name = "pyinstaller", - actual = "@ecos-bazel//tools:pyinstaller", -) diff --git a/bazel/scripts/BUILD.bazel b/bazel/scripts/BUILD.bazel index 949775c..5bc06d4 100644 --- a/bazel/scripts/BUILD.bazel +++ b/bazel/scripts/BUILD.bazel @@ -22,11 +22,13 @@ sh_binary( srcs = ["build-wheel.sh"], data = [ "//:raw_wheel", + "@multitool//tools/uv", "@python_3_11//:python3", ], args = [ "$(rlocationpath //:raw_wheel)", "$(rlocationpath @python_3_11//:python3)", + "$(rlocationpath @multitool//tools/uv)", ], ) diff --git a/bazel/scripts/build-wheel.sh b/bazel/scripts/build-wheel.sh index ecc13fd..f8c1ab7 100755 --- a/bazel/scripts/build-wheel.sh +++ b/bazel/scripts/build-wheel.sh @@ -22,16 +22,6 @@ if ! command -v sha256sum >/dev/null 2>&1; then exit 1 fi -auditwheel_bin="" -if [[ -x "${WS}/.venv/bin/auditwheel" ]]; then - auditwheel_bin="${WS}/.venv/bin/auditwheel" -elif command -v auditwheel >/dev/null 2>&1; then - auditwheel_bin="$(command -v auditwheel)" -else - echo "ERROR: auditwheel not found. Install dev deps: uv sync --frozen --all-groups --python 3.11" >&2 - exit 1 -fi - RF="${RUNFILES_DIR:-${BASH_SOURCE[0]}.runfiles}" raw_whl="$RF/$1" if [[ ! -f "$raw_whl" ]]; then @@ -45,72 +35,52 @@ if [[ ! -x "$PYTHON3" ]]; then exit 1 fi +UV="$RF/$3" +if [[ ! -x "$UV" ]]; then + echo "ERROR: uv not found in runfiles: $UV" >&2 + exit 1 +fi + out_root="$WS/dist/wheel" -raw_out="$out_root/raw" -repair_out="$out_root/repaired" -report_out="$out_root/reports" -mkdir -p "$raw_out" "$repair_out" "$report_out" +out_dir="$out_root/repaired" +mkdir -p "$out_dir" # Clean only ecc wheels to preserve prior build -rm -f "$raw_out"/ecc-*.whl "$repair_out"/ecc-*.whl -show_report="$report_out/show.txt" -: > "$show_report" - -smoke_dir="$(mktemp -d)" -cleanup() { rm -rf "$smoke_dir"; } -trap cleanup EXIT +rm -f "$out_dir"/ecc-*.whl -cp "$raw_whl" "$raw_out/" -local_raw_whl="$raw_out/$(basename "$raw_whl")" +cp "$raw_whl" "$out_dir/" +final_whl="$out_dir/$(basename "$raw_whl")" -echo "[wheel] running auditwheel show/repair" -if [[ ! -f "$local_raw_whl" ]]; then - echo "ERROR: raw wheel not found after copy: $local_raw_whl" >&2 - exit 1 -fi - -for whl in "$local_raw_whl"; do - { - echo "=== $(basename "$whl") ===" - "$auditwheel_bin" show "$whl" - echo - } >> "$show_report" - "$auditwheel_bin" repair "$whl" -w "$repair_out" -done - -# Find only the ecc repaired wheel -shopt -s nullglob -repaired_wheels=("$repair_out"/ecc-*.whl) -if [[ ${#repaired_wheels[@]} -eq 0 ]]; then - echo "ERROR: no repaired ecc wheel found in $repair_out" >&2 - exit 1 -fi -shopt -u nullglob +echo "[wheel] ecc wheel is pure Python (ecc_py bindings come from ecc-tools wheel)" +echo "[wheel] skipping auditwheel — no native code in this wheel" +# Smoke test: verify the Python package is importable echo "[wheel] running smoke test" -"$PYTHON3" -m pip install --target "$smoke_dir/site" "${repaired_wheels[@]}" -PYTHONPATH="$smoke_dir/site" "$PYTHON3" -c " -from chipcompiler.tools.ecc.bin import ecc_py +smoke_dir="$(mktemp -d)" +trap 'rm -rf "$smoke_dir"' EXIT -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}' +# Use a temp venv (via uv) because hermetic Python lacks ensurepip. +"$UV" venv --python "$PYTHON3" "$smoke_dir/venv" +venv_python="$smoke_dir/venv/bin/python" -from chipcompiler.tools.ecc.module import ECCToolsModule -m = ECCToolsModule() -assert m.ecc is ecc_py +# ecc-dreamplace and ecc-tools are not on PyPI; install them from GitHub URLs first, +# then install the local ecc wheel so uv resolves the remaining PyPI deps. +"$UV" pip install --python "$venv_python" \ + "https://github.com/openecos-projects/ecc-tools/releases/download/v0.1.0-alpha/ecc_tools-0.1.0a0-py3-none-manylinux_2_34_x86_64.whl" \ + "https://github.com/openecos-projects/ecc-dreamplace/releases/download/v0.1.0-alpha.1/ecc_dreamplace-0.1.0a1-py3-none-manylinux_2_34_x86_64.whl" \ + "$final_whl" -print(f'ecc_py smoke test passed: {len(required)} bindings verified') +"$venv_python" -c " +import chipcompiler +from chipcompiler.tools.ecc.module import ECCToolsModule +assert chipcompiler.__version__ == '0.1.0', f'unexpected version: {chipcompiler.__version__}' +print('ecc wheel smoke test passed: chipcompiler package importable') " ( - cd "$repair_out" + cd "$out_dir" sha256sum ./*.whl > "$out_root/SHA256SUMS" ) echo "[wheel] done" -echo "[wheel] raw wheels: $raw_out" -echo "[wheel] repaired wheels: $repair_out" -echo "[wheel] report: $show_report" -echo "[wheel] checksums: $out_root/SHA256SUMS" +echo "[wheel] wheel: $out_dir" +echo "[wheel] checksums: $out_root/SHA256SUMS" diff --git a/bazel/scripts/prepare-dev.sh b/bazel/scripts/prepare-dev.sh index 8011294..b113a8a 100755 --- a/bazel/scripts/prepare-dev.sh +++ b/bazel/scripts/prepare-dev.sh @@ -38,8 +38,7 @@ echo -e "${GREEN}Dev environment is ready.${RESET}" echo -e "${CYAN}Run 'source .venv/bin/activate' to activate the venv.${RESET}" echo "" echo "Next steps:" -echo " bazel build //:server_bundle Build API server executable" -echo " bazel build //:tauri_bundle Build Tauri GUI bundle" -echo " bazel build //:release_bundle Build release artifact" +echo " bazel build //:raw_wheel Build Python wheel" +echo " bazel run //:build_wheel Build distributable wheel + smoke test" echo " uv run pytest test/ Run tests" echo " uv run chipcompiler --reload Start API server (dev mode, port 8765)" diff --git a/chipcompiler/tools/ecc/module.py b/chipcompiler/tools/ecc/module.py index 2154ddd..b5b33af 100644 --- a/chipcompiler/tools/ecc/module.py +++ b/chipcompiler/tools/ecc/module.py @@ -9,18 +9,20 @@ class ECCToolsModule: """ def __init__(self): try: - from chipcompiler.tools.ecc.utility import is_eda_exist - if is_eda_exist(): + from ecc_tools_bin import ecc_py as ecc + except ImportError: + try: from chipcompiler.tools.ecc.bin import ecc_py as ecc - except ImportError as exc: - ecc_bin_dir = Path(__file__).resolve().parent / "bin" - candidates = sorted(p.name for p in ecc_bin_dir.glob("ecc_py*.so")) - raise ImportError( - "ecc tool is not installed or not found. " - f"Import error: {exc}. " - f"Available ecc_py binaries in {ecc_bin_dir}: {candidates}" - ) from exc - + except ImportError as exc: + ecc_bin_dir = Path(__file__).resolve().parent / "bin" + candidates = sorted(p.name for p in ecc_bin_dir.glob("ecc_py*.so")) + raise ImportError( + "ecc-tools is not installed. Install the ecc-tools wheel or " + "build from source with: bazel run //:prepare_dev. " + f"Import error: {exc}. " + f"Available ecc_py binaries in {ecc_bin_dir}: {candidates}" + ) from exc + self.ecc = ecc def get_ecc(self): diff --git a/docs/development.md b/docs/development.md index 73e57dc..3212564 100644 --- a/docs/development.md +++ b/docs/development.md @@ -313,7 +313,7 @@ If the tool has compiled artifacts (`.so`, generated configs): ### 3. Release build -Add runtime artifacts to `//chipcompiler:chipcompiler_runtime_data` (consumed by `raw_wheel` and `server_bundle`). For Nix, add to flake build inputs. +Add runtime artifacts to `//chipcompiler:chipcompiler_runtime_data` (consumed by `raw_wheel`). For Nix, add to flake build inputs. ### 4. Bazel sandbox deps diff --git a/uv.lock b/uv.lock index 48cb68b..1ab8297 100644 --- a/uv.lock +++ b/uv.lock @@ -533,6 +533,11 @@ wheels = [ { url = "https://github.com/openecos-projects/ecc-dreamplace/releases/download/v0.1.0-alpha.1/ecc_dreamplace-0.1.0a1-py3-none-manylinux_2_34_x86_64.whl", hash = "sha256:212139c43f825498968eda10309959b99cd93aec0744d182a17bb5d34b53145e" }, ] +[package.dev-dependencies] +dev = [ + { name = "auditwheel" }, +] + [package.metadata] requires-dist = [ { name = "cairocffi", specifier = ">=0.9.0" }, @@ -554,6 +559,9 @@ requires-dist = [ { name = "xgboost", specifier = ">=1.5.1" }, ] +[package.metadata.requires-dev] +dev = [{ name = "auditwheel" }] + [[package]] name = "ecc-tools" version = "0.1.0a0"