Imported from openLuat/LuatOS (
bsp/pc/AGENTS.md). Install upstream withnpx skills add openLuat/LuatOS --skill pc. Copyright stays with the author.
LuatOS PC Simulator
Scope: bsp/pc/ - PC simulator for Windows/Linux/macOS development.
OVERVIEW
PC simulator allowing LuatOS development and testing on desktop without hardware. Uses SDL2 for GUI simulation.
STRUCTURE
pc/
├── src/ # Simulator-specific code
├── port/ # Platform porting layer
├── include/ # Headers
├── win32/ # Windows-specific
├── ui/ # UI test code
└── xmake.lua # Build configuration
BUILD
cd bsp/pc
Build Selection Rules
- Use plain
xmake -yonly for fast verification of non-GUI changes - If the change touches
components/airui/, LVGL, SDL display flow, or any code gated byLUAT_USE_GUI, you MUST use a GUI-enabled build - Do not report AirUI-related verification as complete if you only ran plain
xmake -y - Prefer the existing platform helper scripts when available, because they already set the expected GUI environment flags
- Helper scripts now default to incremental
summarymode to reduce low-value warning noise during routine development - Helper scripts also normalize xmake to
--theme=plainso build logs and terminal output do not include ANSI color noise - Use
fullto see raw compiler output, and usecleanonly when you explicitly need a clean rebuild - Summary/full logs are written to
bsp/pc/build/logs/
gmssl 构建(本地源码,不使用 xmake 包)
PC 模拟器直接编译 components/gmssl(与 ARM 固件同源的 LuatOS 分支),不再依赖 xmake 的 gmssl 包。该目录是裁剪过的 GmSSL:只保留 SM2/SM3/SM4 及其直接依赖,没有 x509 / pem / pkcs8 / ec / digest / base64 / aes 等实现。保留的源码里仍有一些未被调用的辅助函数(*_print、PEM/X509 导入导出)引用这些上游符号:
- ARM 固件构建用
-ffunction-sections+--gc-sections把未引用函数整段丢弃,所以那些符号不进链接; - MSVC 没有等价能力——
/Gy+/OPT:REF(甚至/LTCG)只做减体积,未引用函数里的未定义符号照样报LNK2019。已实测确认,不要指望在 Windows 上靠 GC 通过。
因此 bsp/pc/xmake.lua 里维护一份显式源码清单(gmssl_sources,只含 Lua 绑定真正用到的 TU),其余缺符号由 bsp/pc/port/gmssl/luat_gmssl_pc_stubs.c 兜底:打印/校验类给出真实或最小实现,aes_* / ec_* / pem_* / pkcs8_* / x509_* / DIGEST_sm3 / pbkdf2_genkey 等按“PC 未编译该功能”处理(告警 + 返回失败,均不在 Lua 绑定调用链上)。
新增 Lua 侧 gmssl 接口时,若用到清单外的 TU(例如 sm2_key_share.c、sm4_rng.c),把它加进 gmssl_sources 并按链接报错补 stub。
另外:gmssl/mem.h 在 __LUATOS__ 下把 malloc/free 宏替换成 luat_heap_*,所以 gmssl 的 TU 必须先包含 stdlib.h(xmake.lua 用 /FIstdlib.h / -include stdlib.h 实现),否则 MSVC 会在 corecrt_malloc.h 报 C2375。
不要为了让链接通过而往 components/gmssl/ 里加文件或改其源码——固件构建会 glob 编译该目录下所有 .c(luatos-soc-2022、luatos-sdk-rda8910、luatos-sdk-ccm42xx-gcc 都是全量)。PC 侧需要补齐的符号一律放 bsp/pc/port/gmssl/。
Windows
| Script | Description |
|---|---|
build_windows_32bit_msvc.bat |
32-bit, no GUI |
build_windows_32bit_msvc_gui.bat |
32-bit, GUI (with clean) |
build_windows_64bit_msvc.bat |
64-bit, no GUI |
build_windows_64bit_msvc_gui.bat |
64-bit, GUI |
Recommended:
- Non-GUI verification:
build_windows_64bit_msvc.bat - GUI / AirUI verification:
build_windows_64bit_msvc_gui.bat(orbuild_windows_32bit_msvc_gui.batwhen targeting 32-bit) - Full output: append
full - Clean rebuild: append
clean
Examples:
build_windows_64bit_msvc.batbuild_windows_64bit_msvc.bat fullbuild_windows_64bit_msvc_gui.bat clean
Linux
| Script | Description |
|---|---|
build_linux_32bit.sh |
32-bit i386 |
build_linux_32bit_armv6.sh |
32-bit ARMv6 |
build_linux_64bit.sh |
64-bit x86_64 (requires gcc multilib) |
build_linux_64bit_gui.sh |
64-bit x86_64 GUI (requires gcc multilib) |
build_linux_aarch64.sh |
64-bit ARM/AArch64 native |
build_linux_aarch64_gui.sh |
64-bit ARM/AArch64 native GUI |
Recommended:
- Non-GUI verification:
./build_linux_64bit.sh(x86_64) or./build_linux_aarch64.sh(ARM64) - GUI / AirUI verification:
./build_linux_64bit_gui.sh(x86_64) or./build_linux_aarch64_gui.sh(ARM64) - Full output: append
full - Clean rebuild: append
clean
AArch64 / ARM64 native build
On AArch64 hosts (e.g. Raspberry Pi 4/5, ARM cloud instances, Apple Silicon Linux VMs), the x86_64 helper scripts will fail because the toolchain does not support -m32/-m64. Use the build_linux_aarch64*.sh scripts instead.
Required system packages (Ubuntu/Debian example):
sudo apt-get install -y libsdl2-dev libx11-dev libxau-dev libxdmcp-dev \
xorg-dev libssl-dev libasound2-dev libpulse-dev libwayland-dev \
libxkbcommon-dev libudev-dev libdbus-1-dev libibus-1.0-dev libdecor-0-dev
Then build:
cd bsp/pc
./build_linux_aarch64.sh
Notes:
- The AArch64 build defines
LUAT_CONF_USE_LIBSYS_SOURCEso that the inlinesys.lua/sysplus.luaare loaded as source rather than precompiled 32-bit luac, avoidingsize_t size mismatcherrors on 64-bit platforms. xmakerefuses to run as root by default; setXMAKE_ROOT=yor pass--rootif you are building as root.
macOS
| Script | Description |
|---|---|
build_macos.sh |
no GUI |
build_macos_gui.sh |
GUI |
Recommended:
- Non-GUI verification:
./build_macos.sh - GUI / AirUI verification:
./build_macos_gui.sh - Full output: append
full - Clean rebuild: append
clean
Output: build/out/luatos-lua.exe (Windows) or build/out/luatos-lua (Linux/macOS)
C-Layer UTest Coverage
- Canonical helper:
bsp\pc\pc_utest_coverage.ps1 - Typical usage:
cd bsp\pc && .\pc_utest_coverage.ps1 -Suite dtls_basiccd bsp\pc && .\pc_utest_coverage.ps1 -Suite tcp_basiccd bsp\pc && .\pc_utest_coverage.ps1 -Suite http_basic -SkipBuildcd bsp\pc && .\pc_utest_coverage.ps1 -Suite https_basic -SkipBuild
- After the first suite build, reuse the same binary for more suites with
-SkipBuild -Suiteand-TestcaseScriptsare mutually exclusive; pass only one of them-Suite <name>is resolved by scanningtestcase/utest/{net,lib,sys,fs}/<name>/first, thentestcase/unit/<domain>/<name>/andtestcase/func/<domain>/<name>/as fallbacks- Coverage HTML is written to
build\coverage\<suite>\html\index.html - C-layer utest suites (under
testcase\utest\):net/dtls_basic: PC-only DTLS-PSK loopback against127.0.0.1net/tcp_basic: TCP reachability againstwww.qq.com:80net/http_basic: HTTP reachability againsthttp://www.qq.comnet/https_basic: HTTPS reachability againsthttps://www.qq.comlib/core_basic,lib/crypto_basic,lib/miniz_basic,sys/ndk_basicfs/pgfs_basic: PGFS filesystem replay/FTL/gc selftests (all 17 tests are fully passing)
- Additional PC socket regression:
socket_udp_limit_basic: verifiessocket.rx(..., limit)truncates UDP data and discards the unread datagram tail
- DTLS loopback depends on the PC mbedTLS3 config in
include\mbedtls_config_pc_mbedtls3.henabling DTLS server/PSK support
UTest 快速运行(无覆盖率)
直接跑单个 utest 用例(跳过 OpenCppCoverage,只看 pass/fail):
# ⚠️ 必须先带 LUAT_USE_UTEST=y 编译,否则 socket.utest / http.utest 等函数不存在
cd bsp\pc
$env:LUAT_USE_UTEST = "y"
cmd /c build_windows_32bit_msvc.bat # 或 build_windows_64bit_msvc.bat
# 运行(需要传两个脚本目录:common + 目标 testcase)
cd build\out
.\luatos-lua.exe ..\..\..\..\testcase\common\scripts\ ..\..\..\..\testcase\utest\net\dtls_basic\scripts\
输出中搜索 OVERALL_PASS / OVERALL_FAIL 判断结果。
pc_utest_coverage.ps1 已内置 LUAT_USE_UTEST=y,使用它时无需手动设置。
FEATURES
- Lua 5.3 VM execution
- GUI simulation (SDL2)
- Network (via host OS adapter)
- TCP Client & Server (listen/accept)
- File system (host filesystem)
- Most libraries supported
NETWORK ADAPTER
File: port/network/luat_network_adapter_posix.c
The PC simulator uses the host OS adapter for async network I/O. Key architecture:
Threading Model
- Main thread: Lua VM, message bus, coroutine scheduling
- Network worker: Handles async I/O callbacks
- Bridge:
cb_to_nw_task()forwards network events to framework callbacks
Socket States
SC_IDLE → SC_USED → SC_CONNECTING → SC_CONNECTED → SC_CLOSING → SC_CLOSED
→ SC_LISTENING (TCP Server)
TCP Server Flow (one-to-one mode, no_accept=1)
socket_listen: creates listener handle, binds, listenson_new_connection: accepts client into socket's embeddedtcphandle- State: LISTEN(6) → ONLINE(5)
- Close: closes both
listen_tcpand embeddedtcp
⚠️ Critical Async Pitfalls
| Pitfall | Detail |
|---|---|
| Do not memcpy runtime handles | Runtime-managed handles may carry internal state; copying them can corrupt async flow. |
| Async close lifecycle | Close callbacks fire later; keep handles valid until close callback runs. |
| Accept target readiness | The target handle must be initialized but not connected. |
| Thread safety | Async callbacks may run on a different thread; be careful with Lua state access. |
PC 模拟器测试脚本
独立 PC 脚本存放在 bsp/pc/test/<编号>.<名称>/main.lua,不依赖 testcase/common 的 testrunner/testsuite 框架。新增脚本时从当前最大编号续编。
这些脚本用于验证 PC 模拟器特有功能或需要真实 PC 外设(摄像头、CH347、网卡模拟等)的场景,与 testcase/ 中正式测试框架分离。
示例运行方式:
cd bsp/pc/build/out
./luatos-lua.exe ../../../test/131.camera_pc/
WHERE TO LOOK
| Task | Location |
|---|---|
| Main entry | src/main.c |
| Build config | xmake.lua |
| Platform port | port/ |
| Network adapter | port/network/luat_network_adapter_posix.c |
| UI tests | ui/ |
| Debug records | docs/ |
CONVENTIONS
Adding PC Support:
- Add files to
src/orport/ - Update
xmake.luawith platform detection - Use
#ifdef LUAT_USE_WINDOWSetc.
PC 模拟器测试脚本规范
⚠️ 必须在任务内部调用 os.exit(0)
PC 模拟器的 sys.run() 进入事件循环,该循环不会因为 Lua 任务结束而自动退出——只要还有任何活跃句柄(如网络、文件、定时器),进程就会一直挂起等待。
正确写法(os.exit(0) 在任务协程内、sys.run() 之前执行):
sys.taskInit(function()
-- ... 执行任务逻辑 ...
videoplayer.close(player)
log.info("main", "完成")
os.exit(0) -- ✅ 在任务内部强制退出
end)
sys.run() -- 启动事件循环(os.exit 会在任务中断开它)
错误写法(os.exit(0) 在 sys.run() 之后,永远不会执行):
sys.taskInit(function()
-- ...
end)
sys.run() -- ❌ 挂在这里,os.exit 永远不会执行
os.exit(0)
适用场景:所有在 PC 模拟器上运行的独立测试脚本(非 testrunner 框架管理的)都应遵循此规范。使用 testrunner 框架的标准测试用例由框架自身负责调用 os.exit,无需手动添加。
ad_fopen 文件 API 与 luat_fs VFS
player SDK 中的 plat_support.c 通过 #ifdef __LUATOS__ 条件编译,在 LuatOS 构建(包括 PC 模拟器)上将 ad_fopen/fread/fseek/fclose/fsize 路由到 luat_fs_* VFS 函数,而非直接使用 FatFS 或 stdio。
- 优点:文件路径通过 LuatOS VFS 解析,PC 模拟器启动时传入的脚本目录自动映射为根路径,
ad_fopen("foo.mp4", ...)等价于luat_fs_fopen("/lua/foo.mp4", ...)(取决于 VFS 挂载配置) - 无需替换:不必将
ad_fopen换成fopen或luat_fs_fopen,在__LUATOS__构建中它们本质上是同一个 - 文件路径:测试时将资源文件放在脚本目录下,VFS 会正确解析
ANTI-PATTERNS
- ❌ Do NOT assume PC == hardware behavior (timing differences)
- ❌ Do NOT hardcode paths - use relative paths
- ❌ Do NOT skip hardware validation phase
OPTIONAL FEATURES (环境变量开关)
PC 模拟器支持通过环境变量按需启用可选功能,遵循与 LUAT_USE_GUI、LUAT_USE_MGBA 相同的模式:
| 环境变量 | 值 | 说明 |
|---|---|---|
LUAT_USE_GUI |
y |
启用 SDL2 GUI / AirUI |
LUAT_USE_MGBA |
y |
启用 mGBA GameBoy 模拟器 |
LUAT_USE_MP4PLAYER |
y |
启用 MP4/H.264/AAC 解码器 |
LUAT_USE_UTEST |
y |
启用 C 层 utest(socket.utest、http.utest 等)。覆盖率和直接运行都需要 |
MP4PLAYER_SRC_DIR |
路径 | mp4player 源码根目录(与 LUAT_USE_MP4PLAYER=y 配合使用) |
LUATOS_EXT_ROOT |
路径 | luatos-ext-components 仓库根目录。未设置时自动从脚本目录向上三级查找;在 git worktree 中必须设置此变量(或依赖自动 worktree 检测)。 |
mp4player 启用示例(PowerShell)
$env:LUAT_USE_MP4PLAYER = "y"
$env:MP4PLAYER_SRC_DIR = "D:/github/luatos-sdk-ccm42xx-gcc/csdk/project/luatos/player"
cmd /c build_windows_32bit_msvc.bat
⚠️ xmake remove_files + add_files 陷阱
问题:在 if 块外无条件调用 remove_files("port/mp4player/*.c"),然后在 if 块内调用 add_files("port/mp4player/foo.c") 尝试加回某些文件——该文件永远不会被编译。remove_files 维护内部排除名单,后续 add_files 无法绕过。
正确做法:将只在条件下编译的 stub 文件放到不被任何全局通配符覆盖的目录,如 stubs/<feature>/:
-- ❌ 错误:remove_files 黑名单无法被 add_files 解除
remove_files("port/mp4player/*.c")
if os.getenv("LUAT_USE_MP4PLAYER") == "y" then
add_files("port/mp4player/dac_sound_pc.c") -- 无效
end
-- ✅ 正确:stubs/ 目录不在 port/**.c 通配符范围内
if os.getenv("LUAT_USE_MP4PLAYER") == "y" then
add_files("stubs/mp4player/dac_sound_pc.c") -- 正常编译
end
目录约定:bsp/pc/stubs/<feature>/ — 存放只在特定可选功能开启时才参与编译的 PC stub 文件。