这篇专门面向 Portage。把仓库元数据、目标配置、已安装版本和 world 选择集分开,才能解释重编译与 depclean。命令以有权限维护 Gentoo 的终端为前提;不把切换 profile 或全量重编译当作常规修复。
同步仓库后,先预览变更
记录环境、同步仓库、阅读迁移通知
emerge --info
排错时保留完整输出,同时审阅可能包含的本机信息。维护操作需要相应权限。
emerge --sync
这一步不升级已安装软件;同步失败先修复仓库访问,再解释更新计划。
eselect news list
eselect news read 1
1 替换为实际编号;涉及 profile、Python 或工具链迁移时先完成文中前置步骤。
先审阅求解,再执行同一范围更新
emerge -pvuDN @world --verbose-conflicts
-p 只预览,-v 显示细节,-u 更新,-D 深入依赖,-N 检查新 USE;查看 blockers、slot、降级和重建原因。不要为清掉冲突提示直接删除依赖。
emerge -avuDN @world
-a 替代 -p,求解结果仍应复核;期间改过配置或同步仓库,就以新计划为准。完成后按维护节合并配置,验证关键服务,再考虑 depclean。
按问题选择包查询
equery list '*'
equery 由 app-portage/gentoolkit 提供;下面几条是独立查询,不是维护步骤链。
equery uses app-editors/neovim
equery depends app-editors/neovim
这是反向依赖查询,不是列出 neovim 自己需要的全部依赖。
equery files app-editors/neovim
equery 来自 gentoolkit,eix/qlist 是可选工具。预览关注新包、升级、降级、重装、slot 变化和 blockers;先读相关 news,再执行已审阅的 emerge -avuDN @world。--with-bdeps=y 是否保留构建依赖应与二进制包/源码维护策略一致。
USE、keyword、mask 各管一件事
| 配置 | 用途 | 注意 |
| package.use | 控制包的构建特性 | 先查 equery uses,不能给任意包套 gtk/qt 示例 |
| package.accept_keywords | 接受指定架构的测试版等 | 优先限包/版本,不全局 ** |
| package.mask / package.unmask | 限制版本候选/解除指定屏蔽 | unmask 不自动解决 keyword 或依赖冲突 |
两个独立文件的内容示例,按现有目录或单文件布局合并;不要将二者拼进同一个配置文件。修改前先用下方查询确认标记支持。
/etc/portage/package.use/editor
app-editors/vim python
/etc/portage/package.accept_keywords/editor
app-editors/neovim ~amd64
~amd64 只适用于相应架构的测试关键字;仅在确有需求时接受,不能把示例当成所有系统的默认设置。
修改包级配置前后各看一次
equery uses app-editors/vim
本页 python 是 Vim 的特性示例;先确认当前 ebuild 支持,再将上方配置片段合并到对应文件。
emerge -pv app-editors/vim
预览与期望不符时检查更具体的 package.use、profile 和 mask;不要直接叠加全局 USE。
按维护策略选择 USE 重建范围
emerge -pvuD --changed-use @world
侧重实际状态变化;这是下面 --newuse 策略的替代选项。
emerge -pvuDN @world
选择与系统维护策略一致的一种;执行时用 -a 代替 -p 并保留其余求解选项。
--newuse 与 --changed-use 的预览并不完全等价:newuse 还可能因新增或移除的未启用 flag 触发重装,changed-use 更关注实际启用状态变化。临时 USE=... emerge 不会保存到 package.use;autounmask-write 的结果也需要审阅合并。
不在 world 中,不代表可以删除
正在呈现知识画面
world 选择如何保留依赖取消显式选择不会卸载包;depclean 依据受保护集合的依赖可达性判断候选,被另一包依赖的共享库仍需保留。
{"id":"ops-portage-reachability","title":"world 选择如何保留依赖","summary":"取消显式选择不会卸载包;depclean 依据受保护集合的依赖可达性判断候选,被另一包依赖的共享库仍需保留。","height":600,"html":"<main><p class=\"quiet\" data-i18n=\"scope\"></p><div class=\"checks\"><label><input id=\"a\" type=\"checkbox\" checked><span data-i18n=\"a\"></span></label><label><input id=\"b\" type=\"checkbox\" checked><span data-i18n=\"b\"></span></label></div><div class=\"row code\">A → X\nB → X, Y</div><div class=\"pair\"><div class=\"row\" id=\"nodeA\"><strong>A</strong> · <span id=\"statusA\"></span></div><div class=\"row\" id=\"nodeB\"><strong>B</strong> · <span id=\"statusB\"></span></div><div class=\"row\" id=\"nodeX\"><strong>X</strong> · <span id=\"statusX\"></span></div><div class=\"row\" id=\"nodeY\"><strong>Y</strong> · <span id=\"statusY\"></span></div></div><div class=\"explain\"><div class=\"stable \"><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"hint\"></p><p id=\"message\" aria-live=\"polite\"></p></div></div></main>\n","css":"main {\n display: grid;\n gap: 18px;\n min-width: 0;\n}\np {\n overflow-wrap: anywhere;\n}\n.controls {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n align-items: center;\n}\n.presets {\n display: flex;\n flex-wrap: wrap;\n}\n.presets button {\n flex: 1 1 85px;\n font-size: 15px;\n}\n.cards {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 10px;\n}\n.card {\n border: 1px solid var(--rule);\n border-radius: 10px;\n padding: 14px;\n min-width: 0;\n background: var(--surface);\n}\n.card h3 {\n font-size: 15px;\n color: var(--muted);\n margin-bottom: 8px;\n}\n.card strong {\n font-size: 23px;\n font-weight: 500;\n overflow-wrap: anywhere;\n}\n.code {\n font-family: var(--viz-font-mono);\n font-size: 15px;\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n}\n.stable {\n display: grid;\n min-width: 0;\n}\n.stable > * {\n grid-area: 1/1;\n margin: 0;\n}\n.stable > .reserve {\n visibility: hidden;\n pointer-events: none;\n}\n.explain {\n border-left: 3px solid var(--accent);\n padding: 12px 14px;\n background: var(--accent-soft);\n}\n.arrow {\n text-align: center;\n color: var(--accent);\n font-size: 25px;\n}\n.row {\n padding: 12px;\n border: 1px solid var(--rule);\n border-radius: 8px;\n min-width: 0;\n}\n.row.on {\n border-color: var(--accent);\n background: var(--accent-soft);\n}\n.row.off {\n color: var(--muted);\n}\n.bar {\n height: 18px;\n background: var(--surface);\n border: 1px solid var(--rule);\n border-radius: 5px;\n overflow: hidden;\n margin-top: 8px;\n}\n.bar > i {\n display: block;\n height: 100%;\n background: var(--accent);\n width: 0;\n transition: width 0.22s;\n}\n.checks {\n display: grid;\n gap: 8px;\n}\n.checks label {\n display: flex;\n gap: 10px;\n align-items: center;\n padding: 9px;\n border: 1px solid var(--rule);\n border-radius: 7px;\n font-size: 16px;\n}\n.checks input {\n width: 18px;\n height: 18px;\n accent-color: var(--accent);\n flex-shrink: 0;\n}\nlabel.range {\n display: block;\n font-size: 16px;\n}\nlabel.range output {\n float: right;\n font-variant-numeric: tabular-nums;\n}\n.pair {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 10px;\n}\n.flow {\n display: grid;\n gap: 6px;\n}\n.quiet {\n font-size: 14px;\n color: var(--muted);\n}\n.badge {\n display: inline-block;\n padding: 2px 8px;\n border-radius: 5px;\n background: var(--accent-soft);\n color: var(--accent);\n}\nbutton {\n overflow-wrap: anywhere;\n}\n.large {\n font-size: 32px;\n color: var(--accent);\n font-weight: 500;\n}\n.layers {\n display: grid;\n gap: 12px;\n}\n.layer {\n border-left: 3px solid var(--rule);\n padding-left: 12px;\n}\n.layer.on {\n border-color: var(--accent);\n}\n@media (max-width: 520px) {\n .cards,\n .pair {\n grid-template-columns: 1fr;\n }\n .card {\n padding: 12px;\n }\n .card strong {\n font-size: 22px;\n }\n}\n.pair .row {\n min-height: 4.5em;\n}\n","js":"const $ = (s) => document.querySelector(s);\nconst $$ = (s) => [...document.querySelectorAll(s)];\nconst t = (k) => viz.t(k);\nconst put = (id, v) => ($(\"#\" + id).textContent = v);\nconst say = (k) => put(\"message\", t(k));\nconst state = (v) => ($(\"main\").dataset.state = JSON.stringify(v));\nconst press = (key, v) =>\n $$(\"[data-\" + key + \"]\").forEach((b) =>\n b.setAttribute(\"aria-pressed\", String(b.dataset[key] === v)),\n );\nfunction draw() {\n const a = $(\"#a\").checked,\n b = $(\"#b\").checked,\n retained = { A: a, B: b, X: a || b, Y: b };\n for (const [k, v] of Object.entries(retained)) {\n put(\"status\" + k, t(v ? \"keep\" : \"candidate\"));\n $(\"#node\" + k).classList.toggle(\"on\", v);\n }\n state({ a, b, retained });\n}\n$$(\"input\").forEach((e) => (e.onchange = draw));\nsay(\"hint\");\ndraw();\n","audio":false,"strings":{"a":"world 选择 A","b":"world 选择 B","candidate":"清理候选","hint":"点击取消 A 后,B 仍需要 X。图只计算计划,不执行卸载;所有包的安装状态保持不变。","keep":"需保留","scope":"简化依赖图:A→X、B→X/Y。四个包都已安装,仅显示 world 可达性;真实 Portage 还考虑 @system、slots、USE 等。"}}
先确认显式选择,再取消不再需要的目标
cat /var/lib/portage/world
这是 world 的用户包列表,不是整个 @world 依赖闭包;还可能有 world_sets、system/profile 集合。
emerge --pretend --depclean
emerge --ask --deselect app-editors/neovim
此步改变选择集但不卸载。若只是查看依赖,不执行本条;若取消错了可用 emerge --ask --noreplace app-editors/neovim 重新选择。
重新求解后执行清理
emerge --pretend --depclean
逐项核对服务、临时工具和构建依赖;仍被其他包需要的 neovim 不会仅因 deselect 就消失。
emerge --ask --depclean
先完成更新、解决依赖问题;不要用 --unmerge 强行实现期望列表。
emerge --ask @preserved-rebuild
根据 emerge 的提示执行,结束后检查关键程序与服务。它不是所有编译/运行故障的通用修复。
先完成更新并检查整份清理计划。deselect 只取消显式选择,包仍可能被其他包依赖;emerge -1 安装也不会加入 world。--unmerge 可绕过依赖保护,不用它代替 depclean。preserved-rebuild 处理保留旧共享库的消费者,不能代替所有故障排查。
配置合并、仓库与构建失败
配置更新:先找差异,再逐项合并
find /etc -name '._cfg*'
不足权限导致的错误不表示没有待更新文件。
dispatch-conf
逐项查看 diff、保留本机定制;退出后对受影响服务做语法校验,再按服务需求 reload/restart。不能默认全部采用新文件。
仓库与工具链只读确认
eselect repository list
此命令由相应 eselect repository 模块提供,启用不代表已同步。
emaint sync -a
与前文 emerge --sync 按实际 auto-sync 配置选择使用,不必连续重复。单个已启用仓库可指定 -r NAME。
eselect profile show
gcc-config -l
失败恢复:保留首个错误与求解计划
less /var/log/emerge.log
q 退出;详细编译错误在对应 PORTAGE_TMPDIR 下的包 temp/build.log,清理前先保存。
emerge --resume --pretend
确认确实是要恢复的任务,再用 emerge --resume --ask;不能用 skipfirst 掩盖必需包失败。若目标配置已改变,应重新求解所需更新。
| 问题 | 先保留的证据 | 处理方向 |
| 编译失败 | emerge --info; build.log | 保存该包 temp/build.log,定位第一处实际错误,不只截最后五行 |
| 配置文件更新 | ._cfg* | dispatch-conf 或 etc-update 审阅合并;两者均非默认自动接受 |
| 切换 profile / Python / GCC | eselect news; emerge --info | 遵循对应迁移文档;改变解释器命令不等于完成依赖迁移 |
启用 overlay 前审阅来源与包覆盖范围,再 eselect repository enable NAME、emaint sync -r NAME。MAKEOPTS 的单包并行与 emerge 的并行包数会叠加内存压力;按内存和实际构建观测设置,不机械等于 CPU 线程数。清理内核要保留已验证能启动的回退版本,见 内核手册。
Gentoo Portage · USE · emerge manual