Skip to content

Fix CMake export and include interfaces#181

Open
NikEfth wants to merge 34 commits intoETSInitiative:mainfrom
NikEfth:fix/cmake-export
Open

Fix CMake export and include interfaces#181
NikEfth wants to merge 34 commits intoETSInitiative:mainfrom
NikEfth:fix/cmake-export

Conversation

@NikEfth
Copy link

@NikEfth NikEfth commented Dec 16, 2025

Changes in this pull request

When PETSIRD is used as a subproject and exported by a parent project, CMake
requires that exported targets do not reference source-tree paths and that all
public dependencies are also exported.
This PR fixes those issues while keeping standalone PETSIRD builds unchanged.

Related issues

Checklist before requesting a review

  • [ X ] I have performed a self-review of my code
  • I have added docstrings/doxygen in line with the guidance in the developer guide
  • [ X ] The code builds and runs on my machine

Please tick the following:

  • [ X ] The content of this Pull Request (the Contribution) is intentionally submitted for inclusion in the ETSI software (the Work) under the terms and conditions of the Apache-2.0 License.

@NikEfth NikEfth marked this pull request as draft December 24, 2025 08:16
NikEfth and others added 5 commits December 24, 2025 09:51
- Add install/export rules for PETSIRD targets
- Provide PETSIRDConfig.cmake and version file
- Fix include directories using BUILD/INSTALL interfaces
- Export PETSIRD::petsird target for find_package()
- Prepare PETSIRD for external consumers (STIR -- this has been going on for a while )
@NikEfth NikEfth marked this pull request as ready for review December 24, 2025 11:17
@NikEfth
Copy link
Author

NikEfth commented Dec 24, 2025

Finally, this took me 2 week of bouncing around and many problems in STIR:

Very brief summary.

This PR fixes PETSIRD integration issues by properly exporting PETSIRD as a CMake package instead of relying on in-tree submodule builds.

Specifically, it:
• Adds a proper PETSIRDConfig.cmake export
• Exports PETSIRD targets (petsird, petsird_generated)
• Enables downstream projects (e.g. STIR, STIR2PETSIRD) to use find_package(PETSIRD)
• Eliminates ODR / ABI inconsistencies caused by building PETSIRD multiple times
• Fixes hard-to-debug runtime errors where DetectionPosition map lookups failed despite matching coordinates
• Makes PETSIRD usable as a standalone, installed dependency

Background / Motivation

PETSIRD was previously consumed via a submodule and built inside STIR, which caused:
• Multiple definitions of inline operators (ODR violations)
• ABI mismatches across translation units
• std::map / std::unordered_map lookups failing for identical DetectionPosition values
• AddressSanitizer crashes in listmode and normalisation code paths

These issues became visible once CI enabled stricter sanitizers and newer compilers.

Exporting PETSIRD as a proper CMake package ensures a single canonical definition of all PETSIRD/STIR types across the entire build.

@NikEfth
Copy link
Author

NikEfth commented Dec 25, 2025

Further to the previous posts,
This PR fixes a series of CMake and CI issues that prevented PETSIRD from being reliably consumed by downstream projects (well ... STIR) in a clean build environment.

While PETSIRD built correctly on my PC, it failed when used as an external dependency in CI.
The failures gave linker errors as:
ld: cannot find -lxtensor
and missing include errors in STIR.
fatal error: petsird/protocols.h: No such file or directory
These, only appeared in clean environments, which made them easy to miss locally.

Several independent problems linked.

  1. Header-only depencies linked as libraries
    Yardl-generated CMakeLists added:
    target_link_libraries(petsird_generated xtensor)

  2. Incomplete exported interface

  • PETSIRD headers were installed, STIR didn't receive include paths unless explicitly added.
  1. CI environment differences
    CI used a clean conda-based toolchain. - xtensor is not a system library - architecture-specific compilers were present - implicit system include paths were missing

Fixes:

  • Correct CMake exports
  • Remove linking of header-only libraries

Copy link
Contributor

@KrisThielemans KrisThielemans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the export is a good idea. It'll also make it easier for putting on conda etc. However, I think it'd be best to export 2 targets: PETSIRD::generated and PETSIRD::helpers.

Potentially, we should INSTALL the examples.

And I guess we need a simple CI test for this as well. sorry.

Comment on lines +24 to +33
foreach(t petsird petsird_generated petsird_helpers)
if(TARGET ${t})
get_target_property(_libs ${t} INTERFACE_LINK_LIBRARIES)
if(_libs)
list(FILTER _libs EXCLUDE REGEX "xtensor")
set_target_properties(${t}
PROPERTIES INTERFACE_LINK_LIBRARIES "${_libs}")
endif()
endif()
endforeach()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did you need this? (probably because you removed target_link_libraries)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To undo an overly-aggressive dependency propagation introduced by generated / helper targets.
xtensor is header-only
However… Some targets propagate xtensor via INTERFACE_LINK_LIBRARIES
Something like:
INTERFACE_LINK_LIBRARIES xtensor::xtensor
This becomes a real problem in STIR / STIR2PETSIRD. Header-only deps should not force downstream linking.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as xtensor is header-only, we could use the same trick as https://github.com/UCL/STIR/blob/ec3a5670f9b63cb10f4bebce6edf22d6f028a6fb/src/buildblock/CMakeLists.txt#L125-L137, i.e. only add the include path.

However, this is all quite ugly. I'd rather avoid it as much as possible.

As long as we have find_dependency(xtensor) in the Config.cmake, there should be no problem

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please try without it and document any problems?

@NikEfth NikEfth closed this Dec 28, 2025
@NikEfth NikEfth reopened this Dec 28, 2025
Copy link
Author

@NikEfth NikEfth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will try step-by-step to bring lxtensor-blas back and see when linking becomes an issue.

Comment on lines +24 to +33
foreach(t petsird petsird_generated petsird_helpers)
if(TARGET ${t})
get_target_property(_libs ${t} INTERFACE_LINK_LIBRARIES)
if(_libs)
list(FILTER _libs EXCLUDE REGEX "xtensor")
set_target_properties(${t}
PROPERTIES INTERFACE_LINK_LIBRARIES "${_libs}")
endif()
endif()
endforeach()
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To undo an overly-aggressive dependency propagation introduced by generated / helper targets.
xtensor is header-only
However… Some targets propagate xtensor via INTERFACE_LINK_LIBRARIES
Something like:
INTERFACE_LINK_LIBRARIES xtensor::xtensor
This becomes a real problem in STIR / STIR2PETSIRD. Header-only deps should not force downstream linking.

@NikEfth
Copy link
Author

NikEfth commented Dec 28, 2025

@KrisThielemans I am about to finish the CI yml and I think I have to apologize but my yml skill are not as good as yours (or @casperdcl ) and I make just as typing commands in the terminal :) I could not modify yours successfully, running to all sort of issues. Have a look at mine and let me know if I can improve.

NikEfth and others added 3 commits December 28, 2025 17:49
Co-authored-by: Kris Thielemans <KrisThielemans@users.noreply.github.com>
Co-authored-by: Kris Thielemans <KrisThielemans@users.noreply.github.com>
Co-authored-by: Kris Thielemans <KrisThielemans@users.noreply.github.com>
@NikEfth
Copy link
Author

NikEfth commented Mar 19, 2026

I am sorry I cannot reply inline.

I commented out the lines above and when compiling STIR I get:

/home/n.efthymiou/miniforge3/envs/petsird/bin/../lib/gcc/x86_64-conda-linux-gnu/14.3.0/../../../../x86_64-conda-linux-gnu/bin/ld: cannot find -lxtensor: No such file or directory
collect2: error: ld returned 1 exit status
make[2]: *** [src/utilities/CMakeFiles/manip_image.dir/build.make:142: src/utilities/manip_image] Error 1
make[1]: *** [CMakeFiles/Makefile2:1543: src/utilities/CMakeFiles/manip_image.dir/all] Error 2
make: *** [Makefile:146: all] Error 2

@NikEfth
Copy link
Author

NikEfth commented Mar 19, 2026

In addition, what else did you change?

/home/n.efthymiou/Developer/ETSI/STIR/src/include/stir/listmode/CListModeDataPETSIRD.h:40:10: fatal error: petsird_helpers.h: No such file or directory
   40 | #include "petsird_helpers.h"
      |          ^~~~~~~~~~~~~~~~~~~
compilation terminated.

@KrisThielemans
Copy link
Contributor

hmmm. I just went back to petsird_helpers being an INTERFACE library.

@KrisThielemans
Copy link
Contributor

CI fails due to xtensor now using concept, see microsoft/yardl#272. I guess we should update our reqiurement here. Sigh

@KrisThielemans
Copy link
Contributor

I commented out the lines above and when compiling STIR I get:

/home/n.efthymiou/miniforge3/envs/petsird/bin/../lib/gcc/x86_64-conda-linux-gnu/14.3.0/../../../../x86_64-conda-linux-gnu/bin/ld: cannot find -lxtensor: No such file or directory
collect2: error: ld returned 1 exit status
make[2]: *** [src/utilities/CMakeFiles/manip_image.dir/build.make:142: src/utilities/manip_image] Error 1
make[1]: *** [CMakeFiles/Makefile2:1543: src/utilities/CMakeFiles/manip_image.dir/all] Error 2
make: *** [Makefile:146: all] Error 2

This is quite disappointing. My feeling it's an xtensor CMake problem, as it shouldn't add that for a header-only library (@naegelejd @johnstairs @casperdcl what do you think?), but unlikely we will be able to get that fixed soon.

Maybe we need to keep these lines in for now, but add a documentation line why

@NikEfth
Copy link
Author

NikEfth commented Mar 19, 2026

hmmm. I just went back to petsird_helpers being an INTERFACE library.

But helpers have sources, no?

@KrisThielemans
Copy link
Contributor

hmmm. I just went back to petsird_helpers being an INTERFACE library.

But helpers have sources, no?

The helpers library is include only, which is why I expect that it should be an INTERFACE library. See e.g. https://stackoverflow.com/a/65556039

@KrisThielemans
Copy link
Contributor

I really don't want to restrict to old xtensor, sorry. Did using C++-20 not solve this?

@NikEfth
Copy link
Author

NikEfth commented Mar 19, 2026

But types.cc is not a header

@KrisThielemans
Copy link
Contributor

types.cc etc sit in petsird_generated, not helpers. Also, the yardl-generated CMakeLists.txt. has

add_library(petsird_generated OBJECT
  protocols.cc
  types.cc
  ndjson/protocols.cc
  binary/protocols.cc
  hdf5/protocols.cc
)

i.e. it's on OBJECT library. PETSIRD itself shouldn't need to care about this (and this isn't related to any of the compilation errors that you flagged).

BTW, https://cmake.org/cmake/help/git-stage/manual/cmake-buildsystem.7.html#interface-libraries has some examples, but as usual they're not incredibly helpful.

@NikEfth
Copy link
Author

NikEfth commented Mar 19, 2026

I really don't want to restrict to old xtensor, sorry. Did using C++-20 not solve this?

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON

This didn't

@NikEfth
Copy link
Author

NikEfth commented Mar 19, 2026

OK. You cannot have it all. I'm sorry

set(CMAKE_CXX_STANDARD 20)

breaks yardl.
ss >> date::parse("%FT%T", value);
should be generated to
date::from_stream(ss, "%FT%T", value);

@KrisThielemans
Copy link
Contributor

should have been fixed. microsoft/yardl#245 and microsoft/yardl#270. @naegelejd and @johnstairs Can you please release a new yardl version asap?

@NikEfth
Copy link
Author

NikEfth commented Mar 19, 2026

Besides the cxx20, this is done from myside. STIR links happily.

@naegelejd
Copy link
Contributor

Yes, I will release the next version of Yardl with this fix.

Before doing that, however, have you had a chance to test this issue here using the main branch of Yardl? That would be a good idea before we release the next version.

@KrisThielemans
Copy link
Contributor

@NikEfth not sure if building yardl is an easy one fo ryou. still on Macos?

@naegelejd
Copy link
Contributor

If you give me ~24 hours I can try it myself and report back. If it works I'll release Yardl 0.6.7 ASAP.

@NikEfth
Copy link
Author

NikEfth commented Mar 19, 2026

UPDATE:

This is fine now.

I updated everything as discussed above and when building STIR I have the problem:

/home/n.efthymiou/miniforge3/envs/petsird/include/xtensor/generators/xgenerator.hpp:382:13: error: 'const xt::xgenerator<xt::detail::concatenate_invoker<xt::detail::concatenate_access, const xt::xfixed_container<float, xt::fixed_shape<3, 4>, xt::layout_type::row_major, false, xt::xtensor_expression_tag>&, const xt::xarray_container<xt::uvector<float, std::allocator<float> >, xt::layout_type::row_major, xt::svector<long unsigned int, 4, std::allocator<long unsigned int>, true>, xt::xtensor_expression_tag>&>, float, xt::svector<long unsigned int, 4, std::allocator<long unsigned int>, true> >::functor_type' {aka 'const class xt::detail::concatenate_invoker<xt::detail::concatenate_access, const xt::xfixed_container<float, xt::fixed_shape<3, 4>, xt::layout_type::row_major, false, xt::xtensor_expression_tag>&, const xt::xarray_container<xt::uvector<float, std::allocator<float> >, xt::layout_type::row_major, xt::svector<long unsigned int, 4, std::allocator<long unsigned int>, true>, xt::xtensor_expression_tag>&>'} has no member named 'assign_to'
  382 |         m_f.assign_to(e);
      |         ~~~~^~~~~~~~~

/home/n.efthymiou/miniforge3/envs/petsird/include/xtensor/generators/xgenerator.hpp:382:13: error: 'const xt::xgenerator<xt::detail::fn_impl<xt::detail::eye_fn<float> >, float, std::vector<long unsigned int> >::functor_type' {aka 'const class xt::detail::fn_impl<xt::detail::eye_fn<float> >'} has no member named 'assign_to'
  382 |         m_f.assign_to(e);
      |         ~~~~^~~~~~~~~

@NikEfth
Copy link
Author

NikEfth commented Mar 19, 2026

This is the last problem with compiling STIR (linked to PETSIRD). I am too tired to figure this out:

/home/n.efthymiou/miniforge3/envs/petsird/bin/../libexec/gcc/x86_64-conda-linux-gnu/15.2.0/ld: CMakeFiles/test_PETSIRDInfo_helpers.dir/test_PETSIRDInfo_helpers.cxx.o: in function `stir::PETSIRDTests::test_find_unique_values_1D()':
test_PETSIRDInfo_helpers.cxx:(.text._ZN4stir12PETSIRDTests26test_find_unique_values_1DEv+0x32a): undefined reference to `fmt::v12::vformat[abi:cxx11](fmt::v12::basic_string_view<char>, fmt::v12::basic_format_args<fmt::v12::context>)'
/home/n.efthymiou/miniforge3/envs/petsird/bin/../libexec/gcc/x86_64-conda-linux-gnu/15.2.0/ld: CMakeFiles/test_PETSIRDInfo_helpers.dir/test_PETSIRDInfo_helpers.cxx.o: in function `stir::PETSIRDTests::test_find_unique_values_2D()':
test_PETSIRDInfo_helpers.cxx:(.text._ZN4stir12PETSIRDTests26test_find_unique_values_2DEv+0x63c): undefined reference to `fmt::v12::vformat[abi:cxx11](fmt::v12::basic_string_view<char>, fmt::v12::basic_format_args<fmt::v12::context>)'
collect2: error: ld returned 1 exit status
make[2]: *** [src/test/CMakeFiles/test_PETSIRDInfo_helpers.dir/build.make:120: src/test/test_PETSIRDInfo_helpers] Error 1
make[1]: *** [CMakeFiles/Makefile2:7981: src/test/CMakeFiles/test_PETSIRDInfo_helpers.dir/all] Error 2
make[1]: *** Waiting for unfinished jobs....

@KrisThielemans
Copy link
Contributor

Hi @NikEfth, I'm afraid I cannot follow anymore.

In any case, this is for tomorrow.

@NikEfth
Copy link
Author

NikEfth commented Mar 20, 2026

  • Yes I am sorry for the confusion.
  • Here everything works fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants