2. 开发环境搭建:操作系统选择、编译器安装、依赖库编译

做国密SDK的持续集成验证,第一步就是搭环境。环境搭不好,后面全是坑。我个人习惯把操作系统、编译器、依赖库这三件事先理清楚,再动手。

说白了,环境搭建就像盖房子的地基。你想想看,地基歪了,上面墙再直也没用。我早年碰到过一个项目,开发人员在Windows上用MSVC编译本地通过,放到Linux CI服务器上用GCC就报链接错误,最后查了三天——原来是不同编译器对结构体对齐的默认规则不一样。

2.1 操作系统选哪个?

国密SDK的验证场景主要分为两类:服务器端和客户端。服务器端基本是Linux,客户端可能是Windows或macOS。我建议按以下维度选:

操作系统 适用场景 推荐版本 注意点
Linux (Ubuntu/Debian) 服务器CI、交叉编译、生产验证 Ubuntu 20.04/22.04 LTS
Debian 11/12
apt包管理方便,GCC默认支持
Windows 桌面客户端SDK集成、MSVC测试 Windows 10/11 + Visual Studio 2022 需额外安装Git、CMake、Perl
macOS 开发者本机调试、Clang测试 macOS Ventura / Sonoma Homebrew管理依赖,注意Clang版本
我个人的建议:如果预算允许,CI环境统一用Ubuntu 22.04 LTS。依赖新、兼容性好,而且GmSSL官方CI也是跑在这上面。你也省得处理Windows路径和换行符的问题。

嗯,这里还有一个我没明说的点:如果你需要交叉编译到国产平台(如飞腾、鲲鹏、龙芯),Ubuntu的交叉工具链最方便。我上次为一个信创项目搭环境,apt install一下就搞定ARM64交叉编译器,比在Windows上折腾MinGW省心多了。

2.2 编译器:GCC、Clang、MSVC怎么装?

编译器是SDK编译的核心工具。GmSSL和OpenSSL都用C语言写的,对编译器要求不高——但细节会坑人。我这里把三种常见的编译器安装方法列出来。

2.2.1 GCC (Linux首选)

GCC是Linux上的默认编译器。用apt安装最简单:

# Ubuntu/Debian
sudo apt update
sudo apt install build-essential gcc g++ gdb cmake

验证版本:

gcc --version
# 输出类似:gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
我曾遇到的一个坑:GCC 10以下版本对C11原子操作支持不完整,GmSSL 3.x的SM2多线程加密可能会跑出奇怪的结果。我建议至少装GCC 9+。如果你用Ubuntu 18.04,记得加PPA源升级。

2.2.2 Clang (跨平台/ macOS常用)

Clang的编译错误信息比GCC友好,尤其适合开发阶段。在Linux上装:

sudo apt install clang-14 lld-14

macOS上Xcode自带Clang,但版本可能偏老。我习惯用Homebrew更新:

brew install llvm
export PATH="/usr/local/opt/llvm/bin:$PATH"

2.2.3 MSVC (Windows)

Windows下的编译器是MSVC,集成在Visual Studio里。我推荐用 Visual Studio 2022 Community(免费)。安装时勾选“使用C++的桌面开发”工作负载,然后打开“开发者命令提示符”即可使用 cl.exe。

为了在CI脚本中调用,你还需要设置环境变量:

call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"
关键点:MSVC和GCC对预处理宏有差异。比如 inline 在MSVC中默认是C89标准,需要定义 __STDC__ 或加 /std:c11 开关。我写GmSSL的CMakeLists.txt时,专门加了一段判断:
if(MSVC) target_compile_options(my_target PRIVATE /std:c11 /utf-8) endif()

2.3 依赖库编译:OpenSSL 和 GmSSL

好了,操作系统和编译器就绪后,该编译依赖库了。为什么强调“编译”,而不是直接用包管理器装?因为持续集成环境往往需要静态链接、定制编译选项(比如禁用某些算法、启用国密支持),打包好的二进制不一定满足你的需求。

我习惯把这个流程画成一张图,你一看就明白:

依赖库编译核心流程 获取源码 git clone / 下载tarball 配置 ./configure / CMake 编译 make / cmake --build 安装 make install OpenSSL 1.1.1 / 3.x 需开启 enable-ec enable-sm2 (国密扩展) GmSSL 3.x 默认包含SM2/SM3/SM4 无需额外配置 注意: 1. 编译时最好指定安装路径(--prefix),不要污染系统目录 2. 静态库(.a)和动态库(.so/.dll)可以一起编译,CI验证时静态更稳妥

2.3.1 编译OpenSSL(带国密支持)

OpenSSL 1.1.1开始原生支持SM2/SM3/SM4曲线,但需要编译时显式开启。我一般这么干:

wget https://www.openssl.org/source/openssl-3.0.12.tar.gz
tar xzf openssl-3.0.12.tar.gz
cd openssl-3.0.12
./Configure linux-x86_64 \
  --prefix=/opt/openssl-gm \
  --openssldir=/opt/openssl-gm \
  enable-ec enable-sm2 enable-sm3 enable-sm4 \
  no-afalgeng \
  -fPIC
make -j$(nproc)
make install
我曾经在这里翻过车:如果系统之前装了OpenSSL 1.0.x,编译GmSSL时链接器可能会找到旧版库。解决方案:在CMakeLists中强制指定 find_package(OpenSSL 3.0 REQUIRED PATHS /opt/openssl-gm),或者临时设置 LD_LIBRARY_PATH。

2.3.2 编译GmSSL(国密原生库)

GmSSL 3.x 的作者已经帮你把国密算法内置好了,不需要额外开关。它支持CMake和Configure两种方式。我个人习惯用CMake,因为方便在CI中控制安装路径:

git clone https://github.com/guanzhi/GmSSL.git
cd GmSSL
mkdir build && cd build
cmake .. \
  -DCMAKE_INSTALL_PREFIX=/opt/gmssl \
  -DCMAKE_BUILD_TYPE=Release \
  -DBUILD_SHARED_LIBS=ON \
  -DBUILD_TESTING=ON
cmake --build . -j$(nproc)
cmake --install .

编译完成后,验证一下:

/opt/gmssl/bin/gmssl version
# 输出:GmSSL 3.2.0 - OpenSSL 3.0.12 兼容接口

2.3.3 集成到CI脚本

在持续集成中,我通常把编译脚本写成独立文件,比如 build_deps.sh,内容如下:

#!/bin/bash
set -euo pipefail
DEPS_DIR="$PWD/deps"
mkdir -p "$DEPS_DIR"

# 编译OpenSSL
cd "$DEPS_DIR"
if [ ! -d "openssl-3.0.12" ]; then
  tar xzf ../openssl-3.0.12.tar.gz
  cd openssl-3.0.12
  ./Configure linux-x86_64 --prefix="$DEPS_DIR/openssl" --openssldir="$DEPS_DIR/openssl" \
    enable-ec enable-sm2 enable-sm3 enable-sm4 -fPIC
  make -j$(nproc) && make install
fi

# 编译GmSSL
cd "$DEPS_DIR"
if [ ! -d "GmSSL" ]; then
  git clone --depth 1 https://github.com/guanzhi/GmSSL.git
  cd GmSSL && mkdir -p build && cd build
  cmake .. -DCMAKE_INSTALL_PREFIX="$DEPS_DIR/gmssl" -DCMAKE_BUILD_TYPE=Release
  cmake --build . -j$(nproc) && cmake --install .
fi

# 设置环境变量
export PATH="$DEPS_DIR/openssl/bin:$DEPS_DIR/gmssl/bin:$PATH"
export LD_LIBRARY_PATH="$DEPS_DIR/openssl/lib:$DEPS_DIR/gmssl/lib"
echo "Depencencies built at $DEPS_DIR"
小窍门:CI环境中多使用 set -euo pipefail,任何一步失败立刻停止,避免“半残”的依赖库被后续步骤使用。我之前吃过亏,make失败但make install继续执行,结果装了个残缺的libcrypto.so,API调用直接段错误。

2.4 本章小结

开发环境搭建看似枯燥,但你只要把操作系统、编译器、依赖编译这三步走顺,后面所有验证工作就有了统一的基础。

我再说一句实在的:环境平台尽早统一,宁可在Ubuntu 22.04上花一下午把脚本打磨好,也别到项目后期再回头补环境。那滋味,谁试谁知道。