build(cmake): export dependency usage requirements as imported targets - #1157
build(cmake): export dependency usage requirements as imported targets#1157IvanaGyro wants to merge 10 commits into
Conversation
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d37e9b1ea5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e5cc39bc15
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1157 +/- ##
=======================================
Coverage 67.92% 67.92%
=======================================
Files 208 208
Lines 22354 22354
Branches 72 72
=======================================
Hits 15184 15184
Misses 7148 7148
Partials 22 22
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
cd35277 to
20995b3
Compare
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
The OpenBLAS branch of CytnxBKNDCMakeLists.cmake ran `ldd` (or `otool -L` on macOS) against the arpack library it had just found, looked for a libopenblas among arpack's own dependencies, and -- when that differed from what find_package(BLAS) had resolved, and exported LAPACKE symbols -- overwrote BLAS_LIBRARIES, LAPACK_LIBRARIES and LAPACKE_LIBRARIES with it. The point was to stop a wheel from vendoring two OpenBLAS builds on distributions that install a serial and a threaded build side by side. Dependency provisioning now guarantees a single OpenBLAS on every platform cytnx ships, so the probe has nothing left to correct: * manylinux and macOS take arpack and OpenBLAS from the same conda-forge environment (tools/cibuildwheel_before_all.sh, tools/cibuildwheel_before_all_macos.sh), pinned to "openblas=*=*openmp*", so the arpack that is found is linked against the OpenBLAS that is found. * musllinux keeps Alpine's arpack and builds OpenBLAS from source with USE_OPENMP=1, then patchelf's that build to the soname arpack's own NEEDED entry names and symlinks it into place, so arpack resolves to the one OpenBLAS present. This also takes a build-machine fact out of the configure step: the probe chose libraries by inspecting the arpack installed on the machine running cmake, a choice no other machine can reproduce. Co-Authored-By: Claude <noreply@anthropic.com>
Predefining BLAS_LIBRARIES, LAPACK_LIBRARIES and LAPACKE_LIBRARIES made CytnxBKNDCMakeLists.cmake skip both providers and link those paths straight into the cytnx target, with no finder run at all. Nothing configures cytnx that way. Every build that ships -- the manylinux, macOS and musllinux wheels, the conda recipe, and CI -- provisions its BLAS and LAPACK and then lets the MKL or OpenBLAS branch find them. musllinux, the one platform that needs a specific OpenBLAS build, gets it by building that OpenBLAS into the soname the installed arpack already names (tools/cibuildwheel_before_all.sh) rather than by pinning cmake variables. The branch is also the one path here that cannot produce a relocatable package: the caller's absolute paths land in the exported cytnx target, and a consumer resolving the package on another machine has no way to reproduce them. Remove it, leaving USE_MKL to select between the two branches that detect what they link. Co-Authored-By: Claude <noreply@anthropic.com>
The downstream find_package job built cytnx only with RUN_TESTS=OFF, so nothing checked that a coverage-instrumented libcytnx.a stays consumable. cytnx is a STATIC archive: objects compiled with --coverage leave gcov symbols unresolved until the consumer's final link, which is why cytnx carries --coverage as an unconditional INTERFACE link option instead of one wrapped in $<BUILD_INTERFACE:>. Nothing enforced that distinction. Run the job as a matrix over RUN_TESTS so both the uninstrumented and the coverage configuration are driven against the build tree and the install tree. The coverage variant fails if the option is ever dropped from the install interface, because the install-tree consumer would then link with undefined __gcov_* references. gtest/gmock join the dependency list: RUN_TESTS=ON makes tests/CMakeLists.txt require them at configure time, even though this job only ever builds the `cytnx` target. The ccache keys gain the configuration name so the two variants do not evict each other. Co-Authored-By: Claude <noreply@anthropic.com>
a01daef to
a2bc76a
Compare
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
a2bc76a to
0deaca9
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0deaca9bb4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if(NOT arpackng_FOUND) | ||
| find_package(arpack-ng CONFIG REQUIRED) | ||
| endif() | ||
| target_link_libraries(cytnx PRIVATE ARPACK::ARPACK) |
There was a problem hiding this comment.
Export a stable ARPACK target name
When this build resolves arpackng 3.9, ARPACK::ARPACK is an alias of the un-namespaced arpack target, and CMake serializes the aliased name into CytnxTargets.cmake as $<LINK_ONLY:arpack>. If the installed package is then consumed with the older arpack-ng config that only recreates ARPACK::ARPACK (the case this block explicitly tries to support), arpack is not a target and the downstream link falls back to bare -larpack, losing the package prefix and any static dependency interface; consumers in non-system prefixes fail to link even though find_dependency(arpack-ng) succeeded. Export a Cytnx-owned stable dependency target or recreate the un-namespaced arpack target in the fallback path so the installed find_package(Cytnx) export remains usable.
AGENTS.md reference: AGENTS.md:L118-L119
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
The de-aliasing is real and I verified it, but the proposed remedy does not change the outcome it predicts, so I have left this as is.
Confirmed from the two packagings themselves rather than from memory. conda-forge's arpackngTargets.cmake has add_library(arpack SHARED IMPORTED) and the config then does add_library(ARPACK::ARPACK ALIAS arpack), so the export records arpack. Alpine's arpack-ng 3.8.0 config instead does add_library(ARPACK::ARPACK INTERFACE IMPORTED) and defines no target named arpack. So the 3.9-built / 3.8-consumed cell really does lose the target, exactly as described.
What it loses it to is the same thing either way. I built a package export under both spellings and configured each against both packagings, reading the generated link.txt:
| consumer has | export records arpack |
export records ARPACK::ARPACK |
|---|---|---|
| arpack-ng 3.8.0 (Alpine), non-system prefix | -larpack |
-larpack |
| arpackng 3.9.1 (conda-forge) | -Wl,-rpath,<prefix>/lib <prefix>/lib/libarpack.so.2.1.0 |
-Wl,-rpath,<prefix>/lib <prefix>/lib/libarpack.so.2.1.0 |
The reason is in 3.8's config: its ARPACK::ARPACK carries INTERFACE_LINK_LIBRARIES "arpack" — a bare library name, not a path or a target — and while the file computes set(libdir "${exec_prefix}/lib") it never uses that variable for anything. So the target contributes no -L, and naming it produces the same bare -larpack that falling through to an undefined target name does.
That also means the failure is not specific to consuming Cytnx. A plain project with no Cytnx involvement, using 3.8's own documented target as its README instructs:
find_package(arpack-ng CONFIG REQUIRED)
target_link_libraries(direct ARPACK::ARPACK)links with -larpack and no -L as well. Every consumer of arpack-ng 3.8 from a non-system prefix is in that position; it is an upstream defect in that release's config, and exporting a different name on our side cannot repair it.
Two smaller points. The reverse direction is fine — a 3.8-built export naming ARPACK::ARPACK resolves against 3.9, because 3.9's alias provides that name too, giving the full path. And the include-directory difference between the two target shapes does not reach consumers either, since arpack is PRIVATE and reaches the export under $<LINK_ONLY:>, which carries the link entry only.
Where this genuinely does bite is a consumer with no arpack package config at all, and there find_dependency fails loudly at configure time rather than degrading — @IvanaGyro has ruled on that in the adjacent thread.
Generated by Claude Code
| # below, whose package config resolves BLAS/LAPACK itself when arpack is | ||
| # static. | ||
| set(BLA_VENDOR "@BLA_VENDOR@") | ||
| if(TARGET LAPACK::LAPACK AND NOT "@BLA_VENDOR@" STREQUAL "") |
There was a problem hiding this comment.
Reject a pre-existing BLAS target too
This guard only notices an existing LAPACK::LAPACK, but CMake's FindLAPACK also appends BLAS::BLAS to the newly-created LAPACK::LAPACK when that BLAS target already exists, and FindBLAS will not replace an existing target. A consumer that called find_package(BLAS) first can therefore make an OpenBLAS or MKL-ILP64 Cytnx package link through that older LP64/non-MKL BLAS target even though no LAPACK::LAPACK existed yet; apply the same compatibility check/rejection to BLAS::BLAS before re-running the LAPACK/LAPACKE finder.
AGENTS.md reference: AGENTS.md:L118-L119
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed and fixed in 0028d4a.
The mechanism is exactly as described, and I verified it in the CMake modules before changing anything: FindBLAS.cmake:264 creates the target under if(BLAS_FOUND AND NOT TARGET BLAS::BLAS), and FindLAPACK.cmake:122-133 then removes BLAS_LIBRARIES from the LAPACK::LAPACK it is building and appends the existing BLAS::BLAS in their place. So a pre-existing BLAS target reaches Cytnx's link line through a LAPACK target that this config did create — which is precisely the case the old guard could not see.
Reproduced against a rendered CytnxConfig.cmake for a package built with BLA_VENDOR=OpenBLAS, consumed by a project that resolves reference BLAS first:
-- consumer's pre-existing BLAS::BLAS -> /usr/lib/x86_64-linux-gnu/libblas.so
-- resulting LAPACK::LAPACK -> BLAS::BLAS
Reference BLAS on the link line of an OpenBLAS-built Cytnx, and the old config emitted no warning at all. The new one warns, naming whichever of the two targets it found.
I kept it a warning rather than a rejection, for the same reason as the LAPACK::LAPACK case in this file: compatibility cannot be decided from inside the config. The pre-existing target may well be the matching implementation — a consumer that deliberately resolved OpenBLAS first is a legitimate and probably common ordering — and the only way to tell from here would be comparing against the build machine's library paths, which is the coupling this PR exists to remove.
Verified after the fix: the clashing consumer above now warns and still configures; a consumer with no pre-existing BLAS/LAPACK target stays silent (0 warnings). The two helper variables sit inside the existing block(SCOPE_FOR VARIABLES), checked not to leak into the consuming project.
Generated by Claude Code
0deaca9 to
ba97156
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ba97156712
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| find_package(arpackng CONFIG QUIET) | ||
| if(NOT arpackng_FOUND) | ||
| find_dependency(arpack-ng CONFIG REQUIRED) |
There was a problem hiding this comment.
Reuse existing ARPACK targets before package lookup
When a downstream project has already created the ARPACK imported target (for example ARPACK::ARPACK/arpack from a subproject or toolchain) but does not install an arpackng/arpack-ng package config, this block still runs find_dependency(arpack-ng CONFIG REQUIRED) before loading CytnxTargets.cmake, so find_package(Cytnx) fails even though the exported link dependency is already satisfied. Guard the lookup on the existing ARPACK target, mirroring the cuTENSOR/cuQuantum target reuse below.
AGENTS.md reference: AGENTS.md:L118-L119
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed and fixed in 7bebba3.
Reproduced first, with a consumer that creates the target itself and has no arpack package config anywhere on CMAKE_PREFIX_PATH:
add_library(arpack INTERFACE IMPORTED)
add_library(ARPACK::ARPACK ALIAS arpack)
find_package(Cytnx CONFIG REQUIRED)That failed at find_dependency(arpack-ng CONFIG REQUIRED) — find_package(Cytnx) rejected over a dependency the project had already satisfied, exactly as described. The lookup is now guarded with if(NOT TARGET ARPACK::ARPACK), matching the cuTENSOR/cuQuantum reuse guard directly below it.
There is a second failure the guard picks up on the way: when such a project does also have an arpackng package config installed, the unguarded lookup reaches arpackngTargets.cmake, whose add_library(arpack SHARED IMPORTED) cannot redefine an arpack target the project already created. Skipping the lookup avoids that collision too.
I guarded on ARPACK::ARPACK alone rather than also on a bare arpack. ARPACK::ARPACK is the documented public name in both packagings, so it is the reliable signal; keying off arpack as well would let a project that defines only that name suppress a lookup the export may still need under its ARPACK::ARPACK spelling.
Verified after the fix: the consumer above configures; a consumer with neither the target nor a package config still fails loudly with the missing-package error, so the guard does not mask a genuinely absent dependency; and the normal path — package config present, no pre-existing target — is unchanged, including the vendor-clash warning behaviour.
Generated by Claude Code
| find_package(arpackng CONFIG QUIET) | ||
| if(NOT arpackng_FOUND) | ||
| find_dependency(arpack-ng CONFIG REQUIRED) |
There was a problem hiding this comment.
Reuse existing ARPACK targets before package lookup
When a downstream project has already created the ARPACK imported target (for example ARPACK::ARPACK/arpack from a subproject or toolchain) but does not install an arpackng/arpack-ng package config, this block still runs find_dependency(arpack-ng CONFIG REQUIRED) before loading CytnxTargets.cmake, so find_package(Cytnx) fails even though the exported link dependency is already satisfied. Guard the lookup on the existing ARPACK target, mirroring the cuTENSOR/cuQuantum target reuse below.
Useful? React with 👍 / 👎.
| # named here, and FindLAPACKE needs FindLAPACKEXT (which only delegates to | ||
| # CMake's own FindLAPACK). Add TMG and its closure here as well if cytnx ever | ||
| # starts requesting `LAPACKE COMPONENTS TMG`. | ||
| set(CYTNX_MORSE_FINDS LAPACKE LAPACKEXT) |
There was a problem hiding this comment.
Install BLASEXT with the LAPACKE finder
For installed OpenBLAS builds, CytnxConfig.cmake reruns find_dependency(LAPACKE MODULE REQUIRED) from the copied Morse modules, but the Morse FindLAPACKEXT.cmake used by FindLAPACKE invokes find_package(BLASEXT) before delegating to LAPACK. Since this install list only ships FindLAPACKE.cmake and FindLAPACKEXT.cmake, a clean downstream find_package(Cytnx) has no FindBLASEXT.cmake on CMAKE_MODULE_PATH and fails before it can recreate MORSE::LAPACKE; include BLASEXT in this closure as well.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
This does not hold for the morse_cmake revision vendored here, so I have not changed the closure.
FindLAPACKEXT.cmake in cmake_modules/morse_cmake never calls find_package(BLASEXT). Its only find_package calls are for LAPACK:
46: find_package(LAPACK QUIET REQUIRED)
48: find_package(LAPACK QUIET)
92: find_package(LAPACK QUIET)
102: find_package(LAPACK QUIET)
112: find_package(LAPACK QUIET)
131:find_package_handle_standard_args(LAPACKEXT DEFAULT_MSG
FindBLASEXT.cmake does exist in the submodule, but the only two finders that call it are FindFFTW.cmake and FindCBLAS.cmake, and cytnx requests neither.
The install list is also not the two files described. morse_install_finds() resolves the shared helper closure itself from the names given in CYTNX_MORSE_FINDS, so the installed lib/cmake/cytnx/morse/ holds nine files:
FindHeadersAndLibs.cmake FindMorseCommon.cmake LibrariesAbsolutePath.cmake
FindLAPACKE.cmake FindMorseInit.cmake MORSE-Copyright.txt
FindLAPACKEXT.cmake ParseArguments.cmake PrintFindStatus.cmake
The scenario is covered end to end by CI rather than by inspection alone: DownstreamFindPackage builds with the openblas-cpu preset (so USE_MKL=OFF, i.e. the find_dependency(LAPACKE MODULE REQUIRED) branch), installs, then configures tests/downstream_find_package against the install tree via CMAKE_PREFIX_PATH with nothing else on CMAKE_MODULE_PATH. A missing finder in that closure would fail there before MORSE::LAPACKE could be recreated. Both matrix legs are green on the reviewed commit.
Worth noting for a future change: FindLAPACKE does pull in one more finder, TMG, but only under if(LAPACKE_WITH_TMG), which cytnx does not set. CMakeLists.txt already carries a note beside CYTNX_MORSE_FINDS to add TMG and its closure if cytnx ever starts requesting LAPACKE COMPONENTS TMG.
Generated by Claude Code
ba97156 to
34555b6
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 34555b65bf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # consumer has no arpack package config installed, and would otherwise reach | ||
| # arpack-ng's own targets file, which cannot redefine an arpack target that | ||
| # project already created. | ||
| if(NOT TARGET ARPACK::ARPACK) |
There was a problem hiding this comment.
Keep ARPACK lookup until
arpack exists
When Cytnx is built against arpackng 3.9, CMake serializes the ARPACK::ARPACK alias as $<LINK_ONLY:arpack> in CytnxTargets.cmake; a downstream that predefines only ARPACK::ARPACK from a toolchain or subproject but has no arpack package config now takes this guard, skips the lookup, and later links a bare -larpack because the exported arpack target was never created, so non-system prefixes still fail. Check for or recreate the exact target name the export uses before skipping the dependency lookup. Fresh evidence in this revision is that the new guard tests only ARPACK::ARPACK while the exported link can require arpack.
AGENTS.md reference: AGENTS.md:L118-L119
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Right, and fixed in 18ee3a9 — the guard now tests the exact name the export holds rather than ARPACK::ARPACK.
You are correct that my previous reply on the older thread did not cover this case. I argued there that keying only off ARPACK::ARPACK was the safe choice, but that leaves the 3.9-built export needing arpack while the guard passes on a name that may be a standalone target — exactly the gap you describe.
The build now records which name reached the export instead of either side guessing, since ARPACK::ARPACK being an alias is precisely what decides it:
get_target_property(cytnx_arpack_aliased ARPACK::ARPACK ALIASED_TARGET)
if(cytnx_arpack_aliased)
set(CYTNX_ARPACK_TARGET "${cytnx_arpack_aliased}")
else()
set(CYTNX_ARPACK_TARGET ARPACK::ARPACK)
endif()and CytnxConfig.cmake.in guards on if(NOT TARGET @CYTNX_ARPACK_TARGET@). Against conda-forge's arpackng 3.9 the build reports ARPACK target in the export: arpack, CytnxTargets.cmake carries $<LINK_ONLY:arpack>, and the generated config reads if(NOT TARGET arpack) — the three now agree by construction rather than by assumption.
Verified against the real generated config, with no arpack package config on CMAKE_PREFIX_PATH:
| consumer predefines | behaviour |
|---|---|
arpack + ARPACK::ARPACK alias |
guard holds, configures — the case the guard is for |
only ARPACK::ARPACK |
guard does not hold, lookup runs and fails loudly on the missing package |
So the silent -larpack degradation you predicted can no longer happen: the only consumer that skips the lookup is one that already supplies the target the export names. Both presets reconfigure clean and report the same exported name.
Generated by Claude Code
| # LAPACK::LAPACK alone does not resolve the LAPACKE_* entry points (nor | ||
| # provide <lapacke.h>). One FindLAPACKE run recreates both targets cytnx | ||
| # links: it pulls in FindLAPACKEXT, which calls CMake's FindLAPACK. | ||
| find_dependency(LAPACKE MODULE REQUIRED) |
There was a problem hiding this comment.
Force the Cytnx LAPACKE finder to run
When another dependency package in the same directory has already used find_dependency(LAPACKE MODULE REQUIRED) with its own module, CMake 3.26+'s identical-call optimization can skip this call even though this block just prepended Cytnx's Morse module path; then MORSE::LAPACKE is never created and including CytnxTargets.cmake leaves the exported Cytnx::cytnx target with an unresolved dependency. Use find_package(LAPACKE MODULE REQUIRED) here, or explicitly verify MORSE::LAPACKE and rerun/diagnose before loading the export.
AGENTS.md reference: AGENTS.md:L118-L119
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed and fixed in 43d6dd7.
I checked the mechanism in CMakeFindDependencyMacro.cmake before acting, because I expected the older if(NOT ${dep}_FOUND) short-circuit. It is the one you describe: 3.15 removed the optimization, 3.26 restored it keyed on a hash of the call —
string(SHA256 cmake_fd_call_hash "${dep};${ARGN};${${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED}")
if(_CMAKE_${dep}_${cmake_fd_call_hash}_FOUND)and its own documentation names this exact hazard: "the heuristic cannot account for ambient variables that affect package behavior… package configuration files should avoid setting such variables before their calls to find_dependency." This block sets two of them — the CMAKE_MODULE_PATH prepend and BLA_VENDOR.
Reproduced with a package whose config runs find_dependency(LAPACKE MODULE REQUIRED) against its own FindLAPACKE, found ahead of ours in the same directory:
another package called it first = OFF -> MORSE::LAPACKE created
another package called it first = ON -> MORSE::LAPACKE MISSING -- our lookup was optimized out
The branch is now find_package(LAPACKE MODULE). find_package has no such memo, and MODULE closes the config-mode hole covered in the adjacent thread. I dropped REQUIRED and verify the target after endblock() instead, so a genuine miss is still reported the way find_dependency would have — as a package-not-found reason rather than a bare fatal error:
-- Cytnx_FOUND=0
-- reason: Cytnx requires LAPACKE, but MORSE::LAPACKE was not created by the FindLAPACKE shipped with Cytnx. …
The check sits outside the block deliberately: block(SCOPE_FOR VARIABLES) would swallow Cytnx_FOUND / Cytnx_NOT_FOUND_MESSAGE before find_package could read them, while the imported target it tests is directory-scoped and still visible.
Verified after the fix: the diamond case above now creates the target; the plain consumer is unchanged; the unfindable-LAPACKE case reports the message above instead of failing at generate time; and both the openblas-cpu and mkl-cpu presets configure clean (the MKL branch renders the check inert, if(NOT ON AND …)).
Generated by Claude Code
| # FindLAPACKEXT, which calls CMake's own FindLAPACK under the BLA_VENDOR set | ||
| # above, which in turn finds BLAS. MORSE::LAPACKE and LAPACK::LAPACK both | ||
| # exist afterwards. | ||
| find_package( LAPACKE REQUIRED) |
There was a problem hiding this comment.
Force module mode for the build LAPACKE lookup
If a build environment sets CMAKE_FIND_PACKAGE_PREFER_CONFIG=TRUE and has a LAPACKEConfig.cmake that exports the usual variables but not Morse's MORSE::LAPACKE target, CMake's basic find_package signature searches Config mode first (per cmake --help-command find_package) and this line can bypass the bundled Morse finder; the new target_link_libraries(... MORSE::LAPACKE ...) then fails at generate time even though the old variable-based link path would have configured. Use find_package(LAPACKE MODULE REQUIRED) here, matching the installed config's module-mode lookup.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed and fixed in 43d6dd7 — the line is now find_package(LAPACKE MODULE REQUIRED).
Reproduced the bypass before changing anything, with a LAPACKEConfig.cmake that sets LAPACKE_LIBRARIES/LAPACKE_FOUND and nothing else, Morse on CMAKE_MODULE_PATH, and CMAKE_FIND_PACKAGE_PREFER_CONFIG=TRUE:
find_package(LAPACKE <basic> REQUIRED) -> MORSE::LAPACKE MISSING (config mode won)
find_package(LAPACKE MODULE REQUIRED) -> MORSE::LAPACKE created
So the basic signature really does reach a config package that cannot satisfy target_link_libraries(cytnx PUBLIC MORSE::LAPACKE ...), and the failure would land at generate time rather than at the lookup.
The same one-word fix went into the generated config's LAPACKE branch, which had the same exposure — see the adjacent thread, where it is paired with a find_package/find_dependency change for a second reason. Build and installed config now agree on module mode.
Verified after the fix by configuring both the openblas-cpu and mkl-cpu presets on the rewritten branch; both configure clean.
Generated by Claude Code
Cytnx::cytnx propagated several dependencies as absolute paths detected on the
build machine: ${LAPACKE_INCLUDE_DIRS} and ${LAPACKE_LIBRARIES} in the OpenBLAS
branch of CytnxBKNDCMakeLists.cmake, ${LAPACK_LIBRARIES} in the MKL branch, and
${CUTENSOR_INCLUDE_DIRS} / ${CUQUANTUM_INCLUDE_DIRS} with their libraries under
USE_CUTENSOR / USE_CUQUANTUM. install(EXPORT) copies those verbatim into
CytnxTargets.cmake, so the installed package only resolved on a machine whose
dependencies sit at the same paths.
They cannot simply be hidden behind $<BUILD_INTERFACE:> either, because they are
genuine public usage requirements: the installed header
backend/lapack_wrapper.hpp includes <lapacke.h>, and cytnx_error.hpp /
Network.hpp include <cutensor.h> / <cutensornet.h> under the PUBLIC
UNI_CUTENSOR / UNI_CUQUANTUM definitions.
Name each dependency by imported target and let the consumer rediscover it:
* MORSE::LAPACKE (created by the vendored Morse FindLAPACKE) together with
LAPACK::LAPACK on the OpenBLAS path. Both are required: where LAPACKE is a
standalone library rather than one embedded in LAPACK, MORSE::LAPACKE
carries liblapacke alone, and cytnx also calls plain BLAS/LAPACK entry
points that the removed ${LAPACK_LIBRARIES} entry used to supply.
* LAPACK::LAPACK on the MKL path.
* CUTENSOR::CUTENSOR and CUQUANTUM::CUQUANTUM, now created by
cmake/Modules/FindCUTENSOR.cmake and cmake/Modules/FindCUQUANTUM.cmake.
CytnxConfig.cmake reruns those finders against the consumer's own environment
before including CytnxTargets.cmake. They share one block(SCOPE_FOR VARIABLES),
which holds the single CMAKE_MODULE_PATH prepend and the BLA_VENDOR restore that
all of them need, so neither leaks into the consuming project; the imported
targets the finders create are directory-scoped and outlive the block. Boost,
CUDAToolkit and OpenMP stay outside it, because a consumer legitimately reads the
result variables those set and the block would swallow them. USE_MKL selects the
same branch there that it selects in the build. The Morse finders are loaded by
module path rather than by including Morse's top-level MorseInit.cmake, which
would rewrite project-wide options in the consumer.
The finder modules ship with the package: morse_install_finds() for the install
tree, and a matching file(COPY) of the same closure for the build tree, which
export(PACKAGE Cytnx) makes a consumable package too. Serializing the detected
targets with morse_export_imported_target() would have written the build
machine's paths straight back in.
ARPACK stays a plain absolute path here: it is located with a bare
find_library() and has no finder module for a consumer to rerun.
Co-Authored-By: Claude <noreply@anthropic.com>
CytnxConfig.cmake sets BLA_VENDOR to the value cytnx was built with before the LAPACK/LAPACKE lookup, and claimed that this keeps a consumer from binding to a different implementation or integer ABI. It does not. Neither finder replaces a target that already exists: FindLAPACK creates the imported target under `if(LAPACK_FOUND AND NOT TARGET LAPACK::LAPACK)`, and FindBLAS guards BLAS::BLAS the same way. When another dependency in the consuming project has already run find_package(LAPACK), the existing target survives and BLA_VENDOR steers nothing. An MKL ILP64 build of cytnx then links against an LP64 or reference target and miscomputes instead of failing loudly. A pre-existing BLAS::BLAS is enough on its own, even when no LAPACK target exists: FindLAPACK removes BLAS_LIBRARIES from the LAPACK::LAPACK it creates and links that BLAS target in their place, so the consumer's BLAS ends up on cytnx's link line through a LAPACK target that this config did create. A consumer that resolves reference BLAS first and then finds a Cytnx built against OpenBLAS gets LAPACK::LAPACK -> BLAS::BLAS -> libblas.so. BLA_VENDOR can only steer a lookup that has not happened yet, so say so in the comment and make the remaining case visible: warn when either target already exists, naming the ones found, the vendor cytnx expects, and the two ways out (resolve BLAS and LAPACK with that BLA_VENDOR before find_package(Cytnx), or find Cytnx first). A warning rather than a hard error, because compatibility cannot be decided from here -- the pre-existing target may well be the same implementation, and rejecting it would break that legitimate ordering. Co-Authored-By: Claude <noreply@anthropic.com>
FindCUTENSOR.cmake and FindCUQUANTUM.cmake raise a fatal error when their *_ROOT is unset, before reaching the guard that skips creating an existing imported target. CytnxConfig.cmake ran them unconditionally, so a consuming project that already provides CUTENSOR::CUTENSOR or CUQUANTUM::CUQUANTUM failed find_package(Cytnx) over a dependency it had in fact already satisfied. Skip each lookup when its target exists, which is all the exported cytnx target needs. Co-Authored-By: Claude <noreply@anthropic.com>
Supporting build-tree consumers meant copying Morse's FindLAPACKE closure, and cytnx's own SDK finders, into the build directory so that one generated CytnxConfig.cmake could reach them under a single relative path. That left a second copy of every finder to keep in step with the installed one, and the Morse staging had to reconstruct the file list from morse_find_core, a variable morse_install_finds() only leaves behind because it happens to be a macro. The two trees do not need the same path, only the same config logic. Configure the one template twice instead, injecting the module search paths: the build-tree config reads the finders straight out of the source tree, and the installed config reads the copies inside the package, which stay the sole responsibility of morse_install_finds(). Nothing is staged into the build directory and no file list is restated. FindMorseInit.cmake includes ParseArguments by bare name and that module sits a directory above find/, so the build-tree search path covers both directories; the installed bundle is flat and needs one. Co-Authored-By: Claude <noreply@anthropic.com>
Both provider branches in CytnxBKNDCMakeLists.cmake called finders whose work
another finder in the same branch already does:
* The MKL branch ran find_package(BLAS) before find_package(LAPACK), but
CMake's FindLAPACK looks for BLAS itself.
* The OpenBLAS branch ran find_package(BLAS) and find_package(LAPACK) before
find_package(LAPACKE), but morse_cmake's FindLAPACKE includes FindLAPACKEXT,
which calls CMake's FindLAPACK -- which in turn finds BLAS. Both
MORSE::LAPACKE and LAPACK::LAPACK, the two targets this branch links, exist
after that one call.
Drop the redundant calls, so each branch states one lookup and BLA_VENDOR is
consulted by exactly the finder that acts on it. CytnxConfig.cmake.in mirrors
the same split for the consumer's re-find: the MKL build runs
find_dependency(LAPACK), and the OpenBLAS build runs find_dependency(LAPACKE
MODULE) alone rather than a LAPACK lookup followed by a LAPACKE one.
Define UNI_OPENBLAS on the cytnx target, matching the UNI_MKL the MKL branch
already applies. CYTNX_VARIANT_INFO has always advertised the variant; this
makes it visible to code compiling against the installed headers as well.
Testing: openblas-cpu and mkl-cpu debug presets build and pass ctest; the
exported target still names MORSE::LAPACKE and LAPACK::LAPACK for the OpenBLAS
build and LAPACK::LAPACK for the MKL build.
Co-Authored-By: Claude <noreply@anthropic.com>
CytnxBKNDCMakeLists.cmake resolved arpack with find_library(ARPACK_LIB) and the
main CMakeLists.txt linked ${ARPACK_LIB} into the cytnx target. libcytnx is a
STATIC archive, so even that PRIVATE dependency reaches the export -- as
$<LINK_ONLY:/abs/path/to/libarpack.so> -- which pins the installed package to
the build machine's library layout, the same defect this branch already fixed
for LAPACKE, BLAS/LAPACK, cuTENSOR and cuQuantum.
Use arpack-ng's own CMake package instead: find_package(arpackng CONFIG) defines
ARPACK::ARPACK, cytnx links that target, and CytnxConfig.cmake re-finds the
package so a consumer binds its own arpack. Both packagings cytnx builds against
ship a CMake package config -- conda-forge's arpack for manylinux and macOS,
Alpine's arpack-dev for musllinux -- so no finder module of cytnx's own is needed
and none is installed.
ci-gpu_tests.yml is the one job that did not already provision arpack that way:
the self-hosted GPU runner supplies its dependencies from apt, and apt's arpack
installs the library and headers with no package config, so the configure step
failed outright. It now creates a workspace-local prefix holding conda-forge's
arpack and points CMAKE_PREFIX_PATH at it. micromamba is fetched as a single
static binary into the runner's temp dir rather than installing a conda
distribution, so nothing outside the workspace changes and the job keeps the
plain non-login shell its other steps use.
The lookup accepts either spelling of the package name: arpack-ng renamed it
from arpack-ng to arpackng in 3.9, and Alpine -- which the musllinux wheels
build against -- ships 3.8.0. The two also differ in how they define
ARPACK::ARPACK. 3.9 makes it an alias of a target named arpack, and CMake
records the aliased target's name when it writes an export, so CytnxTargets.cmake
names arpack; 3.8 makes it an imported target in its own right, so the export
names ARPACK::ARPACK. Either way the export holds a target name rather than a
path, and the same two-step lookup in the generated config recreates whichever
name it holds.
The generated config skips that lookup when the consuming project already
supplies ARPACK::ARPACK, mirroring the cuTENSOR/cuQuantum reuse guard beside it.
A project that builds arpack-ng as a subproject, or takes the target from a
toolchain, satisfies the exported dependency without having an arpack package
config installed; running the lookup anyway failed find_package(Cytnx) over a
dependency that was in fact present, and would otherwise reach arpack-ng's own
targets file, which cannot redefine an arpack target that project already
created. A consumer with neither the target nor a package config still fails
loudly.
The lookup runs after the BLAS/LAPACK provider rather than before it: a
statically built arpack's config calls find_dependency(BLAS) and
find_dependency(LAPACK) unless those targets already exist, so resolving cytnx's
provider first stops arpack from binding the build to a vendor other than the one
BLA_VENDOR names.
Testing, against conda-forge's arpack 3.9.1 nompi build -- the one the wheel
provisioning scripts and CI install: CytnxTargets.cmake carries
$<LINK_ONLY:arpack> and no libarpack path in both the build tree and the install
tree, and tests/downstream_find_package's consumer configures, links and runs
against each. Against Alpine's arpack-dev 3.8.0-r3, unpacked from the real apk:
the fallback name resolves, the export names ARPACK::ARPACK, and a consumer
configures against it. A consumer that defines ARPACK::ARPACK itself with no
package config on CMAKE_PREFIX_PATH configures where it previously failed, while
one with neither still reports the missing package.
Co-Authored-By: Claude <noreply@anthropic.com>
The DownstreamFindPackage job builds a consumer against the install tree, which proves the package resolves -- but only on the runner that produced it. An absolute library path baked into INTERFACE_LINK_LIBRARIES resolves there too, so the consumer step passes while the package is unusable anywhere else. That is the exact defect this workflow is meant to guard, and it went unnoticed for arpack. Read the property out of the installed CytnxTargets.cmake after the install step and fail on any entry that is a filesystem path, plus require arpack to still be named: arpack is cytnx's one PRIVATE dependency, exported only because a static archive leaves its symbols for the consumer's final link, so silently losing it from the export would break that link with nothing else in this job to catch it. The name match is case-insensitive because which one the export holds depends on the arpack-ng packaging -- "arpack" where ARPACK::ARPACK is an alias, "ARPACK::ARPACK" where it is an imported target in its own right. Verified against install trees built before and after arpack moved to an imported target: the check reports "/usr/lib/x86_64-linux-gnu/libarpack.so" and exits 1 on the former, passes on the latter. Co-Authored-By: Claude <noreply@anthropic.com>
34555b6 to
1a93594
Compare
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Closes #1120.
Main Improvement
This PR enable us to include
lapacke.hinstalled by pixi and allow us to use pixi to lock/provide/maintain/list system dependencies. This will ease a lot of pain on system configuration.Problem
Cytnx::cytnxpropagated its dependencies as absolute paths detected on the build machine:${LAPACKE_INCLUDE_DIRS}and${LAPACKE_LIBRARIES}in the OpenBLAS branch ofCytnxBKNDCMakeLists.cmake${LAPACK_LIBRARIES}in the MKL branch${CUTENSOR_INCLUDE_DIRS}/${CUQUANTUM_INCLUDE_DIRS}and their libraries underUSE_CUTENSOR/USE_CUQUANTUM${ARPACK_LIB}install(EXPORT)copies those verbatim intoCytnxTargets.cmake, so the installed package only resolved on a machine whose dependencies sit at the same paths.They also cannot simply be hidden behind a
BUILD_INTERFACEgenerator expression, because they are genuine public usage requirements: the installed headerbackend/lapack_wrapper.hppincludeslapacke.h, andcytnx_error.hpp/Network.hppincludecutensor.h/cutensornet.hunder the PUBLICUNI_CUTENSOR/UNI_CUQUANTUMdefinitions. arpack is PRIVATE, butlibcytnxis a STATIC archive, so even that reaches the export underLINK_ONLY— the consumer's final link is what resolves its symbols.Two configure-time mechanisms made this structural rather than incidental, because each selected libraries from a fact about the build machine that no consumer can reproduce:
ldd/otool -Lagainst the installed arpack and overwroteBLAS_LIBRARIES/LAPACK_LIBRARIES/LAPACKE_LIBRARIESwith whatever OpenBLAS arpack happened to be linked against;The build-policy items from the issue (
-w,-Wformat=0,-fsized-deallocation,_LIBCPP_DISABLE_AVAILABILITYmade PRIVATE;--coverageas PRIVATE compile + INTERFACE link;Boost::boostowning its own includes) were already correct onmaster. This PR covers the dependency-path half, and adds CI that exercises the coverage half.Fix
Remove the two machine-specific mechanisms first, then express every remaining dependency as an imported target the consumer rediscovers.
Both removals are safe because dependency provisioning already guarantees a single OpenBLAS on every platform cytnx ships: manylinux and macOS take arpack and OpenBLAS from the same conda-forge environment pinned to
openblas=*=*openmp*, and musllinux keeps Alpine's arpack while building OpenBLAS from source into the soname that arpack's ownNEEDEDentry names (tools/cibuildwheel_before_all.sh). Nothing that ships pins the three*_LIBRARIESvariables either.${LAPACKE_INCLUDE_DIRS},${LAPACKE_LIBRARIES},${LAPACK_LIBRARIES}MORSE::LAPACKE+LAPACK::LAPACKintel-mkl/lib/*.sopathsLAPACK::LAPACKCUTENSOR::CUTENSOR/CUQUANTUM::CUQUANTUM/abs/path/to/libarpack.soARPACK::ARPACK, from arpack-ng's own packagecmake/Modules/FindCUTENSOR.cmakeandFindCUQUANTUM.cmakenow create their imported targets.CytnxConfig.cmakere-runs the matching lookups in the consumer's own environment before includingCytnxTargets.cmake. They share a singleblock(SCOPE_FOR VARIABLES), which holds the oneCMAKE_MODULE_PATHprepend and the oneBLA_VENDORrestore that all of them need, so neither leaks into the consuming project while the imported targets they create — being directory-scoped — outlive the block.Boost,CUDAToolkitandOpenMPstay outside it, because a consumer legitimately reads the result variables those set and the block would swallow them. The Morse finders are loaded by module path rather than by including Morse's top-levelMorseInit.cmake, which would rewrite project-wide options in the consumer. Each SDK lookup is skipped when the consuming project already provides the target, because those finders fatal-error on an unset*_ROOTbefore they would notice the target exists.With only two provider branches left,
USE_MKLalone tells the generated config which lookup to re-run, so no provider variable is exported.One finder per provider
Each branch called finders whose work another finder in the same branch already does. CMake's
FindLAPACKlooks for BLAS itself, and morse_cmake'sFindLAPACKEincludesFindLAPACKEXT, which callsFindLAPACK— so onefind_package(LAPACKE)yields bothMORSE::LAPACKEandLAPACK::LAPACK. The MKL branch is onefind_package(LAPACK); the OpenBLAS branch is onefind_package(LAPACKE).UNI_OPENBLASis now defined on the target, matching theUNI_MKLthe MKL branch already applied.The LAPACKE lookup has to actually run
Linking
MORSE::LAPACKEmeans the lookup that defines it must not be silently skipped, and CMake's defaults allow two ways for that to happen. Both are avoided explicitly, in the build and in the generated config alike:find_packagesignature searches config mode ahead of module mode underCMAKE_FIND_PACKAGE_PREFER_CONFIG. ALAPACKEConfig.cmakethat sets the usual variables satisfies the call without defining Morse's target, and the failure then surfaces at generate time as an unknown target rather than at the lookup. Both call sites passMODULE.find_dependencyoptimizing the call out. CMake 3.26 restored the pre-3.15 optimization, keyed on a SHA256 ofdep;ARGN;required. Another package config in the same directory that already ranfind_dependency(LAPACKE MODULE REQUIRED)against its finder makes ours a no-op — and the heuristic cannot see theCMAKE_MODULE_PATHprepend andBLA_VENDORthis block sets first, which is precisely the case its own documentation warns about. The generated config therefore callsfind_package(LAPACKE MODULE)directly.Dropping
REQUIREDthere would turn a genuine miss into a generate-time error, so the config verifiesMORSE::LAPACKEafterendblock()and reports a package-not-found reason instead. The check lives outside the block becauseblock(SCOPE_FOR VARIABLES)would swallowCytnx_FOUND/Cytnx_NOT_FOUND_MESSAGE, while the imported target it tests is directory-scoped and stays visible.arpack through arpack-ng's package config
find_package(arpackng CONFIG)definesARPACK::ARPACK, cytnx links that, and the generated config re-finds the package so a consumer binds its own arpack. Both the build and the generated config accept either spelling of the package name, because the platforms cytnx ships do not agree on one: manylinux and macOS take arpack from conda-forge, which carries 3.9'slib/cmake/arpackng/arpackng-config.cmake, while musllinux builds against Alpine'sarpack-dev, still on 3.8.0 and shippingusr/lib/cmake/arpack-ng/arpack-ng-config.cmake(upstream renamed the package fromarpack-ngtoarpackngin 3.9).find_package(arpackng CONFIG QUIET)first, thenarpack-ng— in the generated config the first attempt has to befind_packagerather thanfind_dependency, becausefind_dependencyaborts the whole config on a miss even when QUIET. Either way it is arpack-ng's own package config, so cytnx needs no finder module of its own and installs none.The two releases also differ in the shape of
ARPACK::ARPACK, which decides what lands in the export. 3.9 defines it as an alias of a target namedarpack, and CMake records the aliased target's name when it writes an export, soCytnxTargets.cmakenamesarpack. 3.8 defines it as anINTERFACE IMPORTEDtarget in its own right, so the export namesARPACK::ARPACK. Both are target names rather than paths — which is the property this PR is after — and each side's lookup recreates whichever name its own export holds. Verified against both real packages; the CI assertion below is case-insensitive for that reason.Because that name varies, the build records which one it produced rather than letting the generated config assume:
get_target_property(cytnx_arpack_aliased ARPACK::ARPACK ALIASED_TARGET)The result is substituted into
CytnxConfig.cmake, which skips its arpack lookup only when the consuming project already supplies that exact target — a project that builds arpack-ng as a subproject or gets it from a toolchain satisfies the exported dependency without having a package config installed, and re-running the lookup there would both failfind_package(Cytnx)over a present dependency and collide with thearpacktarget that project already defined. Keying the guard off the exported name is what keeps a consumer that supplies only the other spelling from skipping a lookup the export still needs.The lookup runs after the BLAS/LAPACK provider: a statically built arpack's config calls
find_dependency(BLAS)/find_dependency(LAPACK)unless those targets already exist, so resolving cytnx's provider first stops arpack from binding the build to a vendor other thanBLA_VENDORnames. (conda-forge builds arpack shared, so that branch of its config is inert there.)One template, configured once per consumable tree
cytnx ships two consumable packages — the install tree, and the build tree via
export(PACKAGE Cytnx)— and they locate the finder modules differently. Rather than stage a second copy of Morse's closure into the build directory, the single template is configured twice with the module search paths injected: the build-tree config reads the finders straight out of the source tree, and the installed config reads the copies inside the package, which remain the sole responsibility ofmorse_install_finds(). Nothing is staged into the build directory and no file list is restated.BLA_VENDORsteers, it does not overrideCytnxConfig.cmakeasks for the vendor cytnx was built against, but that only steers a lookup that has not happened yet. Neither finder replaces a target that already exists:FindLAPACKcreatesLAPACK::LAPACKunderif(LAPACK_FOUND AND NOT TARGET LAPACK::LAPACK), andFindBLASguardsBLAS::BLASthe same way, so a target another dependency already created survives untouched.A pre-existing
BLAS::BLASis enough on its own, even when no LAPACK target exists:FindLAPACKremovesBLAS_LIBRARIESfrom theLAPACK::LAPACKit creates and links that BLAS target in their place, so the consumer's BLAS reaches cytnx's link line through a LAPACK target this config did create. Rather than bind silently — an MKL LP64/ILP64 or OpenBLAS/reference mismatch miscomputes instead of failing — the config warns when either target already exists, naming the ones it found, the expected vendor, and the two ways out. A warning and not an error, because compatibility cannot be decided from inside the config and the pre-existing target is often the same implementation.Two corrections to the issue's plan
MORSE::LAPACKEalone is not sufficient. The issue and its verification comment state that the target carries the detected libraries, so dropping${LAPACKE_LIBRARIES}/${LAPACK_LIBRARIES}is enough. That holds only when LAPACKE is embedded in LAPACK (MKL, some OpenBLAS packagings). With a standaloneliblapacke— the layout the CI uses —FindLAPACKErecordsliblapacke.soalone, and cytnx also calls plain BLAS/LAPACK entry points. Following the issue literally made the downstream consumer fail to link withlibblas.so.3: error adding symbols: DSO missing from command line. HenceLAPACK::LAPACKalongside it.Finding LAPACK first does not fold it into
MORSE::LAPACKE. Probed directly: the consumer-side target staysliblapacke.soeither way. The ordering is kept becauseFindLAPACKErequires LAPACK regardless, not because it merges the two.Consumer-visible constraint
block()puts a CMake >= 3.25 floor on consuming the package, matching cytnx's own build floor.CytnxConfig.cmakereports it as a package-not-found reason rather than failing on an unknown command.cmake_minimum_required()is deliberately not used there — it would apply cytnx's policy settings to the consuming project's scope.CI
ci-downstream-find-packagegains two things:configurationmatrix —uninstrumented(RUN_TESTS=OFF, what a real downstream user builds) andcoverage(RUN_TESTS=ON). cytnx is a STATIC archive, so--coveragehas to be an unconditional INTERFACE link option: an instrumentedlibcytnx.aneeds the coverage runtime after installation just as much as before. The coverage leg is what proves it — dropping the option from the install interface would leave that consumer with undefined__gcov_*references.INTERFACE_LINK_LIBRARIESout of the installedCytnxTargets.cmakeand fails on any entry that is a filesystem path, and requires arpack to still be named. Verified against install trees built before and after this branch: it reports/usr/lib/x86_64-linux-gnu/libarpack.soand exits 1 on the former, passes on the latter.ci-gpu_tests.ymlgains an arpack provisioning step. It is the one job whose dependencies come from apt rather than conda-forge, and apt's arpack installs the library and headers with no package config, so the configure step failed outright once cytnx started resolving arpack throughfind_package(arpackng CONFIG). The job now creates a workspace-local prefix holding conda-forge's arpack and pointsCMAKE_PREFIX_PATHat it. micromamba is fetched as a single static binary into the runner's temp dir rather than installing a conda distribution, so nothing outside the workspace changes on the self-hosted machine and the job keeps the plain non-login shell its other steps use.Testing
Every acceptance criterion was exercised, not assumed. Built and consumed locally with GCC 13 / CMake 3.28 using
tests/downstream_find_package, against both the build tree and the install tree:debug-openblas-cputest_main+ctestdebug-mkl-cputest_main+ctestintel-mklpaths in the exportCytnxTargets.cmakeBoost::boost;MORSE::LAPACKE;LAPACK::LAPACKplus aLINK_ONLYentry naming arpack — no dependency paths, no source-tree pathsarpack=3.9.1=nompi_*packagelib/cmake/arpackng/arpackng-config.cmake; configuring against it resolvesARPACK::ARPACK, the export namesarpack, and both consumers link and runarpack-dev=3.8.0-r3package (musllinux's arpack).apk: it shipsusr/lib/cmake/arpack-ng/arpack-ng-config.cmakeand definesARPACK::ARPACKas anINTERFACE IMPORTEDtarget, so the fallback name resolves and the export namesARPACK::ARPACK; a consumer configures against that exportlink.txt: the name recorded differs by packaging, and each side's lookup resolves its ownRUN_TESTS=ON) consumers, both trees--coveragein the installedINTERFACE_LINK_OPTIONSand on the install-tree consumer's link linebackend/lapack_wrapper.hppwith LAPACKE under a non-system prefix#errorguard; the prefix's include dir (as-isystem) and itsliblapacke.soboth reached the consumerDevelopmentcomponentCUTENSOR::CUTENSOR/CUQUANTUM::CUQUANTUMFindCUTENSOR.cmake:25with*_ROOTunset; guarded form reuses the targets and reaches the endLAPACK::LAPACKfind_package(Cytnx); silent on the normal pathCMAKE_FIND_PACKAGE_PREFER_CONFIGLAPACKEConfig.cmakethat defines no target, the basic signature leavesMORSE::LAPACKEmissing whileMODULEcreates it — the reason both call sites passMODULEfind_dependencyMORSE::LAPACKEnever appeared;find_package(LAPACKE MODULE)creates it in both orderingsCytnx_FOUND=0with a package-not-found reason, rather than a generate-time unknown-target errorarpack,CytnxTargets.cmakecarries aLINK_ONLY:arpackentry, generated config readsif(NOT TARGET arpack); a consumer predefiningarpack+ alias skips the lookup and configures, one predefining onlyARPACK::ARPACKstill runs it and fails loudlyBLAS::BLASonlyLAPACK::LAPACK -> BLAS::BLAS -> libblas.soand no warning; the guard now fires, the clean consumer stays silent, and neither helper variable escapes theblock()UNI_GPU;UNI_CUTENSOR;UNI_CUQUANTUMandcxx_std_20still propagate; representative.cppand.cuTUs compile-w,-Wformat=0,-fsized-deallocation,_LIBCPP_DISABLE_AVAILABILITYall absent from the consumer's compile line;-std=gnu++20inheritedcurl/tarfetch yields a working micromamba,micromamba createproduces the arpackng config, and configuring withCMAKE_PREFIX_PATHset to that prefix resolvesARPACK::ARPACKKnown gap, deliberately left
MKL's
mkl.hinclude dir is still not propagated.masternever propagated it either (CI and local MKL builds rely on it being on the default include path, e.g. viaCPATH), so this is pre-existing rather than a regression from this PR. Point 6 of the issue would be fully closed by modelling MKL throughMKL::MKLfromMKLConfig.cmake, which is a larger change.