CodePlanet 开发指南
下载原文CodePlanet 开发指南
> Cursor 同步:本指南副本位于 [.cursor/开发指南.md](../.cursor/开发指南.md),并由 [.cursor/rules/](../.cursor/rules/) 在会话中自动注入。 > 项目约定:后续开发须持续保持 .cursor/ 与 docs/ 内容一致(见 [.cursor/README.md](../.cursor/README.md))。
平台终点(北极星):视觉检测全栈演进路线见 [docs/项目目标与架构终点.md](项目目标与架构终点.md)(Ops / Runtime / 插件 / yslib / VisionObject)。
R0 平台契约(已交付)
- yslib Schema:[
docs/schemas/yslib-v1.schema.json](schemas/yslib-v1.schema.json);示例项目 [examples/projects/demo/](../examples/projects/demo/) - 更新 manifest Schema(草案):[
docs/schemas/update-manifest-v1.schema.json](schemas/update-manifest-v1.schema.json) - ProjectValidator:
src/core/project/ProjectValidator.*— Ops 打开.yslib后 Ctrl+B 校验;Launcher 预检复用同一 API(R1.5) - AppPaths 扩展:
userRoot()、userConfigDir()、projectsRoot()、launcherLogPath()、runtimeDumpPath()、projectLogsDir();dataDir()固定为%LOCALAPPDATA%/CodePlanet/(Qt GenericDataLocation,三端共用) - 用户数据:
USER/config/editor.json;出厂默认布局resources/config/editor.default.json global关键字:Lexer/Parser 语法占位(R1 迁移static语义)- 文档:[迁移-static-global.md](迁移-static-global.md)、[附录-路径与日志约定.md](附录-路径与日志约定.md)
- 验收 / 回归:
scripts/run_r0_regression.ps1(自动化 + GUI 手动清单,分工见.cursor/开发指南.md§R0 回归约定)
R1 语义迁移(已交付)
ProjectGlobalStore:src/core/project/ProjectGlobalStore.*—global跨任务存储(按 yslib 路径)static:任务内持久,存于RuntimeWorker会话表;不再写 scope 0import:Lexer/Parser +ScriptAnalyzer静态分析;运行时链接(R2executeImport)StaticMigrationAnalyzer+ 迁移提示 Dock(手动扫描,见项目树右键)- 项目窗口:
USER/projects/下 每个子目录一个根节点(与磁盘文件夹一一对应);主.yslib优先{文件夹名}.yslib,同目录多 yslib 时其余在文件树中打开 recent_projects+ 文件 → 最近项目 + 启动选择对话框YslibStructureWidget:yslib scripts + main entry 编辑;视图 → 项目结构- 迁移提示 Dock:视图 → 迁移提示
- 回归:
scripts/run_r1_regression.ps1
R1.5 Launcher v1(已交付)
CodePlanetr.exe:src/launcher/— 产线启动入口(5 秒倒计时、F8 修复模式、预检、建议性更新、进程监护)LauncherService:编排ProjectValidator预检、U 盘/在线 manifest 验签、拉起目标进程并写launcher.logUpdateManifestVerifier:RSA-SHA256 + canonical JSON 实装;公钥resources/config/update.pubParentProcessGuard:--parent-pid/--nonce校验;CODEPLANET_DEV=1开发豁免DatabaseService::defaultProjectPath():Launcher 读取默认 yslib(app_settings.default_project_path)- R1.5 过渡策略:优先
CodeRuntime.exe;仓库布局(检出含CodePlanet.pro)或CODEPLANET_DEV=1时可回退CodeEditor.exe - 回归:
scripts/run_r1_5_regression.ps1(分层 + Qt Test + GUI 手动清单) - bin 独立测试:
scripts/deploy_bin.ps1、scripts/deploy_staging.ps1(→dist/CodePlanet/bin)、scripts/test_parent_guard_intercept.ps1、scripts/run_launcher_dev.ps1
Launcher 分层:仅依赖 src/core、src/services 与 Qt 模块,不得 link src/gui/ / src/logic/。
Launcher 链接约定(R1.5):launcher.pro 仅编入 ProjectValidator 静态分析链(Lexer / Parser / ExprParser / ScriptAnalyzer),禁止编入 AstInterpreter、AstRuntime、ExprEvaluator、FunctionDispatcher、PluginManager(完整 AST/插件运行时在 Ops/Runtime 侧)。新增 Launcher 依赖前须确认是否仅为静态预检所需。
R2 Runtime 最小闭环(已交付)
CodeRuntime.exe:src/runtime/— 产线运行入口(ParentProcessGuard+--project)RuntimeOrchestrator+MainTaskWorker/SubTaskWorker:按 yslibtasks调度;子任务 R2 用simulateTrigger()- import 运行时:
AstInterpreter::executeImport合并模块函数;静态 YS405/跨模块 call 检查 - global 冲突:
ScriptAnalyzer::analyzeProjectGlobals→ YS408;接入ProjectValidatorCtrl+B #include相对路径:AppPaths::resolveIncludeRelative(includeName, scriptAbsolutePath)MainTaskTimerService+FunctionLib.initimer_*API;FunctionDispatcher内置 dispatch;子任务 YS406- Runtime UI:日志 / global 表 / processGroups 树(150ms 刷新);i18n:Launcher 启动时
--locale=zh_CN|en - 回归样例:
examples/projects/demoV1.1/(import + timer + global) - 回归:
scripts/run_r2_regression.ps1(分层 + Qt Test + Runtime 构建重试/日志 + 父进程拦截 + GUI 手动清单;默认 fail-fast,可选-AllowStaleRuntime) - bin 部署:
scripts/deploy_bin.ps1含三 exe;scripts/test_runtime_parent_guard_intercept.ps1 - 调试稳态约束(R2):
AstRuntime::needsReload/loadSource统一 CRLF/LF 后再比较源码;AstInterpreter::executeReturn回退到最近函数帧(覆盖嵌套块return);Debug 构建启用assertDebugInvariants断言暂停态栈一致性;空语句脚本resetExecution进入Completed(避免Paused + 空栈断言崩溃);global在未绑定ProjectGlobalStore的单脚本调试场景回退到参数槽(避免 F5/F6/F10 出现未定义);F2/F3 在当前文件首行就地重置(RunDebugReset),不回跳旧 IP,重置后可直接 F5/F6/F10;functionDeclAtLine在首轮sourceFilePath场景回退路径匹配,保证“首轮/F2后”函数声明扫描一致;F6 可视策略:未执行语句行跳过,但结构行(else/case/default/})及空白/注释保留可见;switch命中前允许看到 case 标签,break/continue/return后直接落到下一可执行语句;for Step Over 迭代落循环体首行;结束态RunSessionEnded(completed|stopped|error)— completed 保留 EOF 箭头,stopped 清箭头(Stop 路径不回写第1行箭头),error 保留 IP 须 Reset;Idle 打开.ys自动置首行箭头,切到.yslib/非.ys自动清理箭头与调试路径 - 项目树默认展开:
ProjectTreeWidget启动/刷新后仅展开 active 项目,无 active 时全部折叠
R3 更新闭环与触发链路(已交付)
- Apply 主流程:
LauncherService::applyUpdate()— manifest 缓存 → zip 定位/下载 →zipSha256必填 → staging 解压 →ProjectValidator→ 原子替换(保留logs/)→ 失败回滚;QtConcurrent后台 Apply,UI 不长时间阻塞 - 安全基建:
FileService安全解压(防 zip slip)、目录备份/替换;UpdateManifestVerifier::verifyFileSha256;AppPaths::update*Path();HTTPS 下载 超时 abort,不使用不完整 manifest/zip - 组件更新(manifest):
runtime/launcher/editor段(setupUrl+setupSha256);Apply 后 非阻断 引导运行 NSIS 补丁包 - 触发链路:
SubTaskWorker::onHardwareTrigger()+ 队列 burst 不丢触发;TriggerMockService;Runtime--trigger-mock=camA与 UI「Mock 硬件触发」 - mini dump:
MiniDumpWriter→%LOCALAPPDATA%/CodePlanet/dumps/ - NSIS 安装/补丁:
installer/nsis/*.nsi;一键打包installer/build_release_packages.ps1(清 staging → 复制bin\→ windeployqt → makensis);或scripts/build_nsis_packages.ps1;安装根目录$INSTDIR+ 注册表 App PathsCodePlanetr.exe - U 盘更新测试脚本:默认输出
dist/CodePlanetrUpdate/;可用installer/run_r3_update_pipeline.ps1串行执行 build/prepare/sign/verify,installer/copy_codeplanetr_update_to_drive.ps1 -DriveLetter G复制到 U 盘;manifest 签名 canonical 必须与 Qt 字母序规则一致 - Launcher 倒计时页:优先显示本次会话新增
launcher.log,无新增日志时显示config/license.txt - 安装默认目录:NSIS 默认建议路径为
D:\CodePlanet(用户仍可在目录页改盘符) - Apply 项目路径归一化:优先定位当前安装目录
USER/projects,避免误用历史C:\Program Files\CodePlanet路径 - 组件补丁引导策略:Apply 前弹出勾选窗口并先做可用性扫描;Runtime/Editor 可自动启动补丁安装,Launcher 因文件占用保留手动安装
- 补丁安装覆盖策略:Runtime/Editor 补丁安装时 Qt DLL/插件若已存在则跳过覆盖,仅补充缺失文件,避免 Launcher 占用导致弹窗阻塞
- 倒计时自动启动策略:倒计时到期后,若检测到已验证更新则进入更新页面,由用户手动选择“应用更新/跳过”;若无更新则直接启动 Runtime(未指定 yslib 时按失败流程提示)
- 版本元数据约定:唯一维护入口为
resources/config/release_version.json;脚本仅自动更新productVersion/shortVersion/build,launcher/runtime/editor保留手工值 - 发包命名约定:全量包保留
CodeplanetrSetup-latest.exe,每次构建前自动归档旧包为CodeplanetrSetup-{productVersion}.exe;组件补丁包文件名版本优先读取runtimePackageVersion/launcherPackageVersion/editorPackageVersion(缺省回退对应组件版本前三段) - demo payload 版本约定:
prepare_r3_udisk_payload.ps1默认读取release_version.json.demoProjectVersion,用于demo-{version}.zip与 manifestprojects.version - 构建清理约定:
dist/CodePlanet/bin与dist/CodePlanet/Update/*每次构建均清理;仅dist/CodePlanet/Download保留全量包历史归档 - shortVersion 约定:由
productVersion前三段自动同步,手工改版本请改productVersion - 回归:
scripts/run_r3_update_regression.ps1;scripts/run_r3_regression.ps1(= R2 + update + NSIS smoke) - Mock 工具:
scripts/mock_hardware_trigger.ps1
R3 验签排障沉淀(必读)
zipSha256(项目 zip 文件哈希)与canonical_sha256(manifest 规范化文本哈希)是不同对象,不能互相比较- 运行设备不依赖 OpenSSL;OpenSSL 仅用于开发机签名与本地验签脚本
- 若出现“脚本验签通过但 Launcher 失败”,先对比
canonical_sha256:不一致通常是 canonical 规则或文件版本不一致 - 签名脚本 canonical 必须与 Qt 字母序规则一致;脚本升级后旧 manifest 需要重新签名
plugins[].path兼容两种相对路径:bin/plugins/...(相对安装根)和plugins/...(相对applicationDirPath)
bin/ 与 dist/CodePlanet/bin/(勿维护两套)
| 目录 | 来源 | 用途 |
|---|---|---|
bin/ |
Qt Creator 编译(各 .pro → DESTDIR = $$ROOT/bin) |
日常开发运行、deploy_bin.ps1 |
dist/CodePlanet/bin/ |
deploy_staging.ps1 从 bin/ 复制 + windeployqt |
仅 NSIS 编译时打包素材;可整目录删除后重建 |
NSIS 不会在客户机读取 dist/;makensis 在开发机把 staging 文件嵌入 exe。发版步骤见 docs/AST审计执行报告/R3-验收与操作手册.md §3。
Release 本机验收(R2 起推荐)
1. Qt Creator:Release Kit → 构建全部(覆盖 bin/*.exe) 2. 项目根目录:powershell -NoProfile -ExecutionPolicy Bypass -File scripts\deploy_bin.ps1 -Config Release(windeployqt,须与 exe 同 Debug/Release) 3. 启动:powershell -File scripts\run_launcher_dev.ps1 -Config Release 或 bin\CodePlanetr.exe
改界面 tr() 文案时才需 update_i18n_ts.bat → 编辑 .ts → generate_i18n.bat;普通 C++ 改动 不必单独「编译翻译」。
Runtime 分层:依赖 src/logic/、src/core/、src/services/ 与最小 Qt Widgets;不含 Ops 编辑器。Ops RuntimeWorker 不变(F5 调试)。
core 源文件链接约定(R2):凡目标编入 FunctionDispatcher.cpp,须同时编入 MainTaskTimerService.cpp。ScriptAnalyzer 对 timer 的静态检查仅用头文件内联 isTimerFunction,Launcher 无需链接 timer 实现。
多脚本参数槽(R2):产线 Runtime 按 yslib scripts[] 为每个脚本分配 scriptId(从 1 递增)。RuntimeTaskContext::setScripts 与 AstRuntime::setScriptId 会调用 ensureSourceParameterSlots(id+1),避免 sourceParameterL.at(id) 越界。主任务循环脚本中 timer_create 仅应执行一次(用 static 守卫),每 tick 会重入 main()。
头文件约定(R1):GUI 使用 ast::AnalyzeContext 时须 #include "ScriptAnalyzer.h"(勿仅含 AstCommon.h)。
Qt MOC 约定:含 Q_OBJECT 的类,.h 须列入 src/editor/editor.pro 的 HEADERS,否则链接报 LNK2019。
中间产物:editor.pro / tests.pro 的 MOC_DIR、OBJECTS_DIR 使用 $$OUT_PWD,避免影子构建 Debug/Release 共用 moc 导致 LNK2038。改 .pro 后重新 qmake 并全量构建;旧版若曾在源码树 build/moc 留下产物,请删除后再编。
环境
- Qt 5.15+ 或 Qt 6.x(Widgets、Sql、Concurrent 模块)
- C++17 编译器(MSVC / MinGW)
- 使用 Qt Creator 打开根目录 [
CodePlanet.pro](../CodePlanet.pro)
构建
qmake CodePlanet.pro
# MSVC: nmake / MinGW: mingw32-make
MSVC + Qt 5.11 注意:editor.pro 须显式 QMAKE_CXXFLAGS += /std:c++17(P5 使用 std::optional);改 .pro 后请在 Qt Creator 执行 重新运行 qmake 再编译。
Launcher 头文件约定:类内嵌套类型(如 CachedUpdate、ApplyResult)若作为 static 成员函数参数/返回值,须在首次使用之前完成定义(MSVC 否则报 C3646「未知重写说明符」);避免重复声明同一成员函数。
输出:bin/CodeEditor.exe、bin/CodePlanetr.exe、bin/CodeRuntime.exe,插件在 bin/plugins/BuiltinFunctions.dll。
目录结构(禁止在旧路径新增代码)
src/editor 入口、MainWindow、Application(CodeEditor.exe)
src/runtime CodeRuntime(产线任务执行 + 状态 UI)
src/launcher CodePlanetr(启动守门)
src/gui 编辑器与 IDE 控件
src/logic ControlLogiLib、RuntimeWorker 运行编排
src/core 运行时、类型、参数、图像、函数元数据
src/services AppPaths、FileService、DatabaseService、PluginManager、DebugLogService
plugins/ Qt 动态库(IFunctionPlugin)
resources/ config/FunctionLib.ini、icons/、resources.qrc
examples/ 示例 .ys 脚本
tests/ Qt Test 回归(CodePlanetTests)
scripts/ i18n 脚本、check_layer_includes.ps1、run_tests.ps1、run_all_checks.ps1
勿保留 已废弃的 CodePlanetr/ 嵌套副本、EG_File/、根目录 ICO/、根目录 FunctionLib.ini 等旧布局。
依赖规则
gui可依赖core/functions、serviceslogic依赖core/runtime,不得依赖guicore不得依赖gui或logic- 插件仅通过
FunctionCallContext访问运行时,不 include GUI 头文件
分层检查(P4)
编译期仍使用单 editor.pro 目标;分层约束由脚本强制执行:
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/check_layer_includes.ps1
src/logic不得#include任何gui/头文件src/core不得#includegui/或logic/头文件- 违规时 exit 1 并打印文件路径;CI 或提交前建议运行
质量检查(P5)
本项目在 Windows 上使用 PowerShell + 批处理 跑分层检查与 Qt Test,无需手动配置 PATH。Agent 与开发者提交前建议执行。
一键检查(推荐)
在项目根目录打开 PowerShell,执行:
cd E:\HuaweiMoveData\Users\MateBookXPro\Desktop\CodePlanetr
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\run_all_checks.ps1
成功标志:最后一行输出 All checks passed.,进程 exit code 为 0。
脚本顺序:
1. scripts/check_layer_includes.ps1 — 分层 #include 违规扫描 2. scripts/run_tests.ps1 → 内部调用 scripts/build_tests.bat — 编译并运行 bin\CodePlanetTests.exe
本机工具链路径(已写入脚本默认值)
| 组件 | 路径 |
|---|---|
| Qt 5.11.1(msvc2017_64) | D:\Qt\Qt5.11.1\5.11.1\msvc2017_64 |
| qmake | 上述目录下 bin\qmake.exe |
| Visual Studio 2017 | D:\Visual Studio\2017\Community |
| vcvars | ...\VC\Auxiliary\Build\vcvars64.bat |
若 Qt 安装位置变更,可临时覆盖:
$env:QTDIR = "D:\Qt\Qt5.11.1\5.11.1\msvc2017_64"
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\run_all_checks.ps1
或编辑 scripts/build_tests.bat / scripts/run_tests.ps1 中的候选路径。
分项运行
| 目的 | 命令 |
|---|---|
| 仅分层检查 | powershell -NoProfile -ExecutionPolicy Bypass -File scripts\check_layer_includes.ps1 |
| 仅单元测试 | powershell -NoProfile -ExecutionPolicy Bypass -File scripts\run_tests.ps1 |
| 仅测试(不经过 PowerShell 包装) | 双击或 cmd /c scripts\build_tests.bat |
测试产物:bin\CodePlanetTests.exe、bin\testresults.xml(XML 报告)。
自动化未覆盖项(需 GUI 手动)
examples\ast_debug.ys:Ctrl+B → F5 / F6 / F10examples\expr_p1_test.ys:F5- 参数面板运行中修改 / 删除变量
详见 docs/AST审计执行报告/P5-阶段交付报告.md §6。
调试日志(P4 + P5)
- 统一出口:
ControlLogiLib::publishDebugMessage→DebugLogService::log(lineNo, msg)+SendDebugMessage信号 → UI - 插件:
FunctionCallContext::sendDebugMessage→FunctionDispatcher::setDebugMessageSink(P5) - 全局
SendDebugMessage回调(parameterparser.h)已 deprecated,仅经Init_DebugMessage注册后转发 RuntimeWorker::debugMessage在 AST 解析失败时从(at line:col)提取行号写入 DB
最近文件(P4)
DatabaseService::addRecentFile/recentFiles/removeRecentFile- UI:文件菜单 →「最近打开」,读取 SQLite
recent_files表
命名与 API 约定
| 约定 | 说明 |
|---|---|
| 类名 | PascalCase,与文件名一致 |
| 信号槽 | 动词开头,如 RunButtonClick、SendDebugMessage |
| 路径 | 禁止硬编码绝对路径;使用 AppPaths:: |
| 读写文件 | FileService::readTextFile / writeTextFile(UTF-8) |
| 脚本 | 扩展名 .ys、库清单 .yslib |
P4 已清理拼写错误 API(src/ 无旧名残留):
| 旧名 | 处置 / 当前 API |
|---|---|
HeightLightError |
移除 → CodeEditor::applyBuildDiagnostics(BuildResult) 绘制波浪线 |
SendBulidMessage |
移除 → CodeEditorWidget::handleBuildResult(BuildResult) 填充问题面板 |
ParaUpdata |
重命名 → ControlLogiLib::updateParameters() |
Build 信号链路:CodeEditor::buildFinished(ast::BuildResult) → CodeEditorWidget::handleBuildResult(connect 在 codeeditorwidget2.cpp)。
FunctionCatalog(P2)
FunctionCatalog为FunctionLib.ini唯一解析入口(FileService读文件)- 控制流/高亮/补全关键字在 INI 的
[ControlFlow]、[Highlight]等段配置,禁止在编辑器源码重复硬编码 Application启动加载 Catalog 后校验插件函数名
编辑器模块(P3)
src/gui/editor/ 已拆分,codeeditor.cpp 仅保留核心编辑逻辑(≤800 行):
| 文件 | 职责 |
|---|---|
codeeditor.cpp |
构造、搜索高亮、键盘/鼠标核心、调试箭头等 |
buildprogram.cpp |
Ctrl+B 入口,调用 ScriptAnalyzer 并绘制诊断波浪线 |
ScriptAnalyzer.cpp |
静态检查引擎(词法/语法/语义,见下表) |
LineNumberArea.cpp |
行号 gutter 绘制与断点/折叠点击 |
EditorFoldManager.cpp |
折叠区域解析与 toggle |
CppSyntaxHighlighter.cpp |
语法高亮(与 YsHighlightScanner 并存) |
CustomScrollBar.cpp |
搜索匹配滚动条标记 |
CodeEditorCompletion.cpp |
函数库补全 |
EditorBracketHighlight.cpp |
括号/参数高亮 |
CodeEditorIndent.cpp |
自动缩进 |
Ctrl+B 静态检查(ScriptAnalyzer)
| 阶段 | 代码前缀 | 说明 |
|---|---|---|
| 预处理 | YS001 | 字符串引号未成对 |
| 词法 | YS1xx | Lexer 错误 |
| 语法 | YS2xx | Parser 错误 |
| 语义 | YS301 | 作用域内重复变量(警告) |
| 语义 | YS302 | 重复 function 定义(错误) |
| 语义 | YS303 | 未定义函数/指令(错误) |
| 语义 | YS304/305 | break/continue 位置非法 |
| 语义 | YS306 | 函数参数名重复 |
| 语义 | YS307 | 库函数参数个数不匹配(警告) |
运行策略(与 VS Code/MSVC 一致):错误阻止 F5/F6;警告不阻止。F5/F6 预检为静默模式,仅在有错误时切到「问题」面板;Ctrl+B 始终打开问题面板。
Debug 日志:ReceiverDebugMessage 直接 m_debugEdit->append,无 100ms CycTimer。
新增脚本函数
1. 编辑 resources/config/FunctionLib.ini(CSV 函数行 + 可选 [Plugin])
- 插件函数也须写 CSV 行(含
para_count),否则 Ctrl+B 的 YS307 无法校验参数个数
2. 在插件中实现 IFunctionPlugin::execute(或扩展 BuiltinFunctions) 3. supportedFunctions() 注册函数名(匹配时不区分大小写) 4. 编译插件,部署到 bin/plugins/ 5. 用 examples/ 脚本验证
Git
- 不提交
*.pro.user、build/、bin/ - 提交说明写清「为什么」
编码
- 源码与脚本默认 UTF-8
- 旧 GBK 文件由
FileService自动回退解码 - MSVC:
editor.pro启用/utf-8;勿使用#pragma execution_character_set;固定中文用QString::fromUtf8(u8"...")
界面国际化
Application::setLocale+tr()+ 各控件retranslateUi()- 维护流程见 [国际化维护说明.md](国际化维护说明.md)
- 脚本:
scripts/update_i18n_ts.bat、scripts/generate_i18n.bat
维护 .cursor 与 docs
修改架构或上述约定时,请同时更新:
.cursor/开发指南.md与.cursor/rules/*.mdcdocs/下对应文档
相关文档
- [架构说明.md](架构说明.md)
- [插件开发指南.md](插件开发指南.md)
- [国际化维护说明.md](国际化维护说明.md)
- [分析报告/项目分析报告.md](分析报告/项目分析报告.md)
- [维护文件/AST逻辑架构.md](维护文件/AST逻辑架构.md)
- [.cursor/README.md](../.cursor/README.md)