如何跨平台+CMake自动下载C++依赖?

问题描述 投票:0回答:4

我想实现这个工作流程:

  1. 从 Windows 系统(或任何与此相关的平台)上的存储库中签出。
  2. 运行一些获取依赖项的工具,包括和库,并将它们放在适当的位置(例如在 Windows 上的“\Program Files\Microsoft Visual Studio 10.0\VC\Lib and \Includes”中)
  3. 运行 CMake(在 win 上创建 MSVS 项目)
  4. 打开MSVS项目并编译。

我希望在大多数平台上都有这个工作流程。 我不想手动下载依赖项。

如何在不将依赖项存储在存储库中的情况下执行此操作?

c++ windows dependencies cmake
4个回答
33
投票

在 CMake 中,您可以使用

file(DOWNLOAD URL PATH)
下载文件,将其与自定义命令结合起来进行下载和解压:

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}")

您的目标应该取决于自定义命令的输出,然后当您运行 CMake 时,文件将被下载,当您构建、提取和使用时。

这可以全部包含在宏中以使其更易于使用。

您还可以查看使用 CMake 模块 ExternalProject,它可能会做您想要的事情。


9
投票

从cmake 3.11开始有一个新功能:FetchContent

您可以在配置过程中使用它来获取依赖项,例如获取很棒的 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})

我更喜欢获取压缩源而不是直接签出。但是

FetchContent
还允许定义 git 存储库。


4
投票

在 CMake 宇宙中:

vcpkg

vcpkg 是适用于 Windows、Linux 和 macOS 的 C++ 库管理器的包管理器。它可以与 CMake 无缝集成 - 有关详细信息,请参阅此处

柯南

Conan 是一个 C/C++ 包管理器。它还具有用于与 CMake 集成的策略

带有ExternalProject_Add的CMake

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 time
configure_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 Windows
set(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. Eg
add_executable(example example.cpp)
target_link_libraries(example gtest_main)
add_test(NAME example_test COMMAND example)

示例.cpp

#include <iostream>

#include "gtest/gtest.h"

TEST(sample_test_case, sample_test)
{
    EXPECT_EQ(1, 1);
}

CMake 宇宙之外:

我建议你不要使用CMake!使用Bazel

例如,如果您想使用gtest

工作空间

工作空间(名称=“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",
)

构建

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

)

文本.cpp

#include <iostream>

#include "gtest/gtest.h"

TEST(sample_test_case, sample_test)
{
    EXPECT_EQ(1, 1);
}

如何进行测试?

bazel test //...

例如,如果您想使用boost

工作空间

workspace(name = "BoostFilesystemDemo")

load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")

# Fetch Boost repo
git_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()

构建

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;
}

如何构建:

bazel build //...

如何运行:

bazel run //:FilesystemTest

如果您想生成 Visual Studio 解决方案,请使用 lavender。不幸的是,薰衣草还只是实验性的,需要一些改进。但我认为在这里花费精力比让 CMake 处理所有依赖项更有意义。还有一些一些项目尝试进行 Bazel CMake 互操作。


-59
投票

实现这一目标的最佳方法是消除依赖。

依赖是邪恶的。

消灭它们而不是依赖它们。

说真的。

您不想手动下载它们,您不想将它们存储在您的存储库中,您的客户也不想为您下载它们。事实上,你的编译器甚至不想编译它们。

更喜欢切换到 java 而不是添加 C++ 库依赖项...

与此同时,建议检查 CMake 的 ExternalProject 模块,这是您很快就能使用 CMake 获得非存储库存储的自动依赖项下载配置构建和安装的最接近的模块基于构建。

© www.soinside.com 2019 - 2024. All rights reserved.