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

Posted

技术标签:

【中文标题】如何以跨平台方式+ CMake自动下载C++依赖?【英文标题】:How to automatically download C++ dependencies in a cross platform way + CMake? 【发布时间】:2011-12-30 12:15:55 【问题描述】:

基本上我想实现这个工作流程:

    从 Windows 系统(或任何平台)上的存储库中签出。

    运行一些获取依赖项的工具,包括包含和库,并将它们放在适当的位置(如 Windows 上的“\Program Files\Microsoft Visual Studio 10.0\VC\Lib and \Includes”)

    运行 CMake(在 win 上创建 MSVS 项目)

    打开MSVS项目并编译。

我希望在大多数平台上都有这个工作流程。

我不想手动下载依赖项

如何在不将依赖项存储在存储库中的情况下做到这一点? 实现这一目标的最佳方法是什么?

【问题讨论】:

您找到解决问题的方法了吗?我一直在寻找类似 npm 的东西来构建具有依赖关系的 C/C++ 程序。 @hg。据我所知,没有 C++ 的跨平台包管理器。 【参考方案1】:

在 CMake 中,您可以使用 file(DOWNLOAD URL PATH) 到 download a file,将其与自定义命令结合以进行下载和解包:

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,它可以满足您的需求。

【讨论】:

但这不是设置和安装依赖项的跨平台方式,也不是从公共存储库下载的依赖项 使用 CMake file(DOWNLOAD ...) 或 ExternalProject 是跨平台的。如果依赖项是包,那么在 Windows 上,您需要使用 msiexec 来安装 MSI 包;在 Linux 上,它取决于发行版:Red Hat 衍生品 yumrpm 带有 RPM 包,Debian 衍生品 aptdpkg 带有 DEB 包。 是的,下载命令是跨平台的,但这不是安装依赖项的跨平台方式。每个平台对于相同的依赖项通常有不同的文件和包(以及下载链接)。你可以说我正在寻找类似 Maven 的 C++ 依赖项,它可以与 CMake 很好地结合在一起。 @JBeurer 如果您实际上必须事先安装这些依赖项,这可能根本不是一个好主意;相反,只需在安装说明中添加您的软件所具有的任何依赖项,然后让用户手动安装即可。对于那些想要自动化一切的人来说,这听起来可能并不那么令人兴奋,但它肯定是更安全的方式,更重要的是,它有效 @skyhisi 实际上,对于 FILE(DOWNLOAD.... 第二个参数是文件,所以你不能指定路径。【参考方案2】:

从 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 存储库。

【讨论】:

但它不会自动解决传递依赖【参考方案3】:

在 CMake 世界中:

vcpkg

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

柯南

Conan 是一个 C/C++ 包管理器。它还有一个 strategy 用于与 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)

example.cpp

#include <iostream>

#include "gtest/gtest.h"

TEST(sample_test_case, sample_test)

    EXPECT_EQ(1, 1);

在 CMake 领域之外:

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

例如如果你想使用gtest:

工作空间

工作区(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",
)

构建

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

如何运行测试?

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 处理所有依赖项更有意义。还有some projects 尝试制作 Bazel CMake 互操作。

【讨论】:

【参考方案4】:

实现这一点的最佳方法是消除您的依赖关系。

依赖是邪恶的。

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

说真的。

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

宁愿切换到java而不是添加C++库依赖...

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

【讨论】:

不,不能。切换到Java?不。不。不。拜托上帝 NOOOOOOO! 我认为这是一个有趣的点;但是,它的措辞类似于火焰诱饵,并没有考虑到用户的需求。 我猜一个人的幽默是另一个人的“火焰诱饵”......而且我认为在我的答案和另一个更受欢迎的答案中对 ExternalProject 的引用是最接近的这确实考虑了用户的需求。这里没有灵丹妙药;没有 C++ 的 Maven。如果ryppl.org 项目成为现实,它将超越 ExternalProject。目前,这是我得到的最好的指针。 赞成。到目前为止,有 6 人看到了有趣的一面,而 25 人没有看到。 60 - 50 == +10 是的,互联网!我将永远保留这个答案。这是我最喜欢的 Stack Overflow 问题/答案。

以上是关于如何以跨平台方式+ CMake自动下载C++依赖?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用CMake构建c++项目

CMake c++ 库链接

CLion+CMake编写C++程序

CMAKE编译时如何自动下载第三方库并解压安装到指定目录

CMake方式编译grpc

C++ CMake 构建错误“未定义的参考”