59
votes

I'm getting into CMAKE usage with C and actually I'm creating two very small static libraries.

My goal is:

  1. The libraries are compiled and linked into *.a files. [THIS WORKS]
  2. Then I wish to copy that *.a files into /usr/local/lib [THIS ALSO WORKS]
  3. As far as I know about libraries (very little), they are linked using -lnameoflib, which is a compiler flag. OK. I have prepared my CMakeLists.txt and it actually copies *.a files into /usr/local/lib. However, to be able to use them in a program, I also need to copy their header files into /usr/local/include, then I can include them the easy way #include <mylibheader.h>. That's how I understand it now.

And my question is - how is the proper way of copying header files into /usr/include folder with CMAKE? I would like it to copy them automatically when make install is executed, like *.a files are.

For both of the libraries I have a smiliar CMakeLists.txt:

project(programming-network)

add_library(programming-network STATIC
    send_string.c
    recv_line.c
    )

INSTALL(TARGETS programming-network
        DESTINATION "lib"
        )
4
Why not just add a line in Makefile under install: \n\tcp $INCLUDES/* /usr/include/ ? - lukecampbell
OK, but it means, that it can't be done directly in CMakeLists.txt and that I have to write it in Makefile again everytime after I run cmake? - Miroslav Mares
I would assume so, I'm not too familiar with cmake, and CMakeLists.txt, I prefer to use gnu-automake. - lukecampbell
If your target was installed after calling CMake with -DCMAKE_INSTALL_PREFIX=/usr, then your lib would end up in /usr/lib (as expected with prefix set to /usr), but your headers would end up in /include (probably not expected). Per's answer makes more sense. - Fraser
@lukecampbell manually adding lines to the makefile would defeat the purpose of using cmake (which is 100 times better than automake). - Ilia Choly

4 Answers

76
votes

A better way for newest cmake version is to use target's PUBLIC_HEADER properties.

project(myproject)

add_library(mylib some.c another.c)
set_target_properties(mylib PROPERTIES PUBLIC_HEADER "some.h;another.h")
INSTALL(TARGETS mylib 
        LIBRARY DESTINATION some/libpath
        PUBLIC_HEADER DESTINATION some/includepath
)

Some ref:

PUBLIC_HEADER

CMake install command

21
votes

In a much better way, will copy all files that match the pattern and will preserve the directory structure.

INSTALL (
    DIRECTORY ${CMAKE_SOURCE_DIR}/include/
    DESTINATION include
    FILES_MATCHING PATTERN "*.h*")
7
votes

I don't think your solution is the correct one. /usr/include should be reserved for your vendor to put files in.

The proper thing to do IMO is to install the header in /usr/local/include and then instruct the user to export CPATH="/usr/local/include:${CPATH}".

It seems /usr/local/lib was search automatically but if you wish to use another dir export LIBRARY_PATH="/usr/local/lib:${LIBRARY_PATH}" works similar for the .a binary (but may or may not work good for shared libraries depending on your os).

Optionally, but more cumbersome is to add -I /usr/local/include and -L /usr/local/lib while compiling.

This is a somewhat subjective answer, but it's been working well for me.

2
votes

In addition to the accepted answer, if you are creating a lot of libraries and the set_property syntax throws you off. You could wrap it in a very simple macro, such as:

# File: target_public_headers.cmake
macro(target_public_headers TARGET)
  set_target_properties(${TARGET} PROPERTIES PUBLIC_HEADER "${ARGN}")
endmacro()

Then you can use it like:

project(myproject)

include(target_public_headers)

add_library(mylib some.c another.c)
target_public_headers(mylib some.h another.h) # <<<<<

# If you're exporting this library then you need to tell
# CMake how to include the "installed" version of the headers.
target_include_directories(mylib
  PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
  PUBLIC $<INSTALL_INTERFACE:some/includepath>
)

INSTALL(TARGETS mylib 
        LIBRARY DESTINATION some/libpath
        PUBLIC_HEADER DESTINATION some/includepath
)