SGDASS安装脚本解读:命令、配置与执行流程
SGDASS 提供了基于 Python 的自动化安装脚本(简称为“安装器”),能够按照配置文件中的软件包顺序,完成环境检查、配置、编译和安装,这极大地方便了用户。而对用户而言,理解脚本如何解释配置、选择软件包和处理错误,有助于调整安装环境,也能在构建失败时更快找到原因。 本文结合安装脚本源码与 Linux 示例配置,介绍安装器的主要用法和执行流程,并整理其中几处值得注意的问题。
本文所用 SGDASS 软件包的发布标识为 20260818,可在 sgdass_release_name.txt 中查看。安装脚本为 sgdass_install.py,其自身版本为:
sgdass_install 1.43 version of 2026.06.21
除 Python 标准库外,原版脚本还从 pet_misc.py 调用三个辅助函数:
| 函数 | 用途 |
|---|---|
exe() |
执行 shell 命令,等待结束后返回退出码和合并的输出 |
exe_pipe() |
执行 shell 命令,实时显示并收集输出,最后返回退出码 |
read_file() |
将普通或受支持的压缩文本读成字符串列表 |
安装器并不直接承担所有编译工作。其基本方式是:由 Python 读取配置并检查环境,再生成 C shell 脚本,由该脚本调用配置程序、编译器和 make 等工具。
因此,它主要负责四类工作:
- 读取配置文件中的安装描述;
- 检查目录、编译器和动态库环境;
- 生成按配置顺序执行的构建命令;
- 执行命令、记录日志并判断结果。
1 主要用法
该程序以控制文件中的软件包顺序和命令为依据,由 Python 完成解析和环境检查,再生成 C shell 脚本执行配置、编译和安装。
脚本承担四类工作:
- 读取安装描述;
- 检查目录、编译器和动态库环境;
- 生成按顺序执行的构建命令;
- 执行命令并处理日志。
1.1 命令行参数
调用形式为:
python3 /absolute/path/install/sgdass_install.py [选项] task package
| 参数 | 类型和默认值 | 实际作用 |
|---|---|---|
-c、--config |
文件路径;未给定时为 None |
指定安装配置文件 .cnf;正常执行任务时需要提供 |
-v、--verbosity |
整数,默认 1 |
控制诊断输出、子进程输出方式和部分命令重定向 |
-i、--inplace |
布尔开关,默认关闭 | 使用已有源码目录,跳过解包步骤;仍可能执行补丁、配置和构建命令 |
--version |
无值开关 | 显示安装器自身版本并退出 |
-h、--help |
argparse 自动提供 |
显示帮助并退出 |
task |
必填位置参数 | 选择操作 |
package |
必填位置参数 | 指定包名、all 或按配置顺序选择的一组包 |
--inplace 非常有用,特别是当我们对代码做出自己的修改时。它可以跳过重新解包,避免源码被压缩包中的文件覆盖。不过,它不会自动备份修改,也不会跳过补丁、配置或清理命令,这一点还是要留意。
1.2 task
| task | 生成构建脚本 | 自动执行构建脚本 | 具体行为 |
|---|---|---|---|
configure |
是 | 否 | 完成前置检查并生成脚本;脚本中包含后续构建命令,不只包含包的 configure 命令 |
build |
是 | 是 | 对选中的包解包、配置,并执行 build: 命令 |
build_after |
是 | 是 | 从指定包之后开始,排除该包 |
build_and_after |
是 | 是 | 从指定包开始,包含该包 |
rebuild |
是 | 是,独立分支 | 使用已有源码,加入清理和重新配置命令,再执行构建 |
rebuild_and_after |
是 | 是 | 从指定包起按 rebuild 方式处理后续包 |
postinstall |
是 | 是 | 执行 [PostInstall] 中匹配键对应的命令 |
uninstall |
生成函数仍被调用,但不写入脚本文件 | 否 | Python 直接调用 make uninstall,随后尝试删除匹配的源码构建目录 |
configure 不是无副作用的 dry-run。进入任务分支前,程序已经可能创建子目录,并编译、链接和运行一个测试程序。它还会创建或截断安装日志。另一方面,它不会自动执行刚生成脚本中的软件包构建命令。
rebuild 也不是简单的增量 make。当 options 非空时,生成器先安排有条件的 make clean 和 ./reconfigure,后续仍会进入通用配置逻辑,并再次安排 make clean。具体包能否支持这些操作,要看该包是否具有相应脚本和目标。
所以,这两个名字不能只按我们平时对 configure 和 rebuild 的理解来猜,还是要看看脚本实际做了什么。
1.3 package
| package | 实际选择范围 |
|---|---|
具体名称,例如 psolve |
匹配该名称的条目;如果有同名测试条目,也可能一并选中 |
all |
全部包条目,包括 [Tests] |
use_vtd |
从配置开头处理到第一个名为 vtd 的条目后停止 |
use_psolve |
从配置开头处理到第一个名为 psolve 的条目后停止 |
use_pima |
从配置开头处理到第一个名为 pima 的条目后停止 |
这些 use_ 选择器依据顺序截断,没有依赖求解功能。若配置把无关包放在目标前,它们也会被构建;若把必要依赖放在目标之后,它们不会自动补入。
build_after 等续建任务通常应配合具体包名。触发续建标记的逻辑跳过 mode == "tests" 的条目。postinstall 只按后处理键名或 all 匹配,不会把 use_psolve 解释为后处理包集合;uninstall 同样仅按具体包名或 all 选择。
以 pSolve 为例,假设我们已经进入安装脚本所在目录,配置文件路径也已经确认:
# 生成 pSolve 的构建脚本,但不执行该脚本。
python3 sgdass_install.py -c /path/to/example_sgdass_linux.cnf configure psolve
# 按配置顺序构建到 pSolve,包含前面的包。
python3 sgdass_install.py -c /path/to/example_sgdass_linux.cnf build use_psolve
# 依赖已经准备好时,只构建 pSolve。
python3 sgdass_install.py -c /path/to/example_sgdass_linux.cnf build psolve
# 修改源码后,跳过解包,并显示更详细的输出。
python3 sgdass_install.py -c /path/to/example_sgdass_linux.cnf -i -v 2 build psolve
这里的 /path/to/ 要换成自己的实际路径。最后一条也不是“只运行 make”,它仍会按照配置执行相应步骤。
1.4 配置文件
配置文件不需要从头写,官方提供了面向 Linux 和 macOS 的示例。例如 example_sgdass_linux.cnf 就是 Linux 使用的配置。
如果我们的目录布局和编译器路径已经按示例准备好,一般只需要少量修改,再把机构名称和缩写改成自己的即可。机构信息不改,未必马上报错,不过最好还是改一下!
但路径不能想当然。示例里的编译器在 /opt64/bin/ 下,源码包、构建目录和日志也都有指定位置。安装前要核对这些路径,以及配置中的版本号是否与手头的源码包一致。这里不建议等到报错后再改,但也完全不需要重新写一份配置。
配置文件首行必须精确等于下列格式标识,空格也参与比较:
# sgdass_config Version 1.4 of 2021.12.13
安装脚本会检查这一点,因此不要改动此处。这里的 1.4 是配置格式版本,也不要和前面的安装脚本版本混淆。
配置文件包含以下几部分:
| 配置段 | 主要字段 | 作用 |
|---|---|---|
[Directories] |
dir: |
要求已经存在的目录及其权限 |
[SubDirectories] |
subdir: |
可以由程序创建的子目录 |
[Compilers] |
gcc、gcx、gfortran、cla、clap、cmake |
编译和配置工具;其中 gcx 指 C++ 编译器 |
[Where] |
tarball、build、build_aux、prefix、sdk、xcode |
源码归档、构建位置、安装前缀和平台工具位置 |
[Misc] |
num_proc、机构信息、日志路径 |
并行度、机构信息与日志 |
[Tests] |
包描述字段 | 以软件包形式描述测试,默认在 build 下处理 |
[AuxPackages] |
包描述字段 | 第三方包,默认在 build_aux 下处理 |
[Packages] |
包描述字段 | 主软件包,默认在 build 下处理 |
[PostInstall] |
包名: 命令 |
安装后初始化;同一个键可对应多条命令 |
解析器支持重复出现包段。[AuxPackages] 和 [Packages] 最终都记录为 mode="packages",区别主要保留在构建目录中。
软件包条目的字段如下:
| 字段 | 作用 |
|---|---|
package:、version: |
软件包名称和版本,用于匹配归档及默认源码目录 |
build_aux_dir: |
值为 yes 时,生成脚本阶段将该包构建目录切换到 build_aux |
pre_unpack: |
解包前执行的命令 |
post_unpack: |
解包后执行的命令,可设置特殊源码根目录 |
patch: |
位于 tarball 目录中的补丁名称;允许 ${vers} 替换 |
pre_config: |
通用配置逻辑之前执行的命令,rebuild 也会使用 |
with_config: |
当 options 非空时,在配置命令前执行 |
options: |
传给自动选择的配置脚本;特殊值 noconfigure 表示跳过自动配置命令 |
post_config: |
配置逻辑之后执行的命令 |
build: |
顺序执行的构建命令,例如清理、编译、安装 |
2 代码分析
源码包含一个类和五个顶层函数:
| 类或函数 | 主要作用 |
|---|---|
cnf_class |
保存配置和安装过程中使用的状态 |
parse_control_file() |
解析配置文件,并进行部分检查和准备 |
check_dirs() |
检查、准备目录及其权限 |
gen_control_file() |
生成 C shell 构建脚本的内容 |
check_proc_env() |
通过编译和运行 C 程序及动态库检查环境 |
main() |
解析命令行参数,组织整个安装流程 |
main() 中的主要顺序是下面这样。这里省略了参数和错误处理,只看调用顺序:
cnf = cnf_class(args.config_file)
ret, cnf = parse_control_file(...)
ret = check_dirs(...)
ret = check_proc_env(...)
# 读取 SGDASS 发布信息。
# ...
ret, cnt = gen_control_file(...)
# 写出生成的脚本,再根据 task 执行相应操作。
# ...
这里两个变量会反复出现:
cnf:保存配置及相关状态的对象。cnt:生成的脚本内容,是一个字符串列表,每个元素通常对应一行。
ret 是状态码。不过,原版的错误处理没有完全统一,有些地方返回状态码,有些地方直接退出程序。
安装脚本的主流程并不复杂:
- 解析命令行参数,检查启动条件。
- 读取配置文件,检查目录、编译工具和源码包。
- 编译并运行测试程序,检查动态库加载环境。
- 读取发布信息,生成 C shell 脚本。
- 根据任务决定后续操作:configure 只保存脚本;构建和后处理任务保存并执行脚本;uninstall 则直接调用卸载和源码清理命令。 需要注意,环境检查发生在任务分流之前,所以 postinstall 和 uninstall 也绕不过这套检查。
另外,[Tests] 中的软件包测试与 check_proc_env() 不是一回事。前者是配置中的条目,被选入并执行构建脚本后才运行;后者是任务分流之前直接进行的 C 编译与动态库加载测试。通过后者,并不意味着 Fortran、C++ 和所有软件包依赖都已经检查完了。
2.1 安装报错时,该怎么办?
安装出错时,可以先看终端给出的日志路径,再检查相应文件来追溯错误。示例配置里的两个日志是:
install_log /logs/sgdass/sgdass_@DATE@_install.log
build_log /logs/sgdass/sgdass_@DATE@_build.log
@DATE@ 会被替换成运行时的时间戳,例如 20261005_205100。路径来自配置文件,不是写死的。
| 文件 | 主要看什么 |
|---|---|
install_log |
安装进度和安装器捕获的输出 |
build_log |
环境信息、配置副本,以及重定向到此处的配置和编译输出 |
临时 .csh 脚本 |
安装器究竟生成了哪些命令 |
C shell 脚本的路径由下面这句确定:
cnf.control_file = "/tmp/sgdass_install_" + date_string + ".csh"
不过,成功安装后不一定还能找到它。原版普通 build 成功后会删除脚本;rebuild 成功后的保留情况与输出级别有关。configure 生成的脚本则会保留,可以先用它检查将要执行的命令——但别忘了,configure 自己也会做前置检查。
默认 -v 1 时,许多命令的详细输出会写入 build_log;-v 2 时,更多输出会直接显示出来。因此,排查错误最好结合终端和两个日志来看,不要只盯着一个文件。
还有个容易忽略的地方:日志目录在解析配置时就会被检查,比创建子目录的步骤还早。所以 /logs/sgdass 需要预先存在,不能只在 [SubDirectories] 中写一条就等着脚本创建。
2.2 一点读代码的感受
不过,读下来我感觉,这份 Python 脚本的排版似乎带着一些 Fortran 的影子:大量手工对齐、显式换行,还有不少重复代码。至少从现在常见的 Python 写法来看,不算很 Pythonic。 因此,大家可以尝试自己优化,或者借助生成式 AI 的帮助。但最好先做小的整理,保留原有流程,别一下子把安装逻辑全改了。改完之后,也要对照原版生成的命令,不能只看代码变整齐了,就觉得一定没有问题。
3 配置文件中的小问题
检查这份配置时,还能发现几处小问题。以下行号对应本文所用的文件,今后的版本可能不同。
| 位置 | 当前写法 | 问题及建议 |
|---|---|---|
pkg-config,第 116 行 |
CXX=${gcc} |
C++ 编译器变量用了 C 编译器,应改为 CXX=${gcx} |
libpng,第 390 行 |
CPPLAGS="..." |
拼写错误,应为 CPPFLAGS="..." |
libtirpc,第 405 行 |
build: clean |
会尝试执行名为 clean 的程序,通常应为 build: make clean |
vsdc,第 848 行 |
mkdir 775 /cont/vsdc |
缺少 -m;775 会被当作目录名,而不是权限 |
其中,build: clean 不会被安装器自动补成 make clean,它就是一条 clean 命令。mkdir 775 /cont/vsdc 则会在执行时尝试创建两个目录:当前目录下的 775 和 /cont/vsdc。
这些问题不一定会让编译立即失败。有些设置可能恰好没有用到,有些问题可能被其他配置掩盖,还有些命令失败后,脚本仍然继续往下执行。 顺带一提,安装脚本本身也有两处需要留意:
- 普通构建分支检测到失败后,可能打印失败信息却调用
exit(0),因此不能只看退出码判断安装是否成功。 - 卸载时,没有先检查
make uninstall是否成功,就继续尝试删除源码目录。
而这些问题,并不影响最终的编译结果,我不确定这是坏消息还是好消息。