感谢你愿意改进 lspling。这个项目的核心原则是:
- 练习必须能独立编译和运行。
- 初始状态必须失败;参考答案必须通过。
- 每个练习只聚焦一个主要系统调用或概念,但可以覆盖相关细节。
- 提示应当引导思考,而不是直接给出答案。
- 每个练习都要标注书本章节和页码。
- 练习与参考答案由生成器保持同步。
- 在
tools/specs_ch01.py…tools/specs_ch11.py或tools/specs_appendix_a.py中选择对应章节。 - 调用
ex(...)添加规格:
ex(
topic="01_file_io",
slug="14_new_exercise",
title="...",
objective="...",
reference="Ch. 2, ... (pp. 26-31)",
hint="...",
code=r"""
int answer(void)
{
return 42;
}
""",
tests=r"""
LSPLING_CHECK_INT(answer(), 42);
""",
breaks=[
(
" return 42;",
" /* TODO: return the answer. */\n return 0;",
)
],
)- 重新生成:
python3 tools/generate_exercises.py- 验证:
./lspling verify
./lspling selftest
python3 tools/generate_exercises.py --check| 字段 | 说明 |
|---|---|
topic |
主题目录名,例如 01_file_io |
slug |
练习文件名,例如 04_read_all |
title |
练习标题 |
objective |
一句话学习目标 |
reference |
书本章节与页码;不要包含 ` |
hint |
不直接给答案的提示 |
code |
正确代码(包含需要的 #include) |
tests |
main 中执行的测试代码 |
breaks |
将正确代码转成初始练习的替换列表 |
compile_fail |
初始状态是否故意无法编译 |
多文件练习使用 project(),其中 files 是文件名到完整内容的映射,
file_breaks 是 (文件名, 正确片段, 初始片段) 替换列表。
练习只需要包含 include/lspling/test.h,并使用:
LSPLING_CHECK(expr)LSPLING_CHECK_MSG(expr, message)LSPLING_CHECK_INT(actual, expected)LSPLING_CHECK_STR(actual, expected)LSPLING_CHECK_MEM(actual, expected, size)lspling_report()
一个练习要能进入仓库,./lspling selftest 必须能稳定地判定“初始版本
失败、参考答案通过”。因此请遵守:
- 断言的初始状态要稳定失败,不要依赖数据竞争或不稳定的调度结果。
- 只用
/tmp/lspling_*前缀的临时文件,并在测试结束前清理。 - 自己 fork 出来的子进程必须全部
waitpid()回收。 - 跨进程同步用管道,不要用
sleep猜时序。 - 时间、调度相关的断言放宽到安全区间。
- 权限相关的断言放在
geteuid() != 0分支里,或接受EPERM || EINVAL这类等价错误码。 - 不要向不属于本练习的进程发信号(尤其不要向 PID 1 发信号)。
- 如果初始版本会死锁或超时,请确认这是有意的教学效果,并让超时足够短。
- 使用 C17。
- 缩进 4 个空格,花括号另起一行。
- 默认开启
-Wall -Wextra -Wpedantic -Werror。 - 需要 GNU 扩展时,在文件内用
#pragma GCC diagnostic局部关闭警告。 - 不要依赖未定义行为,除非该练习的目标就是演示它。
运行共享头文件格式化:
make format./lspling verify
./lspling selftest
python3 tools/generate_exercises.py --check