You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This issue is a proposal for a series of steps to modernize the build system of qiskit-aer. I am not an expert at C++ build systems, so this proposal needs review and criticism from people who are, both on the plan itself and on the resulting PRs. I am volunteering to do the legwork and coordinate the effort via Claude 4.7, with the understanding that my time is limited and several other things are higher priority for me: the progress of closing this issue will move at a deliberate, perhaps slow, pace so long as I am assigned.
Motivation
The recent windows-latest → windows-2022 pin (#2426) is a workaround for VS 2025 incompatibility, not a fix. The runner image will eventually be retired (as windows-2019 was), and the underlying problems are the actual cause:
Conan v1 is no longer maintained. Conan's own docs label it "legacy, no longer recommended", and Conan Center stopped accepting v1 packages in late 2024. It does not know about apple-clang 17 (hence the sed ~/.conan/settings.yml hack duplicated in three CI locations) and is a poor fit for newer MSVC toolchains. The vendored cmake/conan.cmake is unmaintained (JFrog 2018 vintage).
scikit-build (legacy) is no longer the recommended Python+CMake backend; scikit-build-core is. The current setup.py + pyproject.toml split is internally inconsistent (python_requires=">=3.7" vs classifiers 3.10+).
CMake minimum is 3.8; scikit-build-core requires 3.15+ and FetchContentFIND_PACKAGE_ARGS needs 3.24.
CI inconsistencies: windows-latest in deploy.yml vs windows-2022 elsewhere; mixed cibuildwheel versions (v3.2.1 vs v2.19.2); stale actions/checkout@v3; vestigial actions-rs/toolchain (no Rust here); pip cache keys hash setup.py not pyproject.toml; docs.yml has a Windows pip cache path on a Linux runner.
The intended end state is a build that:
Works on windows-latest (VS 2025) and macos-latest (Apple Silicon, clang 17+) without runner pins or sed hacks.
Compiles against modern toolchains (gcc 14, MSVC 19.40+, CUDA 12.9+, current ROCm).
Each is one commit; the steps below assume them merged. Order is independent (they touch disjoint files); listed smallest-scope-first.
PR Fix pybind11 init template errors #2416 — Fix pybind11 init template errors (42217e8). Replaces py::init([](args){ return new T(args); }) with py::init<args...>() for the 8 constructors in qiskit_aer/backends/wrappers/aer_circuit_binding.hpp. Required before any toolchain bump that triggers the ambiguous-template error (gcc 14, CUDA 12.9, recent MSVC). As a side effect, should also resolve Build fails for CUDA 12.9 #2405.
PR Switch from pybind11 enum_ to native_enum #2417 — Switch from pybind11 enum_ to native_enum (4ca3f2c). Replaces py::enum_ with py::native_enum for AerUnaryOp and AerBinaryOp. Bumps the minimum pybind11 to 3.0, which the rest of this work inherits.
PR Add explicit json conversions #2418 — Add explicit json conversions (13b01f8). Adds explicit std::to_json calls in pybind_json.hpp and the noise-model load path. Required before bumping nlohmann_json past 3.10.3.
Step 1 — Drop Conan; vendor deps via CMake FetchContent
Replace Conan v1 with FetchContent for the small set of deps Aer actually consumes. Keep a system-libs path for downstream packagers (conda-forge etc.).
Why FetchContent rather than upgrading to Conan 2?
The same migration cost applies either way (rewriting recipes/profiles for Conan 2, or FetchContent_Declare calls), but FetchContent is the better fit here:
Aer's dependency set is small and mostly header-only.nlohmann_json, spdlog, and Thrust are header-only; Catch2 is test-only. The value a package manager adds — binary caching, transitive dep resolution, multi-project version pinning — doesn't apply.
FetchContent ships with CMake. No extra build dependency, no extra tool for contributors to install or learn, no profile files to maintain, no ~/.conan/settings.yml for CI to monkey-patch. Toolchain compatibility (apple-clang 17, new MSVC) becomes CMake's problem, which is actively maintained, rather than ours via Conan profiles.
FIND_PACKAGE_ARGS gives packagers what they want. With CMake 3.24+, FetchContent_Declare(... FIND_PACKAGE_ARGS ...) calls find_package first and only fetches if the system version is missing or too old. This is strictly simpler for conda-forge and distros than the current DISABLE_CONAN flag, and it's the same UX as AER_USE_SYSTEM_LIBS=ON in this proposal.
It composes cleanly with scikit-build-core (Step 2). Conan inside a PEP 517 build backend is doable but awkward; FetchContent is just CMake.
Conan 2 doesn't fix the underlying problem. The apple-clang 17 issue isn't that Conan v1 is buggy — it's that Conan needs per-toolchain knowledge (settings/profiles) that someone has to maintain. Switching to Conan 2 moves the problem; switching to FetchContent removes it.
The tradeoff is no binary caching: a clean source build re-downloads (and for Catch2 re-compiles) the deps. CI already pip-caches the build environment.
Files modified
Rewrite cmake/dependency_utils.cmake. Keep the _use_system_libraries branch, gated by a renamed AER_USE_SYSTEM_LIBS (was DISABLE_CONAN). Replace setup_conan() with FetchContent_Declare / FetchContent_MakeAvailable, using FIND_PACKAGE_ARGS so a system install short-circuits the fetch.
Delete cmake/conan_utils.cmake and cmake/conan.cmake.
CMakeLists.txt: bump cmake_minimum_required to 3.24; drop include(conan_utils).
pyproject.toml: drop conan<2.0.0; delete the [tool.cibuildwheel.macos]before-buildsed hack.
Delete the duplicate macOS conan hacks in tests.yml and deploy.yml (CIBW_BEFORE_BUILD).
Dependency version bumps
Dep
Current
Target
nlohmann_json
3.1.1 (2018)
3.11.3 minimum (requires #2418 merged); 3.12.0 is current
spdlog
1.9.2
1.14.x minimum; 1.17.0 is current
Thrust
1.9.5
1.17.2 from the legacy NVIDIA/thrust repo (header-only use for OMP/TBB Thrust backends); CUDA backend keeps using the toolkit-shipped Thrust. Switching to NVIDIA/cccl is also reasonable but a larger lift; happy to revisit.
Today the Conan path fetches llvm-openmp/12.0.1 and copies libomp.dylib (cmake/conan_utils.cmake:82–91). Replace with find_package(OpenMP) + brew install libomp in CI (tests.yml, pyproject.tomlbefore-all). This matches what conda-forge already does; delocate-wheel bundles libomp.dylib into the macOS wheel.
On Apple Silicon, CMake's FindOpenMP doesn't search Homebrew prefixes by default (libomp lives under /opt/homebrew/opt/libomp, not the default search path). CMakeLists.txt should probe brew --prefix libomp and feed the result through OpenMP_ROOT so source builds from a vanilla pip install . find it without per-user environment setup.
Pull version from qiskit_aer/VERSION.txt via [tool.scikit-build.metadata.version] regex provider (preserves current behavior).
Delete setup.py.
tox.ini: drop py38, py39 from envlist; replace python -I -m build --wheel -C=--build-option=… with scikit-build-core's -C cmake.define.… config syntax; drop setup.py references.
deploy.yml: replace python setup.py sdist with python -m build --sdist; drop the explicit pip install -U scikit-build wheel.
All workflows: pip cache keys hash pyproject.toml (not setup.py).
CUDA wheel-name override (QISKIT_AER_CUDA_MAJOR / QISKIT_AER_PACKAGE_NAME, used by deploy.yml to publish qiskit-aer-gpu, qiskit-aer-gpu-cu11, …): scikit-build-core ships four built-in dynamic-metadata providers (setuptools_scm, regex, fancy_pypi_readme, template); none of them is a clean drop-in for "rename the wheel based on an env var". The likely path is one of: (a) keep the existing tools/configure_package_name.py-style pre-build script that rewrites a small portion of pyproject.toml before each CUDA wheel build, (b) use scikit-build-core's template metadata provider to derive the name from a regex'd input file, or (c) write a tiny custom plugin. Open to alternatives — this is one of the spots I'd most like reviewer input on.
Step 3 — Make Windows VS-version-agnostic
Once the toolchain compiles cleanly on VS 2025 (Step 0 + Step 1 dep updates), drop the version pins. (Note: line numbers below assume the windows-2022 pin from PR #2426 has landed on main. If it's reverted instead of merged, the runner-pin items become no-ops.)
build.yml: "windows-2022" → "windows-latest".
tests.yml: runs-on: windows-2022 → windows-latest; drop CMAKE_GENERATOR: "Visual Studio 17 2022" from the tests_windows job env.
pyproject.toml[tool.cibuildwheel.windows]: drop the CMAKE_GENERATOR = "Visual Studio 17 2022" override.
Drop microsoft/setup-msbuild@v2 from build.yml and tests.yml — no longer needed once we're not pinning the multi-config VS generator.
The goal is to be generator-agnostic, not Ninja-specifically. Removing the pin lets scikit-build-core pick whatever's available on the runner, and CI tells us whether that's good enough on windows-latest (which has both VS 2022/2025 and pip-installable Ninja). If CI shows it falls back to a multi-config VS generator that breaks on the runner image of the day, the follow-up is small and obvious: install Ninja explicitly (before-build = ["pip install ninja"]) and pin environment = { CMAKE_GENERATOR = "Ninja" } for cibuildwheel + the test job. scikit-build-core's docs say "ninja/make or MSVC used by default, respects CMAKE_GENERATOR", so an explicit Ninja opt-in is the worst case we expect, not a default we have to plan for.
Step 4 — CI consistency cleanup
Standalone; can land in parallel with Step 3.
deploy.yml: already windows-latest for the wheel matrix; keep consistent with Step 3.
deploy.yml: remove the three calls to actions-rs/toolchain@v1 (no Rust in the repo).
deploy.yml: bump the ppc64le pypa/cibuildwheel@v2.19.2 → @v3.2.1 to match the other cibuildwheel jobs. Optionally bump all three to the current latest (@v3.4.1 at time of writing).
deploy.yml: bump the stragglers from actions/checkout@v3 → @v4 and actions/setup-python@v4 → @v5 in the sdist, gpu-build-cuda11, and gpu-build-cuda12 jobs (the rest of the file is already on v4/v5). Optional: bump everything to checkout@v6 / setup-python@v6 (current latest).
docs.yml: in the tutorials job (Linux runner), fix the pip cache path from ~\AppData\Local\pip\Cache to ~/.cache/pip — copy-paste bug that silently disabled the cache.
Out of scope (intentionally)
The bigger pybind11 / JSON-roundtrip overhaul (src/framework/pybind_json.hpp:221–313 — every Python noise model passes through Python dict → JSON → C++ struct). Performance-relevant but orthogonal to "make the build work"; track separately.
Replacing the numpy-based 3D-loop serialization in pybind_json.hpp:240–245 with the buffer protocol.
Dropping the legacy QasmSimulator / StatevectorSimulator / UnitarySimulator shims.
Issues building on latest macOS #2356 — Issues building on latest macOS. The apple-clang 17 sed hack on ~/.conan/settings.yml goes away in Step 1; find_package(OpenMP) + brew install libomp replaces the Conan llvm-openmp fetch. Should make a clean source build on current macOS work without manual workarounds.
Unable to install qiskit-aer from source #1978 — Unable to install qiskit-aer from source. Source-from-checkout is the workflow most affected by the Conan and setup.py cleanup; Steps 1+2 should make this materially smoother.
ROCm builds are broken #2401 — ROCm builds are broken. Not directly fixed (the proximate cause is rocPRIM requiring C++17, not our build system), but a modern CMake floor and removing Conan reduce the surface where ROCm-specific patches need to fight the build system.
Update cpp deps #2283 (@iyanmv) — Update cpp deps (spdlog, nlohmann_json). Subsumed by Step 1, which does the same bumps and more (Thrust, Catch2, FetchContent-based mechanism). Worth crediting @iyanmv as co-author on the Step 1 PR if their commits are reused.
A look through closed issues shows the same handful of failure modes repeating year after year. The categories below are exactly the things this overhaul targets at the root.
Conan-related build failures (Step 1 makes these unreachable):
Each step is its own PR. I plan to develop them as a local stack (each branch based on the previous) and rebase as parents merge.
Verification per step (locally on darwin/arm64 before pushing): clean venv pip install ., python -m build --wheel/--sdist, tools/verify_wheels.py. Already exercised end-to-end through Step 2; the local pass found one real bug (Apple Silicon OpenMP_ROOT) that's already folded into the Step 1 commit. The CI matrix (Linux / macOS / Windows VS 2025) lights up properly starting at Step 3.
Asks of reviewers:
Does the staging make sense, or should anything be split / merged / reordered?
Better suggestions for the CUDA wheel-name mechanism in Step 2?
This issue is a proposal for a series of steps to modernize the build system of qiskit-aer. I am not an expert at C++ build systems, so this proposal needs review and criticism from people who are, both on the plan itself and on the resulting PRs. I am volunteering to do the legwork and coordinate the effort via Claude 4.7, with the understanding that my time is limited and several other things are higher priority for me: the progress of closing this issue will move at a deliberate, perhaps slow, pace so long as I am assigned.
Motivation
The recent
windows-latest→windows-2022pin (#2426) is a workaround for VS 2025 incompatibility, not a fix. The runner image will eventually be retired (aswindows-2019was), and the underlying problems are the actual cause:apple-clang 17(hence thesed ~/.conan/settings.ymlhack duplicated in three CI locations) and is a poor fit for newer MSVC toolchains. The vendoredcmake/conan.cmakeis unmaintained (JFrog 2018 vintage).scikit-build(legacy) is no longer the recommended Python+CMake backend;scikit-build-coreis. The currentsetup.py+pyproject.tomlsplit is internally inconsistent (python_requires=">=3.7"vs classifiers 3.10+).aer_circuit_binding.hpp(py::init([]{ return new T(...); })andpy::enum_witharithmetic/export_values) trigger ambiguous template instantiation on gcc 14, MSVC from VS 2025, and CUDA 12.9. Addressed by @wshanks's PRs Fix pybind11 init template errors #2416 / Switch from pybind11 enum_ to native_enum #2417.nlohmann_jsonis pinned to 3.1.1 (2018). Newer versions (3.10.3+) need explicit JSON conversion changes — @wshanks's PR Add explicit json conversions #2418.scikit-build-corerequires 3.15+ andFetchContentFIND_PACKAGE_ARGSneeds 3.24.windows-latestindeploy.ymlvswindows-2022elsewhere; mixedcibuildwheelversions (v3.2.1 vs v2.19.2); staleactions/checkout@v3; vestigialactions-rs/toolchain(no Rust here); pip cache keys hashsetup.pynotpyproject.toml;docs.ymlhas a Windows pip cache path on a Linux runner.The intended end state is a build that:
windows-latest(VS 2025) andmacos-latest(Apple Silicon, clang 17+) without runner pins orsedhacks.scikit-build-core, CMakeFetchContent).Step 0 — Merge @wshanks's prereq PRs
Each is one commit; the steps below assume them merged. Order is independent (they touch disjoint files); listed smallest-scope-first.
42217e8). Replacespy::init([](args){ return new T(args); })withpy::init<args...>()for the 8 constructors inqiskit_aer/backends/wrappers/aer_circuit_binding.hpp. Required before any toolchain bump that triggers the ambiguous-template error (gcc 14, CUDA 12.9, recent MSVC). As a side effect, should also resolve Build fails for CUDA 12.9 #2405.4ca3f2c). Replacespy::enum_withpy::native_enumforAerUnaryOpandAerBinaryOp. Bumps the minimum pybind11 to 3.0, which the rest of this work inherits.13b01f8). Adds explicitstd::to_jsoncalls inpybind_json.hppand the noise-model load path. Required before bumpingnlohmann_jsonpast 3.10.3.Step 1 — Drop Conan; vendor deps via CMake
FetchContentReplace Conan v1 with
FetchContentfor the small set of deps Aer actually consumes. Keep a system-libs path for downstream packagers (conda-forge etc.).Why
FetchContentrather than upgrading to Conan 2?The same migration cost applies either way (rewriting recipes/profiles for Conan 2, or
FetchContent_Declarecalls), butFetchContentis the better fit here:nlohmann_json,spdlog, andThrustare header-only;Catch2is test-only. The value a package manager adds — binary caching, transitive dep resolution, multi-project version pinning — doesn't apply.FetchContentships with CMake. No extra build dependency, no extra tool for contributors to install or learn, no profile files to maintain, no~/.conan/settings.ymlfor CI to monkey-patch. Toolchain compatibility (apple-clang 17, new MSVC) becomes CMake's problem, which is actively maintained, rather than ours via Conan profiles.FIND_PACKAGE_ARGSgives packagers what they want. With CMake 3.24+,FetchContent_Declare(... FIND_PACKAGE_ARGS ...)callsfind_packagefirst and only fetches if the system version is missing or too old. This is strictly simpler for conda-forge and distros than the currentDISABLE_CONANflag, and it's the same UX asAER_USE_SYSTEM_LIBS=ONin this proposal.scikit-build-core(Step 2). Conan inside a PEP 517 build backend is doable but awkward;FetchContentis just CMake.FetchContentremoves it.The tradeoff is no binary caching: a clean source build re-downloads (and for
Catch2re-compiles) the deps. CI already pip-caches the build environment.Files modified
cmake/dependency_utils.cmake. Keep the_use_system_librariesbranch, gated by a renamedAER_USE_SYSTEM_LIBS(wasDISABLE_CONAN). Replacesetup_conan()withFetchContent_Declare/FetchContent_MakeAvailable, usingFIND_PACKAGE_ARGSso a system install short-circuits the fetch.cmake/conan_utils.cmakeandcmake/conan.cmake.CMakeLists.txt: bumpcmake_minimum_requiredto 3.24; dropinclude(conan_utils).pyproject.toml: dropconan<2.0.0; delete the[tool.cibuildwheel.macos]before-buildsedhack.tests.ymlanddeploy.yml(CIBW_BEFORE_BUILD).Dependency version bumps
nlohmann_jsonspdlogThrustCatch2<catch2/catch_test_macros.hpp>etc.); ~10–20 mechanical edits inBUILD_TESTS=TruepathsOpenMP on macOS
Today the Conan path fetches
llvm-openmp/12.0.1and copieslibomp.dylib(cmake/conan_utils.cmake:82–91). Replace withfind_package(OpenMP)+brew install libompin CI (tests.yml,pyproject.tomlbefore-all). This matches what conda-forge already does;delocate-wheelbundleslibomp.dylibinto the macOS wheel.On Apple Silicon, CMake's
FindOpenMPdoesn't search Homebrew prefixes by default (libomp lives under/opt/homebrew/opt/libomp, not the default search path).CMakeLists.txtshould probebrew --prefix libompand feed the result throughOpenMP_ROOTso source builds from a vanillapip install .find it without per-user environment setup.Step 2 —
scikit-build→scikit-build-core; deletesetup.py[build-system]:[project]table (name/version/license/classifiers/dependencies/requires-python = ">=3.10") — moved fromsetup.py.[tool.scikit-build]:cmake.version = ">=3.24",cmake.build-type = "Release",wheel.packages = ["qiskit_aer"], sdist include/exclude.qiskit_aer/VERSION.txtvia[tool.scikit-build.metadata.version]regex provider (preserves current behavior).setup.py.tox.ini: droppy38, py39fromenvlist; replacepython -I -m build --wheel -C=--build-option=…with scikit-build-core's-C cmake.define.…config syntax; dropsetup.pyreferences.deploy.yml: replacepython setup.py sdistwithpython -m build --sdist; drop the explicitpip install -U scikit-build wheel.pyproject.toml(notsetup.py).CUDA wheel-name override (
QISKIT_AER_CUDA_MAJOR/QISKIT_AER_PACKAGE_NAME, used bydeploy.ymlto publishqiskit-aer-gpu,qiskit-aer-gpu-cu11, …): scikit-build-core ships four built-in dynamic-metadata providers (setuptools_scm,regex,fancy_pypi_readme,template); none of them is a clean drop-in for "rename the wheel based on an env var". The likely path is one of: (a) keep the existingtools/configure_package_name.py-style pre-build script that rewrites a small portion ofpyproject.tomlbefore each CUDA wheel build, (b) use scikit-build-core'stemplatemetadata provider to derive the name from a regex'd input file, or (c) write a tiny custom plugin. Open to alternatives — this is one of the spots I'd most like reviewer input on.Step 3 — Make Windows VS-version-agnostic
Once the toolchain compiles cleanly on VS 2025 (Step 0 + Step 1 dep updates), drop the version pins. (Note: line numbers below assume the
windows-2022pin from PR #2426 has landed onmain. If it's reverted instead of merged, the runner-pin items become no-ops.)build.yml:"windows-2022"→"windows-latest".tests.yml:runs-on: windows-2022→windows-latest; dropCMAKE_GENERATOR: "Visual Studio 17 2022"from thetests_windowsjob env.pyproject.toml[tool.cibuildwheel.windows]: drop theCMAKE_GENERATOR = "Visual Studio 17 2022"override.microsoft/setup-msbuild@v2frombuild.ymlandtests.yml— no longer needed once we're not pinning the multi-config VS generator.The goal is to be generator-agnostic, not Ninja-specifically. Removing the pin lets scikit-build-core pick whatever's available on the runner, and CI tells us whether that's good enough on
windows-latest(which has both VS 2022/2025 and pip-installable Ninja). If CI shows it falls back to a multi-config VS generator that breaks on the runner image of the day, the follow-up is small and obvious: install Ninja explicitly (before-build = ["pip install ninja"]) and pinenvironment = { CMAKE_GENERATOR = "Ninja" }for cibuildwheel + the test job. scikit-build-core's docs say "ninja/make or MSVC used by default, respectsCMAKE_GENERATOR", so an explicit Ninja opt-in is the worst case we expect, not a default we have to plan for.Step 4 — CI consistency cleanup
Standalone; can land in parallel with Step 3.
deploy.yml: alreadywindows-latestfor the wheel matrix; keep consistent with Step 3.deploy.yml: remove the three calls toactions-rs/toolchain@v1(no Rust in the repo).deploy.yml: bump the ppc64lepypa/cibuildwheel@v2.19.2→@v3.2.1to match the other cibuildwheel jobs. Optionally bump all three to the current latest (@v3.4.1at time of writing).deploy.yml: bump the stragglers fromactions/checkout@v3→@v4andactions/setup-python@v4→@v5in the sdist, gpu-build-cuda11, and gpu-build-cuda12 jobs (the rest of the file is already on v4/v5). Optional: bump everything tocheckout@v6/setup-python@v6(current latest).docs.yml: in thetutorialsjob (Linux runner), fix the pip cache path from~\AppData\Local\pip\Cacheto~/.cache/pip— copy-paste bug that silently disabled the cache.Out of scope (intentionally)
src/framework/pybind_json.hpp:221–313— every Python noise model passes through Python dict → JSON → C++ struct). Performance-relevant but orthogonal to "make the build work"; track separately.numpy-based 3D-loop serialization inpybind_json.hpp:240–245with the buffer protocol.QasmSimulator/StatevectorSimulator/UnitarySimulatorshims.Related issues and PRs
Would directly close
cmake/conan.cmake:404. The vendoredcmake/conan.cmakeis deleted in Step 1.nlohmann_json >= 3.11. Resolved by prereq Add explicit json conversions #2418 (and is the precondition for the Step 1 dep bump).Likely fixes / materially helps
sedhack on~/.conan/settings.ymlgoes away in Step 1;find_package(OpenMP)+brew install libompreplaces the Conan llvm-openmp fetch. Should make a clean source build on current macOS work without manual workarounds.setup.pycleanup; Steps 1+2 should make this materially smoother.Overlapping PRs — coordination needed
nlohmann_json >= 3.11incompatibility. Same fix as @wshanks's Add explicit json conversions #2418 (movesto_json/from_jsonout ofstd::). Maintainers should pick one; either works as the Step 0 prereq.spdlog,nlohmann_json). Subsumed by Step 1, which does the same bumps and more (Thrust, Catch2,FetchContent-based mechanism). Worth crediting @iyanmv as co-author on the Step 1 PR if their commits are reused.Closed issues that would have been prevented
A look through closed issues shows the same handful of failure modes repeating year after year. The categories below are exactly the things this overhaul targets at the root.
Conan-related build failures (Step 1 makes these unreachable):
conan.cmakewchar.hnot found and conan unrecognized argumentsWindows / MSVC toolchain breakage (Step 3 dropping the VS-version pin + scikit-build-core's modern build flow make these toolchain-version-agnostic):
windows-2022pin_apply_matrix_double_avx_q0q1compile issue with the latest VC++macOS toolchain breakage (Step 1's
find_package(OpenMP)+ Homebrew libomp + dropping the~/.conan/settings.ymlsedhack handles these):setup.py/ scikit-build legacy issues (Step 2 removessetup.pyand adopts PEP 621 metadata via scikit-build-core):src,pyproject.toml, andcmakeinto wrong locationspython setup.py installshould only copy relevant files #1457python setup.py installshould only copy relevant filessetup_requiresis deprecatedsetup.pyCMake-version / generic build-from-source breakage (Step 1's CMake floor bump to 3.24 + cleaner
FetchContentflow):size_inQubitVector<data_t>0.13.3fails to install #2053 Aer 0.13.3 fails to installDelivery plan
Each step is its own PR. I plan to develop them as a local stack (each branch based on the previous) and rebase as parents merge.
Verification per step (locally on darwin/arm64 before pushing): clean venv
pip install .,python -m build --wheel/--sdist,tools/verify_wheels.py. Already exercised end-to-end through Step 2; the local pass found one real bug (Apple SiliconOpenMP_ROOT) that's already folded into the Step 1 commit. The CI matrix (Linux / macOS / Windows VS 2025) lights up properly starting at Step 3.Asks of reviewers: