posts/env-migration-20260915.md

换机不翻车:嵌入式开发环境整机迁移复盘

📅 发布日期2026-09-15
工具与效率 复盘 19 分钟阅读 #工具链#PlatformIO#ESP32#VMware#Ubuntu

换机不翻车:嵌入式开发环境整机迁移复盘

换新电脑,最怕的不是装软件,而是「装好了却跑不起来」。这次把整套嵌入式开发环境从旧机器搬到新机器:PlatformIO 工具链、ESP32 工程、VMware 虚拟机、Ubuntu 开发机,一路撞上了四个隐藏得很深的坑。这篇文章把每个坑的现象、根因和解法都记下来——下次再搬家,照着做就是一条直线。

#一、背景与任务清单

旧机器留了一份打包目录(约 20GB),包含 PlatformIO 全套工具链(8.7GB)、ESP32 工程(397 个文件)、VMware 虚拟机(31.5GB 磁盘镜像)和各类文档。目标是在新机器上把它们全部恢复到「能用」状态,而不是「看起来装好了」。

任务验收标准
PlatformIO 工具链恢复pio run 全量编译通过并产出固件
ESP32 工程部署RAM / Flash 占用正常打印,无报错
VMware Workstation 安装静默安装 + 数字签名验证通过
Ubuntu 虚拟机迁移开机进桌面 + 宿主机 SSH 免密连通

#二、工具链复活:8.7GB PlatformIO

直接把 .platformio 目录拷过来是不够的,有三件事必须做:

① 内核版本不够。拷贝来的平台包要求 Core ≥ 6.1.19,而目录里的 Core 是 6.1.18。解法:用 pip 原地升级 PlatformIO 到 6.2.0,版本对齐后平台包校验直接通过。

② aux 保留名文件。Linux/WSL 侧源码树里有 aux.c、aux.h 这类文件——aux 是 Windows 保留设备名,资源管理器和大多数解压工具根本无法创建它们。解法:用 Git Bash 的 tar 解包(MSYS2 层会正确处理保留名),文件原样落盘。

③ GitHub 直连超时。PIO 要补装一个新版工具链包(xtensa-esp-elf 14.2.0),默认直连 GitHub 必然超时。解法:让 pio 走本机代理客户端的 socks 端口。

powershell# Core 升级
python -m pip install -U platformio

# 编译走本机代理(10808 是代理客户端的 socks 端口)
$env:HTTP_PROXY  = 'socks5h://127.0.0.1:10808'
$env:HTTPS_PROXY = 'socks5h://127.0.0.1:10808'
pio run

#三、第一坑:链接器不认中文路径

28 分钟的全量编译走完,所有 .c 文件编译、静态库归档全部成功,最后一步 Linking 突然报错:

textld.exe: cannot open map file D:/嵌入式开发/ESP32/esp32-demo/build/.../firmware.map: Invalid argument
collect2.exe: error: ld returned 1 exit status

编译器(cc1)对中文路径毫无意见,GNU 链接器 ld 写 map 文件时却不认非 ASCII 路径——「Invalid argument」就是这么来的。工程本身不用搬,把构建输出目录指到纯 ASCII 路径即可:

ini; platformio.ini → [platformio] 段
build_dir = C:/pio_build/esp32-demo

改完重新编译:构建缓存全部复用,只剩链接,70 秒出固件:

textRAM:   [=         ]   6.8% (used 22192 bytes from 327680 bytes)
Flash: [=         ]   9.5% (used 317258 bytes from 3342336 bytes)
======================== [SUCCESS] Took 70.56 seconds ========================

#四、第二坑:GBK 与 UTF-8 的暗战

改完 build_dir 反而编译都起不来了,报 UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb9。逐层排查(click 调用栈 → configparser → 字节位置 13)后真相大白:

环节编码要求实际行为
新版 PlatformIO 读 platformio.ini强制 UTF-8——
本机编辑工具写文件——默认按系统区域写成 GBK

「工程配置」四个字的「工」字 GBK 首字节正好是 0xB9——报错的字节位置严丝合缝。教训:换机后第一个要验证的就是本机工具链的默认编码。通用解法(先探测、后转换,避免把 UTF-8 误当 GBK 二次转码):

powershell$p = 'D:\path\platformio.ini'
$bytes = [System.IO.File]::ReadAllBytes($p)
try {
  $null = (New-Object System.Text.UTF8Encoding($false, $true)).GetString($bytes)
  Write-Host '已经是 UTF-8,无需转换'
} catch {
  $text = [System.Text.Encoding]::GetEncoding('GB18030').GetString($bytes)
  [System.IO.File]::WriteAllText($p, $text, (New-Object System.Text.UTF8Encoding($false)))
  Write-Host 'GBK 已转换为 UTF-8 (无 BOM)'
}

#五、第三坑:VMware 静默安装连环坑

VMware 现在个人免费,但下载要注册 Broadcom 账号,且官方门户国内加载极慢、没有中国镜像。两个替代路径:

  1. 官方软件更新 CDN(softwareupdate-prod.broadcom.com)不需要账号,但国内直连和代理都被断 TLS;
  2. 可信镜像站的官方原版包:下载后验证数字签名(右键 → 属性 → 数字签名 → 签名者 Broadcom Inc、状态「有效」),签名通过即为原版。

安装本身的坑更典型。第一层:PowerShell 的 Start-Process -ArgumentList 按数组传参时会把 /v 和它的值拆开,msiexec 收到空属性,静默安装直接静默退出。第二层:换成单字符串精确传参后,MSI 日志显示 VM_ValidateEULAAccepted 返回值 3 → 错误 1603 回滚——EULA 属性没传进去。最终用 ProcessStartInfo 原样传参,并同时带上两种 EULA 属性名:

powershell$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName  = "$env:USERPROFILE\Downloads\VMware-workstation-full-17.6.4-24832109.exe"
$psi.Arguments = '/s /v"/qn EULAS_AGREED=1 EULAgreed=1 REBOOT=ReallySuppress"'
$psi.Verb      = 'runAs'          # 弹 UAC 提权
$psi.UseShellExecute = $true
$proc = [System.Diagnostics.Process]::Start($psi)
$proc.WaitForExit()
Test-Path 'C:\Program Files (x86)\VMware\VMware Workstation\vmware.exe'  # → True

静默安装全程无窗口,验证方式就是上面最后一行 Test-Path——「没有消息」在静默模式下真的是坏消息,必须主动验证。

#六、第四坑:guestOS 标识大换血

虚拟机开机弹「不支持客户机操作系统"ubuntu64"」。vmx 是旧机器 Workstation 写的,而 17.6.4 精简了客户机系统标识:先试 ubuntu64Guest(ESX 风格的规范名)也被拒。与其翻文档猜,不如直接从安装目录的二进制里把合法 ID 扫出来:

powershell$text = [System.Text.Encoding]::ASCII.GetString(
  [System.IO.File]::ReadAllBytes('C:\Program Files (x86)\VMware\VMware Workstation\vmwarebase.dll'))
[regex]::Matches($text, '[a-zA-Z0-9_-]*ubuntu[a-zA-Z0-9_-]*') |
  ForEach-Object { $_.Value } | Sort-Object -Unique
# → ubuntu / ubuntu-64 / arm-ubuntu / arm-ubuntu-64 / ubuntuServer ...

结论:新版标识是带连字符的 ubuntu-64,老写法全部废弃。改 vmx 一行,开机直达 Ubuntu 桌面。顺带把 vmx 里指向旧机器 E 盘的 ISO 引用删掉,避免开机弹找不到光盘。

#七、虚拟机搬家与 SSH 免密验证

31.5GB 的虚拟机目录 robocopy 对拷只要 26 秒(1.28 GB/s)。开机时 VMware 问「移动还是复制」——整机搬迁选「我已移动」,保留 UUID 和 MAC;只有当同一台 VM 要复制多份并存时才选「复制」。

旧文档里记录的 IP(192.168.41.130)是旧机器的 NAT 网段,搬到新机器必然失效。不进系统也能拿到新 IP——vmrun 直接问 VMware 要:

powershell$vmrun = 'C:\Program Files (x86)\VMware\VMware Workstation\vmrun.exe'
& $vmrun -T ws getGuestIPAddress 'D:\嵌入式开发\VMs\OrangePi-Ubuntu\OrangePi-Ubuntu.vmx'
# → 192.168.254.128

# 免密 SSH 验证(密钥认证,不用输密码)
ssh -o BatchMode=yes zhangshaohua@192.168.254.128 "hostname"
# → zhangshaohua-virtual-machine

密钥认证直接通过——迁移前部署在宿主机 ~/.ssh 的公钥已经写在虚拟机镜像的 authorized_keys 里,整机搬家不影响。NAT 网段每台机器不同,且 DHCP 可能变化,文档里写死的 IP 迟早变坑,vmrun 一查才是真相。

#八、命令速查

text# 编码探测 + 转换(任何中文配置文件通用)
见第四节脚本

# 静默安装 VMware(记日志可加 /l*v "C:\vmmsi.log")
VMware-workstation-full-17.6.4.exe /s /v"/qn EULAS_AGREED=1 EULAgreed=1 REBOOT=ReallySuppress"

# 虚拟机管理
vmrun -T ws start   "<vmx>"          # 开机(弹 GUI)
vmrun -T ws list                        # 列出运行中的 VM
vmrun -T ws getGuestIPAddress "<vmx>"   # 查 VM IP

# ESP32 编译(中文路径工程必配 build_dir)
$env:HTTP_PROXY='socks5h://127.0.0.1:10808'; $env:HTTPS_PROXY=$env:HTTP_PROXY
pio run

#九、防坑清单

  1. 工程路径只用 ASCII:源码目录可以中文,但构建输出目录(build_dir)必须纯英文——ld 写 map 文件会翻车。
  2. 换机先测编码:新机器上任何中文配置文件被编辑工具碰过之后,先验 UTF-8 再运行程序;检测后再转换,别对 UTF-8 文件做二次 GBK 转码。
  3. 静默安装 = 无反馈安装:一定留日志(/l*v)+ 装完主动验证文件存在;Start-Process 传参用 ProcessStartInfo 单字符串,别用 ArgumentList 数组。
  4. 软件版本迁移先查「废弃列表」:vmx、配置文件里的老标识在跨大版本后可能失效;拿不准就从安装目录二进制里扫字符串,那是最权威的清单。
  5. IP 会变,密钥不变:跨机器迁移后第一时间用 vmrun 重查 IP,SSH 密钥认证随镜像走,不用重配。
  6. 第三方安装包先验签名:数字签名「有效」= 官方原版未改动;缺失或无效立即删除。

从工具链恢复到 SSH 连通,整场迁移约 4 小时,其中 90% 的时间花在四个坑上。把它们写下来,就是为了让下次搬家只花 40 分钟。

#十、相关阅读

#十、更新日志

📝共 1 条记录
日期类型更新内容
2026-09-15新增首次发布:PlatformIO / VMware / 虚拟机整机迁移全记录