[CMake] add_subdirectory and build directory

Michael Wild themiwi at gmail.com
Fri Sep 11 10:28:14 EDT 2009


On 11. Sep, 2009, at 15:12, Pierre-Julien Villoud wrote:

> Hi everyone,
>
> After unsuccessfully looking for an answer on Google, I contact you.
> I have a question regarding the use of add_subdirectory. When a  
> project A is depending on a project B, I add the following in A's  
> CMakeLists.txt :
>
> Add_subdirectory(B Path/To/B/Build/Directory)
>
> It does build B before A. But when I build B in its build directory  
> and I build A after, it builds B again whereas it should not since B  
> is up to date.
>
> Anyone sees what's wrong ?
>
> Thanks in advance
>
> Pierre-Julien VILLOUD
>

That's NOT what add_subdirectory is made for. It is intended for  
adding a sub-directory in the source tree. So, if your directory  
structure looks like this (i.e. B is a sub-project of A)

A/CMakeLists.txt
A/B/CMakeLists.txt

things are simple:

A/CMakeLists.txt:
------>8------
# ....
add_subdirectory(B)

include_directories(B/include)

add_executable(a)
target_link_libraries(a b)
------<8------

However, if the two projects are unrelated (apart from A depending on  
B), you should use the find_package mechanism which is much more  
complex. I.e, in project B you create the files BConfig.cmake (or b- 
config.cmake), BUse.cmake, BBuildSettings.cmake and  
BLibraryDepends.cmake:

B/BConfig.cmake.in:
------->8------
# Tell the user project where to find our headers and libraries
set(B_INCLUDE_DIRS "@CONFIG_HEADER_PATH@")
set(B_LIBRARY_DIRS "@CONFIG_LIBRARY_PATH@")

# Our build settings and library dependencies
set(B_BUILD_SETTINGS_FILE
   "@CONFIG_LIBRARY_PATH@/BBuildSettings.cmake"
   )
include("@CONFIG_LIBRARY_PATH@/BLibraryDepends.cmake")

# Defines
set( B_DEFINITIONS "@CONFIG_DEFINITIONS@" )

# USE file
set(B_USE_FILE "@CONFIG_USE_FILE_PATH@/BUse.cmake")
------<8------

B/BUse.cmake:
------->8------
# import B build settings
include(CMakeImportBuildSettings)
cmake_import_build_settings(${B_BUILD_SETTINGS_FILE})

# set up header search path
include_directories(${B_INCLUDE_DIRS})

# set up library search path
link_directories(${BM_LIBRARY_DIRS})

# defines
add_definitions(${B_DEFINITIONS})
------<8------

In the B/CMakeLists.txt file you do the following (just a snippet,  
showing the relevant stuff):

B/CMakeLists.txt:
------>8------
# ....

# say, you build two libraries, called b1 and b2
# with sources b1src1.c through b1src3.c and
# b2src1.c through b2src3.c, respectively.
add_library(b1 b1src1.c b1src2.c b1src3.c)
set_target_properties(b1 PROPERTIES
   PUBLIC_HEADER "b1hdr1.h;b1hdr2.h;b1hdr3.h"
   )

add_library(b2 b2src1.c b2src2.c b2src3.c)
set_target_properties(b2 PROPERTIES
   PUBLIC_HEADER "b2hdr1.h;b2hdr2.h;b2hdr3.h"
   )

# ....

# install the two libraries
install(TARGETS b1 b2
   LIBRARY DESTINATION lib COMPONENT shlibs
   RUNTIME DESTINATION bin COMPONENT shlibs
   ARCHIVE DESTINATION lib COMPONENT dev
   PUBLIC_HEADER DESTINATION include COMPONENT dev
   )

# ....

# AT THE VERY END OF THE FILE...

# Export library dependencies
#############################
export( TARGETS b1 b2
   NAMESPACE B_
   FILE ${CMAKE_BINARY_DIR}/BLibraryDepends.cmake
)

# Export the build settings
###########################
include(CMakeExportBuildSettings)
cmake_export_build_settings(${CMAKE_BINARY_DIR}/BBuildSettings.cmake)

# create BConfig.cmake for the build tree
#########################################
# SET TO THE PREPROCESSOR FLAGS REQUIRED TO USE PROJECT B
set(CONFIG_DEFINITIONS)
# SET TO WHATEVER INCLUDE PATH IS REQUIRED TO USE B'S BINARY TREE
set(CONFIG_HEADER_PATH ${CMAKE_SOURCE_DIR}/include )
# SET TO WHATEVER LIBRARY PATH IS REQUIRED TO USE B'S BINARY TREE
# (ACTUALLY, DUE TO CMAKE-MAGIC THIS CAN BE EMPTY)
set(CONFIG_LIBRARY_PATH)
set(CONFIG_USE_FILE_PATH ${CMAKE_SOURCE_DIR})
configure_file(
   ${CMAKE_SOURCE_DIR}/BConfig.cmake.in
   ${CMAKE_BINARY_DIR}/BConfig.cmake
   @ONLY
   )

# create BConfig.cmake for the install tree
###########################################
set(CONFIG_HEADER_PATH "${CMAKE_INSTALL_PREFIX}/include")
set(CONFIG_LIBRARY_PATH "${CMAKE_INSTALL_PREFIX}/lib")
set(CONFIG_USE_FILE_PATH "${CMAKE_INSTALL_PREFIX}/share/cmake/B" )
configure_file(
   ${CMAKE_SOURCE_DIR}/BConfig.cmake.in
   ${CMAKE_BINARY_DIR}/InstallFiles/BConfig.cmake
   @ONLY
   )

# install the CMake config files
################################
install( FILES
   BUse.cmake
   "${CMAKE_BINARY_DIR}/InstallFiles/BConfig.cmake"
   "${CMAKE_BINARY_DIR}/BBuildSettings.cmake"
   DESTINATION share/cmake/B
   COMPONENT dev
   )

# install the BLibraryDepends.cmake file
########################################
install( EXPORT BLibraryDepends
   DESTINATION share/cmake/B
   NAMESPACE B_
   COMPONENT dev
   )
------<8------


And then, in your A/CMakeLists.txt you do the following to use B:

A/CMakeLists.txt:
------>8------

find_package(B REQUIRED)
include(${B_USE_FILE})

add_executable(a asrc1.c asrc2.c asrc3.c)
target_link_libraries(a B_b1 B_b2)

------<8------

In order for CMake to find the BConfig.cmake, it searches some known  
system directories (refer to the documentation of find_package). If  
CMake can't find the file (which is very likely if you want to use B's  
build tree), you have to set the cache variable B_DIR to the binary  
directory of B.


I hope this explains how things work. If not, there's always the CMake  
book...

Michael


More information about the CMake mailing list