语言: Object Pascal (Free Pascal / Lazarus)
主要用途: Free Pascal 编译器与 Lazarus IDE 的开发环境管理工具
架构: 模块化分层设计,命令模式 + Git 集成 + 沙箱构建系统
- 项目概述
- 核心开发范式:红-绿-重构(TDD)
- 构建与测试
- 项目架构
- 核心实现细节
- 配置与源码管理
- 重点提醒(Common Gotchas)
- 文件命名规范
- 测试策略
- 外部依赖
- 常用命令参考
- 仓库目录结构
FPDev 是一个跨平台的命令行工具,用于管理 Free Pascal 编译器(FPC)和 Lazarus IDE 的安装、配置、构建和包管理。该项目采用 模块化分层架构,遵循 命令模式(Command Pattern) 和 测试驱动开发(TDD) 原则,确保代码的可维护性、可扩展性和可测试性。
- 工具链管理: 安装、列举、切换和删除 FPC/Lazarus 版本
- 交叉编译支持: 添加和管理交叉编译目标
- 包管理: 安装和管理 Lazarus 包
- Git 集成: 使用 libgit2 进行源码管理和版本控制
- 沙箱构建: 默认安全构建,所有构建产物限制在沙箱内,不污染系统目录
- 模块化命令系统: 基于命令模式的可扩展命令框架
- 跨平台: Windows、Linux、macOS 支持
在实现任何功能之前,必须先编写测试。测试应该:
- 明确描述预期行为: 测试用例名称应清晰表达意图(如
test_git2_adapter_init_success) - 覆盖边界情况: 包括正常流程、异常输入、边界条件
- 快速失败: 测试应该立即失败,证明功能尚未实现
示例(参考 tests/test_git2_adapter.lpr):
var
Adapter: TGit2Manager;
RepoDir: string;
Repo: git_repository;
begin
Adapter := TGit2Manager.Create;
try
if not Adapter.Initialize then
begin
WriteLn('INIT_FAIL');
Halt(1);
end;
RepoDir := NewTempRepoDir('adapter_repo');
// 初始化一个空仓库(离线)
if git_repository_init(Repo, PChar(RepoDir), 0) <> GIT_OK then
begin
WriteLn('INIT_REPO_FAIL');
Halt(1);
end;编写最简单的代码使测试通过,不追求完美:
- 快速实现: 优先让测试变绿,不过度设计
- 单一职责: 每次只实现一个测试所需的功能
- 可验证: 确保测试能够稳定通过
原则:
- ✅ 先让测试通过(即使代码不优雅)
- ❌ 不在此阶段进行优化或重构
测试通过后,在保持测试绿色的前提下改进代码:
- 消除重复: 提取公共逻辑到辅助函数或类
- 改善命名: 使变量、函数名更具表达力
- 优化结构: 分离关注点,提高模块内聚性
- 持续验证: 每次改动后立即运行测试确保功能不被破坏
重构清单:
- 消除魔法数字(使用常量或枚举)
- 简化复杂条件判断(提取为有意义的函数)
- 减少函数参数(使用对象封装相关参数)
- 移除僵尸代码(未使用的变量、函数)
1. 🔴 编写测试 → 测试失败
↓
2. 🟢 实现功能 → 测试通过
↓
3. 🔵 重构代码 → 测试仍然通过
↓
4. 重复 1-3 直到功能完成
红阶段:
// tests/fpdev.build.manager/test_build_manager.lpr
// 期望:构建应限制在沙箱,不写系统目录
procedure TestSandboxBuildIsolation;
begin
LBM := TBuildManager.Create('sources/fpc/fpc-main', 2, False);
LBM.SetSandboxRoot('sandbox_test');
LBM.SetAllowInstall(True);
Assert(LBM.BuildCompiler('main'), 'Compiler build failed');
Assert(LBM.Install('main'), 'Install failed');
// 验证:系统目录未被修改,产物仅存在于沙箱
Assert(DirectoryExists('sandbox_test/fpc-main'), 'Sandbox missing');
Assert(not FileExists('C:\FPC\...'), 'System dir polluted!');
end;绿阶段: 实现 SetSandboxRoot 和 SetAllowInstall,确保 make 命令使用 DESTDIR 指向沙箱。
重构阶段: 提取路径构建逻辑到 BuildSandboxPath 函数,统一日志记录格式。
红阶段:
// tests/test_git2_adapter.lpr
// 期望:适配器初始化应成功,并能打开仓库
if not Adapter.Initialize then Halt(1);
Repo := Adapter.OpenRepository(RepoDir);
if Repo = nil then Halt(1);绿阶段: 实现 TGit2Manager.Initialize 和 OpenRepository,调用 libgit2 API。
重构阶段: 将错误处理逻辑抽取为 CheckGitError 辅助函数。
- 设计驱动: 先写测试强迫你思考接口设计和用户体验
- 快速反馈: 立即知道代码是否按预期工作
- 回归保护: 防止修改破坏现有功能
- 文档作用: 测试即活文档,展示如何使用 API
- 重构信心: 有测试覆盖,可以放心优化代码
- Free Pascal Compiler (FPC): 3.2.2 或更高版本
- Lazarus IDE: 2.2.0 或更高版本(可选,用于打开
.lpi项目) - Git: 用于克隆仓库和版本控制
- Make: 用于构建 FPC 源码(Windows 可用 MinGW/MSYS2 的
mingw32-make)
# 1. 克隆仓库
git clone <repository-url> fpdev
cd fpdev
# 2. 工具链体检(推荐)
scripts\check_toolchain.bat # Windows
bash scripts/check_toolchain.sh # Linux/macOS
# 3. 编译主程序
lazbuild fpdev.lpi
# 或使用 fpc 直接编译
fpc fpdev.lpr
# 4. 运行主程序
.\bin\fpdev.exe --help # Windows
./bin/fpdev --help # Linux/macOS# Windows
tests\fpdev.build.manager\run_tests.bat
# Linux/macOS
bash tests/fpdev.build.manager/run_tests.sh测试日志输出到 logs/build_*.log,包含详细的环境快照、命令行参数和产物验证信息。
| 命令 | 说明 |
|---|---|
lazbuild fpdev.lpi |
使用 Lazarus 构建系统编译主程序 |
fpc fpdev.lpr |
直接使用 FPC 编译主程序 |
lazbuild tests/<test>.lpi |
编译指定测试程序 |
scripts\run_examples.bat |
编译并运行示例(干跑模式) |
scripts\run_examples_real.bat |
运行真实构建示例(仅写沙箱) |
lazbuild 是 Lazarus IDE 的命令行构建工具,用于编译 .lpi 项目文件,无需打开 IDE。这是 FPDev 项目的标准构建方式。
lazbuild [选项] <项目文件.lpi>| 选项 | 说明 |
|---|---|
-B 或 --build-all |
完全重新编译(清理构建) |
-r 或 --recursive |
递归构建依赖包 |
--build-mode=<模式> |
指定构建模式(Debug、Release 等) |
-q 或 --quiet |
减少输出信息 |
-v 或 --verbose |
增加详细输出 |
--os=<目标> |
目标操作系统(win32、linux、darwin 等) |
--cpu=<目标> |
目标 CPU 架构(x86_64、i386、aarch64 等) |
--widgetset=<组件集> |
目标窗口组件集(win32、gtk2、qt5、cocoa 等) |
--build-mode-list |
列出项目中定义的所有构建模式 |
# 1. 检查可用的构建模式
lazbuild --build-mode-list fpdev.lpi
# 2. 简单编译(增量构建)
lazbuild fpdev.lpi
# 3. 完全重新编译(推荐用于测试重大更改)
lazbuild -B fpdev.lpi
# 4. 编译并构建依赖包
lazbuild -B -r fpdev.lpi
# 5. 指定构建模式(Release 模式,启用优化)
lazbuild -B --build-mode=Release fpdev.lpi
# 6. 安静模式(减少输出)
lazbuild -B -q fpdev.lpi
# 7. 交叉编译示例(Linux x86_64)
lazbuild --os=linux --cpu=x86_64 fpdev.lpi项目中定义的构建模式(在 fpdev.lpi 中配置):
- Default - 标准调试构建(默认)
- Debug - 完整调试符号,禁用优化
- Release - 启用优化,去除调试信息
- Test - 用于运行单元测试
- 重大更改时使用
-B: 确保所有文件完全重新编译 - 依赖更改时使用
-r: 递归构建所有依赖包 - 自动化脚本中明确指定构建模式: 避免依赖默认值
- CI/CD 流程中检查退出码:
lazbuild成功返回0,失败返回非零值 - 使用绝对路径: 在脚本中使用绝对路径避免路径问题
0- 编译成功1- 编译错误(语法错误、链接失败等)2- 无效参数
问题:包未找到
错误: Package 'xxx' not found
解决: 使用 -r 标志递归构建依赖包
lazbuild -B -r fpdev.lpi
问题:单元文件未找到
错误: Fatal: Can't find unit xxx
解决: 检查项目选项中的单元搜索路径,确保所有依赖已编译
问题:编译期间访问冲突
错误: Runtime error 216 (Access Violation)
解决: 使用 -B 清理构建,检查是否存在循环单元引用
lazbuild -B fpdev.lpi
# 定义构建辅助函数
function Build-FPDev {
param(
[switch]$Clean,
[string]$Mode = "Default",
[switch]$Recursive,
[switch]$Quiet
)
$args = @()
if ($Clean) { $args += "-B" }
if ($Recursive) { $args += "-r" }
if ($Quiet) { $args += "-q" }
$args += "--build-mode=$Mode"
$args += "fpdev.lpi"
Write-Host "Building with: lazbuild $($args -join ' ')" -ForegroundColor Cyan
lazbuild @args
if ($LASTEXITCODE -ne 0) {
Write-Host "Build failed with exit code $LASTEXITCODE" -ForegroundColor Red
exit $LASTEXITCODE
} else {
Write-Host "Build succeeded" -ForegroundColor Green
}
}
# 使用示例
Build-FPDev -Clean -Mode Release -Recursive# GitHub Actions / GitLab CI 示例
steps:
- name: Build FPDev (Release)
run: |
lazbuild -B -q --build-mode=Release fpdev.lpi
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
shell: pwsh
- name: Build Tests
run: |
lazbuild -B --build-mode=Test tests/test_git2_adapter.lpi
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
shell: pwsh
- name: Run Tests
run: |
.\bin\test_git2_adapter.exe
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
shell: pwsh# 构建主程序(标准)
lazbuild -B fpdev.lpi
# 构建主程序(Release,用于发布)
lazbuild -B --build-mode=Release fpdev.lpi
# 构建测试(Git 适配器测试)
lazbuild -B tests\test_git2_adapter.lpi
# 构建测试(构建管理器测试)
lazbuild -B tests\fpdev.build.manager\test_build_manager.lpi
# 构建所有测试(批处理)
foreach ($test in Get-ChildItem -Path tests -Filter *.lpi -Recurse) {
Write-Host "Building $($test.FullName)"
lazbuild -B $test.FullName
}┌─────────────────────────────────────────────────────────────┐
│ 命令行界面层 (CLI) │
│ - 参数解析 │
│ - 用户交互 │
├─────────────────────────────────────────────────────────────┤
│ 命令处理层 (Command Layer) │
│ ┌─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐ │
│ │ help │ version │ fpc │ lazarus │ package │ cross │ │
│ └─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 核心服务层 (Service Layer) │
│ ┌─────────────┬─────────────┬─────────────┬─────────────┐ │
│ │ 配置管理 │ 版本管理 │ 构建系统 │ Git 操作 │ │
│ │ (Config) │ (Version) │ (Build) │ (Git2) │ │
│ └─────────────┴─────────────┴─────────────┴─────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 基础设施层 (Infrastructure) │
│ ┌─────────────┬─────────────┬─────────────┬─────────────┐ │
│ │ 文件系统 │ 进程管理 │ 网络操作 │ 系统信息 │ │
│ │ (FileSystem)│ (Process) │ (Network) │ (SysInfo) │ │
│ └─────────────┴─────────────┴─────────────┴─────────────┘ │
└─────────────────────────────────────────────────────────────┘
FPDev 使用 命令模式 组织所有功能,核心接口 ICommand 提供统一的命令接口:
ICommand = interface
function Execute(Context: ICommandContext): Integer;
end;命令层次结构:
fpdev (根命令)
├── help (显示帮助)
├── version (显示版本信息)
├── fpc (FPC 管理)
│ ├── install
│ ├── list
│ ├── default
│ └── remove
├── lazarus (Lazarus 管理)
│ ├── install
│ ├── list
│ ├── default
│ └── remove
├── cross (交叉编译)
│ ├── add
│ ├── list
│ └── remove
├── package (包管理)
│ ├── install
│ ├── list
│ └── remove
└── project (项目管理)
├── new
├── build
└── clean
- 主程序导入命令模块
- 每个模块的
initialization部分调用RegisterCommand(path, factory) - 运行时,分发器在注册表中查找命令路径
- 工厂函数创建命令实例并调用
Execute(context)
示例命令结构:
// fpdev.cmd.fpc.list.pas
type
TFPCListCommand = class(TInterfacedObject, ICommand)
function Execute(Context: ICommandContext): Integer;
end;
initialization
RegisterCommand('fpc.list', @CreateFPCListCommand);
end.FPDev 使用 libgit2 进行 Git 操作,采用三层适配器模式隔离外部依赖:
应用层 (TFPCSourceManager)
↓ 调用高级接口
适配器层 (TGit2Manager / IGitManager) ← fpdev.git2.pas / git2.api.pas
↓ 封装 Git 操作
绑定层 (libgit2.pas) ← 3rd/libgit2
↓ FFI 调用
原生库 (git2.dll / libgit2.so / libgit2.dylib)
高级面向对象接口 (fpdev.git2.pas):
TGitManager: 单例管理器,通过GitManager访问TGitRepository: 仓库操作TGitCommit: 提交对象- 抛出
EGitError异常
现代接口风格 (git2.api.pas + git2.impl.pas):
NewGitManager(): 获取IGitManager接口- 更易于测试和替换后端
- 推荐用于新代码
C API 绑定 (libgit2.pas):
- 直接 libgit2 调用
- 仅在需要底层控制时使用
- 必须调用
git_libgit2_init()和git_libgit2_shutdown()
运行时要求: Windows 上 git2.dll 必须在 PATH 或可执行文件目录中
- 隔离变更: libgit2 API 变更不影响应用层
- 可测试性: 可 Mock
TGit2Manager或IGitManager进行单元测试 - 跨平台: 统一接口屏蔽平台差异(DLL 路径、调用约定)
TBuildManager (src/fpdev.build.manager.pas) 负责 FPC 源码的构建、安装和验证,核心设计原则:
- 无 make 时优雅降级: 检测不到
make时打印提示并返回成功,不阻塞流程 - 沙箱隔离: 所有构建产物写入
sandbox/<version>目录,不污染系统目录 - 不触发网络: 不下载外部依赖
- 不修改全局配置: 不写
/etc/fpc.cfg或系统注册表
TBuildManager = class
// 构造函数
constructor Create(ASourceRoot: string; AParallelJobs: Integer; AVerbose: Boolean);
// 沙箱配置
procedure SetSandboxRoot(const APath: string);
procedure SetAllowInstall(AEnable: Boolean);
// 构建流程
function BuildCompiler(const AVersion: string): Boolean;
function BuildRTL(const AVersion: string): Boolean;
function Install(const AVersion: string): Boolean;
function Configure(const AVersion: string): Boolean;
// 验证与诊断
function TestResults(const AVersion: string): Boolean;
function Preflight(): Boolean; // 预检环境
// 日志控制
procedure SetLogVerbosity(ALevel: Integer); // 0=简洁, 1=详细
property LogFileName: string read FLogFile;
end;1. Preflight() → 检查 make/源码路径/沙箱可写性
2. BuildCompiler() → 编译编译器 (make compiler)
3. BuildRTL() → 编译运行时库 (make rtl)
4. Install() → 安装到沙箱 (make install DESTDIR=sandbox)
5. Configure() → 占位(不写系统配置)
6. TestResults() → 验证产物完整性
启用严格校验后(SetStrictResults(True)),TestResults() 会检查:
bin/目录是否包含编译器可执行文件(fpc.exe,ppcx64.exe等)lib/目录是否包含子目录(如fpc/<version>)- 可通过
build-manager.strict.ini自定义清单
配置示例 (plays/fpdev.build.manager.demo/build-manager.strict.ini):
[bin]
required_prefix=fpc,ppc
required_ext=.exe,.sh,
min_count=1
[lib]
require_subdir=true
min_count=1
[fpc]
require_cfg=true
cfg_relative_list=etc/fpc.cfg,lib/fpc/fpc.cfg每次构建生成独立日志文件 logs/build_yyyymmdd_hhnnss_zzz.log,包含:
- Start/End 标记: 每个阶段的开始和结束时间
- 环境快照: OS、PATH 前 N 项(详细模式)
- 命令行: 完整的
make命令及参数 - 产物样本:
bin/、lib/目录的前 N 个文件 - Summary: 汇总信息(版本、阶段、结果、耗时)
TConfigManager (fpdev.config.pas) 管理应用程序配置,支持:
- 工具链管理: 添加、删除、切换 FPC/Lazarus 版本
- 交叉编译目标: 管理
target-cpu和target-os组合 - 持久化: JSON 格式存储到
~/.fpdev/config.json(Windows:%APPDATA%\.fpdev\config.json)
配置文件结构:
{
"version": "1.0",
"toolchains": {
"fpc-3.2.2": {
"path": "C:\\FPC\\3.2.2\\bin\\i386-win32",
"version": "3.2.2",
"default": true
}
},
"lazarus_installs": {
"lazarus-2.2.0": {
"path": "C:\\lazarus",
"version": "2.2.0"
}
},
"cross_targets": [
{"cpu": "x86_64", "os": "linux"},
{"cpu": "aarch64", "os": "darwin"}
],
"settings": {
"default_toolchain": "fpc-3.2.2",
"parallel_jobs": 4
}
}sources/
└── fpc/
├── fpc-main/ # 主分支源码
├── fpc-3.2.2/ # 稳定版本
└── fpc-fixes/ # 修复分支
sandbox/
└── fpc-<version>/
├── bin/ # 编译器可执行文件
├── lib/ # 运行时库和单元文件
├── etc/ # 配置文件
├── share/ # 文档和示例
└── include/ # 头文件
logs/
├── build_*.log # 构建日志
├── check/ # 工具链检查日志
│ └── toolchain_*.txt
└── examples/ # 示例程序日志
└── real/ # 真实构建日志
- ✅ 使用
PathDelim常量(跨平台) - ❌ 硬编码
\或/
// 正确
Result := Base + PathDelim + 'bin' + PathDelim + 'fpdev';
// 错误
Result := Base + '\bin\fpdev'; // Windows only!- ✅ 调用
git_libgit2_init()后必须调用git_libgit2_shutdown() - ❌ 忘记释放资源会导致内存泄漏
// 正确
Adapter := TGit2Manager.Create;
try
if not Adapter.Initialize then Exit;
// ... 使用 ...
finally
Adapter.Free; // 内部调用 shutdown
end;- ✅ 默认
AllowInstall = False,必须显式启用 - ❌ 未启用安装时调用
Install()会返回False
// 正确
LBM := TBuildManager.Create('sources/fpc/fpc-main', 2, False);
LBM.SetSandboxRoot('sandbox');
LBM.SetAllowInstall(True); // 必须显式启用
LBM.Install('main');- ✅ 确保命令模块在主程序中有强引用导入
- ❌ 未导入的命令模块不会注册,导致 "command not found"
// fpdev.lpr 中必须导入所有命令模块
uses
fpdev.cmd.fpc,
fpdev.cmd.fpc.install,
fpdev.cmd.fpc.list,
// ...其他命令模块⚠️ Windows 日志时间戳可能包含空格(小时 < 10)- 建议:使用零填充格式(如
FormatDateTime('yyyymmdd_hhnnss', Now))
- ✅ 构建前先运行
Preflight()检查环境 - ❌ 假设
make总是存在会导致运行时错误
- ❗ 绝对不要在终端输出中文!
⚠️ Windows 终端输出中文会导致 "Disk Full" 错误- ✅ 所有用户可见的输出必须使用英文
- ✅ 日志文件和配置文件可以使用 UTF-8 编码
// 错误 - 会在 Windows 上导致 Disk Full
WriteLn('用法: fpdev help');
WriteLn('错误: 未知命令');
// 正确 - 使用英文
WriteLn('Usage: fpdev help');
WriteLn('Error: Unknown command');原因: Windows 控制台的编码问题会导致 Pascal 的 WriteLn 在输出中文时触发 I/O 错误,表现为 "Disk Full" 异常。这是 已知问题,必须使用英文输出。
- 格式:
fpdev.<module>.pas - 示例:
fpdev.config.pas- 配置管理fpdev.git2.pas- Git 适配器fpdev.build.manager.pas- 构建管理器fpdev.terminal.pas- 终端输出fpdev.cmd.fpc.pas- FPC 根命令fpdev.cmd.fpc.install.pas- FPC 安装子命令
- 格式:
test_<module>.lpr - 示例:
test_git2_adapter.lprtest_build_manager.lprtest_build_manager_strict_pass.lpr
- 主程序:
fpdev.lpr(主项目源码) +fpdev.lpi(Lazarus 项目配置) - 测试:
tests/<test_name>/<test_name>.lpr+<test_name>.lpi - 示例:
plays/<example_name>/<example_name>.lpr+<example_name>.lpi
| 类型 | 位置 | 运行方式 | 目标覆盖率 |
|---|---|---|---|
| 单元测试 | tests/ |
lazbuild <test>.lpi |
> 80% |
| 集成测试 | tests/ |
run_tests.bat |
> 70% |
| 示例演示 | plays/ |
run_examples.bat |
N/A |
- 隔离性: 每个测试独立运行,不依赖其他测试
- 可重复性: 测试结果稳定,不受环境影响
- 快速反馈: 单元测试应在秒级完成
- 清理资源: 测试结束后清理临时文件和目录
- 成功场景:
test_<module>_<action>_success - 失败场景:
test_<module>_<action>_fail - 边界条件:
test_<module>_<action>_edge_case
# Windows
tests\fpdev.build.manager\run_tests.bat
# Linux/macOS
bash tests/fpdev.build.manager/run_tests.sh| 依赖 | 版本 | 用途 | 许可证 |
|---|---|---|---|
| Free Pascal | ≥ 3.2.2 | 编译器和运行时 | LGPL + 修改版 |
| Lazarus | ≥ 2.2.0 | IDE 和构建工具(可选) | GPL + LGPL |
| libgit2 | ≥ 1.5.0 | Git 操作 | GPL v2 + GCC RE |
| fpJSON | 内置 | JSON 解析 | FPC RTL |
- libgit2 绑定:
3rd/libgit2/libgit2.pas - 静态链接库:
3rd/libgit2/lib/(Windows:git2.dll, Linux:libgit2.so, macOS:libgit2.dylib)
# 安装 FPC + Lazarus
choco install fpc lazarus
# 或下载官方安装包
# https://www.lazarus-ide.org/index.php?page=downloads
# libgit2 已包含在 3rd/ 目录,无需额外安装sudo apt-get update
sudo apt-get install -y fpc lazarus libgit2-devbrew install fpc lazarus libgit2# 编译主程序
lazbuild fpdev.lpi
# 编译并输出详细信息
lazbuild -B fpdev.lpi
# 直接使用 FPC 编译
fpc -Mobjfpc -Scghi -O3 -Xs -XX fpdev.lpr
# 清理构建产物
del /Q bin\*.exe bin\*.o bin\*.ppu # Windows
rm -f bin/*.exe bin/*.o bin/*.ppu # Linux/macOS# 运行单个测试
lazbuild tests\test_git2_adapter.lpi
bin\test_git2_adapter.exe
# 运行所有 BuildManager 测试
tests\fpdev.build.manager\run_tests.bat
# 查看最新测试日志
type logs\build_*.log | findstr /C:"Summary" /C:"FAIL" # Windows
tail -f logs/build_*.log | grep -E "Summary|FAIL" # Linux/macOS# 干跑模式(不执行 make)
scripts\run_examples.bat # Windows
bash scripts/run_examples.sh # Linux/macOS
# 真实构建(仅写沙箱)
scripts\run_examples_real.bat # Windows
bash scripts/run_examples_real.sh # Linux/macOS
# 严格模式 + 详细日志
cd plays\fpdev.build.manager.demo
buildOrTest.bat strict # Windows
STRICT=1 VERBOSE=1 bash buildOrTest.sh # Linux/macOS# 克隆 FPC 源码
.\bin\fpdev.exe source clone main
# 检查源码状态
.\bin\fpdev.exe source status main
# 切换分支
.\bin\fpdev.exe source checkout fixes_3_2fpdev/
├── bin/ # 编译产物(可执行文件、.o、.ppu)
├── lib/ # 中间构建产物
├── src/ # 源代码
│ ├── fpdev.lpr # 主程序入口
│ ├── fpdev.config.pas # 配置管理
│ ├── fpdev.git2.pas # Git 适配器(高级 OO)
│ ├── git2.api.pas # Git 接口(现代风格)
│ ├── git2.impl.pas # Git 实现
│ ├── fpdev.build.manager.pas # 构建管理器
│ ├── fpdev.terminal.pas # 终端输出
│ ├── fpdev.utils.pas # 工具函数
│ ├── fpdev.cmd.*.pas # 根命令模块
│ ├── fpdev.cmd.*.<action>.pas # 子命令模块
│ └── commands/ # 命令实现(按领域组织)
├── tests/ # 测试程序
│ ├── test_git2_adapter.lpr
│ └── fpdev.build.manager/
│ ├── test_build_manager.lpr
│ ├── test_build_manager_strict_pass.lpr
│ ├── test_build_manager_strict_fail.lpr
│ └── run_tests.bat
├── plays/ # 示例和演示
│ └── fpdev.build.manager.demo/
│ ├── demo.lpr
│ ├── buildOrTest.bat
│ └── build-manager.strict.ini
├── scripts/ # 辅助脚本
│ ├── check_toolchain.bat
│ ├── run_examples.bat
│ └── run_examples_real.bat
├── docs/ # 文档
│ ├── README.md
│ ├── fpdev.md # 详细设计文档
│ ├── ARCHITECTURE.md # 架构设计
│ └── build-manager.md # BuildManager 文档
├── 3rd/ # 第三方库
│ └── libgit2/
│ ├── libgit2.pas # Pascal 绑定
│ └── lib/ # 原生库(.dll/.so/.dylib)
├── sources/ # FPC/Lazarus 源码(构建时使用)
│ └── fpc/
│ ├── fpc-main/
│ └── fpc-3.2.2/
├── sandbox/ # 沙箱构建产物
│ └── fpc-<version>/
│ ├── bin/
│ ├── lib/
│ └── etc/
├── logs/ # 日志文件
│ ├── build_*.log
│ ├── check/
│ └── examples/
├── fpdev.lpi # Lazarus 项目文件
├── fpdev.lpr # 主程序源码
└── WARP.md # 本文档
- 每个新功能先写测试
- 测试应该简洁、清晰、易读
- 频繁运行测试(每次代码改动后)
- 保持测试独立(不依赖执行顺序)
- 测试失败时立即修复(不积累技术债)
- 重构时保持测试绿色
- 为边界条件和异常路径编写测试
- 跳过红阶段直接实现功能
- 在绿阶段过度设计
- 在测试失败时继续添加新功能
- 编写依赖外部服务的测试(应使用 Mock)
- 测试私有实现细节(应测试公开接口)
- 忽略失败的测试
- 注释掉失败的测试(应修复或删除)
- Free Pascal 官方文档: https://www.freepascal.org/docs.html
- Lazarus Wiki: https://wiki.freepascal.org/
- libgit2 文档: https://libgit2.org/docs/
- 测试驱动开发(TDD): Kent Beck - "Test-Driven Development: By Example"
- 命令模式: Gang of Four - "Design Patterns"
最后更新: 2025-01-28
维护者: FPDev Team
许可证: 见 LICENSE 文件