Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/branch-protection.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ jobs:
- miri
- qpy
- neko
- pyo3-ffi
# This job needs to trigger even if its predecessors failed and return the same status. By
# default, GitHub Actions will "skip" the job if a requirement failed, but that counts as a
# "success" state, which is not acceptable for a branch-protection rule. We don't use `always`
Expand Down Expand Up @@ -84,6 +85,13 @@ jobs:
python-version: ${{ needs.config.outputs.python-old }}
runner: ${{ needs.config.outputs.runner-linux-x86_64 }}

pyo3-ffi:
name: pyo3-ffi
needs: config
uses: ./.github/workflows/pyo3-ffi.yml
with:
runner: ${{ needs.config.outputs.runner-linux-x86_64 }}

test-linux:
name: Unit Python / Linux / ${{ matrix.config.id }} - ${{ matrix.runner }}
needs: config
Expand Down
28 changes: 28 additions & 0 deletions .github/workflows/pyo3-ffi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
name: qiskit-pyo3-ffi
on:
workflow_call:
inputs:
runner:
description: Runner image to use.
type: string
required: true

jobs:
check:
name: Checks
runs-on: ${{ inputs.runner }}
timeout-minutes: 60
steps:
- uses: actions/checkout@v6
- name: Prepare package
run: make qiskit-pyo3-ffi
- name: Package
working-directory: dist/rust/qiskit-pyo3-ffi
run: cargo package
- name: Lint
working-directory: dist/rust/qiskit-pyo3-ffi
run: cargo clippy
- name: Docs
working-directory: dist/rust/qiskit-pyo3-ffi
run: cargo doc --no-deps
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,11 @@ ctest: cheader build-clib-dev
ccoverage: C_LIB_RUSTC_FLAGS=-Cinstrument-coverage
ccoverage: ctest

.PHONY: qiskit-pyo3-ffi
qiskit-pyo3-ffi:
rm -rf dist/rust/qiskit-pyo3-ffi
cargo run -p qiskit-bindgen-cli -- generate-pyo3 --cext-path crates/cext --output-path dist/rust/qiskit-pyo3-ffi

.PHONY: cclean
cclean:
rm -rf $(C_DIR_OUT) $(C_DIR_TEST_BUILD) $(C_INCLUDE_FILES_ABS_GENERATED)
Expand Down
10 changes: 10 additions & 0 deletions crates/bindgen-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ The `-c` (`--cext-path`) argument specifies the location of the `cext` crate sou
internal calls to `cbindgen`. The `-o` (`--output-path`) argument specifies where to place the
files.


## Produce the `qiskit-pyo3-ffi` crate for distribution

Use the `generate-pyo3` subcommand, such as

```bash
cargo run -p qiskit-bindgen-cli -- generate-pyo3 -c crates/cext -o dist/rust/qiskit-pyo3-ffi
```


## Linting the current vtable slots

Use the `lint-slots` subcommand, such as
Expand Down
16 changes: 16 additions & 0 deletions crates/bindgen-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ enum Command {
#[arg(short, long)]
cext_path: PathBuf,
},
GeneratePyo3 {
/// Path to the `cext` sources to generate headers for.
#[arg(short, long)]
cext_path: PathBuf,
/// Path to write the output crate.
#[arg(short, long)]
output_path: PathBuf,
},
}

fn main() -> anyhow::Result<()> {
Expand All @@ -61,5 +69,13 @@ fn main() -> anyhow::Result<()> {
let bindings = qiskit_bindgen::generate_bindings(cext_path)?;
lint::lint(&bindings, &SlotsLists::ours())?.map_err(|fails| anyhow!(fails.explain()))
}
Command::GeneratePyo3 {
cext_path,
output_path,
} => {
let bindings = qiskit_bindgen::generate_bindings(cext_path)?;
qiskit_bindgen::install_rust_pyo3_ffi(&bindings, output_path)?;
Ok(())
}
}
}
1 change: 1 addition & 0 deletions crates/bindgen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ workspace = true
name = "qiskit_bindgen"

[dependencies]
qiskit-cext-vtable = { workspace = true, features = ["python_binding"] }
anyhow.workspace = true
cbindgen = { workspace = true, features = ["unstable_ir"] }
hashbrown.workspace = true
Expand Down
3 changes: 2 additions & 1 deletion crates/bindgen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ library produced by that crate.
This is an internal library only used as part of the build and distribution process of Qiskit.

This crate owns all parts of the stand-alone header-file generation logic, including the
custom-written include files, and the installation logic.
custom-written include files (the `include` directory), the Rust template crate (the `pyo3-ffi`
directory), and the installation logic of both.

## Usage

Expand Down
5 changes: 5 additions & 0 deletions crates/bindgen/pyo3-ffi/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[build]
# Share the `target` with the root of the repository as a developer convenience, so using
# `rust-analyzer` or `clippy` within the template crate will not need to keep separate compilations
# of `pyo3`; we can just re-use Qiskit's.
target-dir = "../../../target"
158 changes: 158 additions & 0 deletions crates/bindgen/pyo3-ffi/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions crates/bindgen/pyo3-ffi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "qiskit-pyo3-ffi"
version = "0.1.0"
edition = "2021"
# This doesn't need to be the same as Qiskit itself.
rust-version = "1.80"
license = "Apache-2.0"
homepage = "https://www.ibm.com/quantum/qiskit"
repository = "https://github.com/Qiskit/qiskit"
description = "Raw bindings to the Qiskit C FFI accessed through the Python-space 'qiskit' package"
# These files are Qiskit developer convenience, and don't have a meaning for the packaged crate.
exclude = [".cargo"]

# Forcibly exclude this from any containing workspace.
[workspace]

[lib]
name = "qiskit_pyo3_ffi"

[dependencies]
# We don't inherit from the workspace because this crate is intended for distribution to others to
# use as a dependency. For PyO3 in particular, we don't want to hard-pin the required version.
pyo3.version = ">=0.22,<=0.28"
num-complex.version = "0.4"
19 changes: 19 additions & 0 deletions crates/bindgen/pyo3-ffi/src/ffi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// This code is part of Qiskit.
//
// (C) Copyright IBM 2026
//
// This code is licensed under the Apache License, Version 2.0. You may
// obtain a copy of this license in the LICENSE.txt file in the root directory
// of this source tree or at https://www.apache.org/licenses/LICENSE-2.0.
//
// Any modifications or derivative works of this code must retain this
// copyright notice, and modified files need to carry a notice indicating
// that they have been altered from the originals.

// This is a dummy file that's overwritten by the crate-generation script. It's just here as a base
// test that the macro works, and to suppress clippy/rust-analyzer warnings while editing the
// template crate.

use crate::declare_fn;

declare_fn!(crate::QK_FFI_CIRCUIT[0]; qk_api_version() -> u32);
Loading
Loading