I'm working on project that use some third-party libraries. I can clone and put them into my project as a git submodule
and use add_subdirectory
to use them in my project. But it take much time to compile these libs, and I can not manage cmake
variables in my projects, and install()
command make my package contains many things that I don't need.
So what is correct way to use third-party libraries in my project. I can not tell the client to install these libraries as dependencies of my project.
Answering this question requires to cover few aspects, you will find below two sections:
- config-file package
- ExternalProject CMake module
config-file package
If you are looking into integrating libraries that are not built within the scope of your project, the first step is to make sure the libraries all provide a config-file package.
A config-file package usually include files like FooConfig.cmake
, FooConfigVersion.cmake
and FooTargets.cmake
.
Generally speaking, if the library Foo
is already using CMake and already provide a config-file package, configuring your project with -DFoo_DIR:PATH=/path/to/build-or-install-dir/
allow you to call find_package(Foo REQUIRED)
from your own project. This will import CMake targets that you can link against your own libraries or executables.
Now if the library Foo
is not already using CMake, there are options:
Case 1:
- (a) library
Foo
is already using CMake
- (b) but do NOT provide a config-file package
- action: I suggest to improve their build system
Case 2:
- (1) library
Foo
is not using CMake
- (2) and maintainer of
Foo
are willing to transition to CMake (or at least have the CMakeLists.txt
along side their current build system)
- action: I suggest to improve their build system
Case 3:
- (1) library
Foo
is not using CMake
- (2) and maintainer of
Foo
do not want to transition to CMake
- (3) but maintainer are willing to generate config-file package from their current build system
- action: I suggest to help them. This is for example what was done for Qt5, it now provides a config-file package.
Case 4:
Case 5:
- (1) for any reason the maintainer of
Foo
do not want to transition, or maintaining an alternative build system is not possible, or library is already available on the system
- action: You could create a
FindFoo.cmake
that would create imported targets.
- such file could be specific to your project or could be contributed to CMake directly
- this is for example the case of
FindOpenSSL.cmake
, FindGit.cmake
, ...
To learn more about config-file package, see https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html
ExternalProject CMake module
If the library Foo
is:
* (1) not available on the system:
* or can't be installed using package manager
* or working with the community maintaining packages (debian, conda-forge, chocolatey, ...) to have such package is not possible
* (2) or need to be compiled specially for your project
Then, the ExternalProject
CMake module will allow you to download, configure, build ... these projects from within your own project.
There are few approaches to make this happen.
Here is one that has been working well: You could setup a 2-level build system that we call: SuperBuild
.
To support the SuperBuild
approach, your CMakeLists.txt could have the following structure:
project(AwesomeProject)
[...]
option(Awesome_ENABLE_EXTRA "Enable more awesome stuff" OFF)
option(AwesomeProject_SUPERBUILD "Build ${PROJECT_NAME} and the projects it depends on." ON)
[...]
if(AwesomeProject_SUPERBUILD)
include("${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild.cmake")
return()
endif()
find_package(Foo REQUIRED)
add_library(AwesomeLib ....)
target_library_libraries(AwesomeLib PUBLIC Foo)
[...]
Then, in the file SuperBuild.cmake
you would roughly have these two calls:
ExternalProject_Add(Foo
GIT_REPOSITORY "git://github.com/Foo/Foo"
GIT_TAG "123456"
SOURCE_DIR ${CMAKE_BINARY_DIR}/Foo
BINARY_DIR ${CMAKE_BINARY_DIR}/Foo-build
CMAKE_CACHE_ARGS
-DFOO_ENABLE_BAR:BOOL=1
INSTALL_COMMAND ""
)
ExternalProject_Add(AwesomeProject
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}
BINARY_DIR ${CMAKE_BINARY_DIR}/AwesomeProject-build
DOWNLOAD_COMMAND ""
UPDATE_COMMAND ""
CMAKE_CACHE_ARGS
-Foo_DIR:PATH=${CMAKE_BINARY_DIR}/Foo-build
-DAwesome_ENABLE_EXTRA:BOOL=${Awesome_ENABLE_EXTRA}
INSTALL_COMMAND ""
)
This means that you usual build tree will now be found in the subdirectory AwesomeProject-build
.
Note that Foo-build
and AwesomeProject-build
are two independent build tree, the link between them
is the config-file package discussed above.
This is made possible by configuring AwesomeProject
sub project with -Foo_DIR:PATH=${CMAKE_BINARY_DIR}/Foo-build
and the calling find_package(Foo REQUIRED)
.
If you use tools like VisualStudio you can open the solution file found in any of these sub-directory.
To learn more about external project: https://cmake.org/cmake/help/latest/module/ExternalProject.html
conclusion
There are many more details, but I hope this will allow you to get a better understanding of what is possible.