Update for libquil on modern sbcl-librarian - #68
Conversation
a1d4b5d to
e323a96
Compare
libquil no longer ships a core the caller initializes explicitly; the libsbcl_librarian runtime brings Lisp up from a constructor when it loads, and libquil is the generated bindings on top. The core-file search and the init(core) call are gone, along with LIBQUIL_CORE_PATH and the CoreFileNotFound error; init_libquil now only makes libquil's symbols globally visible, which the Python extension still needs. Errors move to the runtime's API: lisp_err_t and get_error_message replace libquil_error_t and libquil_error. The generated header declares real functions rather than function pointers, so bindgen no longer wraps them in Option and the .unwrap() calls on binding functions are dropped. build.rs links libsbcl_librarian alongside libquil, feeds bindgen the runtime's header and include directory (libquil.h now includes sbcl_librarian_err.h), and accepts LIBQUIL_LIB_PATH for installs that keep headers and libraries in separate directories. It also reports its own errors, since Cargo prints a build script's error with Debug, and says plainly when it finds a libquil too old to carry the runtime headers. CI installs $LIBQUIL_VERSION, which has to be a libquil release from after this change, and the macOS job moves off the Intel runner: libquil no longer publishes Intel macOS binaries. The macOS __PAGEZERO link argument is dropped: the Lisp image is mapped by the runtime library now, not by the consumer executable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
6f68ec1 to
5237131
Compare
Author/email change that was present in the working copy; separated out so it is not mixed into unrelated CI work.
8ede176 to
585c154
Compare
libquil-sys is only usable with a matching libquil, so an ABI change has to be published somewhere testable before either side can be released for real. There was no way to do that: knope.toml defined only a 'release' workflow, and the dispatch input listing it was not a choice input, so its options list was inert. Add a 'prerelease' knope workflow that cuts an -rc version and marks the GitHub release as a prerelease, which still triggers the existing crates.io publish. Cargo never resolves a prerelease unless asked for by name, so it is safe to publish alongside stable versions. Verified with knope 0.10.0 and 0.11.0: the config validates and the workflow dry-runs to 0.5.0-rc.0. The test workflow now takes the libquil release to install, and the repository and ref to fetch install.sh from -- they have to move together, because the installer that ships with a release knows which files that release contains, and the move to modern sbcl-librarian added the runtime directory. They currently point at v0.4.0-rc.0 in a fork, since the libquil change is not merged; revert to rigetti/libquil and a stable version once it lands. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
585c154 to
a3d2917
Compare
Cutting a prerelease from a branch produced a tag pointing at the wrong code.
knope 0.10/0.11 create the GitHub release against the default branch, so the
version bump landed on the branch while the tag landed on main -- and the publish
job, which checks out whatever that tag resolves to, then tried to republish main's
already-released version:
error: crate libquil-sys@0.4.2 already exists on crates.io index
Adopt the setup rigetti-pyo3 arrived at (36a1850, 9b5235c, 3505971):
- knope 0.23, which tags the ref being released and takes --prerelease-label, so
the separate prerelease workflow in knope.toml is no longer needed;
- the ref decides what gets cut: main releases, anything else prereleases;
- check out ${{ github.ref }} so a release acts on the dispatched branch;
- pass the token to the Release step through GITHUB_TOKEN, which is how it
authenticates;
- dry-run the release on pull requests, so a broken config is visible before it is
dispatched.
The publish job now checks out the release tag explicitly, so it always publishes
exactly what was tagged rather than whatever the target commitish points at.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
350b6e1 to
545d05d
Compare
get_header_path and get_lib_search_paths read LIBQUIL_SRC_PATH, LIBQUIL_LIB_PATH and C_INCLUDE_PATH with option_env!, which resolves when the build script is *compiled*. The chosen paths were baked into the compiled build script, and nothing told cargo the values mattered, so pointing the build at a different libquil after the first build silently kept linking the old one until a manual cargo clean. Read them with env::var at runtime and emit cargo:rerun-if-env-changed for each, so a changed value re-runs the build script. Also fix the rerun-on-header-change directive, which was spelled cargo:rustc-rerun-if-changed -- not a directive cargo recognizes, so a modified libquil.h did not trigger a rebuild of the bindings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The prerelease carrying the sbcl-librarian runtime is now published from rigetti/libquil itself (v0.4.0-rc.0, built by its own CI), so the fork it was coming from is going away. Drop the two repository overrides, and take install.sh from the tag being installed rather than from a branch, so the installer always matches the release it unpacks. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| # 0.23 is needed for `--prerelease-label` and for tagging the ref being | ||
| # released. 0.10 tagged the default branch instead, so a prerelease cut from | ||
| # a branch produced a tag pointing at main's version, and the publish job | ||
| # then tried to republish that already-released version. |
There was a problem hiding this comment.
| # 0.23 is needed for `--prerelease-label` and for tagging the ref being | |
| # released. 0.10 tagged the default branch instead, so a prerelease cut from | |
| # a branch produced a tag pointing at main's version, and the publish job | |
| # then tried to republish that already-released version. |
| # The libquil release these bindings are built against. libquil's move to modern | ||
| # sbcl-librarian changed its C ABI and the set of files it installs, so this crate | ||
| # needs a release from after that change: 0.3.x installs neither the runtime | ||
| # headers nor libsbcl_librarian, and the build fails in build.rs. | ||
| LIBQUIL_VERSION: "0.4.0-rc.0" | ||
| # install.sh is fetched from the tag being installed. It has to match the release | ||
| # above: the installer that ships with a release knows which files that release | ||
| # contains, and the modern-sbcl-librarian layout added the runtime directory. | ||
| LIBQUIL_INSTALL_REF: "v0.4.0-rc.0" |
There was a problem hiding this comment.
Update to "release" tags once rigetti/libquil#58 is merged & released
| description = "Python bindings for quilc" | ||
| license = { text = "Apache-2.0" } | ||
| authors = [{ name = "Rigetti Computing", email = "softapps@rigetti.com" }] | ||
| authors = [{ name = "Rigetti QPU Software", email = "qpu-software@rigetti.com" }] |
There was a problem hiding this comment.
TBD: if this project is adopted by quil-lang, then there may be a more appropriate authors list. However, the QPU Software team has a vested interest in supporting the use of quilc via Python bindings, so it may be preferable to have two authors entries, one for quil-lang and one for Rigetti QPU Software.
| ## 0.5.0-rc.2 (2026-08-12) | ||
|
|
||
| ### Breaking Changes | ||
|
|
||
| - build against libquil on modern sbcl-librarian | ||
|
|
||
| ### Fixes | ||
|
|
||
| - read libquil path envvars at build-script runtime | ||
|
|
||
| ## 0.5.0-rc.1 (2026-08-12) | ||
|
|
||
| ### Breaking Changes | ||
|
|
||
| - build against libquil on modern sbcl-librarian | ||
|
|
||
| ## 0.5.0-rc.0 (2026-08-12) | ||
|
|
||
| ### Breaking Changes | ||
|
|
||
| #### build against libquil on modern sbcl-librarian | ||
|
|
There was a problem hiding this comment.
Note: delete the redundant entries before merging
| // Cargo prints a build script's error with Debug, which would hide the | ||
| // explanation these errors carry, so report it and exit rather than returning it. |
There was a problem hiding this comment.
I assume this comment is explaining why fn main() does not return a Result, but I'm not sure whether that's actually something that needs to be explained.
There was a problem hiding this comment.
I don't think it's necessary to explain
There was a problem hiding this comment.
Frankly I'd accept a panic!() instead of eprintln + exit
| // If this isn't set on MacOS, memory allocation errors occur when trying to initialize the | ||
| // library | ||
| if cfg!(target_os = "macos") { | ||
| println!("cargo:rustc-link-arg=-pagezero_size 0x100000"); | ||
| } |
There was a problem hiding this comment.
This change is no longer necessary with sbcl-librarian managing the runtime.
| /// There is no core file to locate and no initialization call to make: the Lisp | ||
| /// image is brought up by a constructor in the libsbcl_librarian runtime when it is | ||
| /// loaded, and libquil's own constructor then loads its embedded FASL bundles into | ||
| /// that image. All this function does is make libquil's symbols globally visible. |
There was a problem hiding this comment.
This is nice to know for reviewing this PR, but doesn't seem useful after this.
| // Cargo prints a build script's error with Debug, which would hide the | ||
| // explanation these errors carry, so report it and exit rather than returning it. |
There was a problem hiding this comment.
I don't think it's necessary to explain
| // Cargo prints a build script's error with Debug, which would hide the | ||
| // explanation these errors carry, so report it and exit rather than returning it. |
There was a problem hiding this comment.
Frankly I'd accept a panic!() instead of eprintln + exit
Resolves #69
Must follow rigetti/libquil#58
Unlike other PRs in this sequence, this one mostly touches Rust code, so I'm more comfortable with my self-review. I consider the actual Rust changes a strict improvement (mostly just removing
unwrap()calls that are no longer necessary).Remainder of PR description written by Claude.
libquil no longer ships a core the caller initializes explicitly; the libsbcl_librarian runtime brings Lisp up from a constructor when it loads, and libquil is the generated bindings on top. The core-file search and the init(core) call are gone, along with LIBQUIL_CORE_PATH and the CoreFileNotFound error; init_libquil now only makes libquil's symbols globally visible, which the Python extension still needs.
Errors move to the runtime's API: lisp_err_t and get_error_message replace libquil_error_t and libquil_error. The generated header declares real functions rather than function pointers, so bindgen no longer wraps them in Option and the .unwrap() calls on binding functions are dropped.
build.rs links libsbcl_librarian alongside libquil, feeds bindgen the runtime's header and include directory (libquil.h now includes sbcl_librarian_err.h), and accepts LIBQUIL_LIB_PATH for installs that keep headers and libraries in separate directories.
The macOS __PAGEZERO link argument is dropped: the Lisp image is mapped by the runtime library now, not by the consumer executable.