diff --git a/src/chsrc-main.c b/src/chsrc-main.c index 896065e1..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,6 +813,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); + for (size_t i=0; isub_dishes); i++) { Dish_t *sub_dish = xy_seq_at (dish->sub_dishes, i); @@ -822,6 +823,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..60eda137 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,23 +111,29 @@ 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, }; @@ -151,8 +155,6 @@ ProgStore = .contributors = NULL, }; - - #define Exit_OK 0 #define Exit_Fatal 1 #define Exit_Unknown 2 @@ -175,6 +177,33 @@ ProgStore = /* 多语句必须括起来,否则在不带 { } 的 if else 等语句中会出错 */ #define chsrc_breakdown(reason) { xy_error(App_Name "(BREAKDOWN)",reason); exit(Exit_MaintainerCause); } +bool +chsrc_in_dish_group_mode (void) +{ + return ProgStatus.ComboStackDepth > 0; +} + +void +chsrc_begin_combo_source_resolution (Dish_t *dish) +{ + if (ProgStatus.ComboStackDepth >= MaxComboStackDepth) + chsrc_breakdown ("combo dish 嵌套层级过深"); + + int depth = ProgStatus.ComboStackDepth; + ProgStatus.ComboStack[depth] = dish; + ProgStatus.ComboBackedUpPaths[depth] = xy_seq_new (); + ProgStatus.ComboStackDepth++; +} + +void +chsrc_end_combo_source_resolution (void) +{ + if (ProgStatus.ComboStackDepth <= 0) + chsrc_breakdown ("combo dish 解析上下文已为空"); + + ProgStatus.ComboStackDepth--; +} + #define faint(str) xy_str2faint(str) #define red(str) xy_str2red(str) #define blue(str) xy_str2blue(str) @@ -1039,6 +1068,54 @@ source_has_empty_url (Source_t *source) return source->url == NULL; } +static bool +subdish_sibling_has_source_from_mirror (Dish_t *current, const char *code) +{ + if (ProgStatus.ComboStackDepth <= 0) + return false; + + Dish_t *combo = ProgStatus.ComboStack[ProgStatus.ComboStackDepth - 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 (dish_has_source_from_mirror (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 (dish_has_source_from_mirror (dish, option)) + return query_mirror_exist (dish->sources, dish->sources_n, dish->aliases, option); + + if (subdish_sibling_has_source_from_mirror (dish, option)) + return auto_select_mirror (dish->sources, dish->sources_n, dish->aliases); + + chsrc_breakdown (ENGLISH + ? "This combo sub dish has no source from the requested mirror" + : "该 combo sub dish 没有所请求镜像站的源"); + return 0; +} + /** @@ -1051,10 +1128,9 @@ source_has_empty_url (Source_t *source) * 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 @@ -1069,14 +1145,29 @@ 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) - { - 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) + if (chsrc_in_dish_group_mode() && option) { - 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)) { @@ -1972,6 +2063,22 @@ chsrc_backup (const char *path) return; } + /* combo dish 中多个 sub dish 可能操作同一配置文件。首个 sub dish + * 已经保留原始内容后,后续 sub dish 不应再覆盖该备份。 */ + if (chsrc_in_dish_group_mode()) + { + 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; + } + + xy_seq_push (backed_up_paths, xy_strdup (path)); + } + if (xy.on_bsd || xy.on_macos) { /* BSD 和 macOS 的 cp 不支持 --backup 选项 */ 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},