I am building a library in C++ that requires a few libraries to be included, some of which being GLEW, SDL2, and GLM. I am using CMake to build this library, and have successfully set up (at least to my knowledge) a CMakeLists.txt that adequately does this, but currently without dependencies. I would like to know the proper conventions for adding these external libraries to my own library, keeping in mind that someone on a different machine may be using this library (i.e. not defined file structure/local installs).
This is my current CMakeLists.txt:
cmake_minimum_required(VERSION 3.8)
project(mylib VERSION 1.0.1 LANGUAGES CXX)
set (DEFAULT_BUILD_TYPE "Release")
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' as none was specified.")
set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE STRING "Choose the type of build." FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()
include (GNUInstallDirs)
set (SOURCE_FILES "src/driver.cpp")
add_library(${PROJECT_NAME} ${SOURCE_FILES})
target_include_directories(
${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE src
)
set_target_properties (
${PROJECT_NAME} PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION 1
)
install (
TARGETS ${PROJECT_NAME} EXPORT mylibConfig
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install (
DIRECTORY include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}
)
install (
EXPORT mylibConfig
DESTINATION share/mylib/cmake
)
export (
TARGETS ${PROJECT_NAME}
FILE mylibConfig.cmake
)
If you similarly notice any key errors/mistakes in my present file, please feel free to let me know, but the more important matter is how I should be properly including these libraries.
To dynamically link GLEW, SDL2, and GLM, you can use the find_package command.
find_package(GLEW REQUIRED)
find_package(SDL2 REQUIRED)
find_package(glm REQUIRED)
Then, after you've called add_library, you will need to link the libraries to your library:
target_link_libraries(mylib PRIVATE GLEW::GLEW SDL2::SDL2 glm::glm)
If you expose any of those dependencies in your API, then you can call target_link_libraries with PUBLIC (instead of PRIVATE) for those dependencies instead.
Consider using a package manager like conan. I took a look for you at conan center and there are recipes for your dependencies. You can follow the getting started guide and it should just work.
In CMake there are many ways to handle dependencies,
manually
using submodule
using fine_package
...
But here I just refer to 2 methods which are easy and portable.
Submodule
If the dependency exists on the github/gitlab or some places that uses git, then you can easily use submodule method.
with submodule your dependencies can be updated always(through their github/gitlab pages), but you can't change them yourself because your changes will be locally(like every time you clone a repository, your changed will be locally unless you do a pull request or be a contributor).
Usage
For using submodule, you need to have this section on your .CMakeLists.txt file:
#--------------------------------------------------------------
# submodule section
# here we use the following code to support
# old versions of git(that don't download submodule
# contents automatically).
#--------------------------------------------------------------
# [[[
find_package(Git QUIET)
if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
if(GIT_SUBMODULE)
message(STATUS "Submodule update")
execute_process(
COMMAND ${GIT_EXECUTABLE} submodule update
--init --recursive
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE GIT_SUBMOD_RESULT
)
if(NOT GIT_SUBMOD_RESULT EQUAL "0")
message(FATAL_ERROR "Git submodule update --init failed with ${GIT_SUBMODULE_RESULT}, Please checkout submodules")
endif()
endif()
endif()
# ]]]
#
This easily run git submodule update --init --recursive automatically for you. and then you can use your submodule like this:
# your submodule directory that has a main .CMakeLists.txt file inside it.
add_subdirectory("YOUR_SUBMODULE_DIRECTORY")
target_include_directories(
executable_or_library_name PRIVATE
YOUR_SUBMODULE_DIRECTORY/include
)
Here's an example for submodule.
find_package
If the dependency has a find_package support, then you use find_package method, specially if it has this support and it's not on the github/gitlab or a place that uses git, you should use this method then for sure.
Usage
It's also easy to use, with just most of the times one command:
find_package(dependency)
target_include_directories(YOUR_TARGET
"${dependency_INCLUDE_DIR}"
# ... your other include directories
)
target_link_libraries(YOUR_TARGET
"${dependency_LIBRARIES}"
# ... your other libraries to add
)
There are many possible arguments like REQUIRED and COMPONENTS that are explained in the official cmake website
Not part of this question but useful (Side Note)
I usually have this at the end of my main .CMakeLists.txt to support emacs/vim/... and every editors which uses LSP for finding symbols:
#--------------------------------------------------------------
# Generating compile_commands.json file for lsp servers
#--------------------------------------------------------------
# [[[
option(CMAKE_EXPORT_COMPILE_COMMANDS "Generate lsp command file" ON)
if(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/compile_commands.json")
execute_process(
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_BINARY_DIR}/compile_commands.json
${CMAKE_CURRENT_SOURCE_DIR}/compile_commands.json
)
endif()
# ]]]
#--------------------------------------------------------------
Related
I am building a C++ project from Github and want to deploy the code to a remote Linux machine. This is all new to me.
The project has a main.cpp, which includes the various headers/sources like a library.
The CMake outputs an executable (to represent main.cpp) AND a separate static library. The project also uses OpenSSL, which I have linked statically.
I presume the OpenSSL functions are included within the static library? So when I deploy, I don't need to copy-over or install any OpenSSL on the remote machine?
Is it possible to modify the CMake so the application and the library are merged in to one file?
I am trying to make deployment as simple as copying over a single file, if this is possible.
Any additional advice/references are most-welcome.
UPDATE the CMake script:
cmake_minimum_required(VERSION 3.20)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}")
project(helloworld C CXX)
set (CMAKE_CXX_STANDARD 20)
set (CMAKE_BUILD_TYPE Release)
set (BUILD_MAIN TRUE)
set (BUILD_SHARED_LIBS FALSE)
set (OPENSSL_USE_STATIC_LIBS TRUE)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set( HELLOWORLD_HEADERS helloworld/File1.h helloworld/File2.h )
set( HELLOWORLD_SOURCES helloworld/File1.cpp helloworld/File2.cpp )
# Static library
add_library( helloworld ${HELLOWORLD_SOURCES} ${HELLOWORLD_HEADERS} )
# Rapidjson
include_directories(/tmp/rapidjson/include/)
# OpenSSL
if (NOT OPENSSL_FOUND)
find_package(OpenSSL REQUIRED)
endif()
add_definitions(${OPENSSL_DEFINITIONS})
target_include_directories(helloworld PUBLIC $<BUILD_INTERFACE:${OPENSSL_INCLUDE_DIR}>)
target_link_libraries(helloworld PRIVATE ${OPENSSL_LIBRARIES})
set( HELLOWORLD_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR})
include(GNUInstallDirs)
target_include_directories(helloworld PUBLIC
$<BUILD_INTERFACE:${HELLOWORLD_INCLUDE_DIRS}/>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/helloworld>
)
set_target_properties(helloworld PROPERTIES PUBLIC_HEADER "${HELLOWORLD_HEADERS}")
add_library(helloworld::helloworld ALIAS helloworld)
option(HELLOWORLD_INSTALL "Install HelloWorld" TRUE)
if (HELLOWORLD_INSTALL)
install(TARGETS helloworld
EXPORT helloworld
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/helloworld/
)
configure_file("${CMAKE_CURRENT_LIST_DIR}/helloworld-config.cmake.in" "${CMAKE_BINARY_DIR}/helloworld-config.cmake" #ONLY)
install(FILES "${CMAKE_BINARY_DIR}/helloworld-config.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/helloworld")
install(EXPORT helloworld
FILE helloworld-targets.cmake
NAMESPACE helloworld::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/helloworld
)
endif()
if (BUILD_MAIN)
add_executable(main main.cpp)
target_link_libraries(main helloworld)
endif()
ITNOA
I it is very helpful to make URL of your GitHub's project, but I write some public notes about that
In generally in CMake for static linking your library to your executable, you can write simple like below (from official CMake example)
add_library(archive archive.cpp zip.cpp lzma.cpp)
add_executable(zipapp zipapp.cpp)
target_link_libraries(zipapp archive)
In above example your executable file is just work without needing .a library file and you can simple copy single file.
if you want to make all of thing static, you make sure all dependencies make static link to your project, like CMake: how to produce binaries "as static as possible"
if you want to prevent library creation, Probably in your CMake file, you can find add_library command, and add_executable command. you can remove add_library command and add all sources to add_executable command.
for example add_executable(a.out main.cpp lib.cpp)
I am learning how to use ExternalProject to download header only libaries and link to my executable.
My workflow is following:
I download header library Eigen using ExtenalProject:
cmake -DGET_LIBS=ON -DBUILD_SHARED_LIBS=ON -DBUILD_MY_PROJECTS=OFF -G
"Visual Studio 17 2022" -A x64 .. && cmake --build . --config Release
Then I run the same CMakeLists a second time, but this time I disable ExternalProject and compile the executable that links the Eigen:
cmake -DGET_LIBS=OFF -DBUILD_SHARED_LIBS=OFF -DBUILD_MY_PROJECTS=ON
-G "Visual Studio 17 2022" -A x64 .. && cmake --build . --config Release
Question
Why I need to use both of these commands since in target_include_directories command I specify the same path as in include_directories?
In the code below I need two commands include_directories and target_include_directories.
I thought that it would be enough to use only target_include_directories, but without include_directories it wont work.
if (BUILD_MY_PROJECTS)
add_executable(my_exe main.cpp)
include_directories("${CMAKE_BINARY_DIR}/install/eigen/") #add directory of the header-only library without this the next line wort work
target_include_directories(my_exe INTERFACE "${CMAKE_BINARY_DIR}/install/eigen/")# link exe to exectable
endif ()
My full CMakeLists.txt is following:
project(superbuild LANGUAGES CXX)
cmake_minimum_required(VERSION 3.19)
########################################################################
# EIGEN
########################################################################
SET(GET_LIBS "" CACHE STRING "Set option to download dependencies")
if (GET_LIBS)
message(AUTHOR_WARNING ${GET_LIBS})
ExternalProject_Add(eigen
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
GIT_TAG 3.4.0
CMAKE_ARGS
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/install/eigen #this does nothing...
SOURCE_DIR "${CMAKE_BINARY_DIR}/install/eigen" #install in my local build dir
#SOURCE_DIR ${CMAKE_INSTALL_PREFIX}
BUILD_COMMAND "" #do not build
INSTALL_COMMAND "" #do not install
)
endif ()
###############################################################################
#EXECUTABLES
###############################################################################
if (BUILD_MY_PROJECTS)
add_executable(my_exe main.cpp)
include_directories("${CMAKE_BINARY_DIR}/install/eigen/") #add directory of the header-only library without this the next line wort work
target_include_directories(my_exe INTERFACE "${CMAKE_BINARY_DIR}/install/eigen/")# link exe to exectable
endif ()
Tl;dr never use include_directories. Ever. Learn how property visibility works in CMake instead.
include_directories("${CMAKE_BINARY_DIR}/install/eigen/") #add directory of the header-only library without this the next line wort work
target_include_directories(my_exe INTERFACE "${CMAKE_BINARY_DIR}/install/eigen/")# link exe to exectable
In the code you supplied, include_directories is implicitly setting the INCLUDE_DIRECTORIES property on your my_exe target. Then the second line sets the INTERFACE_INCLUDE_DIRECTORIES property.
INCLUDE_DIRECTORIES is a list of include directories that must be passed to the compiler when building the target. INTERFACE_INCLUDE_DIRECTORIES is a list of include directories that must be passed to the compiler when building targets that link to this one. This doesn't make much sense for an executable
Slightly better would be to write:
target_include_directories(my_exe PRIVATE "${CMAKE_BINARY_DIR}/install/eigen/")
This will just populate INCLUDE_DIRECTORIES without touching the INTERFACE_ version. If you wanted both you could write PUBLIC instead of PRIVATE or INTERFACE. By definition, PUBLIC is just both of the others.
But this isn't a great dependency management strategy anyway... digging into the source tree guts of a project isn't scalable. It's also difficult to try different versions of Eigen without editing your build files.
I would just write:
cmake_minimum_required(VERSION 3.23)
project(example)
find_package(Eigen3 REQUIRED)
add_executable(my_exe main.cpp)
target_link_libraries(my_exe PRIVATE Eigen3::Eigen)
Use a proper package manager like vcpkg or Conan to handle downloading Eigen when it isn't available on the system.
Coming from other package managers I expect to be able to do the following on a project:
configure the required libraries (in a config file, e.g. CMakelists.txt or nuget's packages.config)
install/upgrade all the required libraries
This seems to not be possible with vcpkg and CMake:
I managed to set up vcpkg and CMakefile (linux):
# in ternimal
vcpkg install <package>
# CMakeLists.txt
find_package(nlohmann_json CONFIG REQUIRED)
target_link_libraries(<app> <package>)
This however requires to manually install all the required libraries (vcpkg install <package>).
How can I list the required packages and have vcpkg or CMake managed them as required (e.g. automatically install them if they are missing)?
I've recently spent some time working with CMake and vcpkg. I dislike the typical approach of managing vcpkg and its installed packages separately from my CMake projects, so I wrote some CMake scripts to handle vcpkg during the CMake build process.
The CMake script to install vcpkg is primarily a call to ExternalProject_Add.
# ./vcpkg_utilities.cmake (part 1)
include(ExternalProject)
function(get_vcpkg)
ExternalProject_Add(vcpkg
GIT_REPOSITORY https://github.com/microsoft/vcpkg.git
CONFIGURE_COMMAND ""
INSTALL_COMMAND ""
UPDATE_COMMAND ""
BUILD_COMMAND "<SOURCE_DIR>/bootstrap-vcpkg.sh"
)
ExternalProject_Get_Property(vcpkg SOURCE_DIR)
set(VCPKG_DIR ${SOURCE_DIR} PARENT_SCOPE)
set(VCPKG_DEPENDENCIES "vcpkg" PARENT_SCOPE)
endfunction()
The script to have vcpkg install a package requires a custom command and target.
# ./vcpkg_utilities.cmake (part 2)
function(vcpkg_install PACKAGE_NAME)
add_custom_command(
OUTPUT ${VCPKG_DIR}/packages/${PACKAGE_NAME}_x64-linux/BUILD_INFO
COMMAND ${VCPKG_DIR}/vcpkg install ${PACKAGE_NAME}:x64-linux
WORKING_DIRECTORY ${VCPKG_DIR}
DEPENDS vcpkg
)
add_custom_target(get${PACKAGE_NAME}
ALL
DEPENDS ${VCPKG_DIR}/packages/${PACKAGE_NAME}_x64-linux/BUILD_INFO
)
list(APPEND VCPKG_DEPENDENCIES "get${PACKAGE_NAME}")
set(VCPKG_DEPENDENCIES ${VCPKG_DEPENDENCIES} PARENT_SCOPE)
endfunction()
Getting the main project linked to vcpkg appropriately (via toolchain file) requires vcpkg to be available when the main project is configured, so the main project is configured as an ExternalProject.
# /CMakeLists.txt
cmake_minimum_required(VERSION 3.17)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
project(top LANGUAGES CXX)
include(vcpkg_utilities)
get_vcpkg()
vcpkg_install(nlohmann_json)
include(ExternalProject)
set_property(DIRECTORY PROPERTY EP_BASE main_projectname)
ExternalProject_Add(main_projectname
DEPENDS ${VCPKG_DEPENDENCIES}
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/src
INSTALL_COMMAND ""
CMAKE_ARGS --no-warn-unused-cli;
-DCMAKE_TOOLCHAIN_FILE=${VCPKG_DIR}/scripts/buildsystems/vcpkg.cmake
)
Using this structure, CMake will manage the installation of both vcpkg and packages maintained by it.
Note that this is a point solution. I have a more generalized template for this in my Github that covers Windows and Linux, and allows the use of a separately-maintained vcpkg instance.
I've done something similar in my projects using ExternalProject_Add.
Code will be similar to the below pattern (I've replaced my dependency with your)
find_package(nlohmann_json CONFIG) # make package not REQUIRED to avoid error...
if(NOT NLOHMANN_JSON_FOUND) # or whatever variable indicating _FOUND
...
# here ExternalProject_Add from https://github.com/nlohmann/json.git
# using specific tag...
...
ExternalProject_Get_Property(... INSTALL_DIR)
# create imported target here similar to nlohmann_json, as if we found it
...
else()
# We've found lib already installed... will use it...
...
endif()
Another option can be using FetchContent, but I haven't tried it yet.
I've wrote a C++ library MyLib and I'd like to integrate it with another project ExternPro. So in ExternPro I wrote CMakeLists.txt like this:
add_subdirectory(MyLib)
ADD_EXECUTABLE(test test.cpp)
include_directories(${MyLib_INCLUDE_DIRS})
target_link_libraries(test ${MyLib_LIBRARIES})
To set variables MyLib_LIBRARIES and MyLib_INCLUDE_DIRS I wrote:
set(MyLib_LIBRARIES ${PROJECT_SOURCE_DIR}/src/MyLib.a CACHE INTERNAL "")
set(MyLib_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include CACHE INTERNAL "")
But something wrong "No rule to make target MyLib/src/MyLib.a, needed by test. Stop."
So my question is, how should I wrote CMakeLists.txt correctly so cmake could help me build MyLib first and then take care of dependencies of ExternPro?
If those are two separate projects, I normally use "CMake find scripts" to reference one library from the other: http://www.vtk.org/Wiki/CMake:How_To_Find_Libraries#Writing_find_modules
But I normally use slightly different find script than described there (FindMyLibrary.cmake):
# Find MyLibrary installation
#
# This module needs following variables specified (e.g. through cmake -Dvar=)
# MyLibrary_ROOT_DIR - root directory of the library installation
#
# This module defines the following variables:
# MyLibrary_INCLUDE_DIRS - Where to find the public headers
# MyLibrary_LIBRARIES - List of mandatory and optional libraries
# MyLibrary_FOUND - True if an installation was found
#
# Configuration variables for tis module:
# MyLibrary_USE_STATIC_LIBS - Set to ON to force the use of the static
# libraries. Default is OFF.
# If MyLibrary_ROOT_DIR was defined in the environment, use it.
if(NOT MyLibrary_ROOT_DIR AND NOT $ENV{MyLibrary_ROOT_DIR} STREQUAL "")
set(MyLibrary_ROOT_DIR $ENV{MyLibrary_ROOT_DIR})
endif()
if(NOT MyLibrary_ROOT_DIR)
set(MyLibrary_ROOT_DIR /usr)
endif()
message(STATUS "Using MyLibrary_ROOT_DIR: ${MyLibrary_ROOT_DIR}")
find_path(MyLibrary_INCLUDE_DIRS
NAMES mylib/mylib.hpp
PATHS ${MyLibrary_ROOT_DIR}
PATH_SUFFIXES include)
# Here we set the default components
if(NOT MyLibrary_FIND_COMPONENTS)
set(MyLibrary_FIND_COMPONENTS mylibrary)
endif()
# Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES
if(MyLibrary_USE_STATIC_LIBS)
set(_mylib_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
if(WIN32)
set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
else()
set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
endif()
endif()
foreach(COMPONENT ${MyLibrary_FIND_COMPONENTS})
find_library(MyLibrary_${COMPONENT}_LIBRARY
NAMES ${COMPONENT}
HINTS ${MyLibrary_ROOT_DIR}
PATH_SUFFIXES lib64 lib
NO_DEFAULT_PATH)
set(MyLibrary_LIBRARIES ${MyLibrary_LIBRARIES} ${MyLibrary_${COMPONENT}_LIBRARY})
endforeach()
# Restore the original find library ordering
if(MyLibrary_USE_STATIC_LIBS)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${_mylib_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES})
endif()
# handle the QUIETLY and REQUIRED arguments and set MyLibrary_FOUND to
# TRUE if all listed variables are TRUE
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
MyLibrary "Could NOT find MyLibrary: set MyLibrary_ROOT_DIR to a proper location"
MyLibrary_LIBRARIES
MyLibrary_INCLUDE_DIRS)
mark_as_advanced(MyLibrary_INCLUDE_DIRS MyLibrary_LIBRARIES)
Then used like this:
find_package(MyLibrary REQUIRED)
include_directories(SYSTEM ${MyLibrary_INCLUDE_DIRS})
target_link_libraries(${TARGET}
${MyLibrary_LIBRARIES}
)
Basically, it goes like this:
The library is built and installed (make install) to either the default location (e.g. /usr), or some other location (usual in development).
The FindMyLibrary.cmake is part of the library installation (for RPM the library devel package) and installs as well (into ${instdir}/share/cmake/Modules, for example).
The dependent project then adds the path to the CMAKE_MODULE_PATH and uses the find script to find the public headers and libraries as installed.
The advantage is that this way you can either use it during the development (when you have the library sources and build the library), or without the library sources as well (with just the library and headers - the devel package - installed in the system).
Similar technique is normally used by Boost etc. (the find scripts provided already by the CMake).
Instead of path to the library, use library target for target_link_libraries.
Assuming your library project contains
add_library(MyLib ...)
Linking of executable in main project should be performed with
target_link_libraries(test MyLib)
First of all this doesn't work because variables set in a subdirectory are not set for the parent directory.
So to solve this properly you should define MyLib like:
add_library(MyLib ...)
target_include_directories(MyLib INTERFACE ${PROJECT_SOURCE_DIR}/include)
And for the ExternPro you just need to link to MyLib:
target_link_libraries(test MyLib)
This will automatically add the include directory to test and link MyLib properly.
I have the following situation: a Project A depends on a Project B, but both are built at the same time. Project A has to include the includes of project B and it needs also to link its libraries. Up to now I've tried this way:
ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/other_project other_project)
and then:
INCLUDE_DIRECTORIES(includ ${CMAKE_SOURCE_DIR}/other_project/include})
LIST(APPEND LINK_LIBS other_project)
in the CMakeLists.txt of Project A
but it doesn't seem to work, the compiler also gives me error when including the headers of Project B saying that they do not exist.
What is the right way to add dependencies in A? How should the CMakeLists.txt look like?
EDIT:
as suggested in the comments, this question was addressed in this, however I'd like to see an example of how to use it in a CMakeList.txt file.
The following is a simple example which builds zlib and then builds libxml2 which depends on the zlib we build. One thing to note, I quickly pulled this example from stuff I've done before. The libxml2 example uses make, thus will only actually build on a system which has it, e.g. Linux, Mac ...
Here is the proposed directory structure for this example ...
src/
-- CMakeLists.txt
CMake/
---- External_ZLib.cmake
---- External_libxml2.cmake
Dowloads/ ( no need to create this directory, CMake will do it for you )
build/ ( Out of source build to prevent littering source tree )
Files:
CMakeLists.txt
# Any version that supports ExternalProject should do
cmake_minimum_required( VERSION 3.1)
project(test_ext_proj)
set(test_ext_proj_CMAKE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/CMake")
set(CMAKE_MODULE_PATH ${test_ext_proj_CMAKE_DIR} ${CMAKE_MODULE_PATH})
set(test_ext_proj_BUILD_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/install)
set(test_ext_proj_DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Downloads CACHE PATH "Directory to store downloaded tarballs.")
include(ExternalProject)
include(External_ZLib)
include(External_libxml2)
External_ZLib.cmake:
set(ZLib_version 1.2.8)
set(ZLib_url "http://zlib.net/zlib-${ZLib_version}.tar.gz")
set(ZLib_md5 "44d667c142d7cda120332623eab69f40")
ExternalProject_Add(ZLib
URL ${ZLib_url}
URL_MD5 ${ZLib_md5}
PREFIX ${vision-tpl_BUILD_PREFIX}
DOWNLOAD_DIR ${test_ext_proj_DOWNLOAD_DIR}
INSTALL_DIR ${test_ext_proj_BUILD_INSTALL_PREFIX}
CMAKE_GENERATOR ${gen}
CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX:PATH=${test_ext_proj_BUILD_INSTALL_PREFIX}
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}
)
#This variable is required so other packages can find it.
set(ZLIB_ROOT ${test_ext_proj_BUILD_INSTALL_PREFIX} CACHE PATH "" FORCE)
External_libxml2.cmake:
set(libxml2_release "2.9")
set(libxml2_patch_version 0)
set(libxml2_url "ftp://xmlsoft.org/libxml2/libxml2-sources-${libxml2_release}.${libxml2_patch_version}.tar.gz")
set(libxml2_md5 "7da7af8f62e111497d5a2b61d01bd811")
#We need to state that we're dependent on ZLib so build order is correct
set(_XML2_DEPENDS ZLib)
#This build requires make, ensure we have it, or error out.
if(CMAKE_GENERATOR MATCHES ".*Makefiles")
set(MAKE_EXECUTABLE "$(MAKE)")
else()
find_program(MAKE_EXECUTABLE make)
if(NOT MAKE_EXECUTABLE)
message(FATAL_ERROR "Could not find 'make', required to build libxml2.")
endif()
endif()
ExternalProject_Add(libxml2
DEPENDS ${_XML2_DEPENDS}
URL ${libxml2_url}
URL_MD5 ${libxml2_md5}
PREFIX ${test_ext_proj_BUILD_PREFIX}
DOWNLOAD_DIR ${test_ext_proj_DOWNLOAD_DIR}
INSTALL_DIR ${test_ext_proj_BUILD_INSTALL_PREFIX}
BUILD_IN_SOURCE 1
CONFIGURE_COMMAND ./configure
--prefix=${test_ext_proj_BUILD_INSTALL_PREFIX}
--with-zlib=${ZLIB_ROOT}
BUILD_COMMAND ${MAKE_EXECUTABLE}
INSTALL_COMMAND ${MAKE_EXECUTABLE} install
)