Git版本控制系统中的代码复用

在软件开发过程中,代码复用是一种常见的实践,它可以帮助提高开发效率,减少重复劳动。Git作为目前最流行的版本控制系统,为提供了强大的代码管理功能。然而,当涉及到在不同仓库之间复用代码时,如何做到这一点并没有统一的标准。可以在Google上搜索Git子模块(submodules)或Bitbucket子树(subtrees),会发现有人推荐使用它们,但紧接着也会有人警告要避免使用,就像避免瘟疫一样。在尝试了不同的解决方案后,最终形成了自己的一套系统,并在之前的文章《吃自己的狗粮》中进行了描述。它展示了如何使用符号链接来组织多个C/C++项目。收到的反馈非常积极。

现在决定更进一步,提供一个自动化这个过程的工具。这就是CPM(C Package Manager)。这是一个简单的工具,只做非常特定的工作:构建依赖于许多其他C/C++库的C/C++项目。它不做的事情包括但不限于:炸薯条、早上帮整理床铺……好吧,懂的。

使用这个工具,有两个部分:一是必须遵循一定模式的项目布局,二是使用一个小的JSON文件描述项目的依赖树。

例如,考虑两个库cool_A和cool_B,它们需要在应用程序super_App中使用。cool_A和cool_B都使用另一个库utils的代码。每个都有自己的Git仓库。

项目布局

必须遵循之前提到的提示中展示的原则。

规则1:所有项目都有自己的文件夹,所有项目文件夹都在一个父文件夹中。环境变量DEV_ROOT指向开发树的根。以下是一些ASCII艺术,展示了一般代码布局:

DevTreeRoot | +-- cool_A | | | +-- include | | | | | +-- cool_A | | | | | +-- hdr1.h | | | | | +-- hdr2.h | +-- src | | | | | +-- file1.cpp | | | | | +-- file2.cpp | +-- 项目文件(cool_A.vcxproj)和其他东西 | +-- cool_B | +-- include | | | +-- cool_B | | | +-- hdr1.h | | | +-- hdr4.h +-- src | | | +-- file1.cpp | | | +-- file2.cpp | +-- 项目文件(cool_B.vcxproj)和其他东西

规则2:需要对用户可见的包含文件放置在include文件夹的子文件夹中。子文件夹与库同名。如果cool_A的用户可以这样引用hdr1.h文件:

C++ #include < cool_A/hdr1.h >

这种组织的额外好处是,它防止了不同库之间的名称冲突。在这种情况下,如果一个程序同时使用cool_A和cool_B,相应的include指令将是:

C++ #include < cool_A/hdr1.h > #include < cool_B/hdr1.h >

规则3:依赖模块的包含文件夹通过符号链接变得可见。在之前展示的结构中,使用cool_A和cool_B的应用程序将有一个include文件夹,但在这个文件夹中,有指向cool_A和cool_B包含文件夹的符号链接。文件夹结构看起来像这样(尖括号表示符号链接):

DevTreeRoot | +-- SuperApp | | | +-- include | | | | | +-- | | | | | | | +-- hdr1.h | | | | | | | +-- hdr2.h | | | | | +-- | | | | | | | +-- hdr1.h | | | | | | | +-- hdr4.h | | other header files | | | +-- src | | | | | +-- source files | other files ...

规则4:所有库都位于开发树根的lib文件夹中。每个模块都包含一个指向这个文件夹的符号链接。不重复已经展示的文件布局部分,这里是与lib文件夹相关的一部分(再次,尖括号表示符号链接):

DevTreeRoot | +-- cool_A | | | ... | +-- | | | 所有链接库都在这里 +-- cool_B | | | ... | +-- | | | 所有链接库都在这里 +-- SuperApp | | | ... | +-- | | | 所有链接库都在这里 +-- lib | 所有链接库都在这里

如果有不同的链接库版本(调试、发布、32位、64位),它们可以作为lib文件夹的子文件夹来容纳。

依赖描述文件

为了描述项目之间的关系,每个项目使用一个名为CPM.JSON的文件。super_App文件夹中的CPM.JSON具有以下内容:

{ "name": "super_App", "git": "git@github.com:user/super_App.git", "depends": [ { "name": "cool_A", "git": "git@github.com:user/cool_A.git" }, { "name": "cool_B", "git": "git@github.com:user/cool_B.git" } ], "build": [ { "os": "windows", "command": "msbuild", "args": ["super_app.proj"] }, { "os": "linux", "command": "cmake" } ] }

对于每个依赖项目,都有一行描述依赖并给出Git仓库地址。构建部分指定了用于构建包的命令,针对每个操作系统。

类似地,cool_A文件夹中的描述如下:

{ "name": "cool_A", "git": "git@github.com:user/cool_A.git", "depends": [ { "name": "utils", "git": "git@github.com:user/utils.git" } ], "build": [ { "os": "windows", "command": "msbuild", "args": ["cool_a.proj"] }, { "os": "linux", "command": "cmake" } ] }

在cool_B中:

{ "name": "cool_B", "git": "git@github.com:user/cool_B.git", "depends": [ { "name": "utils", "git": "git@github.com:user/utils.git" } ], "build": [ { "os": "windows", "command": "msbuild", "args": ["cool_b.proj"] }, { "os": "linux", "command": "cmake" } ] }

最后,在utils中,没有依赖;只有构建规则:

{ "name": "utils", "git": "git@github.com:user/utils.git", "build": [ { "os": "windows", "command": "msbuild", "args": ["utils.proj"] }, { "os": "linux", "command": "cmake" } ] }

操作

一旦创建了依赖描述文件并设置了DEV_ROOT环境变量,只需要克隆最顶层的仓库(super_App)并使用如下命令调用CPM工具:

cpm -v super_app
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485