|
1 | 1 | # chsrc — Change Source Everywhere |
2 | 2 |
|
3 | | -跨平台换源 CLI 工具与框架。使用 **C11**(推荐 C17 或更高版本)编写,从 `src/chsrc-main.c` 单文件编译。支持 Linux、Windows(原生/MSYS2/Cygwin)、macOS、BSD、Android。主程序采用 GPL-3.0-or-later 许可,`lib/xy.h` 采用 MIT 许可。 |
| 3 | +跨平台换源 CLI 工具与框架。使用 **C11**(推荐 C17 或更高版本)编写,从 `src/chsrc-main.c` 单文件编译(`#include` 汇聚所有框架与 recipe 文件,无中间目标文件)。支持 Linux、Windows(原生/MSYS2/Cygwin)、macOS、BSD、Android。主程序 GPL-3.0-or-later 许可,`lib/xy.h` 为 MIT 许可。 |
4 | 4 |
|
5 | | -## 构建与测试 |
| 5 | +**本项目在 `doc/` 中维护了详细的中文文档,遇到构建、recipe 编写、代码风格等问题时,请优先阅读对应文档,而不要凭空猜测:** |
6 | 6 |
|
7 | | -单一入口:`src/chsrc-main.c` 通过 `#include` 引入所有框架和 recipe 文件。无需构建编排工具。 |
| 7 | +| 主题 | 文档 | |
| 8 | +| -------------------------------- | --------------------------------------------- | |
| 9 | +| 开发环境、构建(just/make)、Debug、测试 | `doc/01-开发与构建.md` | |
| 10 | +| 分支策略、提交与贡献流程 | `doc/02-提交与贡献.md` | |
| 11 | +| 为什么不用代码格式化工具、风格哲学 | `doc/03-为什么拒绝使用代码格式化工具.md` | |
| 12 | +| 如何编写新 recipe(核心概念、步骤、准则) | `doc/10-如何编写recipe.md` | |
| 13 | +| 如何设置换源链接与测速链接 | `doc/11-如何设置换源链接与测速链接.md` | |
| 14 | +| 协作者与维护者 | `doc/50-协作者与维护者.md` | |
| 15 | + |
| 16 | +## 快速命令 |
8 | 17 |
|
9 | 18 | ``` |
10 | | -# 构建 (just — 推荐用于原生 Windows) |
11 | | -just build # DEV 模式 → chsrc.exe |
12 | | -just bd # DEBUG 模式 (-DXY_DEBUG) → chsrc-debug.exe |
13 | | -just br # RELEASE 模式 (-O2) → chsrc-release.exe |
14 | | -
|
15 | | -# 构建 (make — Linux/macOS/MSYS2) |
16 | | -make build / bd / br |
17 | | -make STATIC=1 br # 静态链接 |
18 | | -
|
19 | | -# 调试 / 清理 |
20 | | -just debug # 构建 DEBUG 版并启动调试器 (gdb/lldb) |
21 | | -just clean # 清理构建产物 |
22 | | -
|
23 | | -# 测试 |
24 | | -just test # xy.h + framework 单元测试 |
25 | | -just test-cli # CLI 集成测试 (Perl) |
26 | | -just fastcheck # CLI 快速检查 (test/cli.pl fastcheck) |
| 19 | +just build / bd / br # DEV / DEBUG / RELEASE 模式构建(推荐原生 Windows) |
| 20 | +make build / bd / br # 同上(Linux/macOS/MSYS2) |
| 21 | +just test / test-cli / fastcheck |
27 | 22 | ``` |
28 | 23 |
|
29 | | -DEBUG 模式(`-DXY_DEBUG`)会启用诊断功能:输出 `chsrc_debug*` 日志和 `chef_debug_dish()` 调试信息,并在每次 Get/Set/Reset 操作后重跑所有 `_prepare()` 以验证其可重复执行且不会崩溃(见 `src/chsrc-main.c` 中的 `chsrc_perform_all_prepare()`)。 |
30 | | - |
31 | | -原生 Windows 上 `just br` 会先用 `windres` 编译 `src/resource/chsrc.rc` 生成 `chsrc.res` 再链接。开发与 PR 请使用 `dev` 分支(CI 仅在 PR 目标为 `dev` 时运行测试,见 `doc/01-开发与构建.md`)。 |
32 | | - |
33 | | -## 架构 |
34 | | - |
35 | | -### 单文件编译 |
36 | | - |
37 | | -所有代码通过 `#include` 汇聚到 `src/chsrc-main.c`。整个编译过程不产生中间目标文件,也无额外链接步骤。 |
38 | | - |
39 | | -### 目录结构 |
40 | | - |
41 | | -| 路径 | 职责 | |
42 | | -| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------- | |
43 | | -| `src/chsrc-main.c` | 入口点:`main()`、CLI 解析、输出显示 | |
44 | | -| `src/framework/` | 核心:全局状态、Chef DSL、类型定义、镜像站注册、辅助工具 | |
45 | | -| `src/framework/struct.h` | `Dish_t`、`Source_t`、`MirrorSite_t`、Chef DSL 宏 — **chsrc 的骨架** | |
46 | | -| `src/framework/core.c` | 框架核心:全局状态、C11 版本检查、速度测量(curl)、CLI 辅助 | |
47 | | -| `src/framework/chef.c` | Chef DSL 实现,供 recipe 作者使用 | |
48 | | -| `src/framework/helper.c` | `hp_*` 通用辅助函数(URL 判断、字符串处理等),供 recipe 与 framework 使用 | |
49 | | -| `src/framework/mirror.c` | 通用镜像站定义与测速链接(TUNA、BFSU 等教育网镜像) | |
50 | | -| `src/framework/version.h` | 版本宏(`Chsrc_Version` 等),发布新版本前修改 | |
51 | | -| `src/recipe/lang/` | 编程语言 recipe:按语言分子目录(`Ruby/`、`Python/`、`JavaScript/`、`Rust/`、`Dart/`)加平铺文件(Go、Java、Perl、PHP 等) | |
52 | | -| `src/recipe/os/` | 操作系统 recipe:按发行版分子目录(`APT/`、`BSD/`、`pacman/`、`YUM/`)加平铺文件(AlmaLinux、Alpine、Fedora 等) | |
53 | | -| `src/recipe/ware/` | 软件 recipe:按工具分子目录(`Anaconda/`、`Docker/`、`Homebrew/`)加平铺文件(CocoaPods、Flatpak、Nix 等) | |
54 | | -| `src/recipe/*/rawstr4c.h` | 各 recipe 子目录内使用的原始字符串资源(如 `src/recipe/lang/rawstr4c.h` 供 lang/ 下的 recipe 共享);文档见 `src/rawstr4c.md` | |
55 | | -| `src/recipe/*/common.h` | 同一 recipe 子目录内共享的公共头文件(如 `lang/Python/common.h`、`os/APT/common.h`) | |
56 | | -| `src/recipe/menu.c` | 注册表,将所有 recipe 接入三个分类列表(`pl`、`os`、`wr`) | |
57 | | -| `src/recipe/recipe-template.c` | 新 recipe 作者的模板 | |
58 | | -| `lib/xy.h` | 独立的 C11 工具库(MIT):字符串、操作系统检测、日志、数据结构、文件 I/O | |
59 | | -| `src/resource/` | Windows 资源文件(`chsrc.rc`、`logo.ico`),RELEASE 构建时由 `windres` 编译 | |
60 | | -| `include/` | 空占位目录(仅 `.keep`),配合构建参数 `-Iinclude -Ilib` | |
61 | | -| `test/` | `xy.c`(xy.h 测试)、`fw.c`(framework 测试)、`cli.pl`(CLI 集成测试,Perl) | |
62 | | -| `doc/` | 开发者和用户文档(中文) | |
63 | | - |
64 | | -### 核心抽象 |
65 | | - |
66 | | -- **`Dish_t`** — 换源菜品。字段:别名(`aliases`)、函数指针(`getfn`/`setfn`/`resetfn`/`preparefn`)、`inited` 标志、源列表(`sources`/`sources_n`)、特性(`can_english`、`can_user_define`)、作用域(`scope_caps[]`、`default_scope`)、`note`、维护信息(`created_on`/`last_updated`)、贡献者列表(`chefs`/`sauciers`)。 |
67 | | -- **`Scope_t`** — 作用域:`ProjectScope`/`UserScope`/`SystemScope`,外加特殊的 `ImplementationDefinedScope`(由 recipe 按实际情况决定;多数 recipe 的 `default_scope` 设为它)。 |
68 | | -- **`SourceProvider_t` / `MirrorSite_t`** — 镜像站或上游源提供者。类型:`IS_GeneralMirrorSite`(通用镜像站)、`IS_DedicatedMirrorSite`(专用镜像站)、`IS_UpstreamProvider`(上游默认源)、`IS_UserDefinedProvider`(用户自定义源)。 |
69 | | -- **Chef DSL** — 宏与函数的混合 API,供 recipe 作者使用: |
70 | | - - 宏:`def_dish()`、`chef_prep_this_dish()`、`chef_allow_gsr()/s()/sr()/gs()/NOOP()`、`def_sources_begin()/end()`、`chsrc_use_this_source()`。 |
71 | | - - 函数:贡献者 `chef_set_chefs()`/`chef_set_sauciers()`;作用域 `chef_set_scope_cap()`/`chef_set_default_scope()`;换源与测速链接 `chef_set_repoURL()`、`chef_set_smURL()`、`chef_set_smURL_with_postfix()`、`chef_set_smURL_with_func()`、`chef_set_provider_smURL()`;特性 `chef_allow_english()`/`chef_deny_english()`、`chef_allow_user_define()`/`chef_deny_user_define()`;其他 `chef_set_note()`、`chef_use_other_dish_sources()`、`chef_verify_contributor()`。 |
72 | | -- **xy.h** — 跨平台运行时:`xy_on_windows`、字符串操作(`xy_str_gsub`、`xy_2strcat`)、`XySeq_t`(链表)、`XyMap_t`(哈希表)、命令执行、文件 I/O。内存约定:`return caller-free` 表示调用方必须释放返回值。 |
73 | | - |
74 | | -### 执行流程 |
| 24 | +开发与 PR 请使用 `dev` 分支。详细依赖、调试方式见 `doc/01-开发与构建.md`。 |
75 | 25 |
|
76 | | -1. `main()` → `chsrc_init_framework()` → `chsrc_init_menu()`(填充 dish 列表),并调用 `chsrc_register_contributors()` 把所有贡献者登记到代码中 |
77 | | -2. 解析 CLI → 在 `pl`/`os`/`wr` 三个菜单中搜索匹配的 dish → 调用 `preparefn()` → 按 `dishOp`(Get/Set/Reset/Measure/List_Config)分派到 `getfn`/`setfn`/`resetfn` 等 |
78 | | -3. 执行 `set` 时:通过系统 `curl` 自动测量镜像站速度(精准/粗略测速,见 `doc/11-如何设置换源链接与测速链接.md`),选择最快的,recipe 的 `_setsrc()` 执行换源 |
| 26 | +## 架构速览 |
79 | 27 |
|
80 | | -### Recipe 模式 |
| 28 | +- `src/chsrc-main.c` — 入口:`main()`、CLI 解析、输出显示 |
| 29 | +- `src/framework/` — 核心骨架:`struct.h`(`Dish_t`/`Source_t`/`MirrorSite_t`/Chef DSL 宏定义)、`core.c`(全局状态、测速)、`chef.c`(Chef DSL 实现)、`helper.c`(`hp_*` 辅助函数)、`mirror.c`(通用镜像站) |
| 30 | +- `src/recipe/{lang,os,ware}/` — 按类别(`pl`/`os`/`wr`)组织的 recipe;新 recipe 须在 `src/recipe/menu.c` 中注册(`#include` + `add()`)才会出现在菜单中 |
| 31 | +- `lib/xy.h` — 独立 C11 工具库(MIT):字符串、系统检测、日志、数据结构、文件 I/O。约定 `return caller-free` 表示调用方必须释放返回值 |
| 32 | +- 执行流程:`main()` → `chsrc_init_framework()` → 解析 CLI → 在 `pl`/`os`/`wr` 菜单中匹配 dish → `preparefn()` → 按操作分派 `getfn`/`setfn`/`resetfn` |
81 | 33 |
|
82 | | -每个 dish 由 `.c` 文件实现(一个文件可包含多个 dish),包含: |
| 34 | +概念(`Dish_t`/`Scope_t`/`SourceProvider_t`/`MirrorSite_t`/Chef DSL)与编写步骤详见 `doc/10-如何编写recipe.md`;换源/测速链接设置详见 `doc/11-如何设置换源链接与测速链接.md`。 |
83 | 35 |
|
84 | | -- `_prepare()` — **必需。** 通过 Chef DSL 初始化元数据和源列表。 |
85 | | -- `_setsrc()` — **必需。** 执行换源操作。必须调用 `chsrc_use_this_source (dish)` 注入选中的源。 |
86 | | -- `_getsrc()` / `_resetsrc()` — 可选。 |
| 36 | +## 代码风格与命名 |
87 | 37 |
|
88 | | -新 recipe 还必须在 `src/recipe/menu.c` 中注册(`#include` 该文件,然后用 `add()` 将其加入对应的 `pl`/`os`/`wr` 列表);未注册的 recipe 文件不会出现在任何菜单中。 |
89 | | - |
90 | | -参考:`src/recipe/recipe-template.c`、`doc/10-如何编写recipe.md`、`doc/11-如何设置换源链接与测速链接.md`。 |
91 | | - |
92 | | -### 开发辅助与质量保障 |
93 | | - |
94 | | -- **Lefthook git hooks**(`lefthook.yml`)— 提交前自动验证: |
95 | | - - 修改 `*.{c,h}`(不含 `test/`):`just build-in-debug-mode` + `./chsrc-debug get pip`(触发所有 `_prepare()` 检查) |
96 | | - - 修改 `lib/xy.h` 或 `test/xy.c`:`just test-xy` |
97 | | - - 修改 `src/framework/*`、`src/chsrc-main.c` 或 `test/fw.c`:`just test-fw` |
98 | | -- **CI**(`.github/workflows/`)— 多平台构建:Linux(x64/AArch64/ARMv7/riscv64)、macOS、Windows(x64/Arm64);PR 时 Ubuntu 运行 `make` + `make test`、Windows 运行 `just` + `just test`(`PR-test.yml`,仅 PR 目标分支为 `dev` 时触发) |
99 | | -- **参考文档**:`doc/README.md`、`doc/01-开发与构建.md`、`doc/02-提交与贡献.md`、`doc/50-协作者与维护者.md` |
100 | | - |
101 | | -### 命名约定 |
102 | | - |
103 | | -- `pl*` = programming **l**anguage(编程语言)→ `src/recipe/lang/` |
104 | | -- `os*` = **o**perating **s**ystem(操作系统)→ `src/recipe/os/` |
105 | | -- `wr*` = soft**w**a**r**e(软件)→ `src/recipe/ware/` |
106 | | -- 类型名:`PascalCase_t` |
107 | | -- 函数名与 `()` 之间必须有空格 — 代码应写 `foo ()` 而非 `foo()`。(本文档中的 API 提及如 `_prepare()` 仅为简写。) |
108 | | - |
109 | | -## 代码风格 |
110 | | - |
111 | | -- 基于 GNU 风格并做了项目特化调整(见 `doc/03-为什么拒绝使用代码格式化工具.md`)。 |
112 | | -- **不使用代码格式化工具。** 采用刻意的手动排版(对齐、预处理器缩进等)。 |
113 | | -- **保留已有注释** — 它们记录了重要的维护元数据。 |
114 | | -- 约定优于配置。NO UFO 原则:工具不会在用户目录下产生任何配置/数据文件。 |
115 | | -- 贡献者通过 `src/chsrc-main.c` 中的 `chef_register_contributor()` 注册到代码中,而非仅记录在 git 历史里。 |
| 38 | +- **不使用代码格式化工具**,采用手动排版对齐(原因见 `doc/03-为什么拒绝使用代码格式化工具.md`) |
| 39 | +- 保留已有注释;函数名与 `()` 之间留空格,如 `foo ()` 而非 `foo()` |
| 40 | +- `pl*`/`os*`/`wr*` 前缀分别对应 `lang`/`os`/`ware` 目录;类型名用 `PascalCase_t` |
| 41 | +- Convention over Configuration,NO UFO 原则(不向用户目录写配置/数据文件) |
116 | 42 |
|
117 | 43 | ## 内存管理规则 |
118 | 44 |
|
119 | | -**Recipe 层(`src/recipe/**`):** |
120 | | -除非不得不,否则不要调用 `free()`。单次调用的 CLI 模型意味着操作系统在退出时回收内存,释放短生命周期的临时对象收益过低,且易导致二次释放等额外问题。仅当指针由 xy.h 的 caller-free 函数堆分配、且生命周期止于 recipe 函数内部时才 `free()`(如 `Cargo.c`释放`cargo_config_file` 路径)。避免改动已有 recipe —— 保持工作代码不变。 |
121 | | - |
122 | | -**Framework 层(`lib/xy.h`、`src/framework/**`):** |
123 | | -严格检查所有内存分配是否泄漏。xy.h 中标记为 `return caller-free` 的函数要求调用方释放返回值。`xy_malloc0`、`xy_strdup`、`xy_str_gsub`、`xy_strcat`、`xy_2strcat`、`xy_file_read` 的每个返回结果都必须被释放或移交所有权。 |
| 45 | +除非不得不,不要调用 `free()`(单次 CLI 运行,进程退出即回收)。仅当指针来自 xy.h 的 caller-free 函数、且生命周期止于函数内部时才释放。避免改动已有 recipe。 |
124 | 46 |
|
125 | 47 | ## 代码审查清单 |
126 | 48 |
|
127 | | -进行代码审查时,检查以下各项: |
128 | | - |
129 | | -1. **指针、边界与内存** — NULL 解引用、缓冲区溢出、use-after-free。 |
130 | | -2. **逻辑缺陷与结构设计** — 错误假设、脆弱的耦合、缺失的边界情况。 |
131 | | -3. **竞态条件** — 工具为单线程;标记任何引入的并发。 |
132 | | -4. **死锁** — 同上,标记任何引入的锁。 |
133 | | -5. **权限问题** — 文件/目录访问权限、特权要求。 |
134 | | -6. **标准合规** — 目标标准为 C11(推荐 C17 或更高版本,见开头说明)。POSIX 专有 API 必须有 `#ifdef` 守卫。 |
135 | | -7. **注释与代码不匹配** — 注释必须准确描述其所文档化的代码。 |
136 | | -8. **冗余代码** — 死代码、不可达分支、重复逻辑。 |
137 | | -9. **不充分的错误处理** — 静默失败、对可能失败的操作缺少 NULL 检查。 |
138 | | -10. **未定义行为** — 整数溢出、越界访问、使用未初始化内存、严格别名违反。 |
139 | | -11. **兼容性** — 必须能在 Linux(GCC/Clang)、macOS(Clang)、Windows(MinGW)上编译。不得使用 C11 不兼容的构造。 |
140 | | -12. **构建与测试验证** — 改动后至少本地运行 `just test`(或 `make test`)与 `just test-cli`(或 `make test-cli`);修改 `*.{c,h}` 后运行 `just bd && ./chsrc-debug get <受影响dish>` 确认不崩溃(lefthook 提交时会自动执行同样检查)。 |
| 49 | +检查:指针/边界/内存安全(NULL 解引用、溢出、use-after-free)、逻辑缺陷、竞态条件(工具单线程,标记任何新引入的并发)、死锁、权限问题、C11 标准合规(POSIX 专有 API 需 `#ifdef` 守卫)、注释与代码一致性、冗余代码、错误处理是否充分、未定义行为、跨平台兼容性(Linux/macOS/Windows MinGW)。 |
| 50 | + |
| 51 | +改动后至少本地运行 `just test`(或 `make test`)与 `just test-cli`(或 `make test-cli`);修改 `*.{c,h}` 后运行 `just bd && ./chsrc-debug get <受影响dish>` 确认不崩溃(lefthook 提交时会自动执行同样检查,见 `doc/01-开发与构建.md`)。 |
0 commit comments