Fetch esp-emulator browser build from GitHub releases; release packaging (0.10.1)
- tools/fetch_esp_emu.py: download esp-emu-<ver>-wasm.tar.gz from the espressif/esp-emulator release into vendor/esp-emu-browser/www (proxy-aware via https_proxy, sha256-verified against SHA256SUMS, cached in vendor/downloads/, idempotent, --version to switch baseline) - npm run build/test trigger the fetch automatically; serve.py, sync_www.py and the m0/m1 tests default to the vendored copy (ESP_EMU_WWW/--www override) — no machine-local absolute paths left in the dev flow - install.sh/install.bat + tools/package_release.py (npm run package): release archives ship the pre-compiled UI + the esp-emulator fetch script - docs updated (README/AGENT/UPGRADE/ROADMAP/CHANGELOG, EN+zh)
This commit is contained in:
1 parent
9cd5863d36
commit
81915288f5
22 files changed
+532
-34
No files matched your search
@@ -152,3 +152,10 @@ tests/firmware/peris_probe/build/
|
|||||||
|
|
||||||
# build output (compiled from src/ by tsc)
|
# build output (compiled from src/ by tsc)
|
||||||
/build/
|
/build/
|
||||||
|
|
||||||
|
# esp-emulator browser (wasm) build fetched from GitHub releases by
|
||||||
|
# tools/fetch_esp_emu.py (npm run build does this automatically)
|
||||||
|
/vendor/
|
||||||
|
|
||||||
|
# release packaging output (npm run package)
|
||||||
|
/dist/
|
||||||
@@ -10,10 +10,12 @@ verification method learned so far. **Read it fully before touching anything.**
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install # typescript via npmmirror (see §1)
|
npm install # typescript via npmmirror (see §1)
|
||||||
npm run build # tsc: src/ → build/ (mandatory after src changes; build/ not in git)
|
npm run build # prebuild fetches the esp-emulator browser build into
|
||||||
|
# vendor/esp-emu-browser/ on first run, then tsc: src/ → build/
|
||||||
|
# (mandatory after src changes; build/ not in git)
|
||||||
npm test # unit tests + integration smoke
|
npm test # unit tests + integration smoke
|
||||||
npm run serve # http://127.0.0.1:8080/esp-emu-peris.html
|
npm run serve # http://127.0.0.1:8080/esp-emu-peris.html
|
||||||
npm run sync # publish into _tools/esp-emu-browser/www (build first; --clean removes stale output)
|
npm run sync # publish into vendor/esp-emu-browser/www (build first; --clean removes stale output)
|
||||||
```
|
```
|
||||||
|
|
||||||
Architecture & data flow: `docs/ARCHITECTURE.md`; peripheral development:
|
Architecture & data flow: `docs/ARCHITECTURE.md`; peripheral development:
|
||||||
@@ -28,6 +30,13 @@ and `docs/FEASIBILITY.md` (M0 measurements).
|
|||||||
`https://registry.npmmirror.com/<pkg>/-/<name>-<ver>.tgz` and unpack
|
`https://registry.npmmirror.com/<pkg>/-/<name>-<ver>.tgz` and unpack
|
||||||
(that is how xterm was vendored into `peris/vendor/`; the package ships the
|
(that is how xterm was vendored into `peris/vendor/`; the package ships the
|
||||||
**unminified** build, no .min).
|
**unminified** build, no .min).
|
||||||
|
- **GitHub downloads need the local proxy**:
|
||||||
|
`https_proxy=http://127.0.0.1:10809`. The esp-emulator browser (wasm)
|
||||||
|
build is fetched from the espressif/esp-emulator GitHub release by
|
||||||
|
`tools/fetch_esp_emu.py` (runs automatically on `npm run build`/`npm test`;
|
||||||
|
honours `https_proxy`; sha256-verified; cached in `vendor/downloads/`;
|
||||||
|
unpacked into `vendor/esp-emu-browser/www`). Override the location with
|
||||||
|
`ESP_EMU_WWW=/path/to/www`.
|
||||||
- **Playwright browser binaries** not installed (network); to install:
|
- **Playwright browser binaries** not installed (network); to install:
|
||||||
`PLAYWRIGHT_DOWNLOAD_HOST=https://cdn.npmmirror.com/binaries/playwright`.
|
`PLAYWRIGHT_DOWNLOAD_HOST=https://cdn.npmmirror.com/binaries/playwright`.
|
||||||
- **ESP-IDF**: master checkout at `C:\esp\master\esp-idf`; enable the
|
- **ESP-IDF**: master checkout at `C:\esp\master\esp-idf`; enable the
|
||||||
|
|||||||
+10
-2
@@ -9,10 +9,12 @@
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install # typescript 走 npmmirror(见 §1)
|
npm install # typescript 走 npmmirror(见 §1)
|
||||||
npm run build # tsc:src/ → build/(改 src 后必须;build/ 不入 git)
|
npm run build # prebuild 先把 esp-emulator 浏览器构建下载到
|
||||||
|
# vendor/esp-emu-browser/(首次),然后 tsc:src/ → build/
|
||||||
|
#(改 src 后必须;build/ 不入 git)
|
||||||
npm test # 单元测试 + 集成冒烟
|
npm test # 单元测试 + 集成冒烟
|
||||||
npm run serve # http://127.0.0.1:8080/esp-emu-peris.html
|
npm run serve # http://127.0.0.1:8080/esp-emu-peris.html
|
||||||
npm run sync # 发布进 _tools/esp-emu-browser/www(先 build;--clean 清残留)
|
npm run sync # 发布进 vendor/esp-emu-browser/www(先 build;--clean 清残留)
|
||||||
```
|
```
|
||||||
|
|
||||||
架构与数据流见 `docs/ARCHITECTURE.zh-cn.md`;外设开发见 `docs/DEVICE-API.zh-cn.md`;
|
架构与数据流见 `docs/ARCHITECTURE.zh-cn.md`;外设开发见 `docs/DEVICE-API.zh-cn.md`;
|
||||||
@@ -24,6 +26,12 @@ esp-emulator wasm 能力边界见 `docs/EMU-API.zh-cn.md` 与 `docs/FEASIBILITY.
|
|||||||
`npm --registry=https://registry.npmmirror.com install ...`;静态资源从
|
`npm --registry=https://registry.npmmirror.com install ...`;静态资源从
|
||||||
`https://registry.npmmirror.com/<pkg>/-/<name>-<ver>.tgz` 下 tarball 解包
|
`https://registry.npmmirror.com/<pkg>/-/<name>-<ver>.tgz` 下 tarball 解包
|
||||||
(xterm 就是这样 vendor 进 `peris/vendor/` 的;包内是**非压缩**版,无 .min)。
|
(xterm 就是这样 vendor 进 `peris/vendor/` 的;包内是**非压缩**版,无 .min)。
|
||||||
|
- **GitHub 下载要走本机代理**:`https_proxy=http://127.0.0.1:10809`。
|
||||||
|
esp-emulator 浏览器(wasm)构建由 `tools/fetch_esp_emu.py` 从
|
||||||
|
espressif/esp-emulator 的 GitHub release 下载(`npm run build`/`npm test`
|
||||||
|
自动触发;识别 `https_proxy`;sha256 校验;tarball 缓存在
|
||||||
|
`vendor/downloads/`;解压到 `vendor/esp-emu-browser/www`)。用
|
||||||
|
`ESP_EMU_WWW=/path/to/www` 可改指向。
|
||||||
- **Playwright 浏览器二进制**未装(网络);要装:
|
- **Playwright 浏览器二进制**未装(网络);要装:
|
||||||
`PLAYWRIGHT_DOWNLOAD_HOST=https://cdn.npmmirror.com/binaries/playwright`。
|
`PLAYWRIGHT_DOWNLOAD_HOST=https://cdn.npmmirror.com/binaries/playwright`。
|
||||||
- **ESP-IDF**:master 检出在 `C:\esp\master\esp-idf`,环境启用用
|
- **ESP-IDF**:master 检出在 `C:\esp\master\esp-idf`,环境启用用
|
||||||
|
|||||||
@@ -47,7 +47,10 @@ places; devices carry their own versions).
|
|||||||
npm install
|
npm install
|
||||||
|
|
||||||
# dev loop: edit src/ → build → test → merged dev server
|
# dev loop: edit src/ → build → test → merged dev server
|
||||||
npm run build
|
npm run build # first run also downloads the esp-emulator browser
|
||||||
|
# (wasm) build from the espressif/esp-emulator
|
||||||
|
# GitHub release into vendor/esp-emu-browser/ —
|
||||||
|
# set https_proxy first if you need a proxy
|
||||||
npm test
|
npm test
|
||||||
npm run serve # http://127.0.0.1:8080/esp-emu-peris.html
|
npm run serve # http://127.0.0.1:8080/esp-emu-peris.html
|
||||||
|
|
||||||
@@ -64,12 +67,30 @@ npm run serve # http://127.0.0.1:8080/esp-emu-peris.html
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Publish** the peripherals panel into the official www directory (add-only,
|
**Publish** the peripherals panel into the official www directory (add-only,
|
||||||
never modifies; build first):
|
never modifies; build first) — by default the fetched `vendor/esp-emu-browser/www`,
|
||||||
|
or any checkout via `--www` / `ESP_EMU_WWW`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run sync # copies the entry html + peris/ (incl. build output) + examples/
|
npm run sync # copies the entry html + peris/ (incl. build output) + examples/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## From a release package
|
||||||
|
|
||||||
|
Download `esp-emu-peris-<version>.zip` (or `.tar.gz`) from the
|
||||||
|
[releases page](https://github.com/darceye/esp-emu-peris/releases), unpack
|
||||||
|
and run the installer — it downloads the esp-emulator browser (wasm) build
|
||||||
|
from GitHub releases into `vendor/` (needs Python 3; behind a firewall set
|
||||||
|
`https_proxy` first, e.g. `https_proxy=http://127.0.0.1:10809`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./install.sh # Windows: install.bat
|
||||||
|
python serve.py # http://127.0.0.1:8080/esp-emu-peris.html
|
||||||
|
```
|
||||||
|
|
||||||
|
No npm/node needed — the package ships the UI pre-compiled. For a different
|
||||||
|
esp-emulator version: `./install.sh --version 0.52.0` (or
|
||||||
|
`python tools/fetch_esp_emu.py --version 0.52.0`).
|
||||||
|
|
||||||
## Capabilities & limits (measured; details in docs/FEASIBILITY.md)
|
## Capabilities & limits (measured; details in docs/FEASIBILITY.md)
|
||||||
|
|
||||||
| Peripheral | Status | Approach |
|
| Peripheral | Status | Approach |
|
||||||
|
|||||||
+21
-2
@@ -40,7 +40,10 @@ AGENT.md 环境/架构/坑(贡献前必读)
|
|||||||
npm install
|
npm install
|
||||||
|
|
||||||
# 开发循环:改 src/ → 构建 → 测试 → 起 merged 开发服务器
|
# 开发循环:改 src/ → 构建 → 测试 → 起 merged 开发服务器
|
||||||
npm run build
|
npm run build # 首次运行会自动从 espressif/esp-emulator 的
|
||||||
|
# GitHub release 下载浏览器(wasm)构建到
|
||||||
|
# vendor/esp-emu-browser/——需要代理的话先设
|
||||||
|
# https_proxy
|
||||||
npm test
|
npm test
|
||||||
npm run serve # http://127.0.0.1:8080/esp-emu-peris.html
|
npm run serve # http://127.0.0.1:8080/esp-emu-peris.html
|
||||||
|
|
||||||
@@ -54,12 +57,28 @@ npm run serve # http://127.0.0.1:8080/esp-emu-peris.html
|
|||||||
# 选中)
|
# 选中)
|
||||||
```
|
```
|
||||||
|
|
||||||
把外设面板**发布**进官方 www 目录(只增不改;先 build):
|
把外设面板**发布**进官方 www 目录(只增不改;先 build)——默认目标是自动下载
|
||||||
|
的 `vendor/esp-emu-browser/www`,也可用 `--www` / `ESP_EMU_WWW` 指向任意目录:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run sync # 复制入口 html + peris/(含编译产物)+ examples/
|
npm run sync # 复制入口 html + peris/(含编译产物)+ examples/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 从 release 包使用
|
||||||
|
|
||||||
|
从 [releases 页面](https://github.com/darceye/esp-emu-peris/releases)下载
|
||||||
|
`esp-emu-peris-<版本>.zip`(或 `.tar.gz`)解压后运行安装脚本——它会从
|
||||||
|
GitHub release 下载 esp-emulator 浏览器(wasm)构建到 `vendor/`(需要
|
||||||
|
Python 3;有防火墙先设 `https_proxy`,如 `https_proxy=http://127.0.0.1:10809`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./install.sh # Windows 下:install.bat
|
||||||
|
python serve.py # http://127.0.0.1:8080/esp-emu-peris.html
|
||||||
|
```
|
||||||
|
|
||||||
|
无需 npm/node——包内已带编译好的 UI。要换 esp-emulator 版本:
|
||||||
|
`./install.sh --version 0.52.0`(或 `python tools/fetch_esp_emu.py --version 0.52.0`)。
|
||||||
|
|
||||||
## 能力与限制(实测结论,详见 docs/FEASIBILITY.zh-cn.md)
|
## 能力与限制(实测结论,详见 docs/FEASIBILITY.zh-cn.md)
|
||||||
|
|
||||||
| 外设 | 状态 | 途径 |
|
| 外设 | 状态 | 途径 |
|
||||||
|
|||||||
@@ -8,6 +8,23 @@ CHANGELOG.zh-cn.md).
|
|||||||
Rules: major = breaking protocol/architecture change; minor = new capability;
|
Rules: major = breaking protocol/architecture change; minor = new capability;
|
||||||
patch = fixes and small changes.
|
patch = fixes and small changes.
|
||||||
|
|
||||||
|
## peris 0.10.1
|
||||||
|
|
||||||
|
- **No more machine-local absolute paths**: the esp-emulator browser (wasm)
|
||||||
|
build is fetched on demand from the `espressif/esp-emulator` GitHub
|
||||||
|
release into `vendor/esp-emu-browser/` by the new
|
||||||
|
`tools/fetch_esp_emu.py` — `npm run build` / `npm test` trigger it
|
||||||
|
automatically on first run. Proxy-aware (`https_proxy`), sha256-verified
|
||||||
|
against the release `SHA256SUMS`, cached in `vendor/downloads/`,
|
||||||
|
idempotent; `--version <ver>` switches the baseline (see UPGRADE.md).
|
||||||
|
`serve.py` / `sync_www.py` / the tests default to the vendored copy
|
||||||
|
(override with `--www` / `ESP_EMU_WWW`).
|
||||||
|
- **Release packaging**: `npm run package` (`tools/package_release.py`)
|
||||||
|
builds `esp-emu-peris-<ver>.zip` / `.tar.gz` for the GitHub release page;
|
||||||
|
the archives ship the UI pre-compiled plus `install.sh` / `install.bat`,
|
||||||
|
which fetch the esp-emulator browser build — a release user needs only
|
||||||
|
Python 3, no npm/node.
|
||||||
|
|
||||||
## peris 0.10.0
|
## peris 0.10.0
|
||||||
|
|
||||||
- **Logic analyzer** (`logic-analyzer.js` v1.0.0, logic in `la-lib.js`):
|
- **Logic analyzer** (`logic-analyzer.js` v1.0.0, logic in `la-lib.js`):
|
||||||
|
|||||||
@@ -6,6 +6,20 @@
|
|||||||
与英文版 CHANGELOG.md)。
|
与英文版 CHANGELOG.md)。
|
||||||
规则:主版本=协议/架构破坏性变更;次版本=新能力;修订=修复与小改。
|
规则:主版本=协议/架构破坏性变更;次版本=新能力;修订=修复与小改。
|
||||||
|
|
||||||
|
## peris 0.10.1
|
||||||
|
|
||||||
|
- **不再依赖本机绝对路径**:esp-emulator 浏览器(wasm)构建改由新增的
|
||||||
|
`tools/fetch_esp_emu.py` 按需从 `espressif/esp-emulator` 的 GitHub
|
||||||
|
release 下载到 `vendor/esp-emu-browser/`——`npm run build` / `npm test`
|
||||||
|
首次运行自动触发。支持代理(`https_proxy`)、按 release `SHA256SUMS`
|
||||||
|
做 sha256 校验、tarball 缓存在 `vendor/downloads/`、幂等;`--version
|
||||||
|
<版本>` 可切换基线(见 UPGRADE.zh-cn.md)。`serve.py` / `sync_www.py` /
|
||||||
|
测试默认指向该目录(`--www` / `ESP_EMU_WWW` 可覆盖)。
|
||||||
|
- **发布打包**:`npm run package`(`tools/package_release.py`)生成面向
|
||||||
|
GitHub release 页面的 `esp-emu-peris-<版本>.zip` / `.tar.gz`;包内自带
|
||||||
|
编译好的 UI 与 `install.sh` / `install.bat` 安装脚本(负责下载
|
||||||
|
esp-emulator 浏览器构建)——release 用户只需 Python 3,无需 npm/node。
|
||||||
|
|
||||||
## peris 0.10.0
|
## peris 0.10.0
|
||||||
|
|
||||||
- **逻辑分析仪**(`logic-analyzer.js` v1.0.0,逻辑在 `la-lib.js`):面向固件
|
- **逻辑分析仪**(`logic-analyzer.js` v1.0.0,逻辑在 `la-lib.js`):面向固件
|
||||||
|
|||||||
+3
-2
@@ -126,7 +126,8 @@ esp-emulator releases (upgrade procedure in UPGRADE.md):
|
|||||||
|
|
||||||
## Maintenance items
|
## Maintenance items
|
||||||
|
|
||||||
- `update_esp_emu_browser.bat` (in `_tools`, currently empty): script the
|
- ~~`update_esp_emu_browser.bat`~~ — solved differently: `tools/fetch_esp_emu.py`
|
||||||
UPGRADE.md procedure.
|
downloads/unpacks any esp-emulator release (`--version <ver>`), see
|
||||||
|
UPGRADE.md.
|
||||||
- `src/core/mmio/`: add the C5/C6/H2/P4/S3 address tables (extracted from each
|
- `src/core/mmio/`: add the C5/C6/H2/P4/S3 address tables (extracted from each
|
||||||
chip's IDF soc headers, same method as M0).
|
chip's IDF soc headers, same method as M0).
|
||||||
@@ -102,5 +102,6 @@
|
|||||||
|
|
||||||
## 维护项
|
## 维护项
|
||||||
|
|
||||||
- `update_esp_emu_browser.bat`(在 `_tools`,当前为空):按 UPGRADE.zh-cn.md 流程脚本化。
|
- ~~`update_esp_emu_browser.bat`~~——已用更好的方式解决:`tools/fetch_esp_emu.py`
|
||||||
|
可下载/解压任意 esp-emulator release(`--version <版本>`),见 UPGRADE.zh-cn.md。
|
||||||
- `src/core/mmio/`:补 C5/C6/H2/P4/S3 地址表(从各自 IDF soc 头文件提取,方法同 M0)。
|
- `src/core/mmio/`:补 C5/C6/H2/P4/S3 地址表(从各自 IDF soc 头文件提取,方法同 M0)。
|
||||||
+9
-6
@@ -4,15 +4,18 @@
|
|||||||
|
|
||||||
## Pinned baseline
|
## Pinned baseline
|
||||||
|
|
||||||
Current baseline: **esp-emulator v0.51.0** (`_tools/esp-emu-browser/www/pkg`,
|
Current baseline: **esp-emulator v0.51.0** (`vendor/esp-emu-browser/www/pkg`,
|
||||||
wasm-bindgen `--target web`).
|
fetched from the GitHub release by `tools/fetch_esp_emu.py`, wasm-bindgen
|
||||||
|
`--target web`).
|
||||||
|
|
||||||
## Upgrade steps
|
## Upgrade steps
|
||||||
|
|
||||||
1. Download the new wasm package (e.g. `esp-emu-<ver>-wasm.tar.gz`) and
|
1. Fetch the new wasm package:
|
||||||
unpack the new `www/` (`_tools` already has
|
`python tools/fetch_esp_emu.py --version <ver>` — downloads
|
||||||
`update_esp_emu_browser.bat`, currently empty — fill it following this
|
`esp-emu-<ver>-wasm.tar.gz` from the espressif/esp-emulator GitHub
|
||||||
procedure).
|
release and unpacks its `www/` into `vendor/esp-emu-browser/www`
|
||||||
|
(honours `https_proxy`; bump `DEFAULT_VERSION` in the script afterwards).
|
||||||
|
Alternatively point `ESP_EMU_WWW=/path/to/new/www` at any unpacked copy.
|
||||||
2. **Run the M0 probe first**: `node tests/m0_probe.mjs <firmware path
|
2. **Run the M0 probe first**: `node tests/m0_probe.mjs <firmware path
|
||||||
unchanged; run straight against the pkg>`. The probe imports the new
|
unchanged; run straight against the pkg>`. The probe imports the new
|
||||||
`pkg/esp_emu.js` directly; if any of the six conclusions
|
`pkg/esp_emu.js` directly; if any of the six conclusions
|
||||||
|
|||||||
@@ -4,12 +4,16 @@
|
|||||||
|
|
||||||
## 锁定版本
|
## 锁定版本
|
||||||
|
|
||||||
当前基线:**esp-emulator v0.51.0**(`_tools/esp-emu-browser/www/pkg`,wasm-bindgen `--target web`)。
|
当前基线:**esp-emulator v0.51.0**(`vendor/esp-emu-browser/www/pkg`,由
|
||||||
|
`tools/fetch_esp_emu.py` 从 GitHub release 下载,wasm-bindgen `--target web`)。
|
||||||
|
|
||||||
## 升级步骤
|
## 升级步骤
|
||||||
|
|
||||||
1. 下载新版 wasm 包(如 `esp-emu-<ver>-wasm.tar.gz`)解压出新的 `www/`
|
1. 拉取新版 wasm 包:`python tools/fetch_esp_emu.py --version <ver>`——从
|
||||||
(`_tools` 下已有 `update_esp_emu_browser.bat`,目前为空,可按此逻辑补全)。
|
espressif/esp-emulator 的 GitHub release 下载 `esp-emu-<ver>-wasm.tar.gz`
|
||||||
|
并把其中的 `www/` 解压到 `vendor/esp-emu-browser/www`(识别 `https_proxy`;
|
||||||
|
事后把脚本里的 `DEFAULT_VERSION` 一并升级)。也可以用
|
||||||
|
`ESP_EMU_WWW=/path/to/new/www` 直接指向任意解压副本。
|
||||||
2. **先跑 M0 探针**:`node tests/m0_probe.mjs <新固件路径不动,直接对 pkg>`。
|
2. **先跑 M0 探针**:`node tests/m0_probe.mjs <新固件路径不动,直接对 pkg>`。
|
||||||
探针直接 import 新 `pkg/esp_emu.js`,六项结论(Q1a/Q1b/Q2a/Q2b/Q3a/Q3b)若有变化,
|
探针直接 import 新 `pkg/esp_emu.js`,六项结论(Q1a/Q1b/Q2a/Q2b/Q3a/Q3b)若有变化,
|
||||||
先更新 `docs/FEASIBILITY.zh-cn.md` 与 `peris/mmio/` 地址表,再动其它代码。
|
先更新 `docs/FEASIBILITY.zh-cn.md` 与 `peris/mmio/` 地址表,再动其它代码。
|
||||||
|
|||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
@echo off
|
||||||
|
rem esp-emu-peris installer: fetches the esp-emulator browser (wasm) build
|
||||||
|
rem from https://github.com/espressif/esp-emulator/releases into vendor\.
|
||||||
|
rem Requires Python 3. Behind a firewall set the proxy env first, e.g.
|
||||||
|
rem set https_proxy=http://127.0.0.1:10809
|
||||||
|
rem install.bat
|
||||||
|
setlocal
|
||||||
|
cd /d "%~dp0"
|
||||||
|
where python >nul 2>nul
|
||||||
|
if %errorlevel%==0 (
|
||||||
|
python tools\fetch_esp_emu.py %*
|
||||||
|
exit /b %errorlevel%
|
||||||
|
)
|
||||||
|
where python3 >nul 2>nul
|
||||||
|
if %errorlevel%==0 (
|
||||||
|
python3 tools\fetch_esp_emu.py %*
|
||||||
|
exit /b %errorlevel%
|
||||||
|
)
|
||||||
|
echo error: Python 3 is required (python or python3 on PATH) 1>&2
|
||||||
|
exit /b 1
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# esp-emu-peris installer: fetches the esp-emulator browser (wasm) build
|
||||||
|
# from https://github.com/espressif/esp-emulator/releases into vendor/.
|
||||||
|
# Requires Python 3. Behind a firewall set the usual proxy env first, e.g.
|
||||||
|
# https_proxy=http://127.0.0.1:10809 ./install.sh
|
||||||
|
set -e
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
if command -v python3 >/dev/null 2>&1; then PY=python3; else PY=python; fi
|
||||||
|
exec "$PY" tools/fetch_esp_emu.py "$@"
|
||||||
+9
-2
@@ -1,18 +1,25 @@
|
|||||||
{
|
{
|
||||||
"name": "esp-emu-peris",
|
"name": "esp-emu-peris",
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Simulated peripherals for the esp-emulator browser (wasm) build",
|
"description": "Simulated peripherals for the esp-emulator browser (wasm) build",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"fetch": "python tools/fetch_esp_emu.py",
|
||||||
|
"prebuild": "python tools/fetch_esp_emu.py",
|
||||||
"build": "tsc -p tsconfig.json",
|
"build": "tsc -p tsconfig.json",
|
||||||
"check": "tsc -p tsconfig.json --noEmit",
|
"check": "tsc -p tsconfig.json --noEmit",
|
||||||
"serve": "python serve.py",
|
"serve": "python serve.py",
|
||||||
"sync": "python sync_www.py",
|
"sync": "python sync_www.py",
|
||||||
|
"presync": "python tools/fetch_esp_emu.py",
|
||||||
"test": "node --test tests/unit/core.test.mjs tests/unit/devices.test.mjs tests/unit/protocol.test.mjs && node tests/m1_smoke.mjs",
|
"test": "node --test tests/unit/core.test.mjs tests/unit/devices.test.mjs tests/unit/protocol.test.mjs && node tests/m1_smoke.mjs",
|
||||||
|
"pretest": "python tools/fetch_esp_emu.py",
|
||||||
"test:unit": "node --test tests/unit/core.test.mjs tests/unit/devices.test.mjs tests/unit/protocol.test.mjs",
|
"test:unit": "node --test tests/unit/core.test.mjs tests/unit/devices.test.mjs tests/unit/protocol.test.mjs",
|
||||||
"test:smoke": "node tests/m1_smoke.mjs",
|
"test:smoke": "node tests/m1_smoke.mjs",
|
||||||
"test:m0": "node tests/m0_probe.mjs"
|
"pretest:smoke": "python tools/fetch_esp_emu.py",
|
||||||
|
"test:m0": "node tests/m0_probe.mjs",
|
||||||
|
"pretest:m0": "python tools/fetch_esp_emu.py",
|
||||||
|
"package": "python tools/package_release.py"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "5.9"
|
"typescript": "5.9"
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ Routes:
|
|||||||
everything else -> this repository
|
everything else -> this repository
|
||||||
|
|
||||||
Usage: python serve.py [port] [--www PATH]
|
Usage: python serve.py [port] [--www PATH]
|
||||||
default port 8080, default www ../_tools/esp-emu-browser/www
|
default port 8080, default www vendor/esp-emu-browser/www
|
||||||
|
(fetched from the espressif/esp-emulator release by
|
||||||
|
tools/fetch_esp_emu.py — npm run build does that automatically)
|
||||||
"""
|
"""
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
@@ -58,14 +60,18 @@ class Handler(SimpleHTTPRequestHandler):
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
default_www = os.path.normpath(os.path.join(REPO, '..', '_tools', 'esp-emu-browser', 'www'))
|
default_www = os.environ.get(
|
||||||
|
'ESP_EMU_WWW',
|
||||||
|
os.path.normpath(os.path.join(REPO, 'vendor', 'esp-emu-browser', 'www')))
|
||||||
ap = argparse.ArgumentParser(description=__doc__)
|
ap = argparse.ArgumentParser(description=__doc__)
|
||||||
ap.add_argument('port', nargs='?', type=int, default=8080)
|
ap.add_argument('port', nargs='?', type=int, default=8080)
|
||||||
ap.add_argument('--www', default=default_www, help='esp-emu-browser www directory')
|
ap.add_argument('--www', default=default_www, help='esp-emu-browser www directory')
|
||||||
args = ap.parse_args()
|
args = ap.parse_args()
|
||||||
|
|
||||||
if not os.path.isdir(os.path.join(args.www, 'pkg')):
|
if not os.path.isdir(os.path.join(args.www, 'pkg')):
|
||||||
print(f'error: {args.www}/pkg not found — pass --www /path/to/esp-emu-browser/www', file=sys.stderr)
|
print(f'error: {args.www}/pkg not found — run "python tools/fetch_esp_emu.py" '
|
||||||
|
f'(or "npm run build") first, or pass --www /path/to/esp-emu-browser/www',
|
||||||
|
file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
Handler.www_dir = args.www
|
Handler.www_dir = args.www
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -2,4 +2,4 @@
|
|||||||
* Project version (keep in sync with package.json and docs/CHANGELOG.md).
|
* Project version (keep in sync with package.json and docs/CHANGELOG.md).
|
||||||
* Device plugins carry their own per-device version in Device.version.
|
* Device plugins carry their own per-device version in Device.version.
|
||||||
*/
|
*/
|
||||||
export const PERIS_VERSION = '0.10.0';
|
export const PERIS_VERSION = '0.10.1';
|
||||||
+5
-2
@@ -5,7 +5,8 @@ entry HTML, the peris/ tree and examples/. Existing stock files
|
|||||||
(index.html, app.js, worker.js, pkg/) are never touched.
|
(index.html, app.js, worker.js, pkg/) are never touched.
|
||||||
|
|
||||||
Usage: python sync_www.py [--www PATH] [--clean]
|
Usage: python sync_www.py [--www PATH] [--clean]
|
||||||
default www ../_tools/esp-emu-browser/www
|
default www vendor/esp-emu-browser/www (fetched from the
|
||||||
|
espressif/esp-emulator release by tools/fetch_esp_emu.py)
|
||||||
--clean removes a previously synced peris/ from www first
|
--clean removes a previously synced peris/ from www first
|
||||||
"""
|
"""
|
||||||
import argparse
|
import argparse
|
||||||
@@ -39,7 +40,9 @@ def sync(www, clean=False):
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
default_www = os.path.normpath(os.path.join(REPO, '..', '_tools', 'esp-emu-browser', 'www'))
|
default_www = os.environ.get(
|
||||||
|
'ESP_EMU_WWW',
|
||||||
|
os.path.normpath(os.path.join(REPO, 'vendor', 'esp-emu-browser', 'www')))
|
||||||
ap = argparse.ArgumentParser(description=__doc__)
|
ap = argparse.ArgumentParser(description=__doc__)
|
||||||
ap.add_argument('--www', default=default_www, help='esp-emu-browser www directory')
|
ap.add_argument('--www', default=default_www, help='esp-emu-browser www directory')
|
||||||
ap.add_argument('--clean', action='store_true', help='remove old peris/ in www before copying')
|
ap.add_argument('--clean', action='store_true', help='remove old peris/ in www before copying')
|
||||||
|
|||||||
+6
-2
@@ -4,12 +4,16 @@
|
|||||||
* RMT channel-0 RAM symbols during P3, plus cycles() behaviour across a
|
* RMT channel-0 RAM symbols during P3, plus cycles() behaviour across a
|
||||||
* small-batch window. Run: node tests/m0_debug.mjs
|
* small-batch window. Run: node tests/m0_debug.mjs
|
||||||
*/
|
*/
|
||||||
import { readFileSync } from 'node:fs';
|
import { readFileSync, existsSync } from 'node:fs';
|
||||||
import { pathToFileURL, fileURLToPath } from 'node:url';
|
import { pathToFileURL, fileURLToPath } from 'node:url';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
|
||||||
const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||||
const WWW = 'D:/d3/_git/esp_proj/_tools/esp-emu-browser/www';
|
const WWW = process.env.ESP_EMU_WWW || path.join(REPO, 'vendor', 'esp-emu-browser', 'www');
|
||||||
|
if (!existsSync(path.join(WWW, 'pkg', 'esp_emu.js'))) {
|
||||||
|
console.error(`error: ${path.join(WWW, 'pkg', 'esp_emu.js')} not found — run "npm run build" (or "python tools/fetch_esp_emu.py") first, or set ESP_EMU_WWW`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
const FW = path.join(REPO, 'tests/firmware/peris_probe/build/merged-binary.bin');
|
const FW = path.join(REPO, 'tests/firmware/peris_probe/build/merged-binary.bin');
|
||||||
|
|
||||||
const { initSync, WasmEmulator } = await import(
|
const { initSync, WasmEmulator } = await import(
|
||||||
|
|||||||
+6
-2
@@ -17,12 +17,16 @@
|
|||||||
*
|
*
|
||||||
* Usage: node tests/m0_probe.mjs [firmware.bin] [--json]
|
* Usage: node tests/m0_probe.mjs [firmware.bin] [--json]
|
||||||
*/
|
*/
|
||||||
import { readFileSync } from 'node:fs';
|
import { readFileSync, existsSync } from 'node:fs';
|
||||||
import { pathToFileURL, fileURLToPath } from 'node:url';
|
import { pathToFileURL, fileURLToPath } from 'node:url';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
|
||||||
const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||||
const WWW = 'D:/d3/_git/esp_proj/_tools/esp-emu-browser/www';
|
const WWW = process.env.ESP_EMU_WWW || path.join(REPO, 'vendor', 'esp-emu-browser', 'www');
|
||||||
|
if (!existsSync(path.join(WWW, 'pkg', 'esp_emu.js'))) {
|
||||||
|
console.error(`error: ${path.join(WWW, 'pkg', 'esp_emu.js')} not found — run "npm run build" (or "python tools/fetch_esp_emu.py") first, or set ESP_EMU_WWW`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
const FW = process.argv[2] ?? path.join(REPO, 'tests/firmware/peris_probe/build/merged-binary.bin');
|
const FW = process.argv[2] ?? path.join(REPO, 'tests/firmware/peris_probe/build/merged-binary.bin');
|
||||||
|
|
||||||
// --- ESP32-C3 MMIO map (from IDF soc headers, see docs/FEASIBILITY.md) ---
|
// --- ESP32-C3 MMIO map (from IDF soc headers, see docs/FEASIBILITY.md) ---
|
||||||
|
|||||||
+7
-2
@@ -50,7 +50,11 @@ import { pathToFileURL, fileURLToPath } from 'node:url';
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
|
||||||
const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||||
const WWW = 'D:/d3/_git/esp_proj/_tools/esp-emu-browser/www';
|
const WWW = process.env.ESP_EMU_WWW || path.join(REPO, 'vendor', 'esp-emu-browser', 'www');
|
||||||
|
if (!existsSync(path.join(WWW, 'pkg', 'esp_emu.js'))) {
|
||||||
|
console.error(`error: ${path.join(WWW, 'pkg', 'esp_emu.js')} not found — run "npm run build" (or "python tools/fetch_esp_emu.py") first, or set ESP_EMU_WWW`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
const FW = process.argv[2] ?? path.join(REPO, 'tests/firmware/peris_probe/build/merged-binary.bin');
|
const FW = process.argv[2] ?? path.join(REPO, 'tests/firmware/peris_probe/build/merged-binary.bin');
|
||||||
|
|
||||||
const { initSync, WasmEmulator } = await import(
|
const { initSync, WasmEmulator } = await import(
|
||||||
@@ -597,7 +601,8 @@ check('S6 unknown type rejected', ready3?.ok === false && ready3.errors.some((e)
|
|||||||
// decode 0xFF (Code B), scan-limit 7, intensity 4, eight blank digits,
|
// decode 0xFF (Code B), scan-limit 7, intensity 4, eight blank digits,
|
||||||
// shutdown 1 — 14 register words, then the control task pushes values
|
// shutdown 1 — 14 register words, then the control task pushes values
|
||||||
// once WiFi is up (display_show → display_tick → non-zero digits).
|
// once WiFi is up (display_show → display_tick → non-zero digits).
|
||||||
const GUN_FW = 'D:/d3/_git/esp_proj/gun_flag_monster/gun/release/0.1.0/gun-0.1.0-merged.bin';
|
// optional machine-local firmware: set GUN_FW to run this section elsewhere
|
||||||
|
const GUN_FW = process.env.GUN_FW || 'D:/d3/_git/esp_proj/gun_flag_monster/gun/release/0.1.0/gun-0.1.0-merged.bin';
|
||||||
if (existsSync(GUN_FW)) {
|
if (existsSync(GUN_FW)) {
|
||||||
const emu2 = new WasmEmulator('esp32s3');
|
const emu2 = new WasmEmulator('esp32s3');
|
||||||
if (emu2.has_default_rom()) emu2.load_default_rom();
|
if (emu2.has_default_rom()) emu2.load_default_rom();
|
||||||
|
|||||||
@@ -0,0 +1,229 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Fetch the esp-emulator browser (wasm) build from the espressif/esp-emulator
|
||||||
|
GitHub releases and unpack it into vendor/esp-emu-browser/www.
|
||||||
|
|
||||||
|
- Idempotent: skips the download when the requested version is already in
|
||||||
|
place (vendor/esp-emu-browser/VERSION + www/pkg/esp_emu_bg.wasm).
|
||||||
|
- Verifies the tarball against the release SHA256SUMS asset.
|
||||||
|
- Honours the standard proxy environment variables
|
||||||
|
(https_proxy / HTTPS_PROXY / all_proxy ...), e.g.
|
||||||
|
https_proxy=http://127.0.0.1:10809 python tools/fetch_esp_emu.py
|
||||||
|
|
||||||
|
Usage: python tools/fetch_esp_emu.py [--version 0.51.0] [--force] [--no-verify]
|
||||||
|
version default: 0.51.0 (or env ESP_EMU_VERSION)
|
||||||
|
downloaded tarballs are cached in vendor/downloads/ (never re-downloaded)
|
||||||
|
|
||||||
|
Afterwards: python serve.py -> http://127.0.0.1:8080/esp-emu-peris.html
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import hashlib
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
import tarfile
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
import urllib.error
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
DEFAULT_VERSION = '0.51.0'
|
||||||
|
VENDOR_DIR = os.path.join(REPO, 'vendor', 'esp-emu-browser')
|
||||||
|
CACHE_DIR = os.path.join(REPO, 'vendor', 'downloads')
|
||||||
|
RETRIES = 3
|
||||||
|
TIMEOUT = 60
|
||||||
|
|
||||||
|
|
||||||
|
def log(msg):
|
||||||
|
print(f'[fetch_esp_emu] {msg}', flush=True)
|
||||||
|
|
||||||
|
|
||||||
|
def build_opener():
|
||||||
|
"""urllib opener that honours proxy env vars (https_proxy etc.)."""
|
||||||
|
proxies = {}
|
||||||
|
# explicit env first (works everywhere, incl. Windows without registry proxies)
|
||||||
|
for scheme in ('http', 'https', 'all'):
|
||||||
|
for var in (f'{scheme}_proxy'.lower(), f'{scheme}_proxy'.upper()):
|
||||||
|
val = os.environ.get(var)
|
||||||
|
if val:
|
||||||
|
proxies.setdefault('https' if scheme == 'all' else scheme, val)
|
||||||
|
if not proxies:
|
||||||
|
proxies = urllib.request.getproxies()
|
||||||
|
return urllib.request.build_opener(urllib.request.ProxyHandler(proxies))
|
||||||
|
|
||||||
|
|
||||||
|
def download(opener, url, dest, retries=RETRIES):
|
||||||
|
for attempt in range(1, retries + 1):
|
||||||
|
try:
|
||||||
|
log(f'downloading {url}')
|
||||||
|
with opener.open(url, timeout=TIMEOUT) as resp, open(dest, 'wb') as out:
|
||||||
|
total = int(resp.headers.get('Content-Length') or 0)
|
||||||
|
done = 0
|
||||||
|
while True:
|
||||||
|
chunk = resp.read(1 << 16)
|
||||||
|
if not chunk:
|
||||||
|
break
|
||||||
|
out.write(chunk)
|
||||||
|
done += len(chunk)
|
||||||
|
if total:
|
||||||
|
pct = done * 100 // total
|
||||||
|
print(f'\r {done // 1024} / {total // 1024} KiB ({pct}%)', end='', flush=True)
|
||||||
|
print()
|
||||||
|
return True
|
||||||
|
except (urllib.error.URLError, OSError) as e:
|
||||||
|
log(f'download attempt {attempt}/{retries} failed: {e}')
|
||||||
|
if os.path.exists(dest):
|
||||||
|
os.remove(dest)
|
||||||
|
if attempt < retries:
|
||||||
|
time.sleep(2 * attempt)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def sha256_of(path):
|
||||||
|
h = hashlib.sha256()
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
for chunk in iter(lambda: f.read(1 << 20), b''):
|
||||||
|
h.update(chunk)
|
||||||
|
return h.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def verify(opener, tarball, version, asset):
|
||||||
|
"""Check the tarball against the release SHA256SUMS asset (best effort
|
||||||
|
by default: a failed SUMS *download* only warns, a hash mismatch fails)."""
|
||||||
|
sums_url = (f'https://github.com/espressif/esp-emulator/releases/download/'
|
||||||
|
f'v{version}/SHA256SUMS')
|
||||||
|
try:
|
||||||
|
with opener.open(sums_url, timeout=TIMEOUT) as resp:
|
||||||
|
text = resp.read().decode('utf-8', 'replace')
|
||||||
|
except (urllib.error.URLError, OSError) as e:
|
||||||
|
log(f'warning: could not fetch SHA256SUMS ({e}); skipping verification')
|
||||||
|
return True
|
||||||
|
want = None
|
||||||
|
for line in text.splitlines():
|
||||||
|
parts = line.split()
|
||||||
|
if len(parts) == 2 and parts[1] == asset:
|
||||||
|
want = parts[0]
|
||||||
|
break
|
||||||
|
if want is None:
|
||||||
|
log(f'warning: {asset} not listed in SHA256SUMS; skipping verification')
|
||||||
|
return True
|
||||||
|
got = sha256_of(tarball)
|
||||||
|
if got != want:
|
||||||
|
log(f'error: sha256 mismatch for {asset}\n want {want}\n got {got}')
|
||||||
|
return False
|
||||||
|
log(f'sha256 ok ({want[:16]}…)')
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def safe_extract(tar, dest):
|
||||||
|
try:
|
||||||
|
tar.extractall(dest, filter='data') # Python 3.12+
|
||||||
|
return
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
# older Pythons: reject absolute paths and .. escapes manually
|
||||||
|
base = os.path.abspath(dest)
|
||||||
|
for m in tar.getmembers():
|
||||||
|
target = os.path.abspath(os.path.join(dest, m.name))
|
||||||
|
if not target.startswith(base + os.sep) and target != base:
|
||||||
|
raise RuntimeError(f'unsafe path in archive: {m.name}')
|
||||||
|
tar.extractall(dest)
|
||||||
|
|
||||||
|
|
||||||
|
def move_tree(src, dst):
|
||||||
|
"""Directory rename with retries: Windows Defender/indexer transiently
|
||||||
|
locks freshly extracted files (WinError 5); copy+delete as a fallback."""
|
||||||
|
for attempt in range(4):
|
||||||
|
try:
|
||||||
|
os.rename(src, dst)
|
||||||
|
return
|
||||||
|
except OSError:
|
||||||
|
time.sleep(0.5 * (attempt + 1))
|
||||||
|
shutil.copytree(src, dst, dirs_exist_ok=True)
|
||||||
|
shutil.rmtree(src, ignore_errors=True)
|
||||||
|
|
||||||
|
|
||||||
|
def install(tarball, version):
|
||||||
|
"""Unpack <root>/esp-emu-<ver>-wasm/www into vendor/esp-emu-browser/www."""
|
||||||
|
vendor_parent = os.path.join(REPO, 'vendor')
|
||||||
|
staging = os.path.join(vendor_parent, f'.staging-{int(time.time())}')
|
||||||
|
old = os.path.join(VENDOR_DIR, 'www.old')
|
||||||
|
target = os.path.join(VENDOR_DIR, 'www')
|
||||||
|
os.makedirs(staging, exist_ok=True)
|
||||||
|
with tarfile.open(tarball, 'r:gz') as tar:
|
||||||
|
safe_extract(tar, staging)
|
||||||
|
inner = os.path.join(staging, f'esp-emu-{version}-wasm', 'www')
|
||||||
|
if not os.path.isfile(os.path.join(inner, 'pkg', 'esp_emu_bg.wasm')):
|
||||||
|
shutil.rmtree(staging, ignore_errors=True)
|
||||||
|
raise RuntimeError(f'unexpected archive layout: {inner}/pkg missing')
|
||||||
|
os.makedirs(VENDOR_DIR, exist_ok=True)
|
||||||
|
if os.path.isdir(old):
|
||||||
|
shutil.rmtree(old, ignore_errors=True)
|
||||||
|
if os.path.isdir(target):
|
||||||
|
move_tree(target, old)
|
||||||
|
try:
|
||||||
|
move_tree(inner, target)
|
||||||
|
except OSError:
|
||||||
|
if os.path.isdir(old) and not os.path.isdir(target): # best-effort restore
|
||||||
|
move_tree(old, target)
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
shutil.rmtree(staging, ignore_errors=True)
|
||||||
|
shutil.rmtree(old, ignore_errors=True)
|
||||||
|
with open(os.path.join(VENDOR_DIR, 'VERSION'), 'w', encoding='utf-8') as f:
|
||||||
|
f.write(f'{version}\n')
|
||||||
|
log(f'installed esp-emulator browser {version} -> '
|
||||||
|
f'{os.path.relpath(target, REPO).replace(os.sep, "/")}')
|
||||||
|
|
||||||
|
|
||||||
|
def already_installed(version):
|
||||||
|
return (os.path.isfile(os.path.join(VENDOR_DIR, 'VERSION'))
|
||||||
|
and open(os.path.join(VENDOR_DIR, 'VERSION'), encoding='utf-8').read().strip() == version
|
||||||
|
and os.path.isfile(os.path.join(VENDOR_DIR, 'www', 'pkg', 'esp_emu_bg.wasm')))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser(
|
||||||
|
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||||
|
ap.add_argument('--version', default=os.environ.get('ESP_EMU_VERSION', DEFAULT_VERSION),
|
||||||
|
help=f'esp-emulator release tag without "v" (default {DEFAULT_VERSION})')
|
||||||
|
ap.add_argument('--force', action='store_true', help='re-download/re-extract even if present')
|
||||||
|
ap.add_argument('--no-verify', action='store_true', help='skip sha256 verification')
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
version = args.version.strip().lstrip('vV')
|
||||||
|
asset = f'esp-emu-{version}-wasm.tar.gz'
|
||||||
|
url = (f'https://github.com/espressif/esp-emulator/releases/download/'
|
||||||
|
f'v{version}/{asset}')
|
||||||
|
|
||||||
|
if already_installed(version) and not args.force:
|
||||||
|
log(f'esp-emulator browser {version} already present '
|
||||||
|
f'(vendor/esp-emu-browser) — nothing to do')
|
||||||
|
return 0
|
||||||
|
|
||||||
|
os.makedirs(CACHE_DIR, exist_ok=True)
|
||||||
|
tarball = os.path.join(CACHE_DIR, asset)
|
||||||
|
opener = build_opener()
|
||||||
|
|
||||||
|
if os.path.isfile(tarball) and not args.force:
|
||||||
|
log(f'using cached {os.path.relpath(tarball, REPO)}')
|
||||||
|
else:
|
||||||
|
if not download(opener, url, tarball):
|
||||||
|
log(f'error: download failed after {RETRIES} attempts '
|
||||||
|
f'(behind a firewall? set https_proxy=… and retry)')
|
||||||
|
return 1
|
||||||
|
if not args.no_verify and not verify(opener, tarball, version, asset):
|
||||||
|
os.remove(tarball)
|
||||||
|
return 1
|
||||||
|
try:
|
||||||
|
install(tarball, version)
|
||||||
|
except (RuntimeError, tarfile.TarError, OSError) as e:
|
||||||
|
log(f'error: extraction failed: {e}')
|
||||||
|
return 1
|
||||||
|
log('next: python serve.py -> http://127.0.0.1:8080/esp-emu-peris.html')
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Build the esp-emu-peris release archives for the GitHub release page.
|
||||||
|
|
||||||
|
Stages the repository (git-tracked files + the compiled build/ tree) into
|
||||||
|
dist/package/esp-emu-peris-<version>/ and writes
|
||||||
|
dist/release/esp-emu-peris-<version>.zip and .tar.gz.
|
||||||
|
|
||||||
|
The archives include install.sh / install.bat, which fetch the
|
||||||
|
esp-emulator browser (wasm) build from espressif/esp-emulator releases —
|
||||||
|
so a release user needs no local esp-emulator checkout.
|
||||||
|
|
||||||
|
Usage: python tools/package_release.py (run "npm run build" first)
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tarfile
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
DIST = os.path.join(REPO, 'dist')
|
||||||
|
EXTRA_DIRS = ['build'] # not in git, but shipped pre-compiled
|
||||||
|
|
||||||
|
|
||||||
|
def log(msg):
|
||||||
|
print(f'[package_release] {msg}', flush=True)
|
||||||
|
|
||||||
|
|
||||||
|
def tracked_files():
|
||||||
|
out = subprocess.run(['git', 'ls-files'], cwd=REPO, capture_output=True,
|
||||||
|
text=True, check=True).stdout
|
||||||
|
return [line for line in out.splitlines() if line.strip()]
|
||||||
|
|
||||||
|
|
||||||
|
def stage(version):
|
||||||
|
stage_root = os.path.join(DIST, 'package')
|
||||||
|
stage_dir = os.path.join(stage_root, f'esp-emu-peris-{version}')
|
||||||
|
if os.path.isdir(stage_root):
|
||||||
|
shutil.rmtree(stage_root)
|
||||||
|
os.makedirs(stage_dir)
|
||||||
|
|
||||||
|
files = tracked_files()
|
||||||
|
skip = {'.gitignore', '.gitattributes'} # not meaningful outside a repo
|
||||||
|
skip_prefixes = ('.vscode/',) # IDE prefs carry machine-local paths
|
||||||
|
copied = 0
|
||||||
|
for rel in files:
|
||||||
|
if rel in skip or rel.startswith(skip_prefixes):
|
||||||
|
continue
|
||||||
|
src = os.path.join(REPO, rel)
|
||||||
|
if not os.path.isfile(src):
|
||||||
|
continue
|
||||||
|
dst = os.path.join(stage_dir, rel)
|
||||||
|
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
||||||
|
shutil.copy2(src, dst)
|
||||||
|
copied += 1
|
||||||
|
for d in EXTRA_DIRS:
|
||||||
|
src = os.path.join(REPO, d)
|
||||||
|
if not os.path.isdir(src):
|
||||||
|
log(f'warning: {d}/ missing — run "npm run build" first')
|
||||||
|
continue
|
||||||
|
for root, _dirs, names in os.walk(src):
|
||||||
|
for name in names:
|
||||||
|
s = os.path.join(root, name)
|
||||||
|
rel = os.path.relpath(s, REPO)
|
||||||
|
dst = os.path.join(stage_dir, rel)
|
||||||
|
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
||||||
|
shutil.copy2(s, dst)
|
||||||
|
copied += 1
|
||||||
|
log(f'staged {copied} files -> {os.path.relpath(stage_dir, REPO)}')
|
||||||
|
return stage_dir
|
||||||
|
|
||||||
|
|
||||||
|
def make_zip(stage_dir, dest):
|
||||||
|
with zipfile.ZipFile(dest, 'w', zipfile.ZIP_DEFLATED, compresslevel=9) as zf:
|
||||||
|
for root, _dirs, names in os.walk(stage_dir):
|
||||||
|
for name in sorted(names):
|
||||||
|
s = os.path.join(root, name)
|
||||||
|
zf.write(s, os.path.relpath(s, os.path.dirname(stage_dir)))
|
||||||
|
log(f'wrote {os.path.relpath(dest, REPO)} ({os.path.getsize(dest) // 1024} KiB)')
|
||||||
|
|
||||||
|
|
||||||
|
def make_targz(stage_dir, dest):
|
||||||
|
with tarfile.open(dest, 'w:gz') as tf:
|
||||||
|
tf.add(stage_dir, arcname=os.path.basename(stage_dir))
|
||||||
|
log(f'wrote {os.path.relpath(dest, REPO)} ({os.path.getsize(dest) // 1024} KiB)')
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
with open(os.path.join(REPO, 'package.json'), encoding='utf-8') as f:
|
||||||
|
version = json.load(f)['version']
|
||||||
|
release_dir = os.path.join(DIST, 'release')
|
||||||
|
os.makedirs(release_dir, exist_ok=True)
|
||||||
|
stage_dir = stage(version)
|
||||||
|
make_zip(stage_dir, os.path.join(release_dir, f'esp-emu-peris-{version}.zip'))
|
||||||
|
make_targz(stage_dir, os.path.join(release_dir, f'esp-emu-peris-{version}.tar.gz'))
|
||||||
|
log('release archives ready — upload with:')
|
||||||
|
log(f' gh release create v{version} '
|
||||||
|
f'dist/release/esp-emu-peris-{version}.zip '
|
||||||
|
f'dist/release/esp-emu-peris-{version}.tar.gz')
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
Reference in new issue
Block a user