Search code examples
c++cmakeitk

Build of multiple subprojects with library that has external dependency


I'm struggling with an project that includes CMake (version 3.11) and subdirectories. It includes ITK as an external library.

I want to create library that I can use in the different subdirectories like MyApp to build multiple applications that are based on MyLib and every change in MyLib is updated in the other applications. The MyLib is a header-only templated library. In addition this kind of libraries do not get a own project in VisualStudio.

My questions are:

  1. What is the correct way of using CMake in this scenario? I have searched and tried some example but was not successful. Ideally I can build applications depending on the MyLib independently. The top CMakeLists.txt may not pull in the includes from the library. This should be done by the applications.
  2. What would change if the MyLib becomes a non-header-only library? In the future I may add non templated classes.

Bonus question:

  1. How can I add the header-only library to VisualStudio? This is more a question for convencience.

Any help is appreciated and I can provide more details if necessary.

The source environment looks like this:

ProjectDirectory
-  CMakeLists.txt
-- MyLib
   - CMakeLists.txt
   -- include
      - File1.h (template header)
      - File1.hxx (template body)
   -- src
      - (empty for now)
-- MyApp
   - CMakeLists.txt
   - AppFile1.cxx (File containing main function)

This is an out-of-source-build project. Ideally the compiled libraries and application are put in a directory like this (on Windows):

└── bin
    ├── Debug
    │   ├── MyApp.exe
    │   └── MyLib.lib
    └── Release
        ├── MyApp.exe
        └── MyLib.lib

ProjectDirectory CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(project_name)

# Setup build locations.
if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)
  set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
endif()
if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)
  set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
endif()
if(NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
  set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
endif()

add_subdirectory(MyLib)
add_subdirectory(MyApp)

MyLib CMakeLists.txt:

find_package(ITK REQUIRED
    COMPONENTS RTK ITKImageIO)
include(${ITK_USE_FILE})

set(HDRS
    ${CMAKE_CURRENT_SOURCE_DIR}/include/File1.h
    ${CMAKE_CURRENT_SOURCE_DIR}/include/File1.hxx
)

add_library(MyLib ${HDRS})

target_include_directories(MyLib
    PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
        $<INSTALL_INTERFACE:include>
    PRIVATE
        src)

target_link_libraries(MyLib
    PUBLIC ${ITK_LIBRARIES})

export(TARGETS MyLib FILE MyLibConfig.cmake)

MyApp CMakeLists.txt

find_package(ITK REQUIRED
    COMPONENTS RTK ITKRegistrationMethodsv4 ITKTransformIO ITKImageIO)
include(${ITK_USE_FILE})

set(SRCS
    AppFile1.cxx)

add_executable(MyApp ${SRCS})
include_directories(${CMAKE_SOURCE_DIR}/MyLib/include)
target_link_libraries(MyApp
    ${ITK_LIBRARIES}
    MyLib)

Edit1: Remove the project(X) from MyLib and MyApp CMakeLists.txt and change target to MyLib.


Solution

  • You can have an empty project, just create a custom target:

    add_custom_target(MyLib SOURCES ${CMAKE_MYLIB_SRC} ${CMAKE_MYLIB_HDR})
    

    As your code is header only, any depend target will see the changes in the files and would be recompiled. There is nothing to do on top of this. You don't have to create the target, although for your third question, that's the answer.

    But if ITK is only your dependency, now that you have a target, you can add PUBLIC properties on it, like dependent libraries that need to be linked against it because of your library.

    In that case, the code for them needs to add:

    target_link_library(${NEWTARGET} PRIVATE MyLib) # PUBLIC/PRIVATE has to be tailored
    

    That's the answer for question 1.

    If your library becomes non-header only, just change the add_custom_target call.