管理C++的第三方库以及编译

第三方库这个说法,不知道出自哪里,但一般是指开发者,系统/平台提供商之外的第三个参与者提供的程序库。
大多数开源软件库在软件系统中都是第三方库。

完全不使用库的开发,在90年代就已经被放弃了,特别MFC/OWL/QT这些先行者。开源运动的兴起使得第三方库
成为主力使用库。

C++领域有一些非常特殊的库,比如早期的STLport和当前的Boost,它们就像是语言的事实标准,基本在每个程序
中都可以见到他们的身影。

但第三方库也会导致相当多的问题,主要总是包括:

  1. 版本不一致。同一个软件系统中,如果引用了同一个第三方库的两个不同版本,那一定会暗生问题。
  2. 编译选项不一致。一般为了减少编译时间,第三方库都会以编译后的.a/.lib形式参与软件编译,第三方库的编译选项与软件系统的编译选项不同,也是会有一些潜在的问题。特别是x86/x64,ansi/utf-8这些选项不同,根本就不能用。
  3. 版本管理库变大。在一些大项目中常常会有几个G的版本库,每次clone代价很大。其实很多都是第三方库,不同版本,不同编译选项生成的库引入。

除了把编译的库引入项目,也有人以源代码的形式引入开源软件,比如GCC、SDL、WxWights、QT。如果第三方库本身编译不复杂,原代码也很简单,这么做比较好。

但是像Boost.Thread这样的库,就不行了(它需要编译成动态库,静态引用会有问题)。

另一方面,如果第三方库升级,就是一个比较复杂的工程,如果不升级,又只能看着第三方库的问题得不到解决。

在Linux上这个问题并不严重,系统级的软件管理工具可以代管大多数的第三方。比如debian系的,可以使用apt得到大多数的开源库,同时如果需要最新版本,也可以通过第三方源来取得。

在交叉编译和window平台上这个问题就非常头痛了。我们需要从几个层次来解决这个问题。

  1. 需要有中心化的第三方源代码获取平台,这个平台需要支持按用户/组织+第三方库+版本的形式取得源代码,同时还需要保证及时跟踪来源。这个类似于bintray/github都可以。公司内部可以使用gitlab来搭建。
  2. 需要有一个构建脚本平台,存放在不同工具链和平台的情况下,这个脚本可以从源代码中心的源代码,把源代码编译成库。同样可以用github这类工具搭建和管理。
  3. 在项目中提供一个配置文件,需要的开源库(只需指定编译脚本,脚本是针对开源库)。这个只需要一个文本文件即可。
  4. 在开发者的机器和编译服务器上,下载编译脚本,生成库。源代码、最终结果可以缓存在本机上。可以使用项目原本的构建工具。

如果只考虑windows+vc这个工具链,最简单的工具是使用msbuild,即为第三方库构建工程,编译结果生成成为vcprops文件,由项目引用。在项目工程中,可以通过prebuild过程加上下载、编译、安装第三方库的过程,因为有缓存,每台机器上应该只需要做一次。

考虑Boost这个最常用的库,并没有一个msbuild可以调用工程,这个方案通用性很差。但对于只使用vc的公司还是非常实用。因为只需要一个内源的源代码管理工具就可以了,没有git甚至可以用svn。

如果有多平台交叉编译,要考虑先统一项目自己的编译工具,使用CMake/Scons/Gyp这些跨平台编译工具,否则需要在不同的编译工具下都做一套。

以CMake为例,最接近这个系统的是CPM这个工具,它用github做前两个服务器,CMake的脚本来完成下载,编译。但受CMake脚本的能力限制,它无法完成更复杂的版本管理工作,只能做为一个试验原型。

使用CPM,可以先把自己的项目改成使用CMake编译,在CMakeLists.txt中引入CPM语句,再引入需要的第三方库。如果第三方库已经在有一个CPM脚本工程,就很简单直接引用即可。但如果没有,就需要自己先建立CPM脚本工程。即一个CMakeLists.txt,可以下载编译第三方库,再提供CPM一些专用宏,以便后续项目能找到这个第三方库。

一个内源第三方库(指公司内共享库)需要两个git工程(或svn目录),一个提供原始代码和维护工作,另一个提供构建脚本,这个脚本甚至可以是专用于某个项目。最后需一个工具能下载,脚本调用,缓存管理这些工作。

基于C++社区不自己发明工具传统,以及cmake/git的流行程度,我考虑使用bash来完成这个部分。基本数据结构设计如下:

CPM_HOME 做为根目录
sources 源代码下载
hosts 下载服务器
groups 用户或组织
library-name 库
scripts
hosts 下载服务器
groups 用户或组织
library-name 库, 源代码构建完成后会安装到这个目录下
bin 二进制结果,包括可执行文件
include 头文件,一般应支持多配置
lib/xx/ 应对不同的配置,参考boost library
flags/xx/ 应对不同的配置,需导出一些编译选项
build
构建目录,对于不支持外部构建目录的开源软件,可以选择复制过来in-source构建
考虑并发构建,应在这里文件锁定操作。
除了锁定目录外,其它目录可以删除以减少空间占用。

repos
project-id 成生特定于项目第三方库包,这样项目只需要引入一个第三方目录。

以上目录都会在本地保存一个hash文件以保持完整和不变性。

类似python我们使用 requirements.txt 项目的需求第三方库,使用cpp_setup requirements.txt来完成预建编译环境。
requirements使用
[host:port/]username/cpm-library-name
全局配置中,可以默认的host:port。这里可以不写host:port。
这里是指向特定的构建脚本,脚本能支持哪些平台,哪些发布版本,哪些运行时库都由脚本自己决定。缺少项目需要的配置时,只会编译失败。

如果项目支持CMake,那么只需要引入CPM即可。

如果项目可以要求生成一个project-id目录来聚合第三方库,方便make/vc++这些工具集成。

最新文章

  1. Python Day4
  2. iOS中Block使用探索
  3. URAL 1777 D - Anindilyakwa 暴力
  4. hdoj 1728 逃离迷宫
  5. hdu 5092 Seam Carving
  6. python判断两个list包含关系
  7. 什么是REST接口
  8. iOS学习——(转)多线程
  9. 图像识别基本算法之SURF
  10. springBoot 随笔(一)
  11. IDEA与Eclipse
  12. for 循环 以及基本的数据类型
  13. 求树的重心 poj 1655
  14. Android:使用shape制作素材
  15. java-学习6
  16. vue实现按需加载(懒加载)
  17. python-day21--序列化模块模块
  18. mybatise 实现同一字段多模糊查询
  19. winform中webBrowser模拟网页操作中遇到的问题
  20. win32 右键弹出菜单

热门文章

  1. 关于OPC自动化接口编程(OPCDAAuto.dll)几点注意问题
  2. 读取zip文件,不解压缩直接解析,支持文件名中文,解决内容乱码
  3. java图形用户界面BorderLayout布局。冲突
  4. if-else 循环嵌套结构
  5. Velodyne线性激光雷达pcap文件格式及写入、数据解析 Lebal:激光雷达
  6. Mysql参见SHOW命令总结
  7. 缓存 memcached 与 redis
  8. WebSocket实战之JavaScript例子
  9. 阿里云ECS centos7 支持IPv6
  10. mysql int类型范围