# Build the ESP32-S3 firmware from PowerShell without executing .env as code. [CmdletBinding()] param( [string]$EnvFile = '.env', [switch]$Package, [Parameter(ValueFromRemainingArguments = $true)] [string[]]$CargoArgs = @() ) $ErrorActionPreference = 'Stop' Set-StrictMode -Version Latest Push-Location (Split-Path -Parent $PSScriptRoot) $buildEnvironment = @{} foreach ($name in @('CARGO_WORKSPACE_DIR', 'ESP_IDF_GLOB_BASE', 'ESP_IDF_GLOB_PARTITIONS')) { $buildEnvironment[$name] = [Environment]::GetEnvironmentVariable($name, 'Process') } try { if (Test-Path -LiteralPath $EnvFile) { $lineNumber = 0 foreach ($line in Get-Content -LiteralPath $EnvFile -Encoding UTF8) { $lineNumber++ if ($line -match '^\s*(#|$)') { continue } if ($line -notmatch '^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=(.*)$') { throw "Invalid assignment in ${EnvFile}:${lineNumber}" } $key = $Matches[1] $value = $Matches[2].Trim() if ($value.StartsWith("'") -or $value.StartsWith('"')) { $quote = $value.Substring(0, 1) if ($value.Length -lt 2 -or -not $value.EndsWith($quote)) { throw "Unclosed quote in ${EnvFile}:${lineNumber}" } $value = $value.Substring(1, $value.Length - 2) } else { $value = ($value -replace '\s+#.*$', '').TrimEnd() } [Environment]::SetEnvironmentVariable($key, $value, 'Process') } } if ([string]::IsNullOrWhiteSpace($env:WIFI_SSID) -or $env:WIFI_SSID -eq 'your-ssid') { throw 'Set WIFI_SSID in .env (copy .env.example) or the environment before building.' } if ($null -eq $env:WIFI_PASS) { $env:WIFI_PASS = '' } # bindgen 要加载 esp-clang 自带的 libclang.dll,而它依赖**同目录**的 libLLVM-21.dll、 # libclang-cpp.dll 等。LoadLibraryEx 默认不会去 DLL 自己的目录找依赖,所以必须把这个 # 目录放进 PATH,否则报的是一句毫不相干的话: # Unable to find libclang: "...\libclang.dll could not be opened: LoadLibraryExW failed" # (`espup` 的 shell 会替你导出这些变量,直接跑脚本的人不会。) $espClang = Join-Path $env:USERPROFILE '.rustup\toolchains\esp-1.97.0.0\xtensa-esp32-elf-clang\esp-clang\bin' if (Test-Path -LiteralPath (Join-Path $espClang 'libclang.dll')) { $env:LIBCLANG_PATH = $espClang # An existing entry can be behind another LLVM installation. Move it # to the front so libclang-cpp.dll and the runtime match libclang.dll. $otherPaths = @($env:PATH -split ';' | Where-Object { $_ -ne $espClang }) $env:PATH = (@($espClang) + $otherPaths) -join ';' } $env:CARGO_WORKSPACE_DIR = (Get-Location).Path $env:ESP_IDF_GLOB_BASE = (Get-Location).Path $env:ESP_IDF_GLOB_PARTITIONS = 'partitions.csv' $firmwareElf = $null if ($Package) { # Use Cargo's actual artifact path, including --target-dir / --config # overrides, rather than guessing where this build wrote its ELF. & cargo build --release --locked --message-format=json-render-diagnostics @CargoArgs | ForEach-Object { $message = $_ | ConvertFrom-Json if ($message.reason -eq 'compiler-artifact' -and $message.target.name -eq 'esp32' -and $message.executable) { $firmwareElf = $message.executable } } } else { & cargo build --release --locked @CargoArgs } if ($LASTEXITCODE -ne 0) { throw "cargo build failed (exit $LASTEXITCODE)." } if ($Package) { if (-not $firmwareElf) { throw 'Cargo did not report the esp32 firmware ELF; refusing to package old images.' } $python = Get-Command python -ErrorAction SilentlyContinue if ($null -eq $python) { throw '-Package requires Python 3 on PATH (used by scripts/package_firmware.py).' } # 固件包的版本号与客户端同源:都读仓库根的 version.json(由 scripts/version.mjs # 从 git 历史算出)。不传的话 make_bundle.py 会退回 BUILD-INFO.json 里的 # 变体名+提交号,那是另一套编号,装到客户端里会与界面上的版本对不上。 $versionArgs = @() if (Test-Path -LiteralPath 'version.json') { $info = Get-Content -LiteralPath 'version.json' -Raw | ConvertFrom-Json $versionArgs = @('--version', "v$($info.version)+$($info.build)") } & python scripts/package_firmware.py --firmware target/firmware --elf $firmwareElf --name 'RMIC 图传固件' @versionArgs if ($LASTEXITCODE -ne 0) { throw "package_firmware.py failed (exit $LASTEXITCODE)." } } } finally { foreach ($name in $buildEnvironment.Keys) { [Environment]::SetEnvironmentVariable($name, $buildEnvironment[$name], 'Process') } Pop-Location }