From 31f073c6061c51656a15bc39097bb0aaf3ca3877 Mon Sep 17 00:00:00 2001 From: Mikachu2333 Date: Sat, 15 Aug 2026 12:04:06 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E9=9B=86=E4=B8=AD=E4=BF=AE=E5=A4=8Dcombo?= =?UTF-8?q?=20subdish=E7=9A=84=E7=9B=B8=E5=85=B3=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chsrc-main.c | 4 ++ src/framework/core.c | 116 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 117 insertions(+), 3 deletions(-) diff --git a/src/chsrc-main.c b/src/chsrc-main.c index 896065e1..4f21270b 100644 --- a/src/chsrc-main.c +++ b/src/chsrc-main.c @@ -814,6 +814,8 @@ chefs_handle_Set_Source (Dish_t *dish, const char *input, char *option) { if (dish_has_sub_dishes(dish)) { + chsrc_begin_combo_source_resolution (dish, option); + for (size_t i=0; isub_dishes); i++) { Dish_t *sub_dish = xy_seq_at (dish->sub_dishes, i); @@ -822,6 +824,8 @@ chefs_handle_Set_Source (Dish_t *dish, const char *input, char *option) chefs_handle_Set_Source (sub_dish, dish_get_first_alias(sub_dish), option); br(); } + + chsrc_end_combo_source_resolution (); return; } diff --git a/src/framework/core.c b/src/framework/core.c index 1a872cf9..5b17d426 100644 --- a/src/framework/core.c +++ b/src/framework/core.c @@ -132,6 +132,16 @@ ProgStatus = .user_agent = "chsrc/" Chsrc_Version, }; +/* Combo dish source resolution context. + * When a combo dish is being handled, each sub dish may use a different + * source list, but the user only supplied one mirror code. This context lets + * the framework decide whether a sub dish should use the code directly, or + * auto-select its fastest mirror because the code belongs to a sibling. */ +#define MaxComboSourceResolutionDepth 16 +static Dish_t *ComboSourceResolutionDishStack[MaxComboSourceResolutionDepth]; +static char *ComboBackedUpPaths[MaxComboSourceResolutionDepth]; +static int ComboSourceResolutionDepth = 0; + /* Global Program Store */ struct @@ -151,8 +161,6 @@ ProgStore = .contributors = NULL, }; - - #define Exit_OK 0 #define Exit_Fatal 1 #define Exit_Unknown 2 @@ -175,6 +183,32 @@ ProgStore = /* 多语句必须括起来,否则在不带 { } 的 if else 等语句中会出错 */ #define chsrc_breakdown(reason) { xy_error(App_Name "(BREAKDOWN)",reason); exit(Exit_MaintainerCause); } +bool +chsrc_in_combo_source_resolution (void) +{ + return ComboSourceResolutionDepth > 0; +} + +void +chsrc_begin_combo_source_resolution (Dish_t *dish, char *option) +{ + if (ComboSourceResolutionDepth >= MaxComboSourceResolutionDepth) + chsrc_breakdown ("combo dish 嵌套层级过深"); + + ComboSourceResolutionDishStack[ComboSourceResolutionDepth] = dish; + ComboBackedUpPaths[ComboSourceResolutionDepth] = NULL; + ComboSourceResolutionDepth++; +} + +void +chsrc_end_combo_source_resolution (void) +{ + if (ComboSourceResolutionDepth <= 0) + chsrc_breakdown ("combo dish 解析上下文已为空"); + + ComboSourceResolutionDepth--; +} + #define faint(str) xy_str2faint(str) #define red(str) xy_str2red(str) #define blue(str) xy_str2blue(str) @@ -1039,6 +1073,66 @@ source_has_empty_url (Source_t *source) return source->url == NULL; } +static bool +combo_subdish_has_source_code (Dish_t *dish, const char *code) +{ + if (!dish || !dish->sources || !code) + return false; + + for (int i=0; i < dish->sources_n; i++) + { + if (xy_streql (dish->sources[i].mirror->code, code)) + return true; + } + + return false; +} + +static bool +combo_sibling_has_source_code (Dish_t *current, const char *code) +{ + if (ComboSourceResolutionDepth <= 0) + return false; + + Dish_t *combo = ComboSourceResolutionDishStack[ComboSourceResolutionDepth - 1]; + if (!combo || !combo->sub_dishes) + return false; + + for (size_t i=0; i < xy_seq_len(combo->sub_dishes); i++) + { + Dish_t *sub_dish = xy_seq_at (combo->sub_dishes, i); + if (sub_dish == current) + continue; + + if (!sub_dish->inited) + sub_dish->preparefn (); + + if (combo_subdish_has_source_code (sub_dish, code)) + return true; + } + + return false; +} + +/** + * @brief 在 combo dish 中为当前 sub dish 解析源 + * + * 用户只提供一个 code。若当前 sub dish 拥有该 code,直接使用它;否则, + * 若某个兄弟 sub dish 拥有该 code,说明该 code 仅属于另一类源,当前 sub + * dish 应自动测速选择其最快镜像。都不存在时走普通查询路径并报错。 + */ +int +chsrc_resolve_combo_subdish_source (Dish_t *dish, char *option) +{ + if (combo_subdish_has_source_code (dish, option)) + return query_mirror_exist (dish->sources, dish->sources_n, dish->aliases, option); + + if (combo_sibling_has_source_code (dish, option)) + return auto_select_mirror (dish->sources, dish->sources_n, dish->aliases); + + return query_mirror_exist (dish->sources, dish->sources_n, dish->aliases, option); +} + /** @@ -1069,7 +1163,12 @@ chsrc_yield_source (Dish_t *dish, char *option) if (!dish->inited) dish->preparefn(); Source_t source; - if (chsrc_in_dish_group_mode() && ProgStatus.leader_selected_index==-1) + if (chsrc_in_combo_source_resolution() && option && !hp_is_url (option)) + { + int index = chsrc_resolve_combo_subdish_source (dish, option); + source = dish->sources[index]; + } + else if (chsrc_in_dish_group_mode() && ProgStatus.leader_selected_index==-1) { ProgStatus.leader_selected_index = use_specific_mirror_or_auto_select (option, dish); source = dish->sources[ProgStatus.leader_selected_index]; @@ -1972,6 +2071,17 @@ chsrc_backup (const char *path) return; } + /* combo dish 中多个 sub dish 可能操作同一配置文件。首个 sub dish + * 已经保留原始内容后,后续 sub dish 不应再覆盖该备份。 */ + if (chsrc_in_combo_source_resolution()) + { + int depth = ComboSourceResolutionDepth - 1; + if (ComboBackedUpPaths[depth] && xy_streql (ComboBackedUpPaths[depth], path)) + goto log_anyway; + + ComboBackedUpPaths[depth] = xy_strdup (path); + } + if (xy.on_bsd || xy.on_macos) { /* BSD 和 macOS 的 cp 不支持 --backup 选项 */ From 97c73bb7d1dfbd0f2d3a66e86ba629b735d073e4 Mon Sep 17 00:00:00 2001 From: Mikachu2333 Date: Mon, 17 Aug 2026 08:31:08 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dpr=20review=20=E4=B8=AD?= =?UTF-8?q?=E6=8F=90=E5=88=B0=E7=9A=84=E6=89=80=E6=9C=89=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chsrc-main.c | 3 +- src/framework/core.c | 139 ++++++++++---------- src/framework/dish.c | 23 ++++ src/recipe/lang/Python/uv/uv-python-build.c | 5 +- 4 files changed, 96 insertions(+), 74 deletions(-) diff --git a/src/chsrc-main.c b/src/chsrc-main.c index 4f21270b..07f353e5 100644 --- a/src/chsrc-main.c +++ b/src/chsrc-main.c @@ -788,7 +788,6 @@ chefs_handle_Get_Source (Dish_t *dish, const char *input, char *option) Dish_t *sub_dish = xy_seq_at (dish->sub_dishes, i); sub_dish->preparefn(); println (bdpurple(sub_dish->aliases)); - chsrc_set_dish_group_mode(); chefs_handle_Get_Source (sub_dish, dish_get_first_alias(sub_dish), option); br(); } @@ -814,7 +813,7 @@ chefs_handle_Set_Source (Dish_t *dish, const char *input, char *option) { if (dish_has_sub_dishes(dish)) { - chsrc_begin_combo_source_resolution (dish, option); + chsrc_begin_combo_source_resolution (dish); for (size_t i=0; isub_dishes); i++) { diff --git a/src/framework/core.c b/src/framework/core.c index 5b17d426..707ee084 100644 --- a/src/framework/core.c +++ b/src/framework/core.c @@ -35,6 +35,9 @@ static int chsrc_get_cpucore (); +/* 定义于 dish.c,core.c 在 combo 源解析时需要提前声明 */ +bool dish_has_source_from_mirror (Dish_t *dish, const char *code); + /* Global Program Mode */ struct @@ -43,9 +46,6 @@ struct bool MeasureMode; bool ResetMode; - // 内部实现 - bool DishGroupMode; - // 用户命令选项 bool Ipv6Mode; Scope_t Scope; @@ -57,7 +57,6 @@ ProgMode = { .MeasureMode = false, .ResetMode = false, - .DishGroupMode = false, .Ipv6Mode = false, .Scope = ImplementationDefinedScope, .EnglishMode = false, @@ -66,10 +65,9 @@ ProgMode = }; /* recipe 相关 mode */ -bool chsrc_in_dish_group_mode() {return ProgMode.DishGroupMode;} -// 并非作为 sub dish,而是自身作为一个独立的 dish 执行 -bool chsrc_in_standalone_mode() {return !ProgMode.DishGroupMode;} -void chsrc_set_dish_group_mode(){ProgMode.DishGroupMode = true;} +// combo dish 递归不再复用旧的 DishGroupMode;每个 leaf dish 仍负责自己的 +// 确认和收尾输出。 +bool chsrc_in_standalone_mode() {return true;} bool chsrc_in_reset_mode(){return ProgMode.ResetMode;} /* 默认换源作用域就是 ImplementationDefinedScope */ @@ -113,35 +111,31 @@ typedef enum ChgType_t ChgType_Untested } ChgType_t; +#define MaxComboStackDepth 16 /* Global Program Status */ struct { - int leader_selected_index; /* combo dish 选中的索引 */ - ChgType_t chgtype; /* 换源实现的类型 */ + ChgType_t chgtype; /* 换源实现的类型 */ /* 此时 chsrc_run() 不再是recipe中指定要运行的一个外部命令,而是作为一个功能实现的支撑 */ bool chsrc_run_faas; char *user_agent; + + /* combo dish 源解析上下文。用户只给一个 code/URL,但不同 sub dish 可能 + * 使用不同的源列表;该栈记录当前正在处理的 combo dish。 */ + Dish_t *ComboStack[MaxComboStackDepth]; + XySeq_t *ComboBackedUpPaths[MaxComboStackDepth]; + int ComboStackDepth; } ProgStatus = { - .leader_selected_index = -1, .chgtype = ChgType_Auto, .chsrc_run_faas = false, .user_agent = "chsrc/" Chsrc_Version, + .ComboStackDepth = 0, }; -/* Combo dish source resolution context. - * When a combo dish is being handled, each sub dish may use a different - * source list, but the user only supplied one mirror code. This context lets - * the framework decide whether a sub dish should use the code directly, or - * auto-select its fastest mirror because the code belongs to a sibling. */ -#define MaxComboSourceResolutionDepth 16 -static Dish_t *ComboSourceResolutionDishStack[MaxComboSourceResolutionDepth]; -static char *ComboBackedUpPaths[MaxComboSourceResolutionDepth]; -static int ComboSourceResolutionDepth = 0; - /* Global Program Store */ struct @@ -186,27 +180,28 @@ ProgStore = bool chsrc_in_combo_source_resolution (void) { - return ComboSourceResolutionDepth > 0; + return ProgStatus.ComboStackDepth > 0; } void -chsrc_begin_combo_source_resolution (Dish_t *dish, char *option) +chsrc_begin_combo_source_resolution (Dish_t *dish) { - if (ComboSourceResolutionDepth >= MaxComboSourceResolutionDepth) + if (ProgStatus.ComboStackDepth >= MaxComboStackDepth) chsrc_breakdown ("combo dish 嵌套层级过深"); - ComboSourceResolutionDishStack[ComboSourceResolutionDepth] = dish; - ComboBackedUpPaths[ComboSourceResolutionDepth] = NULL; - ComboSourceResolutionDepth++; + int depth = ProgStatus.ComboStackDepth; + ProgStatus.ComboStack[depth] = dish; + ProgStatus.ComboBackedUpPaths[depth] = xy_seq_new (); + ProgStatus.ComboStackDepth++; } void chsrc_end_combo_source_resolution (void) { - if (ComboSourceResolutionDepth <= 0) + if (ProgStatus.ComboStackDepth <= 0) chsrc_breakdown ("combo dish 解析上下文已为空"); - ComboSourceResolutionDepth--; + ProgStatus.ComboStackDepth--; } #define faint(str) xy_str2faint(str) @@ -1074,27 +1069,12 @@ source_has_empty_url (Source_t *source) } static bool -combo_subdish_has_source_code (Dish_t *dish, const char *code) +subdish_sibling_has_source_from_mirror (Dish_t *current, const char *code) { - if (!dish || !dish->sources || !code) + if (ProgStatus.ComboStackDepth <= 0) return false; - for (int i=0; i < dish->sources_n; i++) - { - if (xy_streql (dish->sources[i].mirror->code, code)) - return true; - } - - return false; -} - -static bool -combo_sibling_has_source_code (Dish_t *current, const char *code) -{ - if (ComboSourceResolutionDepth <= 0) - return false; - - Dish_t *combo = ComboSourceResolutionDishStack[ComboSourceResolutionDepth - 1]; + Dish_t *combo = ProgStatus.ComboStack[ProgStatus.ComboStackDepth - 1]; if (!combo || !combo->sub_dishes) return false; @@ -1107,7 +1087,7 @@ combo_sibling_has_source_code (Dish_t *current, const char *code) if (!sub_dish->inited) sub_dish->preparefn (); - if (combo_subdish_has_source_code (sub_dish, code)) + if (dish_has_source_from_mirror (sub_dish, code)) return true; } @@ -1124,13 +1104,16 @@ combo_sibling_has_source_code (Dish_t *current, const char *code) int chsrc_resolve_combo_subdish_source (Dish_t *dish, char *option) { - if (combo_subdish_has_source_code (dish, option)) + if (dish_has_source_from_mirror (dish, option)) return query_mirror_exist (dish->sources, dish->sources_n, dish->aliases, option); - if (combo_sibling_has_source_code (dish, option)) + if (subdish_sibling_has_source_from_mirror (dish, option)) return auto_select_mirror (dish->sources, dish->sources_n, dish->aliases); - return query_mirror_exist (dish->sources, dish->sources_n, dish->aliases, option); + chsrc_breakdown (ENGLISH + ? "This combo sub dish has no source from the requested mirror" + : "该 combo sub dish 没有所请求镜像站的源"); + return 0; } @@ -1145,10 +1128,9 @@ chsrc_resolve_combo_subdish_source (Dish_t *dish, char *option) * 3. 用户什么都没指定, 即 chsrc set * 4. 用户正在重置源, 即 chsrc reset * - * 如果处于 Dish Group 模式下,combo dish 已经测速过了,sub dish - * 不能再次测速,而是直接选择 combo dish 测过的结果 - * - * 5. combo dish 测速出来的某个源 + * 如果处于 combo source resolution 上下文中,则按 sub dish 与 sibling + * 的源列表集中决定:code 命中本 dish 则直接使用;命中 sibling 则本 dish + * 自动测速;传入 URL 时,支持自定义 URL 的 sub dish 使用 URL,其余自动测速。 * */ Source_t @@ -1163,19 +1145,29 @@ chsrc_yield_source (Dish_t *dish, char *option) if (!dish->inited) dish->preparefn(); Source_t source; - if (chsrc_in_combo_source_resolution() && option && !hp_is_url (option)) - { - int index = chsrc_resolve_combo_subdish_source (dish, option); - source = dish->sources[index]; - } - else if (chsrc_in_dish_group_mode() && ProgStatus.leader_selected_index==-1) + if (chsrc_in_combo_source_resolution() && option) { - ProgStatus.leader_selected_index = use_specific_mirror_or_auto_select (option, dish); - source = dish->sources[ProgStatus.leader_selected_index]; - } - else if (chsrc_in_dish_group_mode() && ProgStatus.leader_selected_index!=-1) - { - source = dish->sources[ProgStatus.leader_selected_index]; + if (hp_is_url (option)) + { + /* combo dish 传入 URL 时,支持该 sub dish 自定义 URL 的则直接使用; + * 不支持则自动选择该 sub dish 的最快源。这样 uv 可只把 URL 当作 + * PyPI index,而 python-install-mirror 仍自动选择。 */ + if (dish->can_user_define) + { + Source_t tmp = { &UserDefinedProvider, option }; + source = tmp; + } + else + { + int index = auto_select_mirror (dish->sources, dish->sources_n, dish->aliases); + source = dish->sources[index]; + } + } + else + { + int index = chsrc_resolve_combo_subdish_source (dish, option); + source = dish->sources[index]; + } } else if (hp_is_url (option)) { @@ -2075,11 +2067,16 @@ chsrc_backup (const char *path) * 已经保留原始内容后,后续 sub dish 不应再覆盖该备份。 */ if (chsrc_in_combo_source_resolution()) { - int depth = ComboSourceResolutionDepth - 1; - if (ComboBackedUpPaths[depth] && xy_streql (ComboBackedUpPaths[depth], path)) - goto log_anyway; + int depth = ProgStatus.ComboStackDepth - 1; + XySeq_t *backed_up_paths = ProgStatus.ComboBackedUpPaths[depth]; + + for (size_t i=0; i < xy_seq_len(backed_up_paths); i++) + { + if (xy_streql (xy_seq_at (backed_up_paths, i), path)) + goto log_anyway; + } - ComboBackedUpPaths[depth] = xy_strdup (path); + xy_seq_push (backed_up_paths, xy_strdup (path)); } if (xy.on_bsd || xy.on_macos) diff --git a/src/framework/dish.c b/src/framework/dish.c index 4b95c07e..a77f5192 100644 --- a/src/framework/dish.c +++ b/src/framework/dish.c @@ -35,3 +35,26 @@ dish_has_sub_dishes (Dish_t *dish) { return (dish->sub_dishes && xy_seq_len(dish->sub_dishes) > 1); } + + + +bool +dish_has_source_from_mirror (Dish_t *dish, const char *code) +{ + if (!dish || !dish->sources || !code) + return false; + + if (xy_streql (code, "upstream")) + return dish->sources_n > 0; + + if (xy_streql (code, "first")) + return dish->sources_n > 1; + + for (int i=0; i < dish->sources_n; i++) + { + if (xy_streql (dish->sources[i].mirror->code, code)) + return true; + } + + return false; +} diff --git a/src/recipe/lang/Python/uv/uv-python-build.c b/src/recipe/lang/Python/uv/uv-python-build.c index 25624dca..11742fad 100644 --- a/src/recipe/lang/Python/uv/uv-python-build.c +++ b/src/recipe/lang/Python/uv/uv-python-build.c @@ -37,7 +37,10 @@ pl_uv_python_build_prepare (void) chef_set_default_scope (this, UserScope); chef_deny_english (this); - chef_allow_user_define (this); + chef_deny_user_define (this); + this->can_user_define_explain = + CHINESE ? "python-install-mirror 不支持手动指定,chsrc 会自动选择" + : "python-install-mirror cannot be specified manually; chsrc selects it automatically"; def_sources_begin () {&UpstreamProvider, "https://github.com/astral-sh/python-build-standalone/releases/download", DelegateToUpstream}, From 79068a9b09d5109a51ff55dea493d1e9e0e29a42 Mon Sep 17 00:00:00 2001 From: Mikachu2333 Date: Mon, 17 Aug 2026 08:54:08 +0800 Subject: [PATCH 3/3] rename `chsrc_in_combo_source_resolution` to `chsrc_in_dish_group_mode` --- src/framework/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/framework/core.c b/src/framework/core.c index 707ee084..60eda137 100644 --- a/src/framework/core.c +++ b/src/framework/core.c @@ -178,7 +178,7 @@ ProgStore = #define chsrc_breakdown(reason) { xy_error(App_Name "(BREAKDOWN)",reason); exit(Exit_MaintainerCause); } bool -chsrc_in_combo_source_resolution (void) +chsrc_in_dish_group_mode (void) { return ProgStatus.ComboStackDepth > 0; } @@ -1145,7 +1145,7 @@ chsrc_yield_source (Dish_t *dish, char *option) if (!dish->inited) dish->preparefn(); Source_t source; - if (chsrc_in_combo_source_resolution() && option) + if (chsrc_in_dish_group_mode() && option) { if (hp_is_url (option)) { @@ -2065,7 +2065,7 @@ chsrc_backup (const char *path) /* combo dish 中多个 sub dish 可能操作同一配置文件。首个 sub dish * 已经保留原始内容后,后续 sub dish 不应再覆盖该备份。 */ - if (chsrc_in_combo_source_resolution()) + if (chsrc_in_dish_group_mode()) { int depth = ProgStatus.ComboStackDepth - 1; XySeq_t *backed_up_paths = ProgStatus.ComboBackedUpPaths[depth];