2

I have a couple of CMake projects which have their own install logic defined. Now, I want to add a global CMakeLists.txt that adds those projects using add_subdirectory() in order to build and install all of them at once. How can I provide a different ${CMAKE_INSTALL_PREFIX} to each of them from the outside?

What I tried is changing the install prefix between the calls to add the subdirectories but they are installed when actually calling make install and at that time, the install prefix is already set to the last assigned value.

danijar
  • 32,406
  • 45
  • 166
  • 297
  • If your projects have different install logic, you shouldn't build them using `add_subdirectory` within single global project. Instead, global project may install them using CMake call either via `execute_process` or `ExternalProject_Add`. – Tsyvarev Jul 29 '15 at 07:30
  • @Tsyvarev Could you provide an example of how that would look like, please? I think using `execute_process` would mean that I can't specify dependent targets between those subdirectories anymore, right? Moreover, the command output can only be displayed after the full comman has finished which is a bit annoying. – danijar Jul 29 '15 at 11:58

1 Answers1

5

If your subprojects require same variable to have different values(at global scope) you cannot build them using add_subdirectory within single global project.

Instead, global project may install them using cmake call either via execute_process or ExternalProject_Add. Sometimes, install(SCRIPT) command may be used for that.


Installing using execute_process makes subproject available immediately at configuration stage.

It can be used when subproject has some packaging mechanism (see CMake tutorials about packaging), so global object may execute find_package(<subproject-name>) and link with libraries, defined in subproject, in the most simple way(using variables, filled outside).

# Configure subproject into <subproject-build-dir>
execute_process(COMMAND ${CMAKE_COMMAND} -D<var>=<value> <subproject-source-dir> 
    WORKING_DIRECTORY <subproject-build-dir>)
# Build and install subproject
execute_process(COMMAND ${CMAKE_COMMAND} --build <subproject-build-dir> --target install)
# Extract variables, related to subproject for link with it.
find_package(<subproject-name>)

# Create target in global project and link with subproject
include_directories(${<subproject-name>_INCLUDE_DIRS})
link_directories(${<subproject-name>_LIBRARY_DIRS})
add_executable(global_program main.cpp)
target_link_libraries(global_program ${<subproject-name>_LIBRARIES})

Installing using ExternalProject_add assigns target to the subproject, which will be installed at build stage. Linking with subproject in that case is also possible, but requires to fill variables manually.

# Configure, build and install subproject
ExternalProject_Add(<subproject_target>
    SOURCE_DIR <subproject-source-dir>
    CMAKE_CACHE_ARGS -D<var>=<value>
    BINARY_DIR <subproject-build-dir>
    INSTALL_DIR <CMAKE_INSTALL_PREFIX-for-subproject>
    )

# Create target in global project and link with subproject
include_directories(<subproject-include-files-location>)
link_directories(<subproject-libraries-location>)
add_executable(global_program main.cpp)
target_link_libraries(global_program <subproject-libraries-name>)
# Make sure that subproject will be built before executable.
add_dependencies(global_program <subproject_target>)

Installing using install(SCRIPT) executes script at install stage. This approach can be used when there is no build-dependencies between global project and subproject.

subproject_install.cmake:

# Configure subproject into <subproject-build-dir>
execute_process(COMMAND ${CMAKE_COMMAND} -D<var>=<value> <subproject-source-dir> 
    WORKING_DIRECTORY <subproject-build-dir>)
# Build and install subproject
execute_process(COMMAND ${CMAKE_COMMAND} --build <subproject-build-dir> --target install)

CMakeLists.txt:

install(SCRIPT subproject_install.cmake)
Tsyvarev
  • 60,011
  • 17
  • 110
  • 153
  • Thanks for your comprehensive answer. I'd like to use CMake to generate makefiles or project files that I can use to build the whole project again after changing the code. When using `execute_process()` the subdirectories will be built at configuration time, so I have to re-run CMake after making source changes. Even with a way to execute the second `execute_process()` call at build time, I would be back to the problem of dependency libraries that need to be installed before depending targets are even configured. Do you know how to solve this? – danijar Jul 30 '15 at 02:46
  • If you can modify subproject, you better fix it to use common install logic, so you will be able to use `add_subdirectory` for it. Direct installing of subproject, described in my answer, implies that you treat it as persistent. – Tsyvarev Jul 30 '15 at 06:48
  • I realized that the question I posted as a comment here is not strictly related to this one, so I [created a new post for it](http://stackoverflow.com/q/31755870/1079110). Would be nice if you could check it out. – danijar Jul 31 '15 at 22:05
  • @ruslo: This is just a pattern, not a full code. Of course, real application needs error checking to be performed. – Tsyvarev Aug 06 '15 at 22:01