在不假设 vcpkg 存在的情况下,克隆 repo 和 运行 CMake+vcpkg 项目的一步过程是什么?

What is the one-step process to clone a repo and run a CMake+vcpkg project, not assuming vcpkg exists?

我对 CMake+vcpkg 的理解有所欠缺,而且我也缺少合适的关键字来搜索解决方案。 (另外,不幸的是,我是 CMake 和 vcpkg 的新手。)

我想为使用 CMake 作为其构建系统并使用 vcpkg 作为其包管理器的 C++ 项目创建一个 public 存储库。

根据我目前的理解水平,用户需要先安装 CMake vcpkg 已经安装,然后才能输入 cmake并建立回购协议。我想让它尽可能简单地构建回购协议,而不是在他可以构建之前就告诉他如何进行设置的一堆说明。

我想要一个一步到位的解决方案:在克隆回购用户类型之后......一些......并且回购得到构建。

在这个时代,我愿意假设他已经安装了 CMake ...而且它可以找到合适的工具链。所以也许他只需要输入 'cmake' ...

我不愿意假设他安装了 vcpkg。

(TBH,我什至不知道配置工具链的是 CMake 还是 vcpkg - 我假设是 CMake,但建议的问题之一表明它是 vcpkg ...)

今天的合理假设是什么,最小步解是什么?

假设用户安装了某些工具并没有错。

假设您正在开发依赖于 libbarlibfoo,并且您想让用户尽可能轻松地安装 libfoo

使用包管理器

如果 libfoolibbar 可通过同一个包管理器获得,则所有用户必须做的是:

vcpkg install libbar libfoo

您不必为此在 libfoo 中做任何特别的事情,只需指示用户在您的自述文件中安装所有依赖项即可。

使用什么包管理器并不重要。

没有包管理器

您仍然希望人们可以轻松地直接构建和安装您的项目。似乎在项目的构建或配置阶段调用包管理器并解决所有依赖项是用户友好的,因为用户不再需要处理安装这些问题,但这不是出于多种原因,包括:

  • 您或其他人可能想将您的项目添加到另一个包管理器(如 conan, spack 等)
  • 有人可能想用 FetchContent, CPM, directly with add_subdirectory 消费 libfoo,等等
  • 有些人可能不是 vcpkg 的用户 - 如果可能,没有必要强迫他们使用它
  • 您可能想要添加另一个依赖项 libbaz,它在 vcpkg
  • 上不可用
  • 用户可能已经安装了 libbar 的正确版本(不一定通过 vcpkg

此列表并不详尽。如果您不是在编写库,那么某些要点并不适用。

这意味着已经安装了所有依赖项的人应该能够像这样使用libfoo

git clone your-repo
cd your-repo
cmake -Bbuild
cmake --build build
cmake --install build

在没有包管理器的情况下解决依赖关系

但是,可能需要自动解决依赖关系。如果您的依赖项使用 CMake,最简单的方法是使用 FetchContent. For some of the reasons outlined above you should provide an escape hatch so people can still use the already installed dependencies. This can be done with an option。例如,FOO_USE_EXTERNAL_BAR。这可以默认设置为 yesno,没有正确答案。只要用户可以控制这一点,我认为这并不重要。您应该为您的选项命名,以避免与其他项目使用的选项可能发生冲突。

在这种情况下,您的构建脚本可以这样做:

if (FOO_USE_EXTERNAL_BAR)
    find_package(bar REQUIRED)
else ()
    FetchContent_Declare(
        bar
        GIT_REPOSITORY bar-repo
        GIT_TAG        release-tag
    )
    FetchContent_MakeAvailable(bar)
endif ()

target_link_libraries(foo PRIVATE bar::bar)

根据 libbar 的 CMakeLists.txt 的编写和组织方式,if 和 else 分支可能会变得更加复杂。有关详细信息和提示,请参阅 Effective CMake

现在我可以让 libfoo 解析 libbar,方法是在配置项目时将 FOO_USE_EXTERNAL_BAR 设置为 ON,或者我可以将其设置为 OFF 以更好地控制它的解决方式。我什至可以使用 libfoo 作为已经依赖于 libbar 的项目的依赖项。如果你老是拉进去,这种情况下我避免不了冲突。

使用 CMake 更新依赖项

您可能仍然会发现使用 CMake 更新项目的所有依赖项很容易,而无需通过 FetchContent 下载它们。虽然这可能会引起一些人的注意,但您可以添加一个自定义目标来解决与包管理器的依赖关系。这也应该可以通过一个选项来控制。与上述情况不同,我坚信如果您这样做,该选项应该默认设置为关闭:

if (FOO_AUTO_USE_VCPKG)
    add_custom_target(
        update_deps
        COMMAND vcpkg install libbar
    )
    
    add_dependencies(foo update_deps)
endif ()

这将在您每次构建 foo 时调用 vcpkg,因此它会使您的构建变慢。如果您删除 add_dependencies 调用,您将不得不在需要时手动 运行 update_deps 目标(无论如何不应该那么频繁)。

备注

使用选项是为用户提供选项的好方法。应该注意的是,它们会增加认知负荷,因此选择强默认值会有所帮助。

FetchContent 是一种很好的方式,可以将用户的注意力从用户身上移开,但与此同时,使用它的多个项目最终将一遍又一遍地重新下载相同的库。它仍然比在构建时调用包管理器更加用户友好,只要用户可以禁用此行为,就没有什么可担心的。

这个答案的某些部分可能更多地被视为意见而不是事实。正如我所说,没有一种正确的方法可以做到这一点,不同的人会有不同的方法来解决这个问题。不同的项目,不同的环境会有不同的约束。

我已经推荐了上面的 Effective CMake 演讲,还有其他有用的资源 here. If you're a library author you may also want to take a look at Deep CMake for Library Authors

我也有同样的问题。就我而言,我不愿意假设用户预装了 CMake 或 vcpkg。

到目前为止,这是我的解决方案,作为 Windows 批处理文件:

@REM Bootstrap...
set VCKPG_PARENT_DIR=C:\Projects
set CMAKE_VERSION="3.20.2"

mkdir "%VCKPG_PARENT_DIR%"
pushd "%VCKPG_PARENT_DIR%"
git clone https://github.com/Microsoft/vcpkg.git
.\vcpkg\bootstrap-vcpkg.bat -disableMetrics
set PATH=%PATH%;%VCKPG_PARENT_DIR%\vcpkg\downloads\tools\cmake-%CMAKE_VERSION%-windows\cmake-%CMAKE_VERSION%-windows-i386\bin
set VCPKG_DEFAULT_TRIPLET=x64-windows
set PYTHONHOME=%VCKPG_PARENT_DIR%\vcpkg\packages\python3_x64-windows\tools\python3
popd

@REM Build the project...
cmake -B build -S .\engine\ -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%\scripts\buildsystems\vcpkg.cmake -DCMAKE_BUILD_TYPE=Release -DUSE_PYTHON_3=ON
cmake --build .\build\ --config Release
mkdir bin
xcopy .\build\Release\*.* .\bin\
xcopy .\build\objconv\Release\*.* .\bin\
xcopy .\build\setup\Release\*.* .\bin\

它可能需要一些改进,但希望这能让您了解可以采用的一种路线。