# eim **Repository Path**: AlEX_2473/eim ## Basic Information - **Project Name**: eim - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-23 - **Last Updated**: 2026-10-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # eim — ESP-IDF 安装与维护工具 用 Espressif Installation Manager(EIM CLI)在 Windows 上做可重复、可校验的 ESP-IDF 多版本安装。 把"实战踩到的坑"固化成代码逻辑,而不是留给使用者记忆。 --- ## 1. 使用 ```powershell # 按默认配置安装(v5.2 + v6.1 到 E:\Project\esp\.espressif) .\Install-EspIdf.ps1 # 只做健康检查,不修改任何文件 .\Install-EspIdf.ps1 -VerifyOnly # 强制重装指定版本到指定目录 .\Install-EspIdf.ps1 -IdfVersions v6.1 -InstallPath D:\esp\idf -Force # 卸载指定版本(注册项/源码树/venv/官方环境/constraints,Steam 式永久清除) .\Uninstall-EspIdf.ps1 -IdfVersions v4.4.8, v6.1 # 卸载并顺带清空下载缓存(默认保留缓存供重装复用) .\Uninstall-EspIdf.ps1 -IdfVersions v6.1 -RemoveDownloadCache # 加载会话环境变量(dot-source 才生效) . .\env.ps1 ``` > 必须在 **PowerShell** 中执行 `idf.py`。Git Bash 的 `MSYSTEM` 会让 `idf.py` 误判为 MSys 环境而拒绝运行。 > 退出码:`0` 成功;`1` 执行失败;`2` 校验未通过。 --- ## 2. 目录结构与职责 ``` Install-EspIdf.ps1 入口:参数 -> 配置 -> 编排四个步骤(不含业务细节) Uninstall-EspIdf.ps1 入口:参数 -> 配置 -> 编排卸载四步(快照/卸载/缓存/复核) env.ps1 环境入口:把配置解析为会话环境变量 config/ EimConfig.psd1 唯一默认配置(所有环境配置项都在这) EimConfig.user.psd1 本地覆盖(可选,git 忽略) lib/ Eim.Logging.psm1 日志门面:格式、级别、配色、步骤序号 Eim.Config.psm1 配置中心:加载、合并、校验、路径派生 Eim.Native.psm1 外部程序调用:stderr/EAP 适配、输出捕获 Eim.Host.psm1 宿主环境:编码、长路径、会话 TEMP、磁盘、临时残留 Eim.Git.psm1 git 定位、仓库探测、按长路径安全方式重建仓库 Eim.Idf.psm1 源码树健康度与可用性判定(纯读) Eim.PythonEnv.psm1 venv 就绪判定、v4.x 环境补丁调用 Eim.EimCli.psm1 EIM 命令参数构造(CLI 怪癖收敛在此) Eim.Registry.psm1 eim_idf.json 读写:登记版本解析、条目手术式移除 Eim.Uninstall.psm1 单版本卸载编排(eim remove + 残留清理 + 卸净复核) Eim.Preflight.psm1 前置检查编排 Eim.Verify.psm1 单版本校验与自动修复编排 Eim.Report.psm1 结果汇总与提示输出 patchs/ v4.x 兼容补丁(随仓库维护,不放进安装产物目录) ``` 依赖方向严格单向,不存在循环: ``` Install-EspIdf.ps1 / Uninstall-EspIdf.ps1 / env.ps1 │ ├─ Eim.Preflight ─ Eim.Git ─ Eim.Native ─ Eim.Logging │ └ Eim.Host ─ Eim.Logging ├─ Eim.Verify ─ Eim.Idf ─ Eim.Git / Eim.Host │ ├ Eim.PythonEnv ─ Eim.Native │ └ Eim.EimCli(纯参数,无依赖) ├─ Eim.Uninstall ─ Eim.Registry ─ Eim.Logging │ ├ Eim.EimCli / Eim.Native / Eim.Config │ └ Eim.Report ─ Eim.Logging └─ Eim.Config(纯数据,无依赖) ``` 约定: - **叶子模块**(`Eim.Logging`、`Eim.Config`、`Eim.EimCli`)不依赖其他模块,便于单独测试。 - 跨模块只调用对方 `Export-ModuleMember` 导出的函数,不触碰内部变量。 - 需要"当前 git 路径/日志路径"这类运行期状态时,通过**参数与返回值**传递,不使用全局变量。 - 只有 `Eim.Config` 读配置文件;需要路径的模块接收**已解析好的绝对路径**。 --- ## 3. 配置中心 所有环境配置项集中在 [`config/EimConfig.psd1`](config/EimConfig.psd1),脚本里不再出现字面量路径、镜像地址或阈值。 生效顺序(后者覆盖前者): | 层级 | 来源 | 说明 | | --- | --- | --- | | 1 | `config/EimConfig.psd1` | 默认值,必须存在 | | 2 | `config/EimConfig.user.psd1` | 本地/机器专属覆盖,可选,已被 `.gitignore` 忽略 | | 3 | `-SettingsPath ` | 按环境切换(CI、演示机等) | | 4 | 命令行参数 | 仅**显式传入**的参数参与覆盖 | 主要配置段: | 段 | 内容 | | --- | --- | | `Toolchain` | EIM 可执行文件名、CLI 版本 | | `EspIdf` | 默认版本、源码树布局、venv 布局、site-packages 判定阈值 | | `Mirrors` | PyPI / 工具归档 / IDF 仓库镜像 | | `Paths` | 安装根目录及各子目录(相对 InstallRoot 解析) | | `Install` | 目标芯片、`--cleanup`、是否跳过组件下载 | | `Thresholds` | 最小剩余空间、单版本预估占用 | | `Verification` | 自动修复、严格子模块、严格长路径等默认开关 | | `Git` | git 候选路径、长路径注册表位置、克隆深度/并发/HTTP 版本 | | `Logging` | 时间戳格式、日志文件名模板、%TEMP% 污染正则 | | `Backup` | 临时残留(%TEMP%)的备份目录与命名模板 | | `Uninstall` | 卸载行为:注册表快照名、constraints 文件名模板、卸载日志名 | | `Patches` | v4.x 兼容补丁路径 | 覆盖示例(`config/EimConfig.user.psd1`,只写需要改的键即可,深度合并): ```powershell @{ Paths = @{ InstallRoot = 'D:\esp\.espressif' } Mirrors = @{ PyPi = 'https://pypi.tuna.tsinghua.edu.cn/simple' } Install = @{ Cleanup = $false } } ``` > 注意:`-PypiMirror ''` 这类"显式传空"是合法覆盖(表示改用官方源), > 因此程序用 `$PSBoundParameters` 判断"是否指定",而不是判断值是否为空。 --- ## 4. 已固化的 EIM 行为约束 | 约束 | 落点 | | --- | --- | | `--cleanup` 必须带值(`--cleanup true`),裸开关解析失败 | `Eim.EimCli` | | 指定安装路径只能用 `-p`,本版不识别 `ESP_PATH` | `Eim.EimCli` | | `dist`/`tools` 默认落 `C:\Espressif`,必须显式指定 | `Eim.EimCli` | | `--esp-idf-json-path` 传**目录**而非文件 | `Eim.EimCli` / `Eim.Config` | | EIM 不复用非空目录,中断会留下残骸 | `Eim.Preflight` 提示 + `Eim.Verify` 重建 | | 内置 gix 在 `LongPathsEnabled=0` 时 checkout 失败却不中止 | `Eim.Host` 检测 + `Eim.Git` 用系统 git 重建 | | EIM 退出码 0 不代表源码树完整 | `Eim.Verify` 独立校验 | | v4.x 无法建 Python 环境(空串 `-r` / 复用错误 constraints),且 `eim fix` 对 v4.x 破坏性 | `Eim.PythonEnv` + `Eim.Verify` 一律重跑补丁 + `patchs/` | | `eim remove` 不认 `-n`,且只删源码树与注册项;venv / 官方环境 / constraints 需脚本兜底 | `Eim.Uninstall` | | 原生程序 stderr 在 `Stop` 下会被当作终止错误 | `Eim.Native` 局部降级 EAP | | `Remove-Item Env:X` 变量不存在时被安全钩子拦截 | `Eim.Host` 用 .NET API | --- ## 5. 删除机制:Steam 式永久删除 卸载与补丁中的删除统一参考 [Steam 式卸载](https://zhuanlan.zhihu.com/p/717461144): **删除只做元数据级摘除——不移动、不复制、不进回收站,瞬间完成且立即释放空间。** - 实现收敛在 `Eim.Uninstall` 的 `Remove-EimPathPermanently`(补丁脚本内为同名内联函数),三层递进: 1. `.NET` 直接删除(`Directory/File.Delete`,不经过任何 shell 包装); 2. `robocopy` 空目录镜像 `/MIR`(长路径、只读属性的兜底通道); 3. `.NET` 收尾删除镜像后剩下的空壳。 - 为什么不进回收站:卸载产物(源码树、venv、constraints、EIM 误建的 Python 环境)都是**可再生资产**, 重跑 `Install-EspIdf.ps1` 即可完整获取;软删除只会把"秽土转生"的残留留在盘上, 还会让"是否真的卸净"的复核失去意义。 - `eim_idf.json` 的快照写入 `logs\registry-snapshot-.json`,仅作诊断留档,不承担回滚职能; 回滚 = 重新安装。 - 目录联接(junction,v4.x 的 venv)只删链接本身,绝不下探目标目录。 --- ## 6. 行为等价性 重构保持行为不变,判定方式: 1. `-VerifyOnly` 前后各跑一次,逐行比对日志(仅归一化时间戳与会话标识); 2. 退出码一致(`0`/`1`/`2` 的语义未变); 3. 参数面保持兼容:原有参数名与含义不变,新增参数只有 `-SettingsPath`; 4. 默认值变化只发生在"未显式传参"时,且取到的仍是原先写死的同一个值。 > 唯一的输出差异:显式使用配置覆盖层时,会多打印一行 `配置来源: ...`,用于留痕。