通用教程:脚本化定制内核配置 & 用 sed 改造构建脚本
一、总体思路
vendor SDK 的内核通常由 build.sh 里的 gen_rt_kernel_image() 函数编译。 要"免交互"地定制内核,做两件事:
- 生成基线配置:make <board>_defconfig
- 直接改 .config,再 make olddefconfig 让依赖收敛
此外还要改造 build.sh 本身:修正已有命令、跳过交互式 menuconfig、 插入自动执行命令。这些都用 sed 完成。
二、sed 定位:地址
/pattern/ 只处理匹配该模式的行
/start/,/end/ 处理从 start 到 end 的范围
/funcname()/,/^}/ 函数体范围(函数名到第一个顶格 })
/范围/ { /pattern/ 命令 } 在范围内再按 pattern 过滤
本教程统一用函数体范围:
| |
含义:只动 gen_rt_kernel_image() 函数内部,不影响文件其它地方。
三、sed 常用动作
- 替换 s —— 改已有内容
| |
- 分隔符可替换:s|旧|新|、s#旧#新#。替换内容含 /(如路径)时, 换成 | 就不必转义。
- 捕获组:\(...\) 分组,\1、\2 引用;\0 表示整个匹配。
- :space: 匹配空白,\+ 表示一个或多个(GNU)。
例:修正 build.sh 里 publickey 的拷贝路径
| |
把 cp publickey 改成 cp kbuild/publickey。
- 行首插注释 —— 注释掉某行
| |
\1 是原缩进,\2 是整条 make 命令。结果:
make ... menuconfig -> # make ... menuconfig
注意:shell 的注释符是 #,不是 // 。用 // 会被当成命令去执行
(//指向根目录),可能报错、甚至在 set -e 下中断构建。
这个写法是幂等的:再跑一次时行首已是 # 而不是 make,不会匹配。
- 追加 a\ —— 在匹配行后加一行
| |
a\ 后面直接写要追加的内容,缩进要自己敲空格。 缺点:不同 sed 对 a\ 后空白的处理略有差异,缩进不够可控。
- 用 s + \n 插入并保留缩进(更稳)
| |
- \0 = 整个匹配行
- \1 = 行首缩进
- \n = 换行(GNU sed 支持)
结果:在匹配行之后插入一行,缩进自动对齐。推荐用这种,而不是 a\。
- 插入文件内容 r —— 把整段脚本嵌进 build.sh
| |
- r 文件名:把该文件内容插入到匹配行之后。
- 文件名要读到行尾,所以 "$BLK" 必须放行末,} 另起一行。
- 用 '"$BLK"' 拼接(单引号脚本 + 双引号变量),既让 shell 展开路径, 又不用给整段 sed 脚本转义。
四、预览 vs 落盘
sed '...' file 只输出到 stdout,不改文件(预览用) sed -i '...' file 原地修改 sed -i.bak '...' file 修改并生成 .bak 备份
预览定位某几行:
| |
注意:sed -e '…' 若不加 -i,跑完什么都不会变,这是最容易踩的坑。
五、defconfig 与 olddefconfig
| |
要点:
- defconfig 只跑一次。之后改 .config 即可;重新 defconfig 会重置, 需要重跑配置脚本。
- olddefconfig 会按依赖自动开启/丢弃配置项;依赖不满足的项会被静默 删掉(比如 USB_STORAGE=y 但 SCSI=n)。
- Kconfig 中一个符号的规范形态只有三种:=y、=m、# … is not set。 没有 CONFIG_X=n 这种写法。
六、非交互式配置 .config 的脚本模板
| |
三分支覆盖 .config 里两种现状(KEY=… / # KEY is not set), 幂等、可重复执行。
七、把脚本注入 build.sh(mktemp + heredoc + r)
| |
要点:
- mktemp 造临时文件,避免污染目录;用完 rm。
- 两层 heredoc 都要加引号('EOFF'、'EOF'),否则里层的 ${config}、 ${key} 会在注入时就被提前展开,脚本就废了。
- r 插入的内容是顶格的;shell 函数体不依赖缩进,功能没问题, 只是不美观。
- 这段只让 build.sh 生成 config-packages.sh,还要另加一行去执行它 (见第三节第 4 点)。
八、幂等守卫
补丁脚本可能被重复执行,注入类操作要加守卫,避免重复插入:
| |
- 首次:grep 找不到 -> 进入 if -> 两条注入都做。
- 再次:grep 命中(注入内容里就含 config-packages.sh)-> 整块跳过。
注意:守卫要包住"所有会重复的注入",只包一半仍会重复。
九、环境适配:ARCH / CROSS_COMPILE
defconfig 和 olddefconfig 都要按目标环境指定架构和工具链:
目标 ARCH CROSS_COMPILE ARM64 arm64 aarch64-linux-gnu- 或 aarch64-target-linux-gnu- ARM32 arm arm-linux-gnueabihf- x86_64(本机) x86_64 (留空) RISC-V 64 riscv riscv64-linux-gnu- MIPS mips mips-linux-gnu-
变量化写法(换环境只改两行):
| |
注意:olddefconfig 本身不编译,但某些平台解析依赖时仍需要 ARCH; CROSS_COMPILE 一起带上最保险,也方便和 build.sh 保持一致。
十、完整流程串联
| |
最终 build.sh 的 gen_rt_kernel_image() 内大致为:
make ... ascend310B_defconfig cat > config-packages.sh <<'EOF' <- r 注入 ... EOF # make ... menuconfig <- 已注释 bash config-packages.sh <- 插入的执行命令 make ... <- 真正编译
十一、常见坑
- sed -e 不加 -i 等于没改,只预览;真正生效要 -i。
- shell 注释符是 #,不是
//=;用 =//会被当命令执行。 - r 的文件名必须在行末,后面不能跟 }。
- heredoc 不加引号会导致变量提前展开,务必用 'EOF'。
- 单引号里的 ${VAR} 不展开;要展开就用 '"$VAR"' 拼接或改双引号。
- olddefconfig 会丢弃依赖不满足的项,别只 grep 目标项, 把依赖项一起 grep。
- 重复 defconfig 会覆盖已加的功能,需要重跑配置脚本。
- =m 只对 tristate 符号有效,bool 写 =m 会被强制改回 y/n。
- 注入类操作要加幂等守卫,且守卫要覆盖所有会重复的注入。
- 变量做参数时加引号:rm "$BLK"、grep ... "${DTS_FILE}"。