0.前言
最近在做公司一个新的工程,对标海康的MVS, 基于GENICAM通用协议,目标是适配市场上众多的厂牌的基于GENICAM协议的相机。 由于预计到会引用很多开源项目,且会围绕相机扩展很多公司的业务功能。所以我重新设计了新软件总体的架构,由原来的qmake改为cmake,并使用插件式开发。这即方便于引用其他开源框架,也可以使公司的代码模块化,更容易维护以及复用。但在众多开源模块与公司的模块,以及公司的不同的项目之间要如何处理依赖关系?如何科学的减少重复的工作?满地都是cmake + git submodule,我抬头却看到了vcpkg私有仓库。
1.随便聊聊
1.1 包管理器
python有pip, c#有nuget , java有maven, 基本上每一个语言都有一个亲妈背书的包管理器,可以递归解决项目依赖,但是c++没有。
- 语言特性太复杂, 编译选项不一样,同样的代码生成的程序就可能不能一起工作
- 模块元编程只能以源码方式发布,而不是二进制
- ABI兼容性问题导致需要根据cpu架构发布不同的二进制文件版本
基于上面的问题以及等等,导致c++的包管理不好实现,长久以来,c++开发者如果需要采用其他开源项目,通常需要手动把 其他c++项目的源码,静态链接库,动态链接库集成到自己的项目里,在集成的过程中,往往不是顺利的,可能需要处理依赖关系,环境问题,编译选项问题。
1.2 Cmake
cmake的出现简化了这个过程, 它是一款跨平台c++项目的管工具, 可以很方便的把cmake工程集成到另一个cmake工程中,然后指定生成器,生成各种类型的构建工具的项目,如ninja,visual studio 等,,并且它可以枚举项目的依赖项目,但是它没有版本控制的能力,所以通常与git联合使用 。 目前基本主流的IDE都集成了cmake和git,并且c++开源项目大多数也是以cmake的方式组织工程,因此使用cmake+git可以方便的集成开源项目。这也是我想用cmake的原因之一,除此之外,只要项目没有相互依赖,不管所处何目录结点,cmake都支持并行构建(qmkae子目录构建完才会构建父目录),可以更好的利用多核计算机的性能。所以cmake已经足够好了,为什么还需要vcpkg?
想像一下,你有一个巨大的项目,依赖关系比较复杂,如果说顶层项目是第0层,某个第1层的项目A依赖了开源库D,另一个第1层的项目B的第2层项目BA也依赖了项目D, 你如果不修改优化工程结构,你的项目目录里将存在两份D的源码,并且如果修改了其中一份D的代码,需要把它提交给他的父项目们,直到第0层,然后再到另一份D代码处,拉代码后,再一层一层的提交到第0层。这样别人拉取你的第0层代码时,才能简单的git submodule update --init --recursive
,克隆下来。而项目中可能会出现多个D项目这样的存在,这意味着即使使用git submodule,也需要做很多重复工作 。 而vcpkg可以解决这种问题,甚至允许控制每份D的代码版本不一样。
1.3 vcpkg
vcpkg是微软推出的c++ 源码级别的跨平台包管理器。虽然vc开头,但不仅限于visual c++, 它基本上=git + cmake,但扩展了包管理器应该有的功能,可以方便的解决项目依赖问题,也可以方便切换依赖项目的ABI,Cpu架构,目标平台。 vcpkg有经典模式和清单模式两种模式。
可以看出 清单模式可以解决我在1.2第二段落中描述的问题。但拿来就用还是有一些局限性。以下两种情况
- 需要针对性修改某个项目中某个vcpkg安装的开源库依赖,编译选项或bugfix,但其不希望他项目被影响
- 希望把公司的项目需要集成到vcpkg,享受包管理器带来的便利,又不想开源
何以解忧,唯有搭建自己的vcpkg 仓库,这也是本文档想讲的内容(终于酝酿好氛围)
除此这外,vcpkg相对于公司现在的项目管理,还有一个优点,就是减少二进制文件的提交,理论上只提交源码就行,install的时候,vcpkg会根据配置 使用git签出正确分支的源码,构建为本地的二进制文件
2. 搭建公司内私有vcpkg仓库
目标是把开源的项目,如open cv之类的 以及公司的软件模块发布到私有仓库上。理论上,开源的库如果没有额外的修改,直接用vcpkg现有的库就行了,但因为我们在墙内,如果想提升vcpkg安装的速度, 我们最好还是把我们项目中用到的开源库迁移到公司内部git平台上,设置为镜像仓库,定时从github同步。 因此本小节,主要拆分为以下三个步骤
- 搭建vcpkg 私有registry
- 把项目中使用到的开源项目同步到公司git 平台上
- 把公司的git 平台上的repos登记到私有的vcpkg registry
2.1 搭建vcpkg 私有仓库
参考
创建一个空的git repository
mkdir -p ~/source/repos/prism_registry && cd ~/source/repos/prism_registry
git init .
创建一个*** baseline.json*** 文件,放在根目录 versions目录
1
2
|
mkdir versions && cd versions
vim baseline.json
|
写入 下面的json内容
2.2 添加库到vcpkg私有仓库
这里主要演示添加github库 和非github库两种方式
虽然我们应该主要使用第二种方式
2.2.1 添加github仓库示例
不管想添加到vcpkg仓库的项目是不是来自github,都需要执行这两步
2.2.1.1 添加port
创建ports目录,在这里面编辑你想放到你vcpkg私有仓库的信息
例如我想把我的prism 库放到私有库,就在ports目录中再创建一个prism的文件夹
prism是一个只有头文件,且只依赖c++标准库的 c++库
1
|
mkdir -p ~/source/repos/prism_registry/ports/prism && cd ~/source/repos/prism_registry/ports/prism
|
然后在prism目录中创建文件 vcpkg.json ,把prism初始版本的信息写进去,因为我这个库没有依赖,所以没有写依赖项,如果有,在json里加"dependencies “:[]就行
1
2
3
4
5
6
|
{
"name": "prism",
"version": "1.0.0",
"description": "a static reflect library of c++",
"homepage": "https://github.com/nocanstillbb/prism"
}
|
同时把vcpkg.json也提交到prism存储库根目录
然后再在 ~/source/repos/prism_registry/ports/prism 中创建一个空的protfile.cmake
做到这一步,你的文件目录应该是这样的
../
prism_registry/
| .git/
| ports/
| | prism/
| | | protfile.cmake
| | | vcpkg.json
| versions/
| | baseline.json
测试vcpkg能不能正常识别这个json文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
$ cd ~/source/repos/prism_registry/prism_registry
$ vcpkg install prism --overlay-ports=ports/prism
Computing installation plan...
The following packages will be built and installed:
prism:x64-windows -> 1.0.0 -- D:\source\repos\prism_registry/ports/prism
Detecting compiler hash for triplet x64-windows...
Restored 0 package(s) from C:\Users\kokbi\AppData\Local\vcpkg\archives in 155 us. Use --debug to see more details.
Installing 1/1 prism:x64-windows...
Building prism:x64-windows...
-- Installing port from location: D:\source\repos\prism_registry/ports/prism
CMake Error at scripts/ports.cmake:113 (message):
Port is missing portfile:
D:/source/repos/prism_registry/ports/prism/portfile.cmake
error: building prism:x64-windows failed with: BUILD_FAILED
Elapsed time to handle prism:x64-windows: 27 ms
Please ensure you're using the latest port files with `git pull` and `vcpkg update`.
Then check for known issues at:
https://github.com/microsoft/vcpkg/issues?q=is%3Aissue+is%3Aopen+in%3Atitle+prism
You can submit a new issue at:
https://github.com/microsoft/vcpkg/issues/new?title=[prism]+Build+error&body=Copy+issue+body+from+D%3A%2Fsource%2Frepos%2Fvcpkg%2Finstalled%2Fvcpkg%2Fissue_body.md
|
根据打印出来的信息,可以知道vcpkg安装时正确定位到port/prism/vcpkg.json了,只是protfile.cmake中是空的才导致安装失败了
接下来添加protfile.cmake内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO nocanstillbb/prism
REF e4cb55e02bfad354ba86d70f5555fe12d8f70d10
SHA512 a9eebc68ed045ccac650950b5ba8b1f172fde8244856c4b5e0ac38977711f5e3bbfc305ec831c47787e4994d7faff4ef01bf525e589b140dff8a66b10febb6e6
HEAD_REF master
)
#https://learn.microsoft.com/en-us/vcpkg/examples/packaging-github-repos
vcpkg_cmake_configure( SOURCE_PATH "${SOURCE_PATH}")
vcpkg_cmake_install()
#vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/${PORT})
#vcpkg_cmake_config_fixup()
vcpkg_fixup_pkgconfig()
vcpkg_copy_pdbs()
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug")
file(
INSTALL "${SOURCE_PATH}/LICENSE"
DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}"
RENAME copyright)
file(
INSTALL "${SOURCE_PATH}/usage"
DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}")
|
其中的shar512是下载的源码归档包的sha512校验和的值,可以自己下载下来校验,填写进去,也可以先写一个0,安装时会安装失败,但会把真正的值打印出来。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
$ vcpkg install prism --overlay-ports=ports/prism
Computing installation plan...
The following packages will be built and installed:
prism:x64-windows -> 1.0.0 -- D:\source\repos\prism_registry/ports/prism
Detecting compiler hash for triplet x64-windows...
-- Using HTTP(S)_PROXY in environment variables.
Restored 0 package(s) from C:\Users\kokbi\AppData\Local\vcpkg\archives in 171 us. Use --debug to see more details.
Installing 1/1 prism:x64-windows...
Building prism:x64-windows...
-- Installing port from location: D:\source\repos\prism_registry/ports/prism
-- Downloading https://github.com/nocanstillbb/prism/archive/59fc49ca76338364c6351475a4ad5d80578f625.tar.gz -> nocanstillbb-prism-59fc49ca76338364c6351475a4ad5d80578f625.tar.gz...
[DEBUG] To include the environment variables in debug output, pass --debug-env
[DEBUG] Trying to load bundleconfig from D:\source\repos\vcpkg\vcpkg-bundle.json
[DEBUG] Failed to open: D:\source\repos\vcpkg\vcpkg-bundle.json
[DEBUG] Bundle config: readonly=false, usegitregistry=false, embeddedsha=nullopt, deployment=Git, vsversion=nullopt
[DEBUG] Metrics enabled.
[DEBUG] Feature flag 'binarycaching' unset
[DEBUG] Feature flag 'compilertracking' unset
[DEBUG] Feature flag 'registries' unset
[DEBUG] Feature flag 'versions' unset
Downloading https://github.com/nocanstillbb/prism/archive/59fc49ca76338364c6351475a4ad5d80578f625.tar.gz
[DEBUG] Trying to hash D:\source\repos\vcpkg\downloads\nocanstillbb-prism-59fc49ca76338364c6351475a4ad5d80578f625.tar.gz.18080.part
[DEBUG] D:\source\repos\vcpkg\downloads\nocanstillbb-prism-59fc49ca76338364c6351475a4ad5d80578f625.tar.gz.18080.part has hash f03d4c918e5bda46cc1aa129edd192a74274b9c07f6634dedec47b986aad6e0e8689b019620c57507e07fea36adaf9dfbff9b41fcc166da5f0748a3a9d491529
error: Failed to download from mirror set
error: File does not have the expected hash:
url: https://github.com/nocanstillbb/prism/archive/59fc49ca76338364c6351475a4ad5d80578f625.tar.gz
File: D:\source\repos\vcpkg\downloads\nocanstillbb-prism-59fc49ca76338364c6351475a4ad5d80578f625.tar.gz.18080.part
Expected hash: 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Actual hash: f03d4c918e5bda46cc1aa129edd192a74274b9c07f6634dedec47b986aad6e0e8689b019620c57507e07fea36adaf9dfbff9b41fcc166da5f0748a3a9d491529
|
把Actual hash复制下来写到portfile.cmake 重新测试安装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
$ vcpkg install prism --overlay-ports=ports/prism
Computing installation plan...
The following packages will be built and installed:
prism:x64-windows -> 1.0.0 -- D:\source\repos\prism_registry\ports/prism
Detecting compiler hash for triplet x64-windows...
-- Using HTTP(S)_PROXY in environment variables.
Restored 1 package(s) from C:\Users\kokbi\AppData\Local\vcpkg\archives in 44 ms. Use --debug to see more details.
Installing 1/1 prism:x64-windows...
Elapsed time to handle prism:x64-windows: 30.2 ms
Total install time: 30.3 ms
The package prism usage:
find_package(prism::prism)
target_include_directories(main INTERFACE ${PRISM_INCLUDE_DIRS})
more infomation: https://github.com/nocanstillbb/prism
|
好的项目,安装后不会提示警告和错误,如果这里有提示警告的话,就会提示一个错误,提示你如果要让库被收录到vcpkg的优选目录,要更正portfile.cmake消除这些错误和警告。
正常这里还应该在安装之前执行一下项目的单元测试,这个先不管,不是必须的。接下来测试一下刚才安装的库能不能正常使用,先用vcpkg list查看信息
1
2
|
PS D:\Downloads> vcpkg list |findstr prism
prism:x64-windows 1.0.0 a static reflect library of c++
|
然后我把我项目中prism源码目录移除后,用ide查看引用时,已经正确定位到vcpkg的安装目录了
![image-20231003181231934](image-20231003181231934.png)
编译也没有问题,说明成功了。
2.2.1.2 添加versions
接下来添加version相关的信息
在prism_registry/versions 中创建以你的存储库名称头一个字母+ “-”开关的目录, 添加的库是prism,所以创建的目录为p-
1
2
3
|
mkdir -p ~/source/repos/prism_registry/versions/p-
cd ~/source/repos/prism_registry/versions/p-
vim prism.json
|
创建prism.json文件写入
1
2
3
4
5
6
7
8
|
{
"versions": [
{
"version": "1.0.0",
"git-tree": ""
}
]
}
|
git-tree 的值如此获取
1
2
|
git add ports/prism
git commit -m "[prism] new port"
|
单独提交prism的ports目录, 提交内容以[portName]开头,如果你想要合并到vcpkg master的话,这个是规则之一
然后 先不着急提交
1
|
git rev-parse HEAD:prots/prism
|
获取git分支HEAD上的git 目录的分支号,写入/versions/p-/prism.json中,刚才放空的git-tree
然后编辑 versions/baseline.json
1
2
3
4
5
6
7
8
|
{
"default": {
"prism": {
"baseline": "1.0.0",
"port-version": 0
}
}
}
|
然后把prism_registry 提交推送到公司forgejo上
2.2.2 添加非github的库示例
上面的示例是vcpkg团队博客上的示例,所以用 vcpkg_from_github导入的,而我们私有的代码库是放在公司forgejo服务器上的,所以应该探索一种通用的方法,我查看vcpkg源码里的Ports,看到有的库使用的是vcpkg_from_git
1
2
3
4
5
6
7
|
vcpkg_from_git(
OUT_SOURCE_PATH LSS_SOURCE_PATH
URL https://chromium.googlesource.com/linux-syscall-support
REF 7bde79cc274d06451bf65ae82c012a5d3e476b5a
)
file(RENAME "${LSS_SOURCE_PATH}" "${SOURCE_PATH}/src/third_party/lss")
|
和带git补丁的写法
1
2
3
4
5
6
7
|
vcpkg_from_git(
OUT_SOURCE_PATH SOURCE_PATH
URL https://sourceware.org/git/elfutils
REF ca4a785fc3061c7d136b198e9ffe0b14cf90c2be #elfutils-0.186
PATCHES configure.ac.patch
)
|
除此之外没有什么区别, 反而少了sha512 检验的步骤,更简单了
2.2.3 用vcpkg的简化添加version步骤
添加完port 并提交到git后,可以使用这个方式简单的更新version和baseline.json
如果是在微软vcpkg 叉出来的分支上,添加了使用git添加了ports/prism后,就可以使用x-add-version --all
来生成 version/p-/prism.json 以及 version/baseline.json中的prism的条目
如果不是vcpkg叉出来的分支,而是公司的私有库的话,参照教程:使用 Git 将包发布到私有 vcpkg 注册表 |Microsoft学习 加点参数也可以使用这个工具
1
|
vcpkg --x-builtin-ports-root=./ports --x-builtin-registry-versions-dir=./versions x-add-version --all --verbose
|
了解这个工具,如果需要ci集成的话,就会方便很多
2.3 在项目中使用私有的vcpkg仓库
首先我在公司的forgejo上开了一个空的存储库,把 vcpkg私有仓库上传上去
然后在测试项目的根目录创建vcpkg-configuration.json
基于github的vcpkg ,扩展我们私有的库, 这种是比较合理的,如果需要对github上的库做定制化修改,只需要把库迁移到内网就行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
{
"default-registry": {
"kind": "git",
"repository": "https://github.com/microsoft/vcpkg",
"baseline": "0da9fe986d07603da71f35ef8dd55512154a64bd"
},
"registries": [
{
"kind": "git",
"repository": "http://10.1.8.8/AL/dv-vcpkg",
"baseline": "36f0728c26661f67fca9b583bd99084e9491be93",
"packages": [ "prism" ]
}
]
}
|
baseline是vcpkg仓库的提交,一般建立一个项目时,初始化的时候更新一下就可以了,后继修改个别的库,可以通过指定库版本,或重写库版本的方式,使项目大部分的引用的库都是稳定的。
关于vcpkg版本控制 可以参考这个文档Versioning reference | Microsoft Learn
然后创建一个vcpkg.json
1
2
3
4
5
6
7
|
{
"name": "prismqt-core",
"version": "0",
"dependencies": [
"prism"
]
}
|
这里的prism没有指定任何版本,所以 会从我们私有库的基本版本中获取默认的版本
在测试项目根目录建立好这两个json后, 执行vcpkg install
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
$ vcpkg.exe install
Detecting compiler hash for triplet x64-windows...
-- Using %HTTP(S)_PROXY% in environment variables.
The following packages will be rebuilt:
* catch2:x64-windows -> 3.4.0 -- C:\Users\user\AppData\Local\vcpkg\registries\git-trees\5796c1c0513a7b49f135e8acdd1976f53e9944ea
* vcpkg-cmake:x64-windows -> 2023-05-04 -- C:\Users\user\AppData\Local\vcpkg\registries\git-trees\88a7058fc7fa73a9c4c99cfcae9d79e2abf87a5a
* vcpkg-cmake-config:x64-windows -> 2022-02-06#1 -- C:\Users\user\AppData\Local\vcpkg\registries\git-trees\8d54cc4f487d51b655abec5f9c9c3f86ca83311f
The following packages will be built and installed:
prism:x64-windows -> 1.0.0 -- C:\Users\user\AppData\Local\vcpkg\registries\git-trees\668c45ba147cbef160d9c228712f5775cff242b2
Additional packages (*) will be modified to complete this operation.
Restored 3 package(s) from C:\Users\user\AppData\Local\vcpkg\archives in 1.3 s. Use --debug to see more details.
Removing 1/7 catch2:x64-windows
Elapsed time to handle catch2:x64-windows: 217 ms
Removing 2/7 vcpkg-cmake-config:x64-windows
Elapsed time to handle vcpkg-cmake-config:x64-windows: 98 ms
Removing 3/7 vcpkg-cmake:x64-windows
Elapsed time to handle vcpkg-cmake:x64-windows: 113 ms
Installing 4/7 vcpkg-cmake:x64-windows...
Elapsed time to handle vcpkg-cmake:x64-windows: 16 ms
Installing 5/7 vcpkg-cmake-config:x64-windows...
Elapsed time to handle vcpkg-cmake-config:x64-windows: 16.5 ms
Installing 6/7 catch2:x64-windows...
Elapsed time to handle catch2:x64-windows: 58.2 ms
Installing 7/7 prism:x64-windows...
Building prism:x64-windows...
-- Installing port from location: C:\Users\user\AppData\Local\vcpkg\registries\git-trees\668c45ba147cbef160d9c228712f5775cff242b2
-- Using cached D:/Users/user/Documents/source/repos/vcpkg/downloads/prism-62156a21f843168993c8e641f22efb380b8e7e62.tar.gz
-- Cleaning sources at D:/Users/user/Documents/source/repos/vcpkg/buildtrees/prism/src/380b8e7e62-68fffb19a4.clean. Use --editable to skip cleaning for the packages you specify.
-- Extracting source D:/Users/user/Documents/source/repos/vcpkg/downloads/prism-62156a21f843168993c8e641f22efb380b8e7e62.tar.gz
-- Using source at D:/Users/user/Documents/source/repos/vcpkg/buildtrees/prism/src/380b8e7e62-68fffb19a4.clean
-- Found external ninja('1.10.2').
-- Configuring x64-windows
-- Building x64-windows-dbg
-- Building x64-windows-rel
-- Using cached mingw-w64-i686-pkgconf-1~1.8.0-2-any.pkg.tar.zst.
-- Using cached msys2-msys2-runtime-3.4.6-1-x86_64.pkg.tar.zst.
-- Using msys root at D:/Users/user/Documents/source/repos/vcpkg/downloads/tools/msys2/6f3fa1a12ef85a6f
-- Installing: D:/Users/user/Documents/source/repos/vcpkg/packages/prism_x64-windows/share/prism/copyright
-- Installing: D:/Users/user/Documents/source/repos/vcpkg/packages/prism_x64-windows/share/prism/usage
-- Performing post-build validation
Stored binaries in 1 destinations in 58.6 ms.
Elapsed time to handle prism:x64-windows: 21 s
Total install time: 21 s
The package prism usage:
find_package(prism::prism)
target_include_directories(main INTERFACE ${PRISM_INCLUDE_DIRS})
more infomation: https://github.com/nocanstillbb/prism
|
依赖全部安装完毕,我们只需要把所有依赖列到顶层项目上,就可以用vcpkg安装所有依赖,更新所有依赖,也可以利用vcpkg的版本控制功能来管理依赖, 这样git分支只需要保留一个master分支就可以了,所有依赖的版本可以用根目录的vcpkg.json来控制和查看
>> Home
Comments