I have a project written in C++ and I am using cmake to build it. The project has many sub-projects, one of them is a lib that is required for the other sub-projects. I can compile and move the .so to the build directory by using add_library, and INSTALL(TARGETS ...)
However I also need the header files of the lib to be installed under the include directory of the build dir. I use install(FILES ...) to do it, but it seems it simply doesn't do a thing.
To demonstrate it, I have created a test project via qtcreator,
& ls test
CMakeLists.txt empty.hh main.cpp
$ cat test/CMakeLists.txt
project(test)
cmake_minimum_required(VERSION 2.8)
install(FILES empty.hh DESTINATION include)
aux_source_directory(. SRC_LIST)
add_executable(${PROJECT_NAME} ${SRC_LIST})
$ cat test/main.cpp
#include
using namespace std;
int main()
{
cout << "Hello World!" << endl;
return 0;
}
$ cat test/empty.hh
#ifndef EMPTY_HH
#define EMPTY_HH
#endif // EMPTY_HH
If the files under "test" qtcreator will compile (by default) the files to test-build.
$ ls test-build/
CMakeCache.txt CMakeFiles Makefile cmake_install.cmake test test.cbp
$ ./test-build/test
Hello World!
As you can see, there is no include directory or empty.hh file. Also tried to use
install(FILES empty.hh DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
But still see no header files.
$ cmake --help
cmake version 2.8.12
If you have any idea please let me know.
CMake provides the install command to specify how a project is to be installed. This command is invoked by a project in the CMakeLists file and tells CMake how to generate installation scripts. The scripts are executed at install time to perform the actual installation of files.
You can check your CMake version by using the command cmake --version. cmake version 3.11. 2CMake suite maintained and supported by Kitware (kitware.com/cmake).
Install directory used by install. If “make install” is invoked or INSTALL is built, this directory is prepended onto all install directories. This variable defaults to /usr/local on UNIX and c:/Program Files on Windows.
Using the install
command to move files around at build-time is generally a bad approach. The command is meant to be used to set up files and targets which will be installed when the user does make install
or equivalent. Since you're not running make install
, I expect that's why the install(FILES ...)
command appears to not work.
You have a few slightly different approaches here to make this work.
I'd recommend not moving the headers if you don't have to. Say your library is called MyLib
, then you can make these headers available as part of the MyLib
target via target_include_directories
:
target_include_directories(MyLib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
This means if there's a dependent target, e.g MyExe
:
target_link_libraries(MyExe MyLib)
then it automatically has access to MyLib
's source dir.
This can be less desirable if the library's directory structure doesn't lend itself to separating the API headers from the rest of the sources and headers. Say MyLib
is comprised of the following files: my_lib.cpp, my_lib_api.hh (API header - for inclusion by other projects) and my_lib_detail.hh (not for inclusion by other projects). An ideal structure would keep the API header separate from the rest, e.g.
/my_lib
- CMakeLists.txt
- src/
- my_lib.cpp
- my_lib_detail.hh
- include/
- my_lib/
- my_lib_api.hh
With this structure you can just specify
target_include_directories(MyLib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src")
and MyLib
sources will be able to contain #include "my_lib_detail.hh"
and #include "my_lib/my_lib_api.hh"
, but dependent targets' sources will only be able to contain #include "my_lib/my_lib_api.hh"
.
So, if MyLib
has just a flat structure or doesn't separate the internal headers from API ones, you may want to copy out the API headers to a location in your build tree and add that path to the target_include_directories
call. In that case, if MyLib
itself doesn't need access to the copied files (just the originals in the source tree), you can use INTERFACE
instead of PUBLIC
in the target_include_directories
call.
However, the crux of this (and getting round to answering your actual question I suppose) is to copy these files as part of the configure process (when CMake runs) or build process (when make runs) - not as part of the install process.
So, let's assume MyLib
doesn't have the useful directory structure shown above, but instead is flat (i.e. CMakeLists.txt and three source files are all in same dir). We could use a post-build command to copy out the API header to the build tree:
project(my_lib)
cmake_minimum_required(VERSION 2.8.12.2) # for 'target_include_directories'
add_library(MyLib SHARED my_lib.cpp my_lib_api.hh my_lib_detail.hh)
target_include_directories(MyLib INTERFACE "${CMAKE_BINARY_DIR}/include"
PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/include/my_lib")
add_custom_command(TARGET MyLib POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/my_lib_api.hh"
"${CMAKE_BINARY_DIR}/include/my_lib"
COMMENT "Copying MyLib public headers to ${CMAKE_BINARY_DIR}/include/my_lib"
VERBATIM)
Then for MyExe
's CMakeLists.txt, you can just do:
project(my_exe)
cmake_minimum_required(VERSION 2.8.12.2)
add_executable(MyExe main.cpp)
target_link_libraries(MyExe MyLib)
# Copy MyLib.so to this build dir to allow MyExe to find it at runtime
add_custom_command(TARGET MyExe POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$<TARGET_FILE:MyLib>"
"$<TARGET_FILE_DIR:MyExe>"
VERBATIM)
This won't work as-is for MSVC - you'd need to handle Windows export libs (see the CMake wiki and the docs for GenerateExportHeader
for further details), but it would just involve something like adding the following to MyLib
's CMakeLists.txt:
include(GenerateExportHeader)
generate_export_header(MyLib
BASE_NAME MyLib
EXPORT_MACRO_NAME MyLib_EXPORT
EXPORT_FILE_NAME "${CMAKE_BINARY_DIR}/include/my_lib/my_lib_export.hh"
STATIC_DEFINE MyLib_BUILT_AS_STATIC)
If you do need this, you'll have to change INTERFACE
to PUBLIC
in target_include_directories(MyLib ...)
, since MyLib
will then itself need access to the generated file "${CMAKE_BINARY_DIR}/include/my_lib/my_lib_export.hh"
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With