Initial commit
This commit is contained in:
394
scripts/build.ps1
Normal file
394
scripts/build.ps1
Normal file
@@ -0,0 +1,394 @@
|
||||
|
||||
# build.ps1 - 智能 WASM 构建脚本
|
||||
param(
|
||||
[switch]$Clean,
|
||||
[switch]$Serve,
|
||||
[switch]$Test,
|
||||
[string]$BuildType = "Release"
|
||||
)
|
||||
|
||||
Write-Host "=========================================" -ForegroundColor Cyan
|
||||
Write-Host " 智能 JSON-WASM 项目构建系统 " -ForegroundColor Cyan
|
||||
Write-Host "=========================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# 1. 设置环境变量
|
||||
$EmsdkPath = "E:\emsdk"
|
||||
if (-not (Test-Path $EmsdkPath)) {
|
||||
Write-Host "[错误] 未找到 Emscripten" -ForegroundColor Red
|
||||
Write-Host "请确保 Emscripten 安装在: $EmsdkPath" -ForegroundColor Yellow
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 设置 PATH
|
||||
$env:PATH = "$EmsdkPath\upstream\emscripten;$env:PATH"
|
||||
Write-Host "[✓] 设置 Emscripten 环境" -ForegroundColor Green
|
||||
|
||||
# 2. 清理旧构建
|
||||
if ($Clean) {
|
||||
Write-Host "[ ] 清理构建文件..." -ForegroundColor Cyan
|
||||
Remove-Item -Path "build", "public/wasm" -Recurse -Force -ErrorAction SilentlyContinue
|
||||
Write-Host "[✓] 清理完成" -ForegroundColor Green
|
||||
}
|
||||
|
||||
# 3. 创建目录
|
||||
New-Item -Path "build", "public/wasm" -ItemType Directory -Force | Out-Null
|
||||
|
||||
# 4. 编译 WASM
|
||||
Write-Host "[ ] 编译智能 WASM 模块..." -ForegroundColor Cyan
|
||||
$startTime = Get-Date
|
||||
|
||||
# 编译命令 - 使用当前真实业务模块源码
|
||||
$compileResult = emcc `
|
||||
src/KinematicsWebAPI.cpp `
|
||||
src/main.cpp `
|
||||
src/Robot.cpp `
|
||||
src/spc_core.cpp `
|
||||
src/QuadrupedRobotSimulation/RobotConfig.cpp `
|
||||
src/QuadrupedRobotSimulation/BaseClass.cpp `
|
||||
src/QuadrupedRobotSimulation/KinematicsSimulation.cpp `
|
||||
src/QuadrupedRobotSimulation/CompleteJsonExporter.cpp `
|
||||
src/QuadrupedRobotSimulation/KinematicsReverse.cpp `
|
||||
src/QuadrupedRobotSimulation/KinematicsHelper.cpp `
|
||||
src/QuadrupedRobotSimulation/SharedGeometry.cpp `
|
||||
src/FourBarMechanism/CrankSliderMechanism.cpp `
|
||||
src/FourBarMechanism/CrankRockingBlockMechanism_Forward.cpp `
|
||||
src/FourBarMechanism/CrankRockingBlockMechanism_Inverse.cpp `
|
||||
src/FourBarMechanism/FourBarMechanism.cpp `
|
||||
src/FourBarMechanism/SliderCrankMechanism.cpp `
|
||||
src/RobotManager.cpp `
|
||||
src/utils.cpp `
|
||||
src/math_utils.cpp `
|
||||
src/smart_json_wrapper.cpp `
|
||||
src/kdl_parser/src/kdl_parser.cpp `
|
||||
src/kdl_parser/src/joint.cpp `
|
||||
src/kdl_parser/src/link.cpp `
|
||||
src/kdl_parser/src/model.cpp `
|
||||
src/kdl_parser/src/pose.cpp `
|
||||
src/kdl_parser/src/tinystr.cpp `
|
||||
src/kdl_parser/src/tinyxml.cpp `
|
||||
src/kdl_parser/src/tinyxml2.cpp `
|
||||
src/kdl_parser/src/tinyxmlerror.cpp `
|
||||
src/kdl_parser/src/tinyxmlparser.cpp `
|
||||
src/kdl_parser/src/twist.cpp `
|
||||
src/kdl_parser/src/world.cpp `
|
||||
-I./inc `
|
||||
-I./inc/QuadrupedRobotSimulation `
|
||||
-I./inc/FourBarMechanism `
|
||||
-I./inc/SharedGeometry `
|
||||
-I./src/kdl_parser/include `
|
||||
-I./KDL/install_wasm/include `
|
||||
-I./Eigen3 `
|
||||
-I./Eigen3/eigen3 `
|
||||
-L./KDL/install_wasm/lib `
|
||||
-lorocos-kdl `
|
||||
-std=c++17 `
|
||||
-s WASM=1 `
|
||||
-s EXPORTED_FUNCTIONS='["_init_func","_func","_smart_process_json","_smart_get_function_list","_smart_get_function_info","_smart_test_match","_smart_get_version","_smart_free_string","_wasm_malloc","_wasm_free","_malloc","_free","_add","_subtract","_multiply","_divide","_fibonacci","_create_buffer","_destroy_buffer","_compute_sum","_get_greeting","_main"]' `
|
||||
-s EXPORTED_RUNTIME_METHODS='["UTF8ToString","stringToUTF8","lengthBytesUTF8","ccall","cwrap","HEAPU8","HEAP8","HEAPF32","HEAPF64","allocateUTF8"]' `
|
||||
-s MODULARIZE=1 `
|
||||
-s EXPORT_NAME='createSmartMathModule' `
|
||||
-s ALLOW_MEMORY_GROWTH=1 `
|
||||
-s ENVIRONMENT='web,node,worker' `
|
||||
-s ASSERTIONS=1 `
|
||||
-s DISABLE_EXCEPTION_CATCHING=0 `
|
||||
-s INITIAL_MEMORY=67108864 `
|
||||
-s MAXIMUM_MEMORY=268435456 `
|
||||
-s STACK_SIZE=5242880 `
|
||||
-s USE_PTHREADS=0 `
|
||||
-O3 `
|
||||
-Wno-deprecated-literal-operator `
|
||||
-o public/wasm/smart_math.js 2>&1
|
||||
|
||||
$endTime = Get-Date
|
||||
$duration = ($endTime - $startTime).TotalSeconds
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "[✗] 编译失败" -ForegroundColor Red
|
||||
Write-Host $compileResult -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "[✓] 编译完成" -ForegroundColor Green
|
||||
|
||||
# 5. 检查生成的文件
|
||||
$jsFile = Get-Item "public/wasm/smart_math.js" -ErrorAction SilentlyContinue
|
||||
$wasmFile = Get-Item "public/wasm/smart_math.wasm" -ErrorAction SilentlyContinue
|
||||
|
||||
if (-not $jsFile -or -not $wasmFile) {
|
||||
Write-Host "[✗] 未找到生成文件" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
$jsSize = $jsFile.Length / 1KB
|
||||
$wasmSize = $wasmFile.Length / 1KB
|
||||
|
||||
# 6. 显示构建结果
|
||||
Write-Host ""
|
||||
Write-Host "🎉 构建成功!" -ForegroundColor Green
|
||||
Write-Host ("═" * 50) -ForegroundColor DarkGray
|
||||
Write-Host "📦 生成文件:" -ForegroundColor White
|
||||
Write-Host "├─ smart_math.js ($($jsSize.ToString('F1')) KB)" -ForegroundColor Gray
|
||||
Write-Host "└─ smart_math.wasm ($($wasmSize.ToString('F1')) KB)" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
Write-Host "✨ 功能特性:" -ForegroundColor White
|
||||
Write-Host "├─ ✅ 机器人正逆解业务接口 (func/init_func)" -ForegroundColor Gray
|
||||
Write-Host "├─ ✅ SPC / 四足机器人 / 连杆机构业务接口" -ForegroundColor Gray
|
||||
Write-Host "├─ ✅ 智能 JSON 数学测试接口" -ForegroundColor Gray
|
||||
Write-Host "├─ ✅ Node / Web 双环境加载" -ForegroundColor Gray
|
||||
Write-Host "├─ ✅ 与 Emscripten 内存管理兼容" -ForegroundColor Gray
|
||||
Write-Host "└─ ✅ 批量回归测试可直接调用" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
Write-Host "📁 输出目录: $((Get-Location).Path)\public\wasm" -ForegroundColor Gray
|
||||
Write-Host "⏱️ 编译耗时: $($duration.ToString('F2')) 秒" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
|
||||
# 7. 生成 JavaScript 包装器(可选)
|
||||
Write-Host "[ ] 生成 JavaScript 包装器..." -ForegroundColor Cyan
|
||||
|
||||
$jsWrapper = @"
|
||||
// Smart WASM JSON API JavaScript 包装器
|
||||
class SmartWasmAPI {
|
||||
constructor() {
|
||||
this.module = null;
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
if (this.initialized) return;
|
||||
|
||||
// 加载 WASM 模块
|
||||
this.module = await createSmartMathModule();
|
||||
this.initialized = true;
|
||||
|
||||
console.log('Smart WASM API 初始化完成');
|
||||
return this;
|
||||
}
|
||||
|
||||
async initializeBusinessApi() {
|
||||
if (!this.initialized) {
|
||||
await this.initialize();
|
||||
}
|
||||
|
||||
let responsePtr = null;
|
||||
|
||||
try {
|
||||
responsePtr = this.module._init_func();
|
||||
const responseStr = this.module.UTF8ToString(responsePtr);
|
||||
return JSON.parse(responseStr);
|
||||
} finally {
|
||||
if (responsePtr) {
|
||||
this.module._smart_free_string(responsePtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async processBusinessRequest(jsonRequest) {
|
||||
if (!this.initialized) {
|
||||
await this.initialize();
|
||||
}
|
||||
|
||||
let requestPtr = null;
|
||||
let responsePtr = null;
|
||||
|
||||
try {
|
||||
requestPtr = this.module.allocateUTF8(JSON.stringify(jsonRequest));
|
||||
responsePtr = this.module._func(requestPtr);
|
||||
const responseStr = this.module.UTF8ToString(responsePtr);
|
||||
return JSON.parse(responseStr);
|
||||
} catch (error) {
|
||||
console.error('处理业务 JSON 请求时出错:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
if (requestPtr) {
|
||||
this.module._free(requestPtr);
|
||||
}
|
||||
if (responsePtr) {
|
||||
this.module._smart_free_string(responsePtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理 JSON 请求
|
||||
async processJsonRequest(jsonRequest) {
|
||||
if (!this.initialized) {
|
||||
await this.initialize();
|
||||
}
|
||||
|
||||
let requestPtr = null;
|
||||
let responsePtr = null;
|
||||
|
||||
try {
|
||||
// 将 JSON 字符串转换为 WASM 内存中的字符串
|
||||
requestPtr = this.module.allocateUTF8(JSON.stringify(jsonRequest));
|
||||
|
||||
// 调用 WASM 函数
|
||||
responsePtr = this.module._smart_process_json(requestPtr);
|
||||
|
||||
// 将结果转换回 JavaScript 字符串
|
||||
const responseStr = this.module.UTF8ToString(responsePtr);
|
||||
|
||||
// 解析 JSON 结果
|
||||
return JSON.parse(responseStr);
|
||||
} catch (error) {
|
||||
console.error('处理 JSON 请求时出错:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
// 释放内存
|
||||
if (requestPtr) {
|
||||
this.module._free(requestPtr);
|
||||
}
|
||||
if (responsePtr) {
|
||||
this.module._smart_free_string(responsePtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取函数列表
|
||||
async getFunctionList() {
|
||||
if (!this.initialized) {
|
||||
await this.initialize();
|
||||
}
|
||||
|
||||
let responsePtr = null;
|
||||
|
||||
try {
|
||||
responsePtr = this.module._smart_get_function_list();
|
||||
const responseStr = this.module.UTF8ToString(responsePtr);
|
||||
return JSON.parse(responseStr);
|
||||
} catch (error) {
|
||||
console.error('获取函数列表时出错:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
if (responsePtr) {
|
||||
this.module._smart_free_string(responsePtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 调用特定函数
|
||||
async callFunction(funcName, params = {}) {
|
||||
const request = {
|
||||
req_cmd: funcName,
|
||||
...params
|
||||
};
|
||||
|
||||
return await this.processJsonRequest(request);
|
||||
}
|
||||
|
||||
// 测试智能匹配
|
||||
async testSmartMatch(jsonRequest) {
|
||||
if (!this.initialized) {
|
||||
await this.initialize();
|
||||
}
|
||||
|
||||
let requestPtr = null;
|
||||
let responsePtr = null;
|
||||
|
||||
try {
|
||||
requestPtr = this.module.allocateUTF8(JSON.stringify(jsonRequest));
|
||||
responsePtr = this.module._smart_test_match(requestPtr);
|
||||
const responseStr = this.module.UTF8ToString(responsePtr);
|
||||
return JSON.parse(responseStr);
|
||||
} catch (error) {
|
||||
console.error('测试智能匹配时出错:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
if (requestPtr) {
|
||||
this.module._free(requestPtr);
|
||||
}
|
||||
if (responsePtr) {
|
||||
this.module._smart_free_string(responsePtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取版本信息
|
||||
async getVersion() {
|
||||
if (!this.initialized) {
|
||||
await this.initialize();
|
||||
}
|
||||
|
||||
let responsePtr = null;
|
||||
|
||||
try {
|
||||
responsePtr = this.module._smart_get_version();
|
||||
const responseStr = this.module.UTF8ToString(responsePtr);
|
||||
return JSON.parse(responseStr);
|
||||
} catch (error) {
|
||||
console.error('获取版本信息时出错:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
if (responsePtr) {
|
||||
this.module._smart_free_string(responsePtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 直接调用数学函数(绕过 JSON 处理)
|
||||
callMathFunction(name, ...args) {
|
||||
if (!this.initialized) {
|
||||
throw new Error('WASM 模块未初始化');
|
||||
}
|
||||
|
||||
switch (name.toLowerCase()) {
|
||||
case 'add':
|
||||
return this.module._add(args[0] || 0, args[1] || 0);
|
||||
case 'subtract':
|
||||
return this.module._subtract(args[0] || 0, args[1] || 0);
|
||||
case 'multiply':
|
||||
return this.module._multiply(args[0] || 0, args[1] || 0);
|
||||
case 'divide':
|
||||
return this.module._divide(args[0] || 0, args[1] || 1);
|
||||
case 'fibonacci':
|
||||
return this.module._fibonacci(args[0] || 0);
|
||||
default:
|
||||
throw new Error('未知的数学函数: ' + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建全局实例
|
||||
if (typeof window !== 'undefined') {
|
||||
window.SmartWasmAPI = SmartWasmAPI;
|
||||
}
|
||||
|
||||
export default SmartWasmAPI;
|
||||
"@
|
||||
|
||||
$jsWrapper | Out-File -FilePath "public/wasm/smart_api_wrapper.js" -Encoding UTF8
|
||||
Write-Host "[✓] 生成 JavaScript 包装器" -ForegroundColor Green
|
||||
|
||||
# 8. 本地测试(可选)
|
||||
if ($Test) {
|
||||
Write-Host "[ ] 运行本地测试..." -ForegroundColor Cyan
|
||||
|
||||
try {
|
||||
node --version 2>&1 | Out-Null
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host "[ ] 执行 WASM 回归测试..." -ForegroundColor Cyan
|
||||
node scripts/run_wasm_tests.js
|
||||
}
|
||||
} catch {
|
||||
Write-Host "[!] 本地测试跳过(需要 Node.js)" -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
|
||||
# 9. 启动开发服务器(可选)
|
||||
if ($Serve) {
|
||||
Write-Host "[ ] 启动开发服务器..." -ForegroundColor Cyan
|
||||
Write-Host " 访问: http://localhost:8080/smart_test.html" -ForegroundColor Yellow
|
||||
Write-Host " 按 Ctrl+C 停止服务器" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
|
||||
Set-Location "public"
|
||||
python -m http.server 8080
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "🚀 使用提示:" -ForegroundColor White
|
||||
Write-Host "├─ 在 VS Code 中按 F5 启动调试" -ForegroundColor Gray
|
||||
Write-Host "├─ 使用 Ctrl+Shift+B 快速构建" -ForegroundColor Gray
|
||||
Write-Host "├─ 查看 public/wasm/smart_api_wrapper.js 了解 JavaScript API 使用" -ForegroundColor Gray
|
||||
Write-Host "└─ 运行 \`npm run serve\` 启动开发服务器" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
115
scripts/rebuild_with_fixes.ps1
Normal file
115
scripts/rebuild_with_fixes.ps1
Normal file
@@ -0,0 +1,115 @@
|
||||
# rebuild_with_fixes.ps1 - 重新构建并修复错误
|
||||
|
||||
Write-Host "=== 重新构建 WASM 模块 ===" -ForegroundColor Cyan
|
||||
|
||||
# 设置环境
|
||||
$EmsdkPath = "E:\emsdk"
|
||||
if (-not (Test-Path $EmsdkPath)) {
|
||||
Write-Host "[错误] 未找到 Emscripten" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
$env:PATH = "$EmsdkPath\upstream\emscripten;$env:PATH"
|
||||
|
||||
# 创建目录
|
||||
New-Item -Path "public/wasm" -ItemType Directory -Force | Out-Null
|
||||
|
||||
Write-Host "[ ] 编译 WASM 模块(包含完整的运行时方法)..." -ForegroundColor Cyan
|
||||
|
||||
# 完整的编译命令
|
||||
$compileCmd = @'
|
||||
emcc src/math_utils.cpp src/smart_json_wrapper.cpp src/main.cpp -I./src -std=c++17 -s WASM=1 -s EXPORTED_FUNCTIONS='["_smart_process_json","_smart_get_function_list","_smart_get_function_info","_smart_test_match","_smart_get_version","_smart_free_string","_main"]' -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap","UTF8ToString","stringToUTF8","lengthBytesUTF8","allocateUTF8","allocate","ALLOC_NORMAL","getValue","setValue"]' -s MODULARIZE=1 -s EXPORT_NAME="createSmartMathModule" -s ALLOW_MEMORY_GROWTH=1 -s ASSERTIONS=1 -O3 -o public/wasm/smart_math.js
|
||||
'@
|
||||
|
||||
Write-Host "执行命令: $compileCmd" -ForegroundColor Gray
|
||||
Invoke-Expression $compileCmd
|
||||
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host "[✓] 编译成功!" -ForegroundColor Green
|
||||
|
||||
if (Test-Path "public/wasm/smart_math.js") {
|
||||
$jsSize = (Get-Item "public/wasm/smart_math.js").Length / 1KB
|
||||
$wasmSize = (Get-Item "public/wasm/smart_math.wasm").Length / 1KB
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "📦 生成文件:" -ForegroundColor White
|
||||
Write-Host "├─ smart_math.js ($($jsSize.ToString('F1')) KB)" -ForegroundColor Gray
|
||||
Write-Host "└─ smart_math.wasm ($($wasmSize.ToString('F1')) KB)" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
|
||||
Write-Host "🔧 导出的运行时方法:" -ForegroundColor Cyan
|
||||
Write-Host "├─ ccall" -ForegroundColor Gray
|
||||
Write-Host "├─ cwrap" -ForegroundColor Gray
|
||||
Write-Host "├─ UTF8ToString" -ForegroundColor Gray
|
||||
Write-Host "├─ stringToUTF8" -ForegroundColor Gray
|
||||
Write-Host "├─ lengthBytesUTF8" -ForegroundColor Gray
|
||||
Write-Host "├─ allocateUTF8" -ForegroundColor Gray
|
||||
Write-Host "├─ allocate" -ForegroundColor Gray
|
||||
Write-Host "└─ ALLOC_NORMAL" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
|
||||
# 测试模块是否正常工作
|
||||
Write-Host "[ ] 创建测试文件..." -ForegroundColor Cyan
|
||||
|
||||
$testHtml = @"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>WASM 测试</title>
|
||||
<script src="wasm/smart_math.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>WASM 模块测试</h1>
|
||||
<div id="output">加载中...</div>
|
||||
|
||||
<script>
|
||||
async function testModule() {
|
||||
try {
|
||||
const Module = await createSmartMathModule();
|
||||
console.log('Module loaded:', Module);
|
||||
|
||||
// 测试 allocateUTF8 是否存在
|
||||
if (typeof Module.allocateUTF8 === 'function') {
|
||||
console.log('✓ allocateUTF8 is available');
|
||||
|
||||
// 测试版本函数
|
||||
const versionPtr = Module._smart_get_version();
|
||||
const versionStr = Module.UTF8ToString(versionPtr);
|
||||
console.log('Version:', versionStr);
|
||||
Module._smart_free_string(versionPtr);
|
||||
|
||||
document.getElementById('output').innerHTML =
|
||||
'<h3 style="color: green;">✅ WASM 模块加载成功!</h3>' +
|
||||
'<pre>' + versionStr + '</pre>';
|
||||
} else {
|
||||
console.log('✗ allocateUTF8 is NOT available');
|
||||
document.getElementById('output').innerHTML =
|
||||
'<h3 style="color: red;">❌ allocateUTF8 不可用</h3>';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
document.getElementById('output').innerHTML =
|
||||
'<h3 style="color: red;">❌ 加载失败: ' + error.message + '</h3>';
|
||||
}
|
||||
}
|
||||
|
||||
testModule();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"@
|
||||
|
||||
Set-Content -Path "public/test_wasm.html" -Value $testHtml -Encoding UTF8
|
||||
Write-Host "[✓] 测试文件已创建: public/test_wasm.html" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "🌐 启动测试服务器:" -ForegroundColor Cyan
|
||||
Write-Host " cd public" -ForegroundColor Gray
|
||||
Write-Host " python -m http.server 8080" -ForegroundColor Gray
|
||||
Write-Host " 然后在浏览器中打开: http://localhost:8080/test_wasm.html" -ForegroundColor Gray
|
||||
|
||||
} else {
|
||||
Write-Host "[✗] 未找到生成文件" -ForegroundColor Red
|
||||
}
|
||||
} else {
|
||||
Write-Host "[✗] 编译失败,退出码: $LASTEXITCODE" -ForegroundColor Red
|
||||
}
|
||||
480
scripts/run_wasm_tests.js
Normal file
480
scripts/run_wasm_tests.js
Normal file
@@ -0,0 +1,480 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const rootDir = path.resolve(__dirname, "..");
|
||||
const defaultModulePath = path.join(rootDir, "public", "wasm", "smart_math.js");
|
||||
|
||||
function readJson(relativePath) {
|
||||
const filePath = path.join(rootDir, relativePath);
|
||||
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
||||
}
|
||||
|
||||
function ensure(condition, message) {
|
||||
if (!condition) {
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
|
||||
function getByPath(value, pathExpression) {
|
||||
const parts = pathExpression.split(".");
|
||||
let current = value;
|
||||
|
||||
for (const part of parts) {
|
||||
if (current === undefined || current === null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (/^\d+$/.test(part)) {
|
||||
current = current[Number(part)];
|
||||
} else {
|
||||
current = current[part];
|
||||
}
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
function jointArrayToString(joints) {
|
||||
return joints.join(",");
|
||||
}
|
||||
|
||||
function poseToString(position, orientation) {
|
||||
return [...position, ...orientation].join(",");
|
||||
}
|
||||
|
||||
function positionDistance(positionA, positionB) {
|
||||
const dx = positionA[0] - positionB[0];
|
||||
const dy = positionA[1] - positionB[1];
|
||||
const dz = positionA[2] - positionB[2];
|
||||
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||
}
|
||||
|
||||
function quaternionDistance(q1, q2) {
|
||||
const dot = Math.abs(
|
||||
q1[0] * q2[0] +
|
||||
q1[1] * q2[1] +
|
||||
q1[2] * q2[2] +
|
||||
q1[3] * q2[3]
|
||||
);
|
||||
return 1 - Math.min(1, dot);
|
||||
}
|
||||
|
||||
async function loadModule(modulePath) {
|
||||
const required = require(modulePath);
|
||||
|
||||
if (typeof required === "function") {
|
||||
return required({
|
||||
noInitialRun: true,
|
||||
locateFile: (fileName) => path.join(path.dirname(modulePath), fileName),
|
||||
});
|
||||
}
|
||||
|
||||
if (required && typeof required === "object") {
|
||||
return required;
|
||||
}
|
||||
|
||||
throw new Error(`Unsupported WASM loader type: ${typeof required}`);
|
||||
}
|
||||
|
||||
function callJsonFunction(module, exportName, payload) {
|
||||
const requestText = typeof payload === "string" ? payload : JSON.stringify(payload);
|
||||
const requestPtr = module.allocateUTF8(requestText);
|
||||
let responsePtr = 0;
|
||||
|
||||
try {
|
||||
responsePtr = module[exportName](requestPtr);
|
||||
const responseText = module.UTF8ToString(responsePtr);
|
||||
return JSON.parse(responseText);
|
||||
} finally {
|
||||
if (responsePtr) {
|
||||
module._smart_free_string(responsePtr);
|
||||
}
|
||||
module._free(requestPtr);
|
||||
}
|
||||
}
|
||||
|
||||
function callNoArgJsonFunction(module, exportName) {
|
||||
const responsePtr = module[exportName]();
|
||||
try {
|
||||
const responseText = module.UTF8ToString(responsePtr);
|
||||
return JSON.parse(responseText);
|
||||
} finally {
|
||||
module._smart_free_string(responsePtr);
|
||||
}
|
||||
}
|
||||
|
||||
function callBusinessApi(module, request) {
|
||||
return callJsonFunction(module, "_func", request);
|
||||
}
|
||||
|
||||
function buildForwardRequest(robotUuid, joints) {
|
||||
return {
|
||||
msg: "fk roundtrip",
|
||||
req_code: "AUTO_FK",
|
||||
req_from: "wasm_test",
|
||||
req_cmd: "Cmd_Kinematics_forward_pose_str",
|
||||
req_param: {
|
||||
robot_uuid: robotUuid,
|
||||
q_init_str: jointArrayToString(joints),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildInverseRequest(command, robotUuid, poseStr, qInitStr, extra = {}) {
|
||||
return {
|
||||
msg: "ik roundtrip",
|
||||
req_code: "AUTO_IK",
|
||||
req_from: "wasm_test",
|
||||
req_cmd: command,
|
||||
req_param: {
|
||||
robot_uuid: robotUuid,
|
||||
pose_str: poseStr,
|
||||
q_init_str: qInitStr,
|
||||
...extra,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function assertStaticCase(response, assertions) {
|
||||
for (const assertion of assertions) {
|
||||
const actual = getByPath(response, assertion.path);
|
||||
|
||||
if (assertion.equals !== undefined) {
|
||||
ensure(
|
||||
actual === assertion.equals,
|
||||
`${assertion.path} expected ${JSON.stringify(assertion.equals)}, got ${JSON.stringify(actual)}`
|
||||
);
|
||||
}
|
||||
|
||||
if (assertion.exists) {
|
||||
ensure(actual !== undefined && actual !== null, `${assertion.path} is missing`);
|
||||
}
|
||||
|
||||
if (assertion.gte !== undefined) {
|
||||
ensure(actual >= assertion.gte, `${assertion.path} expected >= ${assertion.gte}, got ${actual}`);
|
||||
}
|
||||
|
||||
if (assertion.lengthEquals !== undefined) {
|
||||
ensure(Array.isArray(actual), `${assertion.path} is not an array`);
|
||||
ensure(
|
||||
actual.length === assertion.lengthEquals,
|
||||
`${assertion.path} expected length ${assertion.lengthEquals}, got ${actual.length}`
|
||||
);
|
||||
}
|
||||
|
||||
if (assertion.lengthGte !== undefined) {
|
||||
ensure(Array.isArray(actual), `${assertion.path} is not an array`);
|
||||
ensure(
|
||||
actual.length >= assertion.lengthGte,
|
||||
`${assertion.path} expected length >= ${assertion.lengthGte}, got ${actual.length}`
|
||||
);
|
||||
}
|
||||
|
||||
if (assertion.includes !== undefined) {
|
||||
ensure(
|
||||
String(actual).includes(assertion.includes),
|
||||
`${assertion.path} expected to include ${assertion.includes}, got ${actual}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function runStaticCase(module, testCase) {
|
||||
const request = readJson(testCase.requestFile);
|
||||
const response = callBusinessApi(module, request);
|
||||
assertStaticCase(response, testCase.assertions);
|
||||
return response;
|
||||
}
|
||||
|
||||
async function runRoundtripSingleCase(module, testCase) {
|
||||
const seed = readJson(testCase.seedFile);
|
||||
const forwardResponse = callBusinessApi(module, buildForwardRequest(seed.robot_uuid, seed.target_joints));
|
||||
|
||||
ensure(forwardResponse.success === true, "forward request failed");
|
||||
ensure(getByPath(forwardResponse, "res_data.success") === true, "forward result is not successful");
|
||||
|
||||
const forwardPosition = getByPath(forwardResponse, "res_data.position");
|
||||
const forwardOrientation = getByPath(forwardResponse, "res_data.orientation");
|
||||
const poseStr = poseToString(forwardPosition, forwardOrientation);
|
||||
|
||||
const inverseResponse = callBusinessApi(
|
||||
module,
|
||||
buildInverseRequest(
|
||||
"Cmd_Kinematics_inverse_pose_str_NoDifference",
|
||||
seed.robot_uuid,
|
||||
poseStr,
|
||||
seed.q_init_str
|
||||
)
|
||||
);
|
||||
|
||||
ensure(inverseResponse.success === true, "inverse request failed");
|
||||
ensure(getByPath(inverseResponse, "res_data.success") === true, "inverse result is not successful");
|
||||
|
||||
const jointSolutions = getByPath(inverseResponse, "res_data.joints");
|
||||
ensure(Array.isArray(jointSolutions) && jointSolutions.length >= 1, "inverse result contains no joint solution");
|
||||
|
||||
const finalJoints = jointSolutions[jointSolutions.length - 1];
|
||||
const forwardCheck = callBusinessApi(module, buildForwardRequest(seed.robot_uuid, finalJoints));
|
||||
|
||||
ensure(forwardCheck.success === true, "forward validation request failed");
|
||||
ensure(getByPath(forwardCheck, "res_data.success") === true, "forward validation result is not successful");
|
||||
|
||||
const checkedPosition = getByPath(forwardCheck, "res_data.position");
|
||||
const checkedOrientation = getByPath(forwardCheck, "res_data.orientation");
|
||||
|
||||
const posDiff = positionDistance(forwardPosition, checkedPosition);
|
||||
const quatDiff = quaternionDistance(forwardOrientation, checkedOrientation);
|
||||
|
||||
ensure(
|
||||
posDiff <= seed.position_tolerance,
|
||||
`roundtrip position diff ${posDiff} exceeds tolerance ${seed.position_tolerance}`
|
||||
);
|
||||
ensure(
|
||||
quatDiff <= seed.orientation_tolerance,
|
||||
`roundtrip quaternion diff ${quatDiff} exceeds tolerance ${seed.orientation_tolerance}`
|
||||
);
|
||||
|
||||
return {
|
||||
response: inverseResponse,
|
||||
details: {
|
||||
positionDiff: posDiff,
|
||||
quaternionDiff: quatDiff,
|
||||
solutionCount: jointSolutions.length,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function runRoundtripPathCase(module, testCase) {
|
||||
const seed = readJson(testCase.seedFile);
|
||||
|
||||
const startForward = callBusinessApi(module, buildForwardRequest(seed.robot_uuid, seed.start_joints));
|
||||
const endForward = callBusinessApi(module, buildForwardRequest(seed.robot_uuid, seed.end_joints));
|
||||
|
||||
const startPose = poseToString(
|
||||
getByPath(startForward, "res_data.position"),
|
||||
getByPath(startForward, "res_data.orientation")
|
||||
);
|
||||
const endPose = poseToString(
|
||||
getByPath(endForward, "res_data.position"),
|
||||
getByPath(endForward, "res_data.orientation")
|
||||
);
|
||||
|
||||
const inverseResponse = callBusinessApi(
|
||||
module,
|
||||
buildInverseRequest(
|
||||
"Cmd_Kinematics_inverse_pose_str_2PSteps",
|
||||
seed.robot_uuid,
|
||||
`${startPose};${endPose}`,
|
||||
seed.q_init_str,
|
||||
{ steps_str: String(seed.steps) }
|
||||
)
|
||||
);
|
||||
|
||||
ensure(inverseResponse.success === true, "path inverse request failed");
|
||||
ensure(getByPath(inverseResponse, "res_data.success") === true, "path inverse result is not successful");
|
||||
|
||||
const jointSolutions = getByPath(inverseResponse, "res_data.joints");
|
||||
ensure(Array.isArray(jointSolutions), "path inverse result is not an array");
|
||||
ensure(
|
||||
jointSolutions.length === seed.steps,
|
||||
`path inverse expected ${seed.steps} solutions, got ${jointSolutions.length}`
|
||||
);
|
||||
|
||||
const firstForwardCheck = callBusinessApi(module, buildForwardRequest(seed.robot_uuid, jointSolutions[0]));
|
||||
const lastForwardCheck = callBusinessApi(
|
||||
module,
|
||||
buildForwardRequest(seed.robot_uuid, jointSolutions[jointSolutions.length - 1])
|
||||
);
|
||||
|
||||
const startPosDiff = positionDistance(
|
||||
getByPath(startForward, "res_data.position"),
|
||||
getByPath(firstForwardCheck, "res_data.position")
|
||||
);
|
||||
const endPosDiff = positionDistance(
|
||||
getByPath(endForward, "res_data.position"),
|
||||
getByPath(lastForwardCheck, "res_data.position")
|
||||
);
|
||||
|
||||
ensure(
|
||||
startPosDiff <= seed.position_tolerance,
|
||||
`path start position diff ${startPosDiff} exceeds tolerance ${seed.position_tolerance}`
|
||||
);
|
||||
ensure(
|
||||
endPosDiff <= seed.position_tolerance,
|
||||
`path end position diff ${endPosDiff} exceeds tolerance ${seed.position_tolerance}`
|
||||
);
|
||||
|
||||
return {
|
||||
response: inverseResponse,
|
||||
details: {
|
||||
solutionCount: jointSolutions.length,
|
||||
startPositionDiff: startPosDiff,
|
||||
endPositionDiff: endPosDiff,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const suite = [
|
||||
{
|
||||
id: "list_robots_after_init",
|
||||
type: "static",
|
||||
requestFile: "tests/testdata/kinematics/list_robots.json",
|
||||
assertions: [
|
||||
{ path: "success", equals: true },
|
||||
{ path: "res_data.success", equals: true },
|
||||
{ path: "res_data.count", gte: 2 },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "kinematics_forward_zero",
|
||||
type: "static",
|
||||
requestFile: "tests/testdata/kinematics/forward_zero.json",
|
||||
assertions: [
|
||||
{ path: "success", equals: true },
|
||||
{ path: "res_data.success", equals: true },
|
||||
{ path: "res_data.position", lengthEquals: 3 },
|
||||
{ path: "res_data.orientation", lengthEquals: 4 },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "kinematics_forward_all_joints_path",
|
||||
type: "static",
|
||||
requestFile: "tests/testdata/kinematics/forward_all_joints_path.json",
|
||||
assertions: [
|
||||
{ path: "success", equals: true },
|
||||
{ path: "res_data.success", equals: true },
|
||||
{ path: "res_data.OPERATION.frames", lengthEquals: 2 },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "spc_basic_5x30",
|
||||
type: "static",
|
||||
requestFile: "tests/testdata/spc/basic_5x30.json",
|
||||
assertions: [
|
||||
{ path: "success", equals: true },
|
||||
{ path: "res_data.XR.n", equals: 5 },
|
||||
{ path: "res_data.XR.k", equals: 30 },
|
||||
{ path: "res_data.Cpk.USL", equals: 1.7 },
|
||||
{ path: "res_data.Cpk.LSL", equals: 1.5 },
|
||||
{ path: "res_data.Cpk.Cpk", gte: 0.1 },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "fourbar_valid",
|
||||
type: "static",
|
||||
requestFile: "tests/testdata/fourbar/crank_slider_valid.json",
|
||||
assertions: [
|
||||
{ path: "success", equals: true },
|
||||
{ path: "res_data.success", equals: true },
|
||||
{ path: "res_data.points.B.x", exists: true },
|
||||
{ path: "res_data.trajectory", lengthGte: 1 },
|
||||
{ path: "res_data.slider_trajectory", lengthGte: 1 },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "fourbar_invalid",
|
||||
type: "static",
|
||||
requestFile: "tests/testdata/fourbar/crank_slider_invalid.json",
|
||||
assertions: [
|
||||
{ path: "success", equals: true },
|
||||
{ path: "res_data.success", equals: false },
|
||||
{ path: "res_data.error", includes: "Invalid parameters" },
|
||||
{ path: "res_data.validation_errors", lengthGte: 1 },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "quadruped_points_from_motor_angles",
|
||||
type: "static",
|
||||
requestFile: "tests/testdata/quadruped/calculate_points_from_motor_angles.json",
|
||||
assertions: [
|
||||
{ path: "success", equals: true },
|
||||
{ path: "res_data.objStates", lengthGte: 20 },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "quadruped_forward_gait",
|
||||
type: "static",
|
||||
requestFile: "tests/testdata/quadruped/perform_forward_kinematics.json",
|
||||
assertions: [
|
||||
{ path: "success", equals: true },
|
||||
{ path: "res_data.GaitInfo.TotalFrames", equals: 40 },
|
||||
{ path: "res_data.ConstraintData.AB1_AB_Constraint.IsValid", equals: true },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "ik_roundtrip_single_pose",
|
||||
type: "roundtrip_single",
|
||||
seedFile: "tests/testdata/kinematics/roundtrip_single_seed.json",
|
||||
},
|
||||
{
|
||||
id: "ik_roundtrip_two_pose_path",
|
||||
type: "roundtrip_path",
|
||||
seedFile: "tests/testdata/kinematics/roundtrip_path_seed.json",
|
||||
},
|
||||
];
|
||||
|
||||
async function runCase(module, testCase) {
|
||||
switch (testCase.type) {
|
||||
case "static":
|
||||
return runStaticCase(module, testCase);
|
||||
case "roundtrip_single":
|
||||
return runRoundtripSingleCase(module, testCase);
|
||||
case "roundtrip_path":
|
||||
return runRoundtripPathCase(module, testCase);
|
||||
default:
|
||||
throw new Error(`Unknown test type: ${testCase.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const modulePath = path.resolve(process.argv[2] || defaultModulePath);
|
||||
ensure(fs.existsSync(modulePath), `WASM JS loader not found: ${modulePath}`);
|
||||
|
||||
console.log(`Loading module: ${modulePath}`);
|
||||
const module = await loadModule(modulePath);
|
||||
|
||||
ensure(typeof module._init_func === "function", "_init_func is not exported");
|
||||
ensure(typeof module._func === "function", "_func is not exported");
|
||||
ensure(typeof module.allocateUTF8 === "function", "allocateUTF8 is not exported");
|
||||
ensure(typeof module.UTF8ToString === "function", "UTF8ToString is not exported");
|
||||
|
||||
const initResponse = callNoArgJsonFunction(module, "_init_func");
|
||||
ensure(initResponse.success === true, `init_func failed: ${JSON.stringify(initResponse)}`);
|
||||
|
||||
console.log("Initialization complete.");
|
||||
|
||||
let passed = 0;
|
||||
const failures = [];
|
||||
|
||||
for (const testCase of suite) {
|
||||
try {
|
||||
const result = await runCase(module, testCase);
|
||||
passed += 1;
|
||||
|
||||
if (result && result.details) {
|
||||
console.log(`[PASS] ${testCase.id} ${JSON.stringify(result.details)}`);
|
||||
} else {
|
||||
console.log(`[PASS] ${testCase.id}`);
|
||||
}
|
||||
} catch (error) {
|
||||
failures.push({ id: testCase.id, message: error.message });
|
||||
console.error(`[FAIL] ${testCase.id}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("");
|
||||
console.log(`Result: ${passed}/${suite.length} passed`);
|
||||
|
||||
if (failures.length > 0) {
|
||||
console.log("Failed cases:");
|
||||
for (const failure of failures) {
|
||||
console.log(`- ${failure.id}: ${failure.message}`);
|
||||
}
|
||||
process.exitCode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
67
scripts/serve.ps1
Normal file
67
scripts/serve.ps1
Normal file
@@ -0,0 +1,67 @@
|
||||
# serve.ps1 - 开发服务器启动脚本
|
||||
Write-Host "=== 启动开发服务器 ===" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# 检查是否已构建
|
||||
if (-not (Test-Path "public/wasm/smart_math.js")) {
|
||||
Write-Host "[!] 未找到 WASM 文件,正在构建..." -ForegroundColor Yellow
|
||||
& "$PSScriptRoot\build.ps1"
|
||||
|
||||
if (-not (Test-Path "public/wasm/smart_math.js")) {
|
||||
Write-Host "[✗] 构建失败,无法启动服务器" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# 检查 Python
|
||||
try {
|
||||
python --version 2>&1 | Out-Null
|
||||
$hasPython = $true
|
||||
} catch {
|
||||
$hasPython = $false
|
||||
}
|
||||
|
||||
if (-not $hasPython) {
|
||||
Write-Host "[!] 未找到 Python,尝试使用系统 Python..." -ForegroundColor Yellow
|
||||
|
||||
# 尝试常见的 Python 路径
|
||||
$pythonPaths = @(
|
||||
"python",
|
||||
"python3",
|
||||
"py",
|
||||
"C:\Python39\python.exe",
|
||||
"C:\Python38\python.exe",
|
||||
"C:\Python37\python.exe"
|
||||
)
|
||||
|
||||
foreach ($path in $pythonPaths) {
|
||||
try {
|
||||
& $path --version 2>&1 | Out-Null
|
||||
$env:PATH = "$(Split-Path $path -ErrorAction SilentlyContinue);$env:PATH"
|
||||
$hasPython = $true
|
||||
Write-Host "[✓] 找到 Python: $path" -ForegroundColor Green
|
||||
break
|
||||
} catch {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $hasPython) {
|
||||
Write-Host "[✗] 未找到 Python,请安装 Python 3.7+ 并添加到 PATH" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 启动服务器
|
||||
Write-Host "[ ] 启动开发服务器..." -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "🌐 服务器信息:" -ForegroundColor White
|
||||
Write-Host "├─ 地址: http://localhost:8080" -ForegroundColor Cyan
|
||||
Write-Host "├─ 目录: $(Resolve-Path "public")" -ForegroundColor Gray
|
||||
Write-Host "├─ 主页: /smart_test.html" -ForegroundColor Gray
|
||||
Write-Host "└─ 按 Ctrl+C 停止服务器" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
Write-Host ("─" * 50) -ForegroundColor DarkGray
|
||||
|
||||
Set-Location "public"
|
||||
python -m http.server 8080
|
||||
42
scripts/test_compile.ps1
Normal file
42
scripts/test_compile.ps1
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
# test_compile.ps1 - 测试编译
|
||||
Write-Host "=== 测试编译 ===" -ForegroundColor Cyan
|
||||
|
||||
# 设置环境
|
||||
$EmsdkPath = "E:\emsdk"
|
||||
if (-not (Test-Path $EmsdkPath)) {
|
||||
Write-Host "[错误] 未找到 Emscripten" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
$env:PATH = "$EmsdkPath\upstream\emscripten;$env:PATH"
|
||||
|
||||
# 测试 1: 编译 math_utils.cpp
|
||||
Write-Host "[ ] 测试编译 math_utils.cpp..." -ForegroundColor Cyan
|
||||
emcc src/math_utils.cpp -I./src -s WASM=1 -c -o test_math.o 2>&1
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host "[✓] math_utils.cpp 编译成功" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[✗] math_utils.cpp 编译失败" -ForegroundColor Red
|
||||
}
|
||||
|
||||
# 测试 2: 编译 smart_json_wrapper.cpp
|
||||
Write-Host "[ ] 测试编译 smart_json_wrapper.cpp..." -ForegroundColor Cyan
|
||||
emcc src/smart_json_wrapper.cpp -I./src -s WASM=1 -c -o test_wrapper.o 2>&1
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host "[✓] smart_json_wrapper.cpp 编译成功" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[✗] smart_json_wrapper.cpp 编译失败" -ForegroundColor Red
|
||||
}
|
||||
|
||||
# 测试 3: 编译 main.cpp
|
||||
Write-Host "[ ] 测试编译 main.cpp..." -ForegroundColor Cyan
|
||||
emcc src/main.cpp -I./src -s WASM=1 -c -o test_main.o 2>&1
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host "[✓] main.cpp 编译成功" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[✗] main.cpp 编译失败" -ForegroundColor Red
|
||||
}
|
||||
|
||||
# 清理
|
||||
Remove-Item test_*.o -ErrorAction SilentlyContinue
|
||||
Reference in New Issue
Block a user