From c262e1ba269fbe981eb56b17ad7ff175e2b6f5bb Mon Sep 17 00:00:00 2001 From: Alex Reinking Date: Tue, 1 Sep 2026 11:17:26 -0400 Subject: [PATCH 1/2] [halidoscope] Add pip packaging via maturin Packages the Tauri binary (GUI + CLI) as a platform wheel using maturin's bin bindings, so `pip install halidoscope` puts it on PATH. The compiled binary only embeds dist/ instead of falling back to Tauri's dev server when built with the tauri/custom-protocol Cargo feature, which plain `cargo build`/`maturin build` don't enable by default -- pyproject.toml sets it explicitly. Halidoscope's version is kept in lockstep with Halide's release version via tbump, matching the other Python packages, but it has no dependency on them: it's a standalone companion tool. Adds a CI workflow that builds and smoke-tests wheels for all four platforms; wiring it into pip.yml's nightly publish job is left for a follow-up once this build has been verified in CI. Also relabels a handful of pre-existing usage examples in README.md from bash to text fences: they use notation that mdformat-shfmt was silently mis-parsing as shell redirection syntax and corrupting. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/pip-halidoscope.yml | 103 ++++++++++++++++++++ pyproject.toml | 12 +++ tools/halidoscope/README.md | 50 ++++++++-- tools/halidoscope/package.json | 2 +- tools/halidoscope/pyproject.toml | 42 ++++++++ tools/halidoscope/src-tauri/Cargo.lock | 2 +- tools/halidoscope/src-tauri/Cargo.toml | 2 +- tools/halidoscope/src-tauri/tauri.conf.json | 2 +- 8 files changed, 205 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/pip-halidoscope.yml create mode 100644 tools/halidoscope/pyproject.toml diff --git a/.github/workflows/pip-halidoscope.yml b/.github/workflows/pip-halidoscope.yml new file mode 100644 index 000000000000..02486d757f43 --- /dev/null +++ b/.github/workflows/pip-halidoscope.yml @@ -0,0 +1,103 @@ +name: Build Halidoscope wheels + +on: + workflow_dispatch: + pull_request: + paths: + - 'tools/halidoscope/**' + - '.github/workflows/pip-halidoscope.yml' + push: + branches: [ main ] + paths: + - 'tools/halidoscope/**' + - '.github/workflows/pip-halidoscope.yml' + +concurrency: + group: '${{ github.workflow }}-${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' + cancel-in-progress: true + +permissions: + contents: read + +# Halidoscope is a companion CLI/GUI packaged independently of Halide's own +# Python wheels (halide, halide-bin, halide-runtime): it doesn't depend on +# them, and this workflow doesn't participate in pip.yml's publish job. Its +# version is kept in sync with Halide's release version via tbump (see +# tool.tbump.file entries for tools/halidoscope/src-tauri/Cargo.toml et al. +# in the top-level pyproject.toml), not via a per-commit dynamic version. +jobs: + build: + name: Build wheel for ${{ matrix.platform_tag }} + + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + platform_tag: manylinux_x86_64 + - os: windows-latest + platform_tag: win_amd64 + - os: macos-15-intel + platform_tag: macosx_x86_64 + - os: macos-15 + platform_tag: macosx_arm64 + + steps: + - uses: actions/checkout@v7 + + - name: Install Tauri's Linux system dependencies + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y \ + libwebkit2gtk-4.1-dev \ + libayatana-appindicator3-dev \ + librsvg2-dev \ + patchelf \ + build-essential \ + libxdo-dev \ + libssl-dev \ + libsoup-3.0-dev + + - uses: dtolnay/rust-toolchain@stable + + - uses: pnpm/action-setup@v4 + with: + version: 11 + package_json_file: tools/halidoscope/package.json + + - uses: actions/setup-node@v5 + with: + node-version: 22 + cache: pnpm + cache-dependency-path: tools/halidoscope/pnpm-lock.yaml + + - uses: astral-sh/setup-uv@v7 + + - name: Build frontend + working-directory: tools/halidoscope + run: | + pnpm install --frozen-lockfile + pnpm build + + - name: Build wheel + working-directory: tools/halidoscope + run: uvx maturin build --release --out wheelhouse + + - name: Smoke-test wheel + working-directory: tools/halidoscope + shell: bash + run: | + uv venv .smoke-test-venv + uv pip install --python .smoke-test-venv --no-index --find-links wheelhouse halidoscope + if [[ "${{ runner.os }}" == "Windows" ]]; then + ./.smoke-test-venv/Scripts/halidoscope --version + else + ./.smoke-test-venv/bin/halidoscope --version + fi + + - uses: actions/upload-artifact@v7 + with: + name: wheels-halidoscope-${{ matrix.platform_tag }} + path: tools/halidoscope/wheelhouse/*.whl diff --git a/pyproject.toml b/pyproject.toml index 7a259de5cc88..1f3e82390958 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -130,6 +130,18 @@ src = "vcpkg.json" [[tool.tbump.file]] src = "apps/vcpkg.json" +[[tool.tbump.file]] +src = "tools/halidoscope/src-tauri/Cargo.toml" +search = 'version = "{current_version}"' + +[[tool.tbump.file]] +src = "tools/halidoscope/src-tauri/tauri.conf.json" +search = '"version": "{current_version}"' + +[[tool.tbump.file]] +src = "tools/halidoscope/package.json" +search = '"version": "{current_version}"' + [[tool.tbump.file]] src = "src/runtime/HalideRuntime.h" version_template = "{major}" diff --git a/tools/halidoscope/README.md b/tools/halidoscope/README.md index b0f61dc0e2f4..e195f12cb6a9 100644 --- a/tools/halidoscope/README.md +++ b/tools/halidoscope/README.md @@ -15,6 +15,22 @@ You'll need a few prerequisites to get everything working. 4. [PNPM](https://pnpm.io/), a space-efficient package manager for the JavaScript ecosystem. +## Installing Halidoscope + +The easiest way to get `halidoscope` on your `PATH` is via pip: + +```bash +pip install halidoscope +``` + +This installs a prebuilt binary wheel containing both the GUI and the CLI. Note +that pip cannot install the system webview Halidoscope's GUI renders into +(WebKitGTK on Linux, WebView2 on Windows, WKWebView on macOS) -- `halidoscope`'s +CLI subcommands (`list`, `stats`, `dot`, `snapshot`) work anywhere the wheel +installs, but the interactive GUI additionally needs +[Tauri's system dependencies](https://v2.tauri.app/start/prerequisites/#system-dependencies) +present on Linux; macOS and Windows ship a compatible webview out of the box. + ## Building Halidoscope To get a production build locally, run the following two commands: @@ -32,6 +48,25 @@ symlink this executable to any directory on your `PATH`. On Unix systems: ln -sf tools/halidoscope/src-tauri/target/release/halidoscope /some/dir/on/your/path/halidoscope ``` +### Building the pip package locally + +Halidoscope's wheel is built with [maturin](https://www.maturin.rs/), which +compiles `src-tauri`'s `halidoscope` binary and packages it directly (no Python +extension module involved). The frontend must be built first, since the binary +embeds `dist/` at compile time: + +```bash +pnpm install +pnpm build +pip install maturin +maturin build --release +``` + +`pyproject.toml` enables the `tauri/custom-protocol` Cargo feature for this +build. Without it, the compiled binary always tries to load its UI from Tauri's +dev server instead of the files embedded from `dist/`, so the resulting wheel's +GUI can't launch outside of `pnpm tauri dev`. + ## Using Halidoscope ### Calling Halidoscope from a Halide program @@ -83,14 +118,14 @@ Halidoscope directly from the command line to launch the GUI. To work with a pre-recorded trace, simply specify the path to a Halide trace binary file via the `--trace` flag. -```bash +```text halidoscope --trace ``` If you'd also like to visualize a pre-recorded profile JSON file, pass the path to that file via the `--profile` flag. Note that `--trace` is always required. -```bash +```text halidoscope --trace --profile ``` @@ -103,7 +138,7 @@ your Halide pipeline. List the `Func`s in a trace, along with their dimensionality. -```bash +```text halidoscope list --trace [--json] ``` @@ -115,7 +150,7 @@ halidoscope list --trace [--json] Print statistics (minimum/maximum coordinates, minimum/maximum value, maximum store/load counts, and thread count) for one or all `Func`s in a trace. -```bash +```text halidoscope stats --trace [--func ] [--json] ``` @@ -129,7 +164,7 @@ halidoscope stats --trace [--func ] [--json] Generate a [Graphviz DOT](https://graphviz.org/doc/info/lang.html) representation of the pipeline's dataflow graph. -```bash +```text halidoscope dot --trace [destination] ``` @@ -142,7 +177,7 @@ halidoscope dot --trace [destination] Snapshot a `Func`'s values at a given packet index for a given render mode, writing the underlying data to a JSON file. -```bash +```text halidoscope snapshot --trace --func [--packet-index ] [--mode ] ``` @@ -162,6 +197,9 @@ commands. ```bash pnpm install +``` + +```text pnpm tauri dev -- -- --trace [--profile ] ``` diff --git a/tools/halidoscope/package.json b/tools/halidoscope/package.json index e39f90d415a4..7587ef315533 100644 --- a/tools/halidoscope/package.json +++ b/tools/halidoscope/package.json @@ -1,7 +1,7 @@ { "name": "halidoscope", "private": true, - "version": "0.1.0", + "version": "22.0.0", "type": "module", "scripts": { "dev": "vite", diff --git a/tools/halidoscope/pyproject.toml b/tools/halidoscope/pyproject.toml new file mode 100644 index 000000000000..bc24fbef93ff --- /dev/null +++ b/tools/halidoscope/pyproject.toml @@ -0,0 +1,42 @@ +[build-system] +requires = ["maturin>=1.9,<2"] +build-backend = "maturin" + +[project] +name = "halidoscope" +authors = [{ name = "Parker Ziegler", email = "pziegler@adobe.com" }] +maintainers = [ + { name = "The Halide team", email = "halide-dev@lists.csail.mit.edu" }, +] +description = "An interactive GUI and CLI for working with Halide traces." +license = "MIT" +readme = "README.md" +requires-python = ">=3.10" +dynamic = ["version"] +keywords = ["halide", "trace", "profiling", "visualization", "gui"] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Natural Language :: English", + "Operating System :: MacOS", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX", + "Programming Language :: Rust", + "Topic :: Scientific/Engineering :: Visualization", + "Topic :: Software Development :: Debuggers", +] + +[project.urls] +Homepage = "https://halide-lang.org" +Documentation = "https://github.com/halide/Halide/blob/main/tools/halidoscope/README.md" +Issues = "https://github.com/halide/Halide/issues" +Repository = "https://github.com/halide/Halide.git" + +[tool.maturin] +manifest-path = "src-tauri/Cargo.toml" +bindings = "bin" +strip = true +# Without this, the release binary falls back to loading the frontend from +# devUrl instead of embedding `dist/` -- see tauri/custom-protocol in +# src-tauri's Cargo.toml dependency graph. +features = ["tauri/custom-protocol"] diff --git a/tools/halidoscope/src-tauri/Cargo.lock b/tools/halidoscope/src-tauri/Cargo.lock index 61a4ba92f546..fcebe0cbeaab 100644 --- a/tools/halidoscope/src-tauri/Cargo.lock +++ b/tools/halidoscope/src-tauri/Cargo.lock @@ -1603,7 +1603,7 @@ dependencies = [ [[package]] name = "halidoscope" -version = "0.1.0" +version = "22.0.0" dependencies = [ "bindgen", "colorous", diff --git a/tools/halidoscope/src-tauri/Cargo.toml b/tools/halidoscope/src-tauri/Cargo.toml index fa4d693220cc..14b39418d787 100644 --- a/tools/halidoscope/src-tauri/Cargo.toml +++ b/tools/halidoscope/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "halidoscope" -version = "0.1.0" +version = "22.0.0" description = "An interactive visualizer for Halide traces." authors = ["Parker Ziegler "] edition = "2021" diff --git a/tools/halidoscope/src-tauri/tauri.conf.json b/tools/halidoscope/src-tauri/tauri.conf.json index 34025e8d645b..c1cc8c8e3b08 100644 --- a/tools/halidoscope/src-tauri/tauri.conf.json +++ b/tools/halidoscope/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Halidoscope", - "version": "0.1.0", + "version": "22.0.0", "identifier": "com.halide.halidoscope", "build": { "beforeDevCommand": "pnpm dev", From 539691c687644bf8e514e98df1a744bf3c4d9ee1 Mon Sep 17 00:00:00 2001 From: Alex Reinking Date: Tue, 1 Sep 2026 18:08:07 -0400 Subject: [PATCH 2/2] [halidoscope] Fix headless smoke test on Linux, correct README claim CI caught a real issue: tauri::Builder::run() initializes tao's GTK-backed event loop before lib.rs's setup() closure ever dispatches to a CLI subcommand, so on Linux even `halidoscope --version` panics with no display attached ("Failed to initialize GTK backend"). This is true of the compiled binary generally, not something packaging can paper over -- it also means the README's claim that the CLI "works anywhere the wheel installs" was wrong for headless Linux. Runs the Linux smoke-test step under Xvfb (apt-installed alongside the other Tauri system deps) and rewords the README to describe the actual behavior instead of asserting the opposite. Unverified locally -- can't reproduce the Linux GTK path on macOS -- so this is best-effort based on reading the CI failure and the tao/tauri source. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/pip-halidoscope.yml | 8 +++++++- tools/halidoscope/README.md | 12 ++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pip-halidoscope.yml b/.github/workflows/pip-halidoscope.yml index 02486d757f43..30bd9ccc513d 100644 --- a/.github/workflows/pip-halidoscope.yml +++ b/.github/workflows/pip-halidoscope.yml @@ -58,7 +58,8 @@ jobs: build-essential \ libxdo-dev \ libssl-dev \ - libsoup-3.0-dev + libsoup-3.0-dev \ + xvfb - uses: dtolnay/rust-toolchain@stable @@ -93,6 +94,11 @@ jobs: uv pip install --python .smoke-test-venv --no-index --find-links wheelhouse halidoscope if [[ "${{ runner.os }}" == "Windows" ]]; then ./.smoke-test-venv/Scripts/halidoscope --version + elif [[ "${{ runner.os }}" == "Linux" ]]; then + # tauri::Builder::run() initializes tao's GTK-backed event loop + # before dispatching to any CLI subcommand, so even `--version` + # needs a display on Linux. + xvfb-run -a ./.smoke-test-venv/bin/halidoscope --version else ./.smoke-test-venv/bin/halidoscope --version fi diff --git a/tools/halidoscope/README.md b/tools/halidoscope/README.md index e195f12cb6a9..2968392c4c83 100644 --- a/tools/halidoscope/README.md +++ b/tools/halidoscope/README.md @@ -25,11 +25,15 @@ pip install halidoscope This installs a prebuilt binary wheel containing both the GUI and the CLI. Note that pip cannot install the system webview Halidoscope's GUI renders into -(WebKitGTK on Linux, WebView2 on Windows, WKWebView on macOS) -- `halidoscope`'s -CLI subcommands (`list`, `stats`, `dot`, `snapshot`) work anywhere the wheel -installs, but the interactive GUI additionally needs +(WebKitGTK on Linux, WebView2 on Windows, WKWebView on macOS), so [Tauri's system dependencies](https://v2.tauri.app/start/prerequisites/#system-dependencies) -present on Linux; macOS and Windows ship a compatible webview out of the box. +must be present on Linux; macOS and Windows ship a compatible webview out of the +box. Currently, `halidoscope` initializes its windowing toolkit unconditionally +on startup, so on Linux this applies even to the non-interactive CLI subcommands +(`list`, `stats`, `dot`, `snapshot`) -- running any of them on a Linux host with +no display attached (e.g., a headless CI runner or container) additionally +requires a virtual display such as +[Xvfb](https://www.x.org/releases/X11R7.6/doc/man/man1/Xvfb.1.xhtml). ## Building Halidoscope