# 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 if (-not ($env:PATH -split ';' | Where-Object { $_ -eq $espClang })) { $env:PATH = $espClang + ';' + $env:PATH } } $env:CARGO_WORKSPACE_DIR = (Get-Location).Path $env:ESP_IDF_GLOB_BASE = (Get-Location).Path $env:ESP_IDF_GLOB_PARTITIONS = 'partitions.csv' & cargo build --release --locked @CargoArgs if ($LASTEXITCODE -ne 0) { throw "cargo build failed (exit $LASTEXITCODE)." } if ($Package) { $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 --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 }