第一章:库的文档与发布——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就是你的文档首页。
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.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。
1.4 许可证选择:别让你的库“裸奔”
许可证这东西,很多人觉得是法务的事,跟程序员无关。但我在GitHub上见过太多项目,代码写得很好,就是没有许可证。结果别人想用,又不敢用——谁知道你哪天会不会告我侵权?
常见的开源许可证就几种,我帮你理清楚:
| 许可证 | 特点 | 适合场景 |
|---|---|---|
| MIT | 最宽松,允许商用、修改、闭源,只需保留版权声明 | 你想让尽可能多的人用你的库 |
| Apache 2.0 | 类似MIT,但增加了专利授权条款 | 大公司项目,或者涉及专利的库 |
| GPL 3.0 | 强传染性,用了你的库,整个项目必须开源 | 你想强制别人也开源 |
| LGPL 3.0 | 弱传染性,动态链接可以闭源,静态链接必须开源 | 你想保护库本身,但允许商业闭源使用 |
| BSD 3-Clause | 类似MIT,但禁止用作者名字做推广 | 你不想让别人打着你的旗号宣传 |
我个人建议:如果你不确定选哪个,就选MIT。它最简单,限制最少,社区接受度最高。我自己的开源库全部用MIT,省心。
知识体系总览
下面这张图帮你理清本章的核心脉络:
这四个方面环环相扣。文档让用户知道怎么用,版本号让用户知道能不能用,包管理让用户知道怎么装,许可证让用户知道敢不敢用。缺一个,你的库就少了一半的价值。
公众号:蓝海资料掘金营,微信 deep3321