Skip to content

perf(install): pin CARGO_TARGET_DIR + disable PEP 517 isolation#5

Merged
zackees merged 1 commit into
mainfrom
perf/pin-cargo-target-and-no-build-isolation
Jun 22, 2026
Merged

perf(install): pin CARGO_TARGET_DIR + disable PEP 517 isolation#5
zackees merged 1 commit into
mainfrom
perf/pin-cargo-target-and-no-build-isolation

Conversation

@zackees

@zackees zackees commented Jun 22, 2026

Copy link
Copy Markdown
Owner

Summary

Together these let cargo's incremental fingerprint cache survive across pip install . / uv build invocations. Ported from fbuild#743.

  1. os.environ.setdefault("CARGO_TARGET_DIR", ~/.template-python-rust-cmd/cargo-target/wheel-build) at the top of ci/build_wheel.py. PEP 517 isolated builds copy the source tree to a temp dir; <temp>/target/ is thrown away after each install. Pinning preserves cargo's fingerprint.
  2. [tool.uv] no-build-isolation-package in pyproject.toml. maturin is already in the dev group so the build env has what it needs without isolation. Enables the future mtime-skip fast path to see the staged binary.
  3. Refactor: _cargo_target_root() helper so build_cli_binary() actually respects the pinned dir.

Refs #2 — items (4) + (5).

Test plan

  • ci/build_wheel.py still produces a wheel + sdist with both deliverables.
  • CI green.

🤖 Generated with Claude Code

…tems 4+5)

Two complementary changes that together let cargo's incremental
fingerprint cache survive across `pip install .` / `uv build`
invocations. Ported from FastLED/fbuild#743.

1. `os.environ.setdefault("CARGO_TARGET_DIR", ~/.template-python-rust-cmd/cargo-target/wheel-build)`
   at the top of `ci/build_wheel.py`. PEP 517 isolated builds copy the
   source tree to a temp dir and throw `<temp>/target/` away after
   each install — every install runs cargo cold (25-30s wall-clock
   without this). Pinning to a stable home-dir path preserves the
   fingerprint across invocations. Deliberately separate from
   `<repo>/target/` so iteration with bare `cargo check` doesn't churn
   the wheel-build cache and vice versa.

2. `[tool.uv] no-build-isolation-package = ["template-python-rust-cmd"]`
   in `pyproject.toml`. Without this, `uv build` (and `uv pip install
   .`) copy the source tree to a temp dir, and (a) cargo's incremental
   cache lands in that temp dir then gets discarded, (b) the future
   mtime-skip fast path can't see the staged binary because it's
   outside the temp tree. `maturin` already ships in the dev
   dependency group, so the build env has what it needs without
   isolation.

3. Refactor: pull `_cargo_target_root()` out so it respects
   `CARGO_TARGET_DIR`. Previously hardcoded `ROOT / "target" /
   "release"` — would have masked the pinned path.

Refs #2 (items 4 + 5).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@zackees zackees merged commit 943e323 into main Jun 22, 2026
@zackees zackees deleted the perf/pin-cargo-target-and-no-build-isolation branch June 22, 2026 06:03
zackees added a commit that referenced this pull request Jun 22, 2026
…#2 items 1+10) (#7)

Previously the Python entry point `[project.scripts]
template-python-rust-cmd = "template_python_rust_cmd.cli:main"` created
a pip console-script `.exe` that subprocess-launched the staged
`_bin/template-cli`. On Windows that pattern races the next shell
prompt ahead of the child's stdout, because the Python shim returns
to cmd.exe before the spawned native binary finishes flushing. Same
class of bug fbuild fixed in FastLED/fbuild#747.

Drop the shim. ci/build_wheel.py now post-processes the maturin-built
wheel: opens the zip, injects the cargo-built `template-cli[.exe]` at
`<name>-<ver>.data/scripts/template-cli[.exe]`, recomputes the RECORD
row (sha256 + size), and stamps the Unix executable bit on the entry.
Files in `.data/scripts/` are pip's canonical raw-script install
location — pip drops them straight into the venv's `Scripts/` (Win) /
`bin/` (POSIX) directory verbatim, with no Python wrapper for `.exe`
files. Same mechanism cargo-dist and maturin's "bin" mode use.

Removed:
- `[project.scripts]` table from pyproject.toml.
- `src/template_python_rust_cmd/cli.py` (the racy subprocess shim).
- `src/template_python_rust_cmd/_bin/.gitkeep` + the gitignore lines
  that staged binary into the package data directory.
- `action/cleanup/action.yml` step that rm'd the (now-nonexistent)
  `_bin/` staging directory.

Verified end-to-end on Windows:
- Fresh maturin wheel produced via `uv run --no-project --script
  ci/build_wheel.py`.
- `unzip -l dist/*.whl` shows `template_python_rust_cmd-0.1.0.data/
  scripts/template-cli.exe` (1.2 MB) — no `_bin/` entries.
- `uv pip install` of that wheel into a fresh venv lands a `Scripts/
  template-cli.exe` that `file` reports as `PE32+ executable` (NOT a
  Zip-archive console-script stub).
- `cmd /c 'echo === BEFORE === & template-cli.exe --version & echo
  === AFTER ==='` prints `=== AFTER ===` AFTER the version output —
  acceptance criterion from issue #2 met.

The `no-build-isolation-package` setting added in PR #5 was reverted
in this PR: with maturin (vs. setuptools) the trade-off doesn't favor
us — uv reuses a stale build env that lacks maturin, and the preview
`extra-build-dependencies` mechanism is too fragile to ship in a
template. The CARGO_TARGET_DIR pin from that PR stays — it already
gives the cargo-incremental win without touching isolation.

Closes #2 (items 1 + 10).
Refs FastLED/fbuild#747.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
zackees added a commit that referenced this pull request Jun 22, 2026
…oses #9) (#10)

Followup cleanup to #7 (drop Python CLI shim, ship raw wheel script).
The package-side refactor worked at the wheel-build level but left
stale source-tree references in two artifact-walking tools — same
failure shape as fbuild#748 (a packaging refactor that worked at the
wheel level but broke a tool walking the source tree a different way).

Stale references found by `git grep '_bin\|template_python_rust_cmd\.cli'`:

1. `tests/test_cli.py` — imported `template_python_rust_cmd.cli.
   packaged_binary_path` (module deleted in #7). Whole file failed to
   collect with ImportError; the suite was silently 1 file short.

2. `ci/gates/action_surface.py::_binary_path()` — listed
   `src/template_python_rust_cmd/_bin/template-cli[.exe]` as its FIRST
   lookup candidate. That directory was removed in #7. Fallback to
   target/release worked locally but the gate doesn't honor
   CARGO_TARGET_DIR (pinned to ~/.template-python-rust-cmd/cargo-target/
   wheel-build in #5), so it silently misses the binary on a
   wheel-build-driven runner.

3. `tests/README.md` — described `test_cli.py` as covering "the Python
   CLI shim's binary-discovery logic" (module gone).

Fix:

- `tests/test_cli.py` rewritten as a runtime contract check: probe
  PATH for `template-cli`, skip if absent (dev venv without install),
  otherwise assert it's invokable and produces non-empty --version
  output. Matches the new shipping mechanism (raw wheel script) which
  doesn't leave a source-tree probe target.

- `ci/gates/action_surface.py::_binary_path()` now searches, in order:
    1. $CARGO_TARGET_DIR/{release,debug}/ if set
    2. ROOT/target/{release,debug}/ for the local ./test flow
    3. PATH via shutil.which for pip/uv-installed binaries
  Updated the "no binary found" warning to match.

- `tests/README.md` describes what test_cli.py actually does now.

Verified:
- `uv run pytest tests/test_cli.py tests/test_version.py tests/test_bindings.py -v`
  → 2 pass, 2 skip (test_cli skips because dev venv has no installed
  template-cli — expected behavior).
- `./ci.sh test` → 34 pass, 2 skip, [test] ok.
- `./ci.sh action_surface` → finds the binary, gate passes.

Closes #9.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant