A little and lightweight CMake helper tool that automatically downloads, extracts and configures dependencies based on a declaration on a .json file.
First you need to declare a dependency in your dependencies.json
file:
{
"JSON": {
"url": [
"https://github.com/nlohmann/json/releases/download/v3.7.0/include.zip"
],
"download_name": "json-3.7.0.zip",
"target": {
"type": "interface",
"includes": "."
}
}
}
In your CMakeLists.txt all you need is to include the module and import all dependencies from your JSON file:
# Include the CMakeDependency module
include(CMakeDependency/CMakeDependency.cmake)
# Import dependencies from JSON
import_dependencies_from_json(${PROJECT_SOURCE_DIR}/dependencies.json)
You are done! JSON
is now available as a target in CMake!
target_link_libraries(MyTarget PUBLIC JSON)
Note that CMakeDependency will create a file called CMakeLists.deps.txt
in CMAKE_CURRENT_SOURCE_DIR
. This file contains the preprocessed JSON file that is included by CMake. You can commit this file into your repository if you don't want to depend on Python 3 when compiling.
This CMake module requires CMake 3.15 and up. Python3 is required if you want to automatically generate the CMake dependencies from the JSON file.
You don't have to use the JSON file if you don't want to. You can import dependencies directly in CMake by calling import_dependency
:
import_dependency(JSON
URL https://github.com/nlohmann/json/releases/download/v3.7.0/include.zip
DOWNLOAD_NAME json-3.7.0.zip)
Note that DOWNLOAD_NAME
is optional. If not given, CMakeDependency will resolve a name based on the given URL
.
In this mode, no target is created but the ZIP file is downloaded and extracted. The extracted contents are in JSON_SOURCE_DIR
. If this library is a CMake library, you can simply call add_subdirectory
and be done with it.
add_subdirectory(${JSON_SOURCE_DIR} ${JSON_BINARY_DIR})
or if the dependency is NOT a CMake project, you can manually create a target:
add_library(JSON INTERFACE)
target_include_directories(JSON INTERFACE ${JSON_SOURCE_DIR})
If you don't want to duplicate your dependencies across multiple projects you can override the dependency source dir by setting JSON_DIR
cache variable. You can do so when invoking CMake:
cmake -DJSON_DIR="/my/json/lib" [...]
or from within CMake itself, before calling import_dependency
or import_dependencies_from_json
:
set(JSON_DIR "/my/json/lib" CACHE PATH "" FORCE)
Node that when setting JSON_DIR
, if the dependency is not downloaded it will be downloaded and imported to the given directory. If this is not something you want, for example, you want to use a system provided dependency, you can override the directory by setting JSON_SOURCE_DIR
.
Additionally, you can override just the zip file by setting JSON_ZIP
.
This example downloads the Boost source zip and configures it by building the system
library.
{
"Boost": {
"url": [
"https://dl.bintray.com/boostorg/release/1.71.0/source/boost_1_71_0.zip"
],
"target": {
"type": "static",
"srcs": [
"libs/system/src/error_code.cpp"
],
"includes": ".",
"defines": [
"BOOST_ALL_NO_LIB",
"BOOST_ASIO_NO_TYPEID"
],
"links": [
"$<$<PLATFORM_ID:Windows>:bcrypt.dll>"
]
}
}
}
This example will download and configure the Catch testing framework for compilation. Note that the configure
directive is used to create a .cpp
file for the runner and also note that the path is prefixed with :
, this means that the generation will take place into a subdirectory in ${CMAKE_CURRENT_BINARY_DIR}
.
Additionally, two extra_cmake
paths are given to be included by CMake after the target is configured.
{
"Catch2": {
"git": {
"repository": "https://github.com/catchorg/Catch2.git",
"tag": "c8db4e77c4c9aa20776db162e982f1af693ab8fe"
},
"configure": {
":catch2.cpp": {
"type": "generate",
"content": "#include \"catch.hpp\""
}
},
"target": [
{
"type": "interface",
"includes": "single_include/catch2",
"defines": [
"CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS",
"CATCH_CONFIG_ENABLE_BENCHMARKING"
],
"public_defines": {
"CATCH_CONFIG_CONSOLE_WIDTH": 300
}
},
{
"type": "object",
"name": "Runner",
"srcs": ":catch2.cpp",
"private_defines": [
"CATCH_CONFIG_MAIN"
],
"links": "Catch2"
}
],
"extra_cmake": [
"contrib/Catch.cmake",
"contrib/ParseAndAddCatchTests.cmake"
]
}
}
This will Naios/continuable and it's dependency Naios/function2 create both targets as a CMake interface library and link Function2
into Continuable
. As a consumer, you can simply link against Continuabe
and CMake will add Function2
transitively fot you.
{
"Continuable": {
"git": {
"repository": "https://github.com/Naios/continuable.git",
"tag": "cacb84371a5e486567b4aed244ce742ea6509d7d"
},
"target": {
"type": "interface",
"includes": "include",
"links": "Function2"
}
},
"Function2": {
"git": {
"repository": "https://github.com/Naios/function2.git",
"tag": "7cd95374b0f1c941892bfc40f0ebb6564d33fdb9"
},
"target": {
"type": "interface",
"includes": "include"
}
}
}
This will download Rogiel/PacketBuffer and import it by using CMake's add_subdirectory
command.
{
"PacketBuffer": {
"git": {
"repository": "https://github.com/Rogiel/PacketBuffer.git",
"tag": "37e76659eaf4ed7d4407d1ccc5e31dee5454ef2c"
},
"target": {
"type": "subdirectory"
}
}
}
This example will download the Poco library and include /Poco.cmake
for further configuration. You can use Poco_SOURCE_DIR
and Poco_BINARY_DIR
if needed to configure your target. Note that the file is an absolute paht (starting with /
). This means that CMakeDependency will include this dependency from ${PROJECT_SOURCE_DIR}
.
This allows very flexible build configuration for dependencies.
{
"Poco": {
"url": [
"https://pocoproject.org/releases/poco-1.9.4/poco-1.9.4.zip"
],
"target": {
"type": "cmake",
"file": "/Poco.cmake"
}
}
}
This example will download and configure STB Image for compilation. Note that the configure
directive is used to create a .c
file for compilation. Also note that the path is prefixed with :
, this means that the generation will take place into a subdirectory in ${CMAKE_CURRENT_BINARY_DIR}
.
{
"STB": {
"git": {
"repository": "https://github.com/nothings/stb.git",
"tag": "052dce117ed989848a950308bd99eef55525dfb1"
},
"configure": {
":stb_image.c": {
"type": "generate",
"content": "#include <stb_image.h>"
}
},
"target": [
{
"type": "static",
"name": "Image",
"srcs": ":stb_image.c",
"private_defines": [
"STB_IMAGE_IMPLEMENTATION"
],
"includes": "."
}
]
}
}
CMDEP_ROOT_DIR
: A path that points to the location in which CMake dependency files should be downloaded and extracted to. Defaults to${CMAKE_CURRENT_SOURCE_DIR}
.CMDEP_ZIP_DIR
: A path that points to the location in which ZIP files should be downloaded to. Defaults to${CMDEP_ROOT_DIR}/zips
.
<name>_URL
: A path that points to a URL to be downloaded. CMake will use this URL to download the file if needed. Defaults to the value given inimport_dependency
.<name>_DIR
: A path that points to a directory containing the dependency sources. Defaults to${CMDEP_ROOT_DIR}/<name>
.<name>_SOURCE_DIR
: A path that points to a source directory. If manually set, no downloading or extraction will take place and this will be used instead. Defaults to${<name>_DIR}
.<name>_ZIP
: A path that points to a zip file. If this file does not exists, it will be downloaded. You can use this to override the download and point to an already existing ZIP file. Defaults to${CMDEP_ZIP_DIR}/<file name>.zip
.
Some IDEs, such as CLion, use multiple CMake invocations for each configuration. This could cause problems when downloading dependencies to a common location. To avoid such problems, CMakeDependency
uses CMake's advisory locking mechanism to prevent multiple CMake instances from downloading the same file.
This lock is acquired in the CMDEP_ROOT_DIR
directory. Beware that CMake will create a cmake.lock
file in this directory and you should exclude it from your source control if needed.
- Improve documentation on
dependencies.json
syntax.