Reputation: 39807
I have a library that is built and linked to as part of my project. I want to provide the facility to OPTIONALLY have the library installed system-wide (or wherever ${CMAKE_INSTALL_PREFIX} is set). Otherwise, by default, the project's final build products will be linked statically to the library, and the former get installed, but the library binaries stay in the build directory.
In other words:
$ make
$ make install
will build and install, the programs, but only something like
$ make install.foo
will install the library to ${CMAKE_INSTALL_PREFIX}, building it first if needed.
I have something like this so far (simplified from the actual script, so there might be errors):
INCLUDE_DIRECTORIES( "${CMAKE_CURRENT_LIST_DIR}")
SET (FOO_LIBRARY "foo")
# Following builds library and makes it available to
# be linked other targets within project by:
# TARGET_LINK_LIBRARIES(${progname} ${FOO_LIBRARY})
ADD_LIBRARY(${FOO_LIBRARY}
foo/foo.cpp # and other sources ...
)
###########################################################
# Approach #1
# -----------
# Optionally allow users to install it by invoking:
#
# cmake .. -DINSTALL_FOO="yes"
#
# This works, but it means that users will have to run
# cmake again to switch back and forth between the libary
# installation and non-library installation.
#
OPTION(INSTALL_FOO "Install foo" OFF)
IF (INSTALL_FOO)
INSTALL(TARGETS ${FOO_LIBRARY} DESTINATION lib/foo)
SET(FOO_HEADERS foo/foo.h)
INSTALL(FILES ${FOO_HEADERS} DESTINATION include/foo)
UNSET(INSTALL_FOO CACHE)
ENDIF()
###########################################################
###########################################################
# Approach #2
# -----------
# Optionally allow users to install it by invoking:
#
# make install.foo
#
# Unfortunately, this gets installed by "make install",
# which I want to avoid
SET(FOO_INSTALL "install.foo")
ADD_CUSTOM_TARGET(${FOO_INSTALL}
COMMAND ${CMAKE_COMMAND}
-D COMPONENT=foo
-P cmake_install.cmake)
ADD_DEPENDENCIES(${FOO_INSTALL} ${FOO_LIBRARY})
INSTALL(TARGETS ${FOO_LIBRRARY}
DESTINATION lib/foo COMPONENT foo)
SET(FOO_HEADERS foo/foo.h)
INSTALL(FILES ${FOO_HEADERS}
DESTINATION include/foo COMPONENT foo)
###########################################################
As can be seen, approach #1 sort of works, but the required steps to install the library are:
$ cmake .. -DINSTALL_FOO="yes"
$ make && make install
And then, to go back to "normal" builds, the user has to remember to run cmake again without the "-DINSTALL_FOO" option, otherwise the library will be installed on the next "make install".
The second approach works when I run "make install.foo", but it also install the library if I run "make install". I would like to avoid the latter.
Does anyone have any suggestions on how to go about achieving this?
Upvotes: 4
Views: 11646
Reputation: 65791
You are on the right track with approach #2. You can trick CMake into avoiding the installation of the FOO
related files by using the OPTIONAL
switch of the install command.
For the FOO
library target, the commands have to modified in the following way:
SET (FOO_LIBRARY "foo")
ADD_LIBRARY(${FOO_LIBRARY} EXCLUDE_FROM_ALL
foo/foo.cpp
)
INSTALL(TARGETS ${FOO_LIBRARY}
DESTINATION lib/foo COMPONENT foo OPTIONAL)
The EXCLUDE_FROM_ALL
is added to ADD_LIBRARY
to prevent the library from building when you run a plain make
. The installation of FOO_LIBRARY
is made optional by adding the OPTIONAL
switch.
Making the installation of the FOO_HEADERS
optional requires the following changes:
SET(FOO_HEADERS foo/foo.h)
SET(BINARY_FOO_HEADERS "")
FOREACH (FOO_HEADER ${FOO_HEADERS})
ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${FOO_HEADER}
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${FOO_HEADER} ${CMAKE_CURRENT_BINARY_DIR}/${FOO_HEADER}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${FOO_HEADER})
LIST (APPEND BINARY_FOO_HEADERS ${CMAKE_CURRENT_BINARY_DIR}/${FOO_HEADER})
ENDFOREACH()
INSTALL(FILES ${BINARY_FOO_HEADERS}
DESTINATION include/foo COMPONENT foo OPTIONAL)
The FOREACH
loop sets up custom commands which copy the FOO_HEADERS
verbatim to the corresponding binary dir. Instead of directly using the headers in the current source dir the INSTALL(FILES ...
command picks up the copied headers from the binary dir. The paths of the headers in the binary dir are collected in the variable BINARY_FOO_HEADERS
.
Finally the FOO_INSTALL
target has to be set up in the following way:
SET(FOO_INSTALL "install.foo")
ADD_CUSTOM_TARGET(${FOO_INSTALL}
COMMAND ${CMAKE_COMMAND}
-D COMPONENT=foo
-P cmake_install.cmake
DEPENDS ${BINARY_FOO_HEADERS})
ADD_DEPENDENCIES(${FOO_INSTALL} ${FOO_LIBRARY})
The custom FOO_INSTALL
is added with a dependency on BINARY_FOO_HEADERS
to trigger the copying of the header files. A target level dependency on FOO_LIBRARY
triggers the building of the library when you run make install.foo
.
The solution however has the following drawbacks:
Upon configuring CMake will issues a warning Target "foo" has EXCLUDE_FROM_ALL set and will not be built by default but an install rule has been provided for it.
CMake will however do the right thing anyway.
Once you have run make install.foo
a subsequent make install
command will also install all FOO
related files, because the built FOO
library and the headers exist in the binary directory.
Upvotes: 8
Reputation: 20266
Check if this solution works for you.
You would have to use different commands to those you said you would like to use, but I think it addresses the problem fairly well.
Upvotes: -1