Jeet
Jeet

Reputation: 39807

CMake: Exclude custom install target(s) from 'make install [all]'

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

Answers (2)

sakra
sakra

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:

  1. 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.

  2. 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

Antonio
Antonio

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

Related Questions