SGDASS安装脚本解读:命令、配置与执行流程

· 预计阅读 14 分钟

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 是状态码。不过,原版的错误处理没有完全统一,有些地方返回状态码,有些地方直接退出程序。

安装脚本的主流程并不复杂:

  1. 解析命令行参数,检查启动条件。
  2. 读取配置文件,检查目录、编译工具和源码包。
  3. 编译并运行测试程序,检查动态库加载环境。
  4. 读取发布信息,生成 C shell 脚本。
  5. 根据任务决定后续操作: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 是否成功,就继续尝试删除源码目录。

而这些问题,并不影响最终的编译结果,我不确定这是坏消息还是好消息。