ROOT_GENERATE_DICTIONARY with header only target?

Hi

I’m try to create a root dictionary with a CMake target. Most of the time, my data structures are only defined in header files without any source files. Therefore, the CMake target that includes those header files aren’t a really library or executable:

add_library(MyDataStructs INTERFACE)
target_include_directories(MyDataStructs INTERFACE ${CMAKE_CRRUENT_SOURCE_DIR})
target_link_libraries(MyDataStructs INTERFACE ROOT::RIO)

ROOT_GENERATE_DICTIONARY(myData_dict MyData1.hpp MyData2.hpp MODULE MyDataStructs LINKDEF DataLinkDef.h)

This doesn’t work giving an error:

 Cannot find header MyData1.hpp to generate dictionary myData_dict for.  Did
  you forget to set the INCLUDE_DIRECTORIES property for the current
  directory?
Call Stack (most recent call first):
  data/CMakeLists.txt:6 (ROOT_GENERATE_DICTIONARY)

So how do I generate root dictionary with a header-only cmake target?

ROOT Version: 6.30.02
Platform: Debian Buster
Compiler: gcc 13


Can you provide a simple example reproducing the issue? The error message is just telling it doesn’t find the header file:

Cannot find header MyData1.hpp to generate dictionary myData_dict for.  Did
  you forget to set the INCLUDE_DIRECTORIES property for the current
  directory?

Hi, @bellenot

Thanks very much for your reply. Yes, I have a simple example. Please check this github repo. In the repo, I put the data structure in /data/ folder with the corresponding CMakeLists.txt.

So I got the cmake errors as is shown above. But if I change it to a shared library (by defining a constructor in a source file), then everything could works out. But then I have to define that unnecessary constructor in the source file.

OK, thanks I’ll try to test it maybe tomorrow. In the meanwhile, did you see Root_generate_dictionary ? It has a header only dictionary…

So I just tried, and this works (on Windows, but it shouldn’t matter):
I copied the data/MyData.hpp and data/DataLinkDef.h files in the parent directory, then using this CMakeLists.txt:

cmake_minimum_required(VERSION 3.27)

project(test_root)

find_package(ROOT REQUIRED)
include(${ROOT_USE_FILE})
link_directories(${ROOT_LIBRARY_DIR})
include_directories(${ROOT_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR})

add_executable(write_data write.cpp)
target_link_libraries(write_data PRIVATE ROOT::RIO ROOT::Tree)
set_property(TARGET write_data PROPERTY ENABLE_EXPORTS 1)
if(MSVC)
  set_target_properties(write_data PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
endif()

add_executable(read_data read.cpp)
target_link_libraries(read_data PRIVATE ROOT::RIO ROOT::Tree)
set_property(TARGET read_data PROPERTY ENABLE_EXPORTS 1)
if(MSVC)
  set_target_properties(read_data PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
endif()

ROOT_GENERATE_DICTIONARY(readData_dict MyData.hpp MODULE read_data LINKDEF DataLinkDef.h)

ROOT_GENERATE_DICTIONARY(writeData_dict MyData.hpp MODULE write_data LINKDEF DataLinkDef.h)

And it seems to work:

C:\Users\bellenot\rootdev\Forum\EdwinYZ\root_forum_1\build\Release>write_data.exe

C:\Users\bellenot\rootdev\Forum\EdwinYZ\root_forum_1\build\Release>read_data.exe
0
my data
1
my data
2
my data
3
[...]
my data
998
my data
999
my data
C:\Users\bellenot\rootdev\Forum\EdwinYZ\root_forum_1\build\Release>

(note that generating one dictionary per target is maybe not needed…)

Thanks for trying out.

But in your example

ROOT_GENERATE_DICTIONARY(readData_dict MyData.hpp MODULE read_data LINKDEF DataLinkDef.h)

read_data target is NOT a header-only target. Sometimes a project only contains header files without any executable. I won’t whether it’s still possible to generate a dictionary in this case.

As I said, the dictionary is generated with only the header file (there is no source file) and it works. Now, if it’s not what you’re asking for, I might have misunderstood your question…

Sorry for the misunderstanding.

The issue that I’m trying to point here is about header-only targets, instead of only header files. I hope we both agree that these two are very different things. :smiley:

A cmake target could represent a library, an executable or a group of header files without any source files. We could generate root dictionary by giving the target name as the MODULE argument, as is for read_data in

ROOT_GENERATE_DICTIONARY(readData_dict MyData.hpp MODULE read_data LINKDEF DataLinkDef.h)

However, the issue here is that if the target has no source files (either only containing header files or possessing an include dir), the ROOT_GENERATE_DICTIONARY seems not to work anymore.

Hope I could make it clearer.

Cheers

Oh, OK, thanks! So if I understand correctly, it is not a CMake issue, but a dictionary generation issue instead, right? Not even with rootcling in the terminal?

Thanks for this tip. Yes, I tried with rootcling and it does work with only header files. But I also found that rootcling also generates a source file for the dictionary, which could be the reason why header-only target can’t work at all.

Therefore I found the solution with this info. Instead of declaring the target with INTERFACE:

add_library(myData INTERFACE)
target_include_directories(myData PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(myData PRIVATE ROOT::RIO)

declare the target with either SHARED or STATIC:

add_library(myData SHARED)
target_include_directories(myData PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(myData PRIVATE ROOT::RIO)

Then it works as expected.

Can you try with something like this:

ROOT_GENERATE_DICTIONARY(readData_dict MyData.hpp LINKDEF DataLinkDef.h)

I.e. without MODULE?

Yes, but then I have to manually deal with include path for those headers. :smiley:

But thanks for the info.

You’re welcome. And right, the dictionary generation generates source file(s)…