How to automatically download C++ dependencies in a cross platform way + CMake? How to automatically download C++ dependencies in a cross platform way + CMake? windows windows

How to automatically download C++ dependencies in a cross platform way + CMake?


In CMake you can use file(DOWNLOAD URL PATH) to download a file, combine this with custom commands to download and unpack:

set(MY_URL "http://...")set(MY_DOWNLOAD_PATH "path/to/download/to")set(MY_EXTRACTED_FILE "path/to/extracted/file")if (NOT EXISTS "${MY_DOWNLOAD_PATH}")    file(DOWNLOAD "${MY_URL}" "${MY_DOWNLOAD_PATH}")endif()add_custom_command(    OUTPUT "${MY_EXTRACTED_FILE}"    COMMAND command to unpack    DEPENDS "${MY_DOWNLOAD_PATH}")

Your target should depend on the output from the custom command, then when you run CMake the file will be downloaded, and when you build, extracted and used.

This could all be wrapped up in a macro to make it easier to use.

You could also look at using the CMake module ExternalProject which may do what you want.


From cmake 3.11 on there is a new feature: FetchContent

You can use it to get your dependencies during configuration, e.g. get the great cmake-scripts.

include(FetchContent)FetchContent_Declare(  cmake_scripts  URL https://github.com/StableCoder/cmake-scripts/archive/master.zip)FetchContent_Populate(cmake_scripts)message(STATUS "cmake_scripts is available in " ${cmake_scripts_SOURCE_DIR})

I prefer fetching the ziped sources instead of directly checking out. But FetchContent also allows to define a git repository.


Within the CMake universe:

vcpkg

vcpkg is a package manager for C++ Library Manager for Windows, Linux, and macOS. It can be seamlessly integrated with CMake - see here for details.

Conan

Conan is a C/C++ package manager. It also has a strategy for the integration with CMake.

CMake with ExternalProject_Add

CMakeList.txt.in:

cmake_minimum_required(VERSION 2.8.2)project(googletest-download NONE)include(ExternalProject)ExternalProject_Add(googletest  GIT_REPOSITORY    https://github.com/google/googletest.git  GIT_TAG           master  SOURCE_DIR        "${CMAKE_BINARY_DIR}/googletest-src"  BINARY_DIR        "${CMAKE_BINARY_DIR}/googletest-build"  CONFIGURE_COMMAND ""  BUILD_COMMAND     ""  INSTALL_COMMAND   ""  TEST_COMMAND      "")

CMakeList.txt:

cmake_minimum_required(VERSION 3.8)# Download and unpack googletest at configure timeconfigure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt)execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .  RESULT_VARIABLE result  WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )if(result)  message(FATAL_ERROR "CMake step for googletest failed: ${result}")endif()execute_process(COMMAND ${CMAKE_COMMAND} --build .  RESULT_VARIABLE result  WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )if(result)  message(FATAL_ERROR "Build step for googletest failed: ${result}")endif()# Prevent overriding the parent project's compiler/linker# settings on Windowsset(gtest_force_shared_crt ON CACHE BOOL "" FORCE)# Add googletest directly to our build. This defines# the gtest and gtest_main targets.add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src                 ${CMAKE_BINARY_DIR}/googletest-build)# The gtest/gtest_main targets carry header search path# dependencies automatically when using CMake 2.8.11 or# later. Otherwise we have to add them here ourselves.if (CMAKE_VERSION VERSION_LESS 2.8.11)  include_directories("${gtest_SOURCE_DIR}/include")endif()# Now simply link against gtest or gtest_main as needed. Egadd_executable(example example.cpp)target_link_libraries(example gtest_main)add_test(NAME example_test COMMAND example)

example.cpp

#include <iostream>#include "gtest/gtest.h"TEST(sample_test_case, sample_test){    EXPECT_EQ(1, 1);}

Outside of the CMake universe:

I suggest you not to use CMake! Use Bazel!

For instance if you want to use gtest:

WORKSPACE

workspace(name = "GTestDemo")

load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")git_repository(    name = "googletest",    #tag = "release-1.8.1",    commit = "2fe3bd994b3189899d93f1d5a881e725e046fdc2",    remote = "https://github.com/google/googletest",    shallow_since = "1535728917 -0400",)

BUILD

cc_test(    name = "tests",    srcs = ["test.cpp"],    copts = ["-isystem external/gtest/include"],    deps = [        "@googletest//:gtest_main",    ],)

text.cpp

#include <iostream>#include "gtest/gtest.h"TEST(sample_test_case, sample_test){    EXPECT_EQ(1, 1);}

How to run the test?

bazel test //...

For instance if you want to use boost:

WORKSPACE

workspace(name = "BoostFilesystemDemo")load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")# Fetch Boost repogit_repository(    name = "com_github_nelhage_rules_boost",    commit = "49066b7ccafce2609a3d605e3667af3f07e8547c",    remote = "https://github.com/Vertexwahn/rules_boost",    shallow_since = "1559083909 +0200",)load("@com_github_nelhage_rules_boost//:boost/boost.bzl", "boost_deps")boost_deps()

BUILD

cc_binary(    name = "FilesystemTest",    srcs = ["main.cpp"],    defines = ["BOOST_ALL_NO_LIB"],    deps = [        "@boost//:filesystem",    ],)

main.cpp

#include <iostream>#include <boost/filesystem.hpp>using namespace boost::filesystem;int main(int argc, char* argv[]){    if (argc < 2)    {        std::cout << "Usage: tut1 path\n";        return 1;    }    std::cout << argv[1] << " " << file_size(argv[1]) << '\n';    return 0;}

How to build:

bazel build //...

How to run:

bazel run //:FilesystemTest

If you want to generate a Visual Studio solution use lavender. Unfortunately lavender is only experimental and it needs some improvement. But I think it makes more sense to spend effort here instead of getting CMake working with all your dependencies. There are also some projects that try to make an Bazel CMake interop.