52

I had a project which uses CMake as build tool and made a simple template for me and my colleagues to use. As I searched for best and easy to use practices online, I've came across different approaches to make a library.

In this template, I've listed header files and source files in two separate variables, and I'm not passing the headers to add_library command - just sources. And then I use set_target_properties with PUBLIC_HEADER variable to give the header-file list.

So far it seems to work, but I wonder if I'm making thing unnecessarily complex. Some people online give header files to add_library command as well and doesn't even use set_target_properties and such.

In short:

  • Should we include header files to add_library or should we not (as a best practice)? And impacts of the two usage.

  • What is purpose being served by adding headers in the add_library/add_executable? As they seem working even without it (seems forward declaration and symbols only). confirm on understanding please.

Here is the template I'm talking about:

cmake_minimum_required(VERSION 3.1.0)

project(lae CXX C) 
set(CMAKE_CXX_STANDARD 14)

include_directories(
  ${CMAKE_CURRENT_SOURCE_DIR}
)

set(SOURCE_FILES
    ...
)

set(HEADER_FILES 
   ...
)

set( PRIVATE_HEADER_FILES
   ...
)

add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ) 

set( REQUIRED_LIBRARIES
   ...
)

target_link_libraries(${PROJECT_NAME} ${REQUIRED_LIBRARIES} )

SET_TARGET_PROPERTIES( 
  ${PROJECT_NAME}  
PROPERTIES 
  FRAMEWORK ON
  SOVERSION 0
  VERSION 0.1.0
  PUBLIC_HEADER "${HEADER_FILES}"
  PRIVATE_HEADER "${PRIVATE_HEADER_FILES}"
  ARCHIVE_OUTPUT_DIRECTORY "lib"
  LIBRARY_OUTPUT_DIRECTORY "lib"
  OUTPUT_NAME ${PROJECT_NAME}
)
starball
  • 20,030
  • 7
  • 43
  • 238
Ahmet Ipkin
  • 882
  • 1
  • 10
  • 20
  • 2
    See also: https://stackoverflow.com/questions/13703647/how-to-properly-add-include-directories-with-cmake – Daniel Jun 15 '18 at 19:37

2 Answers2

26

In our projects we use a "simple" way of yours - add_library with both headers and sources.

If you add only sources, then you won't see headers in IDE-generated project.

However, when installing, we have to do it like that, using two install commands:

install(TARGETS library_name
        LIBRARY DESTINATION lib)

install(FILES ${PUBLIC_HEADERS} 
        DESTINATION include/library_name)

If you want to do it as a single command, you can use set_target_properties with PUBLIC_HEADER, as you suggested. Then, this kind of install is possible:

install(TARGETS library_name
        LIBRARY DESTINATION lib
        PUBLIC_HEADER DESTINATION include/library_name)

Choose the one you like the most and stick to it.

dreamzor
  • 5,795
  • 4
  • 41
  • 61
  • the second way of "install" doesn't seem working, when "headers" added/not-added in the add_library. It complains about `no targets specified, no makefile found` at build-make time. any clue? – parasrish Oct 27 '17 at 14:28
  • @parasrish If you don't define your headers with SET_TARGET_PROPERTIES command, since no headers are defined, it won't work. However, if you do SET_TARGET_PROPERTIES with "Public headers" are correctly set, install target should work properly. – Ahmet Ipkin Nov 07 '17 at 18:04
  • 1
    Visual Studio supports proper CMake and "Open Folder" since VS2017 and doesn't require headers listed in targets to be shown. CMake-generated VS projects aren't properly made for humans anyway and can confuse people when their changes in build configuration don't persist. Best modern practice should be to avoid opening CMake-generated VS solutions for development and remove headers from project sources – they aren't compiled sources and therefore noise. – WolleTD Sep 04 '19 at 19:52
  • 2
    This does not preserve include directory structure for my project. – Rich Henry Mar 03 '20 at 21:00
0

For add_library and add_executable, it can cause CMake to generate certain IDE-integrated buildsystems like Visual Studio to display those header files in a perhaps more-desirable way, but to my knowledge, that's it. It doesn't have impact on whether the buildsystem itself functions properly or not with respect to doing builds. Whether you do this or not is up to you. I'm not aware of any clear downsides to specifying headers in add_library or add_executable. Do note that CMake does build in some (very very basic) heuristics to detect headers associated to source files, so it can sometimes handle this for your without you needing to do anything (see also my answer to How to properly add include directories with CMake).

If you have cmake_minimum_require(VERSION 3.23) and want to do something that has possible usefulness beyond that, take a look at the target_sources(... FILE_SET HEADERS ...) command, which can have usefulness for doing installs of public headers for targets.

The PUBLIC_HEADER target property is to specify files in a FRAMEWORK shared library target (a thing for building for macOS) to be put in the Headers/ directory of the framework folder. On non-Apple platforms, you can also use this target property with the PUBLIC_HEADERS option in the install(TARGETS ...) command (some similarities to the FILE_SET thing stated above).

starball
  • 20,030
  • 7
  • 43
  • 238