CMake: generate or extract header necessary for shared lib

Viewed 837

I'm currently working on a larger project which is not maintained.

Explanation It tries to use CMake but not very intuitive. Instead of including shared libs and headers from every submodule, one can set a flag in the top level CMakeList which triggers an add_subdirectory, sneaking the top level CMAKE_BUILD_DIR and installs into it. These CMakeLists use exclusively glob RECUSE to find all sources and most operation are not target based but global. Therefor the main target compiles properly. Here is a little snippet of the main CMakeList

set(MY_BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(MY_SOURCE_BASE_DIR "${MY_BASE_DIR}/myproject")

# includes
include(build/global.cmake)
#include(externalLibs/mz-cmaketools-master/global.cmake)


# configuration options
option(MOD1 "" ON)
option(MOD2 "" ON)
option(MOD3 "" OFF)
option(MOD4 "" OFF)
option(MOD5 "" ON)
option(MOD6 "" OFF)
option(MOD7 "" ON)

message("-- Building myproject unit-tests - ${ENABLE_MYPROJECT_TEST}")
set(CMAKE_ECLIPSE_MAKE_ARGUMENTS "-j4")

if(MOD1)
    message(STATUS "Building viral_core + viral_recon")
    add_subdirectory(${MY_BASE_DIR}/externalLibs/viral/trunk/linux)


include_directories(${MY_BASE_DIR}/externalLibs/viral/trunk/source/)
    else(MOD1)
        if(MOD3)
            message(WARNING "Building viral_core + viral_recon")
            add_subdirectory(${MY_BASE_DIR}/externalLibs/viral/trunk/linux)
            include_directories(${MY_BASE_DIR}/externalLibs/viral/trunk/source/)
        endif(MOD3)
    endif(MOD1)

# external libraries
include(${MY_BASE_DIR}/externalLibs.cmake)


include_directories(
  ${MY_BASE_DIR}
  ${MY_SOURCE_BASE_DIR}
  ${MY_BASE_DIR}/externalLibs/viral/trunk/source/
)


# depending on the supported robots we have different dependencies
if( MOD1 ) 
        set(OPT1 TRUE)
endif()

...

# collect binaries
set(LIBRARY_OUTPUT_PATH ${MY_BASE_DIR} CACHE PATH "Library output path")
set(EXECUTABLE_OUTPUT_PATH ${MY_BASE_DIR} CACHE PATH "Executable output path")
message("-- Setting executable output path: ${EXECUTABLE_OUTPUT_PATH}")
message("-- Setting library output path   : ${LIBRARY_OUTPUT_PATH}")


# collect sources

file(GLOB MY 
    "${MY_SOURCE_BASE_DIR}/*.h"
    "${MY_SOURCE_BASE_DIR}/*.cpp"
)
source_group(base FILES ${MY_BASE})#

file(GLOB MY_UTIL 
    "${MY_SOURCE_BASE_DIR}/util/*.h"
    "${MY_SOURCE_BASE_DIR}/util/*.cpp"
)
source_group(util FILES ${MY_UTIL})

file(GLOB_RECURSE MY_KINEMATICS 
    "${MY_SOURCE_BASE_DIR}/kinematics/*.h"
    "${MY_SOURCE_BASE_DIR}/kinematics/*.cpp"
) 
source_group(kinematics FILES ${MY_KINEMATICS})

file(GLOB MY_COLLISION
    "${MY_SOURCE_BASE_DIR}/collision/pqp/*.cpp"
    "${MY_SOURCE_BASE_DIR}/collision/PQP*.cpp"
)
source_group(collision FILES ${MY_COLLISION})
...
add_library(MY SHARED
    ${MY_COLLISION}
    ${MY_UTIL}
    ${MY_KINEMATICS}
    ...}
)
....

In the end the project builds several libs, but does not publish required headers to use them. These libs are put into the top level of the build directory. (without an install step)

Question

Is there a possibility to make CMake to export the included header for a lib (target). To be more precisely, these headers should only lie within the source folder and below; headers from /usr/... should not be considered. Furthermore it is acceptable if the headers are merged into one single header. But from the ~1700 headers only ~40 are relevant, so a simple find RECURSE does not seem adequate to me.

I did have a look at GENERATE_EXPORT_HEADER but do not think that is what I look for. And I do not have the permission to change the project, so I want to make a patch to the SVN repository and do NOT want to make another copy of the repository, because there are around 10 different in use.

I would appreciate any hints towards a solution or strategy

This is my very first question on stackoverflow, so please be merciful :)

1 Answers

I hope I understood your question correctly.

I usually use the CMakePackageConfigHelpers to generate cmake configuration files for the lib, if done correctly the nice thing is that CMake sets up transitive dependencies and include directories automatically when you link against a library with a package config.

The install command can then be used to copy targets, directories, include files to the CMAKE_INSTALL_PREFIX dir, or into an archive via CPack.

After declaring my target I will usually have something along the lines of:

install(TARGETS MyLibrary
    LIBRARY DESTINATION lib
)

include(CMakePackageConfigHelpers)

write_basic_package_version_file(
    "${GENERATED_DIR}/MyLibraryConfigVersion.cmake" COMPATIBILITY SameMajorVersion
)

configure_package_config_file(
    "cmake/Config.cmake.in" 
    "${GENERATED_DIR}/MyLibraryConfig.cmake"
    INSTALL_DESTINATION "cmake"
)

install(FILES 
    inc/someHeader.h
    inc/someHeaderB.h
    DESTINATION include/MyLibrary
)

install(FILES 
    "${GENERATED_DIR}/MyLibraryConfig.cmake" 
    "${GENERATED_DIR}/MyLibraryConfigVersion.cmake" 
    DESTINATION "cmake"
)

Then you can set CMAKE_INSTALL_PREFIX to a directory of your choice and run make install, creating the following structure:

InstallationDir
├── cmake
│   ├── MyLibraryConfig.cmake
│   └── MyLibraryConfigVersion.cmake
├── include
│   └── MyLibrary
│       ├── someHeaderB.h
│       └── someHeader.h
└── lib
    └── MyLibrary.so

Then you can use find_package in CONFIG mode to automatically set up include directories, compile options etc. when linking against MyLibrary.so.

Just a hint: The target_include_directories function is preferred to include_directories. I am not sure how well config file generation works if you do not use it. Also take a look at the following functions for more advanced control on your target: set_target_properties, target_link_libraries, target_compile_definitions

Related