第一章:库的文档与发布——Doxygen文档生成、版本号规范、包管理、许可证选择

做库开发这么多年,我越来越觉得一个共识:代码只占一半,另一半是文档和发布。你写了一个再牛的库,别人看不懂、不会用、不知道怎么集成,那跟没写差不多。今天咱们就聊聊库的“最后一公里”——文档生成、版本号、包管理和许可证。

1.1 Doxygen:自动生成文档,省心省力

我刚开始写库的时候,文档全靠手写Word。后来项目大了,接口改了十几次,文档根本跟不上。直到我用了Doxygen,才明白什么叫“一次注释,到处生成”。

Doxygen能从代码注释里自动提取文档,生成HTML、PDF、LaTeX等格式。说白了,你只需要在头文件里写好注释,剩下的交给它。

注释怎么写?

我个人习惯用@brief@param这种风格。举个例子:

/**
 * @brief 计算两个整数的和
 * @param a 第一个加数
 * @param b 第二个加数
 * @return 两数之和
 *
 * 这个函数是线程安全的,内部没有静态变量。
 * 我在项目中遇到过有人传了负数进来,结果没做校验,导致溢出。
 * 所以建议调用方自己保证输入合法。
 */
int add(int a, int b);

嗯,这里要注意:注释要写“为什么”,而不是“是什么”。比如“返回两数之和”这种废话就别写了,看函数名就知道。要写的是边界条件、线程安全、性能注意事项。

Doxygen配置要点

生成文档前,你需要一个配置文件。运行doxygen -g会生成默认的Doxyfile。我一般改这几个关键项:

配置项 推荐值 说明
PROJECT_NAME "MyLib" 项目名称,会显示在文档标题
PROJECT_VERSION "1.2.3" 版本号,自动关联到文档
OUTPUT_DIRECTORY "./docs" 输出目录,我习惯放在项目根目录
EXTRACT_ALL YES 即使没有注释,也提取所有符号
RECURSIVE YES 递归搜索子目录
GENERATE_LATEX NO 一般用HTML就够了,PDF可以单独生成

配置好之后,运行doxygen Doxyfile,几秒钟后./docs/html/index.html就是你的文档首页。

小技巧: 我习惯在CI/CD里加一步Doxygen生成,每次提交代码自动更新文档。这样文档永远和代码同步,不会出现“文档说支持A,代码实际只支持B”的尴尬。

1.2 版本号规范:语义化版本,别乱来

版本号这东西,看着简单,但坑特别多。我记得有一次,一个依赖库从1.0.0升到1.0.1,结果我整个项目编译不过——因为它的API变了。这就是版本号没遵守规范。

现在业界公认的是语义化版本(SemVer),格式是主版本号.次版本号.修订号

  • 主版本号:做了不兼容的API修改。比如改了函数签名、删了接口。用户升级需要改代码。
  • 次版本号:新增了功能,但向后兼容。用户升级不需要改代码,但可能需要重新编译。
  • 修订号:只做了bug修复,不新增功能。用户升级完全无感。

举个例子:

  • 1.0.0 → 初始版本
  • 1.0.1 → 修了一个内存泄漏
  • 1.1.0 → 新增了sub()函数,旧API不变
  • 2.0.0 → 把add()的参数从int改成了long long
避坑指南: 我曾经在一个项目里看到版本号从1.0.0直接跳到2.0.0,结果只是改了个文档注释。这种“大跃进”会让下游开发者恐慌——他们以为API全变了。记住:版本号是给用户看的,不是给你自己看的

1.3 包管理:Conan vs vcpkg

以前C++的包管理是个老大难。没有统一的包管理器,大家要么手动下载源码,要么用git submodule。现在好了,有了Conan和vcpkg这两个主流工具。

Conan:跨平台,灵活

Conan是纯Python写的,支持几乎所有平台和编译器。它的核心概念是配方(recipe),一个conanfile.py文件描述了怎么下载、构建、打包你的库。

一个简单的conanfile.py长这样:

from conans import ConanFile

class MyLibConan(ConanFile):
    name = "mylib"
    version = "1.2.3"
    license = "MIT"
    author = "Your Name"
    url = "https://github.com/yourname/mylib"
    description = "A simple library for adding numbers"
    
    def source(self):
        self.run("git clone https://github.com/yourname/mylib.git")
    
    def build(self):
        cmake = CMake(self)
        cmake.configure()
        cmake.build()
    
    def package(self):
        self.copy("*.h", dst="include", src="include")
        self.copy("*.lib", dst="lib", keep_path=False)
        self.copy("*.dll", dst="bin", keep_path=False)
    
    def package_info(self):
        self.cpp_info.libs = ["mylib"]

用户只需要conan install .,就能自动下载依赖、编译、安装。我在团队里推广Conan后,新人上手时间从半天缩短到了半小时。

vcpkg:微软出品,Windows友好

vcpkg是微软开发的,对Windows和Visual Studio支持特别好。它的用法更简单:

# 安装库
vcpkg install mylib

# 集成到Visual Studio
vcpkg integrate install

vcpkg的优点是开箱即用,缺点是对自定义构建流程支持不如Conan灵活。我个人习惯:跨平台项目用Conan,Windows-only项目用vcpkg

核心建议: 不管用哪个,一定要把包管理配置文件(conanfile.py或vcpkg.json)纳入版本控制。这样团队所有人都用同一套依赖,不会出现“我机器上能编译,你机器上不行”的玄学问题。

1.4 许可证选择:别让你的库“裸奔”

许可证这东西,很多人觉得是法务的事,跟程序员无关。但我在GitHub上见过太多项目,代码写得很好,就是没有许可证。结果别人想用,又不敢用——谁知道你哪天会不会告我侵权?

常见的开源许可证就几种,我帮你理清楚:

许可证 特点 适合场景
MIT 最宽松,允许商用、修改、闭源,只需保留版权声明 你想让尽可能多的人用你的库
Apache 2.0 类似MIT,但增加了专利授权条款 大公司项目,或者涉及专利的库
GPL 3.0 强传染性,用了你的库,整个项目必须开源 你想强制别人也开源
LGPL 3.0 弱传染性,动态链接可以闭源,静态链接必须开源 你想保护库本身,但允许商业闭源使用
BSD 3-Clause 类似MIT,但禁止用作者名字做推广 你不想让别人打着你的旗号宣传

我个人建议:如果你不确定选哪个,就选MIT。它最简单,限制最少,社区接受度最高。我自己的开源库全部用MIT,省心。

避坑指南: 我曾经在一个项目里混用了GPL和MIT的库,结果律师说不行——GPL的传染性会覆盖MIT的部分。最后只能重写那个GPL的模块。所以,选许可证之前,先看看你的依赖用了什么许可证

知识体系总览

下面这张图帮你理清本章的核心脉络:

库的文档与发布 Doxygen文档生成 @brief / @param 注释风格 Doxyfile 配置要点 CI/CD 自动生成文档 版本号规范 语义化版本 SemVer 主版本·次版本·修订号 向后兼容性规则 包管理工具 Conan: 跨平台灵活 vcpkg: Windows友好 配置文件纳入版本控制 许可证选择 MIT / Apache / GPL / LGPL / BSD 检查依赖的许可证兼容性

这四个方面环环相扣。文档让用户知道怎么用,版本号让用户知道能不能用,包管理让用户知道怎么装,许可证让用户知道敢不敢用。缺一个,你的库就少了一半的价值。


公众号:蓝海资料掘金营,微信 deep3321