From 75ae4e9e22c1beeeddb773cc65e01fd584eab40d Mon Sep 17 00:00:00 2001 From: luk Date: Fri, 16 Oct 2026 22:02:16 +0800 Subject: [PATCH] =?UTF-8?q?update:=20=E5=90=8C=E6=AD=A5=20emu*=20=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=E8=87=B3=20einz=20=E6=9C=80=E6=96=B0=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - emuResolveIos.js:裸跑时 Booted 优先、否则取系统版本最高的 iPhone; EMU 环境变量最高优先;短名表扩至 ip17 系列;修复 rootDir 指向错误 导致项目 config 永远读不到的问题 - emuBootIos.sh:Xcode 27+ 不再随附 Simulator.app,检测后跳过并提示 DeviceHub 来源:~/einz/scripts/(拷贝后 einz 侧持续演进,此处为落后快照) Co-Authored-By: AtomCode (glm5.3-flash) --- emuBootIos.sh | 14 ++++++-- emuResolveIos.js | 88 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 90 insertions(+), 12 deletions(-) diff --git a/emuBootIos.sh b/emuBootIos.sh index 21ca343..887a8d5 100644 --- a/emuBootIos.sh +++ b/emuBootIos.sh @@ -9,7 +9,7 @@ # npm run app-ios-boot -- FF429526-A7EE-… # 直接给 UDID # EMU=ip16@18.1 npm run app-ios-boot # 用环境变量改默认 # -# 短名表见 emuResolveIos.js 的 builtinAlias(ip16 / ip16plus / ip16pro / ip16pm), +# 短名表见 scripts/emuResolveIos.js 的 builtinAlias(ip16 / ip16plus / ip16pro / ip16pm), # 也可以在 package.json 的 config 里加("ipad": "iPad Pro 13-inch (M4)")。 # # 跑 App 永远打「当前已启动的那台」(纯 booted 语义): @@ -27,5 +27,15 @@ if xcrun simctl boot "${udid}" 2>/dev/null; then else echo "(已在运行,跳过 boot)" fi -open "$(xcode-select -p)/Applications/Simulator.app" +# Xcode 27 起不再随附 Simulator.app(simctl 独立工作);模拟器 GUI 改由 +# DeviceHub(Xcode.app/Contents/Applications/DeviceHub.app)承担——有就开它, +# 没有就跳过(flutter run 也会自己唤起模拟器窗口)。 +SIM_APP="$(xcode-select -p)/Applications/Simulator.app" +if [ -d "$SIM_APP" ]; then + open "$SIM_APP" +else + echo "(本机 Xcode 无 Simulator.app(Xcode 27+ 已移除),跳过开 GUI)" + echo " 看模拟器界面:open -b com.apple.dt.Devices # DeviceHub" + echo " 或直接 flutter run,会自动把模拟器窗口带到前台" +fi xcrun simctl list devices booted diff --git a/emuResolveIos.js b/emuResolveIos.js index c3624a9..9c20863 100644 --- a/emuResolveIos.js +++ b/emuResolveIos.js @@ -1,10 +1,10 @@ #!/usr/bin/env node // 把「短名@系统版本」解析成本机真实存在的模拟器 UDID(供 emuBootIos.sh 调用)。 // -// node emuResolveIos.js ip16@26.3 # 短名(见下面别名表) -// node emuResolveIos.js "iPhone 16@26.3" # 写机型全名也行 -// node emuResolveIos.js "iPhone 16" # 不给版本 → 取系统版本最高的那台 -// node emuResolveIos.js FF429526-… # 本来就是 UDID → 原样返回 +// node scripts/emuResolveIos.js ip16@26.3 # 短名(见下面别名表) +// node scripts/emuResolveIos.js "iPhone 16@26.3" # 写机型全名也行 +// node scripts/emuResolveIos.js "iPhone 16" # 不给版本 → 取系统版本最高的那台 +// node scripts/emuResolveIos.js FF429526-… # 本来就是 UDID → 原样返回 // // 为什么不用 UDID:UDID 是每台 Mac 各自生成的,换台机器就失效。 // 机型名 + 系统版本是 Xcode 自带的,任何机器上都能查到,所以跨机器可移植。 @@ -16,10 +16,35 @@ const { execSync } = require('child_process') const path = require('path') const builtinAlias = { + ipxr: 'iPhone XR', + ipxs: 'iPhone Xs', + ip11: 'iPhone 11', + ip11pro: 'iPhone 11 Pro', + ip11pm: 'iPhone 11 Pro Max', + ip12: 'iPhone 12', + ip12mini: 'iPhone 12 mini', + ip12pro: 'iPhone 12 Pro', + ip12pm: 'iPhone 12 Pro Max', + ip13: 'iPhone 13', + ip13mini: 'iPhone 13 mini', + ip13pro: 'iPhone 13 Pro', + ip13pm: 'iPhone 13 Pro Max', + ip14: 'iPhone 14', + ip14plus: 'iPhone 14 Plus', + ip14pro: 'iPhone 14 Pro', + ip14pm: 'iPhone 14 Pro Max', + ip15: 'iPhone 15', + ip15plus: 'iPhone 15 Plus', + ip15pro: 'iPhone 15 Pro', + ip15pm: 'iPhone 15 Pro Max', ip16: 'iPhone 16', ip16plus: 'iPhone 16 Plus', ip16pro: 'iPhone 16 Pro', ip16pm: 'iPhone 16 Pro Max', + ip17: 'iPhone 17', + ip17pro: 'iPhone 17 Pro', + ip17pm: 'iPhone 17 Pro Max', + ip17air: 'iPhone Air' } const rootDir = path.resolve(__dirname, '..') @@ -30,9 +55,13 @@ try { pkgConfig = {} } -const input = (process.argv[2] || '').trim() || String(pkgConfig.defaultIosEmu || 'ip16@26.3').trim() +const input = + (process.argv[2] || '').trim() || + String(process.env.EMU || pkgConfig.defaultIosEmu || '').trim() -if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(input)) { +if ( + /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(input) +) { console.log(input) process.exit(0) } @@ -48,13 +77,48 @@ const version = .replace(/^iOS\s*/i, '') const deviceName = pkgConfig[left] || builtinAlias[left.toLowerCase()] || left -const json = JSON.parse(execSync('xcrun simctl list devices available -j').toString()) -const runtimeKeyFor = (want) => `iOS-${want.replace(/\./g, '-')}` -const versionOf = (runtimeKey) => { +const json = JSON.parse( + execSync('xcrun simctl list devices available -j').toString() +) + +// 辅助函数放在**所有使用之前**:下面 `if (!input)` 分支在模块顶层就跑 +// sort,比较器立刻调用 versionOf——const 声明若在使用之后会触发 TDZ +// (ReferenceError: Cannot access 'versionOf' before initialization, +// 2026-10-12 老板实测)。 +const runtimeKeyFor = want => `iOS-${want.replace(/\./g, '-')}` +const versionOf = runtimeKey => { const matched = runtimeKey.match(/iOS-(\d+)(?:-(\d+))?/) return matched ? [Number(matched[1]), Number(matched[2] || 0)] : [0, 0] } +// 空规格(裸 npm run app-ios-boot):有 Booted 直接用它;否则挑可用的 +// iPhone 里系统版本最高的那台——任何机器上都能裸跑,不再钉死某机型。 +if (!input) { + const all = [] + for (const [runtimeKey, devices] of Object.entries(json.devices)) { + if (!/iOS-/.test(runtimeKey)) continue + for (const d of devices) all.push({ ...d, runtimeKey }) + } + const booted = all.find(d => d.state === 'Booted') + if (booted) { + console.log(booted.udid) + process.exit(0) + } + const iPhones = all.filter(d => /iPhone/i.test(d.name)) + if (iPhones.length === 0) { + console.error('❌ 本机没有可用的 iPhone 模拟器') + console.error(' 看有哪些:xcrun simctl list devices available') + process.exit(1) + } + iPhones.sort((a, b) => { + const [aMajor, aMinor] = versionOf(a.runtimeKey) + const [bMajor, bMinor] = versionOf(b.runtimeKey) + return bMajor - aMajor || bMinor - aMinor + }) + console.log(iPhones[0].udid) + process.exit(0) +} + const matched = [] for (const [runtimeKey, devices] of Object.entries(json.devices)) { if (!/iOS-/.test(runtimeKey)) { @@ -71,7 +135,11 @@ for (const [runtimeKey, devices] of Object.entries(json.devices)) { } if (matched.length === 0) { - console.error(`❌ 本机没有匹配的模拟器:${input}(机型「${deviceName}」${version ? ` + iOS ${version}` : ''})`) + console.error( + `❌ 本机没有匹配的模拟器:${input}(机型「${deviceName}」${ + version ? ` + iOS ${version}` : '' + })` + ) console.error(' 看有哪些:xcrun simctl list devices available') process.exit(1) }