From 2b24591fb79c718611ebae4f5463f37396e6bfbd Mon Sep 17 00:00:00 2001 From: Mikachu2333 Date: Wed, 27 May 2026 16:41:21 +0800 Subject: [PATCH 01/16] =?UTF-8?q?=E5=A2=9E=E5=8A=A0uv=E7=9A=84python?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E9=95=9C=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 增加uv的python下载镜像 2. 现在uv会同时对pypi和python进行换源 --- src/recipe/lang/Python/uv.c | 248 +++++++++++++++++++++++++++--------- 1 file changed, 186 insertions(+), 62 deletions(-) diff --git a/src/recipe/lang/Python/uv.c b/src/recipe/lang/Python/uv.c index 5277bb58..c554159f 100644 --- a/src/recipe/lang/Python/uv.c +++ b/src/recipe/lang/Python/uv.c @@ -10,12 +10,12 @@ pl_python_uv_prelude (void) chef_prep_this (pl_python_uv, gsr); chef_set_recipe_created_on (this, "2024-12-11"); - chef_set_recipe_last_updated (this, "2025-12-29"); - chef_set_sources_last_updated (this, "2025-08-09"); + chef_set_recipe_last_updated (this, "2026-05-27"); + chef_set_sources_last_updated (this, "2026-05-27"); chef_set_chef (this, NULL); chef_set_cooks (this, 2, "@happy-game", "@MingriLingran"); - chef_set_sauciers (this, 2, "@Kattos", "@ccmywish"); + chef_set_sauciers (this, 3, "@Kattos", "@ccmywish", "@Mikachu2333"); chef_set_scope_cap (this, ProjectScope, ScopeCap_Able_And_Implemented); chef_set_scope_cap (this, UserScope, ScopeCap_Able_And_Implemented); @@ -32,11 +32,11 @@ pl_python_uv_prelude (void) /** * chsrc get uv * - * uv的配置优先级顺序如下(高到低): - * 1. $workspaces/uv.toml - * 2. $workspaces/pyproject.toml - * 3. ~/.config/uv/uv.toml - * 4. /etc/uv/uv.toml + * uv 配置文件查找优先级 (代码仅涵盖 ①②,③④为 uv 自身支持): + * ① ./uv.toml (项目级) + * ② ~/.config/uv/uv.toml (用户级, Windows: %APPDATA%\uv\uv.toml) + * ③ $workspace/pyproject.toml (项目级, 未实现) + * ④ /etc/uv/uv.toml (系统级, 未实现) */ #define PL_Python_uv_ConfigFile "uv.toml" @@ -55,7 +55,6 @@ pl_python_find_uv_config (bool mkdir) { if (xy.on_windows) { - /* config path on Windows */ char *appdata = getenv ("APPDATA"); if (!appdata) @@ -73,7 +72,6 @@ pl_python_find_uv_config (bool mkdir) } else { - /* config path on Linux or macOS */ if (mkdir) { chsrc_ensure_dir (PL_Python_uv_User_ConfigPath); @@ -83,106 +81,232 @@ pl_python_find_uv_config (bool mkdir) } } + void pl_python_uv_getsrc (char *option) { char *uv_config = pl_python_find_uv_config (false); - if (!chsrc_check_file (uv_config)) + if (!uv_config || !chsrc_check_file (uv_config)) { - chsrc_error2 ("未找到 uv 配置文件"); + if (!uv_config) + chsrc_error2 ("无法获取 uv 配置文件路径"); + else + chsrc_error2 ("未找到 uv 配置文件"); return; } /* 获取 [[index]] 配置项的 url */ if (xy.on_windows) { - /* 在 Windows 上使用 PowerShell 替代 grep */ char *script = xy_str_gsub (RAWSTR_pl_python_get_uv_config_on_windows, "@f@", uv_config); chsrc_run_as_powershell_file (script); } else { - /* 在类 Unix 系统上使用 grep */ char *cmd = xy_str_gsub (RAWSTR_pl_python_get_uv_config, "@f@", uv_config); chsrc_run (cmd, RunOpt_Default); } + + /* 检查 Python 下载镜像 */ + char *content = xy_file_read (uv_config); + if (content) + { + char *line = strstr (content, "python-install-mirror"); + if (line && (line == content || line[-1] == '\n')) + { + char *end = strchr (line, '\n'); + if (!end) end = line + strlen (line); + printf ("%.*s\n", (int)(end - line), line); + } + free (content); + } } -/** - * @consult https://docs.astral.sh/uv/configuration/files/ - * https://github.com/RubyMetric/chsrc/issues/139 - */ -void -pl_python_uv_setsrc (char *option) +/* + * Python下载镜像 (python-install-mirror) +*/ + +static MirrorSite_t +Py_GHRelease_NJU = { - chsrc_ensure_program ("uv"); + IS_DedicatedMirrorSite, + "nju", "NJU GitHub Release", "南京大学 GitHub 发布镜像", + "https://mirrors.nju.edu.cn/github-release/astral-sh/python-build-standalone", + {NotSkip, NA, NA, + "https://mirror.nju.edu.cn/github-release/astral-sh/python-build-standalone/20260510/cpython-3.14.5+20260510-i686-pc-windows-msvc-install_only_stripped.tar.gz", + ACCURATE} +}; - Source_t source = chsrc_yield_source (&pl_python_group_target, option); - if (chsrc_in_standalone_mode()) - chsrc_confirm_source(&source); +// 中科大的镜像由于仅保留最新的Latest且文件链接内含动态版本号导致无法测速 +static MirrorSite_t +Py_GHRelease_USTC = +{ + IS_DedicatedMirrorSite, + "ustc", "USTC GitHub Release", "中科大 GitHub 发布镜像", + "https://mirrors.ustc.edu.cn/github-release/astral-sh/python-build-standalone", + {NotSkip, NA, NA, + "https://mirrors.ustc.edu.cn/github-release/astral-sh/python-build-standalone/LatestRelease/SHA256SUMS", + ROUGH} +}; - char *uv_config = pl_python_find_uv_config (true); - if (NULL==uv_config) - { - chsrc_error2 ("无法获取 uv 配置文件路径"); - return; - } - chsrc_backup (uv_config); +static MirrorSite_t +Py_GHRelease_LZU = +{ + IS_DedicatedMirrorSite, + "lzu", "LZUOSS GitHub Release", "兰州大学 GitHub 发布镜像", + "https://mirror.lzu.edu.cn/github-release/astral-sh/python-build-standalone", + {NotSkip, NA, NA, + "https://mirror.lzu.edu.cn/github-release/astral-sh/python-build-standalone/20260510/cpython-3.14.5+20260510-i686-pc-windows-msvc-install_only_stripped.tar.gz", + ACCURATE} +}; + +static MirrorSite_t +Py_GHRelease_Aliyun = +{ + IS_DedicatedMirrorSite, + "ali", "Aliyun GitHub Release", "阿里云 GitHub 发布镜像", + "https://mirrors.aliyun.com/github/releases/astral-sh/python-build-standalone", + {NotSkip, NA, NA, + "https://mirrors.aliyun.com/github/releases/astral-sh/python-build-standalone/20260510/cpython-3.14.5+20260510-i686-pc-windows-msvc-install_only_stripped.tar.gz", + ACCURATE} +}; + +/* 内部 target,不注册到 menu,仅用于 chsrc_yield_source 自动测速选取 */ +static Source_t gh_release_sources[] = { + {&UpstreamProvider, "https://github.com/astral-sh/python-build-standalone/releases/download", DelegateToUpstream}, + {&Py_GHRelease_NJU, "https://mirrors.nju.edu.cn/github-release/astral-sh/python-build-standalone", DelegateToMirror}, + {&Py_GHRelease_USTC, "https://mirrors.ustc.edu.cn/github-release/astral-sh/python-build-standalone", DelegateToMirror}, + {&Py_GHRelease_LZU, "https://mirror.lzu.edu.cn/github-release/astral-sh/python-build-standalone", DelegateToMirror}, + {&Py_GHRelease_Aliyun, "https://mirrors.aliyun.com/github/releases/astral-sh/python-build-standalone", DelegateToMirror} +}; - const char *source_content = xy_str_gsub (RAWSTR_pl_python_uv_config_source_content, "@url@", source.url); +static Target_t gh_release_target = { + .sources_n = xy_c_array_len (gh_release_sources), + .sources = gh_release_sources, + .inited = true +}; + + +static void +pl_python_uv_write_pypi_index (const char *uv_config, const char *url) +{ + char *source_content = xy_str_gsub (RAWSTR_pl_python_uv_config_source_content, "@url@", url); if (!xy_file_exist (uv_config)) { - /* 当 uv_config 不存在,直接写入文件 */ chsrc_append_to_file (source_content, uv_config); + return; } + + char *cmd = NULL; + if (xy.on_windows) + cmd = xy_str_gsub (RAWSTR_pl_python_test_uv_if_set_source_on_windows, "@f@", uv_config); else + cmd = xy_str_gsub (RAWSTR_pl_python_test_uv_if_set_source, "@f@", uv_config); + + int status = xy_run_get_status (cmd); + if (0 == status) { - /* 当 uv_config 存在,如果存在 [[index]] 则更新,否则追加到文件末尾 */ - char *cmd = NULL; if (xy.on_windows) { - /* 在 Windows 上使用 PowerShell 替代 grep */ - cmd = xy_str_gsub (RAWSTR_pl_python_test_uv_if_set_source_on_windows, "@f@", uv_config); + char *ps_cmd = xy_str_gsub (RAWSTR_pl_python_set_uv_config_on_windows, "@f@", uv_config); + char *tmp = xy_str_gsub (ps_cmd, "@url@", url); + free (ps_cmd); + chsrc_run (tmp, RunOpt_Default); } else { - cmd = xy_str_gsub (RAWSTR_pl_python_test_uv_if_set_source, "@f@", uv_config); - } - - int status = xy_run_get_status (cmd); - if (0==status) - { - if (xy.on_windows) - { - /* 在 Windows 上使用 PowerShell 替代 sed */ - char *powershell_cmd_with_file = xy_str_gsub(RAWSTR_pl_python_set_uv_config_on_windows, "@f@", uv_config); - char *powershell_cmd = xy_str_gsub(powershell_cmd_with_file, "@url@", source.url); - chsrc_run (powershell_cmd, RunOpt_Default); - } - else - { - /* 非 Windows 系统使用 sed */ - char *sed_cmd = NULL; #if defined(XY_Build_On_macOS) || defined(XY_Build_On_BSD) - sed_cmd = "sed -i '' "; + char *sed_prefix = "sed -i '' "; #else - sed_cmd = "sed -i "; + char *sed_prefix = "sed -i "; #endif - char *update_config_cmd = xy_str_gsub (RAWSTR_pl_python_set_uv_config, "@sed@", sed_cmd); - update_config_cmd = xy_str_gsub (update_config_cmd, "@f@", uv_config); - update_config_cmd = xy_str_gsub (update_config_cmd, "@url@", source.url); - chsrc_run (update_config_cmd, RunOpt_Default); - } + char *cmd2 = xy_str_gsub (RAWSTR_pl_python_set_uv_config, "@sed@", sed_prefix); + char *tmp = xy_str_gsub (cmd2, "@f@", uv_config); + free (cmd2); + cmd2 = xy_str_gsub (tmp, "@url@", url); + free (tmp); + chsrc_run (cmd2, RunOpt_Default); + } + } + else + { + chsrc_append_to_file (source_content, uv_config); + } +} + + +static void +pl_python_uv_write_python_download_mirror (const char *uv_config, Source_t gh_source) +{ + char *content = xy_file_read (uv_config); + if (!content) content = xy_strdup (""); + + size_t estimate = strlen (content) + strlen (gh_source.url) + 128; + char *new_content = xy_malloc0 (estimate); + size_t pos = 0; + + const char *p = content; + while (*p) + { + if (xy_str_start_with (p, "python-install-mirror")) + { + while (*p && *p != '\n') p++; + if (*p == '\n') p++; } else { - chsrc_append_to_file (source_content, uv_config); + while (*p && *p != '\n') new_content[pos++] = *p++; + if (*p == '\n') new_content[pos++] = *p++; } } + pos += snprintf (new_content + pos, estimate - pos, + "python-install-mirror = \"%s\"\n", gh_source.url); + new_content[pos] = '\0'; + + chsrc_overwrite_file (new_content, uv_config); + + free (content); + free (new_content); +} + + +/** + * chsrc set uv + * + * 同时更换两部分: + * 1. PyPI 索引源 ([[index]] 表) + * 2. Python 解释器下载源 (python-install-mirror) + * + * @consult https://docs.astral.sh/uv/reference/settings/#python-install-mirror + */ +void +pl_python_uv_setsrc (char *option) +{ + chsrc_ensure_program ("uv"); + + /* ---- 1. 先获取配置路径,再选取源 ---- */ + char *uv_config = pl_python_find_uv_config (true); + if (NULL == uv_config) + { + chsrc_error2 ("无法获取 uv 配置文件路径"); + return; + } + + Source_t source = chsrc_yield_source (&pl_python_group_target, option); + Source_t gh_source = chsrc_yield_source (&gh_release_target, NULL); + + if (chsrc_in_standalone_mode()) + chsrc_confirm_source (&source); + + /* ---- 2. 写入文件 ---- */ + chsrc_backup (uv_config); + pl_python_uv_write_pypi_index (uv_config, source.url); + pl_python_uv_write_python_download_mirror (uv_config, gh_source); + if (chsrc_in_standalone_mode()) { chsrc_determine_chgtype (ChgType_Auto); From 824e3e7e6480d769c28bf41c6def88a59daa90d5 Mon Sep 17 00:00:00 2001 From: Mikachu2333 Date: Wed, 27 May 2026 18:34:49 +0800 Subject: [PATCH 02/16] =?UTF-8?q?=E4=BF=AE=E5=A4=8Duv=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E5=BE=AE=E5=B0=8F=E5=86=85=E5=AD=98=E6=B3=84=E6=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/recipe/lang/Python/uv.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/recipe/lang/Python/uv.c b/src/recipe/lang/Python/uv.c index c554159f..235b872e 100644 --- a/src/recipe/lang/Python/uv.c +++ b/src/recipe/lang/Python/uv.c @@ -68,7 +68,9 @@ pl_python_find_uv_config (bool mkdir) { chsrc_ensure_dir (config_dir); } - return xy_2strcat (config_dir, PL_Python_uv_ConfigFile); + char *result = xy_2strcat (config_dir, PL_Python_uv_ConfigFile); + free (config_dir); + return result; } else { @@ -93,6 +95,7 @@ pl_python_uv_getsrc (char *option) chsrc_error2 ("无法获取 uv 配置文件路径"); else chsrc_error2 ("未找到 uv 配置文件"); + free (uv_config); return; } @@ -101,11 +104,13 @@ pl_python_uv_getsrc (char *option) { char *script = xy_str_gsub (RAWSTR_pl_python_get_uv_config_on_windows, "@f@", uv_config); chsrc_run_as_powershell_file (script); + free (script); } else { char *cmd = xy_str_gsub (RAWSTR_pl_python_get_uv_config, "@f@", uv_config); chsrc_run (cmd, RunOpt_Default); + free (cmd); } /* 检查 Python 下载镜像 */ @@ -121,6 +126,8 @@ pl_python_uv_getsrc (char *option) } free (content); } + + free (uv_config); } @@ -139,7 +146,7 @@ Py_GHRelease_NJU = ACCURATE} }; -// 中科大的镜像由于仅保留最新的Latest且文件链接内含动态版本号导致无法测速 +/* 中科大的镜像由于仅保留最新的 Latest 且文件链接内含动态版本号导致无法精准测速 */ static MirrorSite_t Py_GHRelease_USTC = { @@ -197,6 +204,7 @@ pl_python_uv_write_pypi_index (const char *uv_config, const char *url) if (!xy_file_exist (uv_config)) { chsrc_append_to_file (source_content, uv_config); + free (source_content); return; } @@ -207,6 +215,8 @@ pl_python_uv_write_pypi_index (const char *uv_config, const char *url) cmd = xy_str_gsub (RAWSTR_pl_python_test_uv_if_set_source, "@f@", uv_config); int status = xy_run_get_status (cmd); + free (cmd); + if (0 == status) { if (xy.on_windows) @@ -215,6 +225,7 @@ pl_python_uv_write_pypi_index (const char *uv_config, const char *url) char *tmp = xy_str_gsub (ps_cmd, "@url@", url); free (ps_cmd); chsrc_run (tmp, RunOpt_Default); + free (tmp); } else { @@ -229,12 +240,15 @@ pl_python_uv_write_pypi_index (const char *uv_config, const char *url) cmd2 = xy_str_gsub (tmp, "@url@", url); free (tmp); chsrc_run (cmd2, RunOpt_Default); + free (cmd2); } } else { chsrc_append_to_file (source_content, uv_config); } + + free (source_content); } @@ -307,6 +321,8 @@ pl_python_uv_setsrc (char *option) pl_python_uv_write_pypi_index (uv_config, source.url); pl_python_uv_write_python_download_mirror (uv_config, gh_source); + free (uv_config); + if (chsrc_in_standalone_mode()) { chsrc_determine_chgtype (ChgType_Auto); From 29d3dadb564bdb44938f77c99e7a93db2697b415 Mon Sep 17 00:00:00 2001 From: Mikachu2333 Date: Wed, 27 May 2026 19:05:50 +0800 Subject: [PATCH 03/16] =?UTF-8?q?=E9=87=8D=E6=9E=84uv=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=86=99=E5=85=A5=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E5=93=8D=E5=BA=94=E9=A1=B9=E7=9B=AE=E7=BA=A7=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/recipe/lang/Python/uv.c | 145 +++++++++++++++++++++++------------- 1 file changed, 94 insertions(+), 51 deletions(-) diff --git a/src/recipe/lang/Python/uv.c b/src/recipe/lang/Python/uv.c index 235b872e..19bea0f8 100644 --- a/src/recipe/lang/Python/uv.c +++ b/src/recipe/lang/Python/uv.c @@ -196,69 +196,97 @@ static Target_t gh_release_target = { }; -static void -pl_python_uv_write_pypi_index (const char *uv_config, const char *url) +/** + * 在 content 中找到 [[index]] 段并替换其 url = "..." 行。 + * 找不到 [[index]] 则追加整个段到末尾。 + * + * @return 新内容 (caller-free) + */ +static char * +replace_pypi_index_url (const char *content, const char *url) { - char *source_content = xy_str_gsub (RAWSTR_pl_python_uv_config_source_content, "@url@", url); + const char *index_tag = "[[index]]"; - if (!xy_file_exist (uv_config)) + char *index_pos = strstr (content, index_tag); + if (!index_pos || (index_pos != content && index_pos[-1] != '\n')) { - chsrc_append_to_file (source_content, uv_config); - free (source_content); - return; + /* 文件中尚无 [[index]] 段,追加到末尾 */ + bool need_sep = (content[0] != '\0'); + size_t len = strlen (content) + strlen (url) + 80; + char *ret = xy_malloc0 (len); + if (need_sep) + snprintf (ret, len, "%s\n[[index]]\nurl = \"%s\"\ndefault = true\n", content, url); + else + snprintf (ret, len, "[[index]]\nurl = \"%s\"\ndefault = true\n", url); + return ret; } - char *cmd = NULL; - if (xy.on_windows) - cmd = xy_str_gsub (RAWSTR_pl_python_test_uv_if_set_source_on_windows, "@f@", uv_config); - else - cmd = xy_str_gsub (RAWSTR_pl_python_test_uv_if_set_source, "@f@", uv_config); + /* 找到 [[index]] 后紧跟的 url = "..." 行 (允许被 default = true 行隔开) */ + const char *search_start = index_pos + strlen (index_tag); + const char *url_key = "url = \""; + char *url_line = NULL; - int status = xy_run_get_status (cmd); - free (cmd); + /* 只在当前 [[index]] 段内搜索: 遇到下一个 [[ 开头的行或文件结尾就停止 */ + const char *next_section = strstr (search_start, "\n[["); + const char *limit = next_section ? next_section + 1 : content + strlen (content); - if (0 == status) + for (const char *p = search_start; p < limit; p++) { - if (xy.on_windows) + if (*p == 'u' && xy_str_start_with (p, url_key) && (p == content || p[-1] == '\n')) { - char *ps_cmd = xy_str_gsub (RAWSTR_pl_python_set_uv_config_on_windows, "@f@", uv_config); - char *tmp = xy_str_gsub (ps_cmd, "@url@", url); - free (ps_cmd); - chsrc_run (tmp, RunOpt_Default); - free (tmp); - } - else - { -#if defined(XY_Build_On_macOS) || defined(XY_Build_On_BSD) - char *sed_prefix = "sed -i '' "; -#else - char *sed_prefix = "sed -i "; -#endif - char *cmd2 = xy_str_gsub (RAWSTR_pl_python_set_uv_config, "@sed@", sed_prefix); - char *tmp = xy_str_gsub (cmd2, "@f@", uv_config); - free (cmd2); - cmd2 = xy_str_gsub (tmp, "@url@", url); - free (tmp); - chsrc_run (cmd2, RunOpt_Default); - free (cmd2); + url_line = (char *)p; + break; } } - else + + if (!url_line) { - chsrc_append_to_file (source_content, uv_config); + /* 有 [[index]] 段但没有 url = "..." 行,追加 url 行 */ + size_t len = strlen (content) + strlen (url) + 32; + char *ret = xy_malloc0 (len); + size_t pos = 0; + + /* 在 [[index]] 行的下一行插入 url */ + const char *insert_at = search_start; + while (*insert_at == '\n') insert_at++; + + pos += snprintf (ret + pos, len - pos, "%.*s", + (int)(insert_at - content), content); + pos += snprintf (ret + pos, len - pos, "url = \"%s\"\n", url); + strcpy (ret + pos, insert_at); + return ret; } - free (source_content); + /* 替换 url = "..." 行 */ + /* 找到该行结尾 */ + char *url_end = strchr (url_line, '\n'); + if (!url_end) url_end = (char *)content + strlen (content); + + size_t est = strlen (content) + strlen (url) + 32; + char *ret = xy_malloc0 (est); + size_t pos = 0; + + /* 拷贝 url_line 之前的内容 */ + pos += snprintf (ret + pos, est - pos, "%.*s", + (int)(url_line - content), content); + /* 写入新的 url 行 */ + pos += snprintf (ret + pos, est - pos, "url = \"%s\"", url); + /* 拷贝 url_line 之后的内容 (从该行原换行符开始, 保留 \n) */ + strcpy (ret + pos, url_end); + + return ret; } -static void -pl_python_uv_write_python_download_mirror (const char *uv_config, Source_t gh_source) +/** + * 过滤 content 中的 python-install-mirror 行,追加新值。 + * + * @return 新内容 (caller-free) + */ +static char * +replace_python_install_mirror (const char *content, const char *url) { - char *content = xy_file_read (uv_config); - if (!content) content = xy_strdup (""); - - size_t estimate = strlen (content) + strlen (gh_source.url) + 128; + size_t estimate = strlen (content) + strlen (url) + 128; char *new_content = xy_malloc0 (estimate); size_t pos = 0; @@ -278,13 +306,29 @@ pl_python_uv_write_python_download_mirror (const char *uv_config, Source_t gh_so } pos += snprintf (new_content + pos, estimate - pos, - "python-install-mirror = \"%s\"\n", gh_source.url); + "python-install-mirror = \"%s\"\n", url); new_content[pos] = '\0'; + return new_content; +} - chsrc_overwrite_file (new_content, uv_config); +/** + * 一次性完成uv配置文件的全部文件操作 + */ +static void +pl_python_uv_write_all (const char *uv_config, const char *pypi_url, const char *py_dl_url) +{ + char *content = xy_file_read (uv_config); + if (!content) content = xy_strdup (""); + + char *updated = replace_pypi_index_url (content, pypi_url); free (content); - free (new_content); + + char *final = replace_python_install_mirror (updated, py_dl_url); + free (updated); + + chsrc_overwrite_file (final, uv_config); + free (final); } @@ -316,10 +360,9 @@ pl_python_uv_setsrc (char *option) if (chsrc_in_standalone_mode()) chsrc_confirm_source (&source); - /* ---- 2. 写入文件 ---- */ + /* ---- 2. 写入文件 (纯C, 无 shell 依赖) ---- */ chsrc_backup (uv_config); - pl_python_uv_write_pypi_index (uv_config, source.url); - pl_python_uv_write_python_download_mirror (uv_config, gh_source); + pl_python_uv_write_all (uv_config, source.url, gh_source.url); free (uv_config); From 928ce52223094764577083da099af361c6b99853 Mon Sep 17 00:00:00 2001 From: Mikachu2333 Date: Wed, 27 May 2026 19:23:14 +0800 Subject: [PATCH 04/16] =?UTF-8?q?uv=E5=93=8D=E5=BA=94reset?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/recipe/lang/Python/uv.c | 116 +++++++++++++++++++++++++++++++++++- 1 file changed, 113 insertions(+), 3 deletions(-) diff --git a/src/recipe/lang/Python/uv.c b/src/recipe/lang/Python/uv.c index 19bea0f8..23d5a417 100644 --- a/src/recipe/lang/Python/uv.c +++ b/src/recipe/lang/Python/uv.c @@ -313,7 +313,84 @@ replace_python_install_mirror (const char *content, const char *url) /** - * 一次性完成uv配置文件的全部文件操作 + * reset 专用: 删除所有 [[index]] 段内的 url 行 (连带 [[index]] 头与 + * default 行一起删除), 删除 python-install-mirror 行, 其余内容原样保留。 + * + * @return 新内容 (caller-free) + */ +static char * +cleanup_config_for_reset (const char *content) +{ + size_t est = strlen (content) + 128; + char *ret = xy_malloc0 (est); + size_t pos = 0; + + const char *p = content; + while (*p) + { + if (xy_str_start_with (p, "[[index]]")) + { + /* 扫描这个 [[index]] 段, 查找是否包含 url = "..." 行 */ + const char *scan = p; + while (*scan && *scan != '\n') scan++; + if (*scan == '\n') scan++; + + bool has_url = false; + const char *scan2 = scan; + while (*scan2 && !xy_str_start_with (scan2, "[[") && !xy_str_start_with (scan2, "[")) + { + if (*scan2 != '\n' && xy_str_start_with (scan2, "url = \"")) + { + has_url = true; + break; + } + while (*scan2 && *scan2 != '\n') scan2++; + if (*scan2 == '\n') scan2++; + } + + if (has_url) + { + /* 跳过整个 [[index]] 段 (包括 [[index]] 头 + url/default 行) */ + p = scan; + while (*p && !xy_str_start_with (p, "[[") && !xy_str_start_with (p, "[")) + { + if (xy_str_start_with (p, "url = \"") || + xy_str_start_with (p, "default = ")) + { + while (*p && *p != '\n') p++; + if (*p == '\n') p++; + } + else + { + /* 遇到非 url/default 行, 段结束 */ + break; + } + } + continue; + } + /* [[index]] 段没有 url, 保留不动, 继续正常拷贝 */ + } + + /* 跳过 python-install-mirror 行 */ + if (xy_str_start_with (p, "python-install-mirror")) + { + while (*p && *p != '\n') p++; + if (*p == '\n') p++; + continue; + } + + /* 拷贝当前行 */ + while (*p && *p != '\n') ret[pos++] = *p++; + if (*p == '\n') ret[pos++] = *p++; + } + + ret[pos] = '\0'; + return ret; +} + + +/** + * 一次性完成uv配置文件的全部文件操作 (set 路径) */ static void pl_python_uv_write_all (const char *uv_config, const char *pypi_url, const char *py_dl_url) @@ -346,7 +423,6 @@ pl_python_uv_setsrc (char *option) { chsrc_ensure_program ("uv"); - /* ---- 1. 先获取配置路径,再选取源 ---- */ char *uv_config = pl_python_find_uv_config (true); if (NULL == uv_config) { @@ -354,13 +430,47 @@ pl_python_uv_setsrc (char *option) return; } + /* reset: 清理 [[index]] 段 url 与 python-install-mirror 行 */ + if (chsrc_in_reset_mode ()) + { + if (!chsrc_check_file (uv_config)) + { + chsrc_info ("没有 uv 配置文件, 无需重置"); + free (uv_config); + return; + } + + /* 读内容, 清理, 直接写回 */ + char *content = xy_file_read (uv_config); + if (!content) + { + chsrc_error2 ("无法读取 uv 配置文件"); + free (uv_config); + return; + } + + char *cleaned = cleanup_config_for_reset (content); + free (content); + + FILE *f = fopen (uv_config, "w"); + if (f) + { + fwrite (cleaned, 1, strlen (cleaned), f); + fclose (f); + } + free (cleaned); + + free (uv_config); + return; + } + + /* set: 选取源并写入 */ Source_t source = chsrc_yield_source (&pl_python_group_target, option); Source_t gh_source = chsrc_yield_source (&gh_release_target, NULL); if (chsrc_in_standalone_mode()) chsrc_confirm_source (&source); - /* ---- 2. 写入文件 (纯C, 无 shell 依赖) ---- */ chsrc_backup (uv_config); pl_python_uv_write_all (uv_config, source.url, gh_source.url); From 1c64b9f08176823ee11012732fd71b05e56fe15d Mon Sep 17 00:00:00 2001 From: Mikachu2333 Date: Wed, 27 May 2026 19:32:37 +0800 Subject: [PATCH 05/16] =?UTF-8?q?=E4=BF=AE=E5=A4=8Duv=E9=87=8D=E7=BD=AE?= =?UTF-8?q?=E6=97=B6=E5=8F=AF=E8=83=BD=E7=9A=84=E6=A0=BC=E5=BC=8F=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/recipe/lang/Python/uv.c | 66 ++++++++++--------------------------- 1 file changed, 17 insertions(+), 49 deletions(-) diff --git a/src/recipe/lang/Python/uv.c b/src/recipe/lang/Python/uv.c index 23d5a417..847d3631 100644 --- a/src/recipe/lang/Python/uv.c +++ b/src/recipe/lang/Python/uv.c @@ -313,8 +313,8 @@ replace_python_install_mirror (const char *content, const char *url) /** - * reset 专用: 删除所有 [[index]] 段内的 url 行 (连带 [[index]] 头与 - * default 行一起删除), 删除 python-install-mirror 行, 其余内容原样保留。 + * reset 专用: 删除所有 [[index]] 段内的 url/default 行 (保留 [[index]] 头 + * 与 name 等其余字段), 删除 python-install-mirror 行, 其余内容原样保留。 * * @return 新内容 (caller-free) */ @@ -328,60 +328,28 @@ cleanup_config_for_reset (const char *content) const char *p = content; while (*p) { - if (xy_str_start_with (p, "[[index]]")) - { - /* 扫描这个 [[index]] 段, 查找是否包含 url = "..." 行 */ - const char *scan = p; - while (*scan && *scan != '\n') scan++; - if (*scan == '\n') scan++; - - bool has_url = false; - const char *scan2 = scan; - while (*scan2 && !xy_str_start_with (scan2, "[[") && !xy_str_start_with (scan2, "[")) - { - if (*scan2 != '\n' && xy_str_start_with (scan2, "url = \"")) - { - has_url = true; - break; - } - while (*scan2 && *scan2 != '\n') scan2++; - if (*scan2 == '\n') scan2++; - } + bool skip = false; - if (has_url) - { - /* 跳过整个 [[index]] 段 (包括 [[index]] 头 + url/default 行) */ - p = scan; - while (*p && !xy_str_start_with (p, "[[") && !xy_str_start_with (p, "[")) - { - if (xy_str_start_with (p, "url = \"") || - xy_str_start_with (p, "default = ")) - { - while (*p && *p != '\n') p++; - if (*p == '\n') p++; - } - else - { - /* 遇到非 url/default 行, 段结束 */ - break; - } - } - continue; - } - /* [[index]] 段没有 url, 保留不动, 继续正常拷贝 */ + if (xy_str_start_with (p, "url = \"") || + xy_str_start_with (p, "default = ")) + { + skip = true; + } + else if (xy_str_start_with (p, "python-install-mirror")) + { + skip = true; } - /* 跳过 python-install-mirror 行 */ - if (xy_str_start_with (p, "python-install-mirror")) + if (skip) { while (*p && *p != '\n') p++; if (*p == '\n') p++; - continue; } - - /* 拷贝当前行 */ - while (*p && *p != '\n') ret[pos++] = *p++; - if (*p == '\n') ret[pos++] = *p++; + else + { + while (*p && *p != '\n') ret[pos++] = *p++; + if (*p == '\n') ret[pos++] = *p++; + } } ret[pos] = '\0'; From 7795ed0b8432b1a2545a493e389b3763771ee8d1 Mon Sep 17 00:00:00 2001 From: Mikachu2333 Date: Wed, 27 May 2026 19:33:42 +0800 Subject: [PATCH 06/16] =?UTF-8?q?=E4=BF=AE=E6=94=B9uv=E7=9A=84=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=E4=B8=AD=E5=8F=AF=E8=83=BD=E4=BA=A7=E7=94=9F=E8=AF=AF?= =?UTF-8?q?=E8=A7=A3=E7=9A=84=E5=9C=B0=E6=96=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/recipe/lang/Python/uv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/recipe/lang/Python/uv.c b/src/recipe/lang/Python/uv.c index 847d3631..d9245cb1 100644 --- a/src/recipe/lang/Python/uv.c +++ b/src/recipe/lang/Python/uv.c @@ -32,7 +32,7 @@ pl_python_uv_prelude (void) /** * chsrc get uv * - * uv 配置文件查找优先级 (代码仅涵盖 ①②,③④为 uv 自身支持): + * uv 配置文件查找优先级: * ① ./uv.toml (项目级) * ② ~/.config/uv/uv.toml (用户级, Windows: %APPDATA%\uv\uv.toml) * ③ $workspace/pyproject.toml (项目级, 未实现) From 9e79f8a4d6926950648dbb3a871ffb2f075f3a48 Mon Sep 17 00:00:00 2001 From: Mikachu2333 Date: Wed, 27 May 2026 19:37:17 +0800 Subject: [PATCH 07/16] =?UTF-8?q?uv=E8=B0=83=E7=94=A8chsrc=5Foverwrite=5Ff?= =?UTF-8?q?ile=E9=81=BF=E5=85=8D=E6=89=8B=E5=8A=A8=E5=A4=84=E7=90=86?= =?UTF-8?q?=E5=86=99=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/recipe/lang/Python/uv.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/recipe/lang/Python/uv.c b/src/recipe/lang/Python/uv.c index d9245cb1..0e0063ea 100644 --- a/src/recipe/lang/Python/uv.c +++ b/src/recipe/lang/Python/uv.c @@ -408,7 +408,7 @@ pl_python_uv_setsrc (char *option) return; } - /* 读内容, 清理, 直接写回 */ + /* 读内容, 清理, 写回 */ char *content = xy_file_read (uv_config); if (!content) { @@ -419,13 +419,7 @@ pl_python_uv_setsrc (char *option) char *cleaned = cleanup_config_for_reset (content); free (content); - - FILE *f = fopen (uv_config, "w"); - if (f) - { - fwrite (cleaned, 1, strlen (cleaned), f); - fclose (f); - } + chsrc_overwrite_file (cleaned, uv_config); free (cleaned); free (uv_config); From fb0233d24881486f88882d8cdd50e0bf7a42cbaa Mon Sep 17 00:00:00 2001 From: Mikachu2333 Date: Sun, 31 May 2026 13:56:09 +0800 Subject: [PATCH 08/16] =?UTF-8?q?uv=E6=B3=A8=E9=87=8A=E6=B6=88=E6=AD=A7?= =?UTF-8?q?=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/recipe/lang/Python/uv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/recipe/lang/Python/uv.c b/src/recipe/lang/Python/uv.c index 0e0063ea..4976ec50 100644 --- a/src/recipe/lang/Python/uv.c +++ b/src/recipe/lang/Python/uv.c @@ -33,10 +33,10 @@ pl_python_uv_prelude (void) * chsrc get uv * * uv 配置文件查找优先级: - * ① ./uv.toml (项目级) - * ② ~/.config/uv/uv.toml (用户级, Windows: %APPDATA%\uv\uv.toml) - * ③ $workspace/pyproject.toml (项目级, 未实现) - * ④ /etc/uv/uv.toml (系统级, 未实现) + * 1 ./uv.toml (项目级,已实现) + * 2. $workspaces/pyproject.toml (项目级,未实现,因过于罕见不考虑适配) + * 3. ~/.config/uv/uv.toml (用户级,已实现) + * 4. /etc/uv/uv.toml (系统级,考虑到权限问题不予考虑) */ #define PL_Python_uv_ConfigFile "uv.toml" From 39e8683fbea978c8c235e4f87e2b51d5f662de31 Mon Sep 17 00:00:00 2001 From: Mikachu2333 Date: Sun, 31 May 2026 14:01:45 +0800 Subject: [PATCH 09/16] =?UTF-8?q?uv=E8=BF=98=E5=8E=9F=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/recipe/lang/Python/uv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/recipe/lang/Python/uv.c b/src/recipe/lang/Python/uv.c index 4976ec50..f8829939 100644 --- a/src/recipe/lang/Python/uv.c +++ b/src/recipe/lang/Python/uv.c @@ -102,12 +102,14 @@ pl_python_uv_getsrc (char *option) /* 获取 [[index]] 配置项的 url */ if (xy.on_windows) { + /* 在 Windows 上使用 PowerShell 替代 grep */ char *script = xy_str_gsub (RAWSTR_pl_python_get_uv_config_on_windows, "@f@", uv_config); chsrc_run_as_powershell_file (script); free (script); } else { + /* 在类 Unix 系统上使用 grep */ char *cmd = xy_str_gsub (RAWSTR_pl_python_get_uv_config, "@f@", uv_config); chsrc_run (cmd, RunOpt_Default); free (cmd); From 078ca82171c972db1cb2cba0b8769aaf227844cd Mon Sep 17 00:00:00 2001 From: Mikachu2333 Date: Sun, 31 May 2026 14:14:42 +0800 Subject: [PATCH 10/16] =?UTF-8?q?uv=E6=8D=A2=E7=94=A8=E6=A0=87=E5=87=86?= =?UTF-8?q?=E9=95=9C=E5=83=8F=E7=AB=99=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/recipe/lang/Python/uv.c | 120 +++++++++++++++++------------------- 1 file changed, 58 insertions(+), 62 deletions(-) diff --git a/src/recipe/lang/Python/uv.c b/src/recipe/lang/Python/uv.c index f8829939..8d201b94 100644 --- a/src/recipe/lang/Python/uv.c +++ b/src/recipe/lang/Python/uv.c @@ -4,6 +4,10 @@ def_target(pl_python_uv, "uv"); +/* 内部 target 前置声明 (定义在文件末尾 def_target 处) */ +void pl_uv_github_release_prelude (void); +extern Target_t pl_uv_github_release_target; + void pl_python_uv_prelude (void) { @@ -26,6 +30,9 @@ pl_python_uv_prelude (void) chef_allow_user_define(this); chef_use_other_target_sources (this, &pl_python_group_target); + + /* 内部 target 的 prelude 未通过 menu.c 的 add() 注册, 手动挂载 */ + pl_uv_github_release_target.preludefn = pl_uv_github_release_prelude; } @@ -135,67 +142,54 @@ pl_python_uv_getsrc (char *option) /* * Python下载镜像 (python-install-mirror) + * + * 内部 target, 不注册到 menu, 仅用于 chsrc_yield_source 自动测速选取。 + * 针对 python-build-standalone 的测速链接。 */ -static MirrorSite_t -Py_GHRelease_NJU = -{ - IS_DedicatedMirrorSite, - "nju", "NJU GitHub Release", "南京大学 GitHub 发布镜像", - "https://mirrors.nju.edu.cn/github-release/astral-sh/python-build-standalone", - {NotSkip, NA, NA, - "https://mirror.nju.edu.cn/github-release/astral-sh/python-build-standalone/20260510/cpython-3.14.5+20260510-i686-pc-windows-msvc-install_only_stripped.tar.gz", - ACCURATE} -}; - -/* 中科大的镜像由于仅保留最新的 Latest 且文件链接内含动态版本号导致无法精准测速 */ -static MirrorSite_t -Py_GHRelease_USTC = -{ - IS_DedicatedMirrorSite, - "ustc", "USTC GitHub Release", "中科大 GitHub 发布镜像", - "https://mirrors.ustc.edu.cn/github-release/astral-sh/python-build-standalone", - {NotSkip, NA, NA, - "https://mirrors.ustc.edu.cn/github-release/astral-sh/python-build-standalone/LatestRelease/SHA256SUMS", - ROUGH} -}; - -static MirrorSite_t -Py_GHRelease_LZU = -{ - IS_DedicatedMirrorSite, - "lzu", "LZUOSS GitHub Release", "兰州大学 GitHub 发布镜像", - "https://mirror.lzu.edu.cn/github-release/astral-sh/python-build-standalone", - {NotSkip, NA, NA, - "https://mirror.lzu.edu.cn/github-release/astral-sh/python-build-standalone/20260510/cpython-3.14.5+20260510-i686-pc-windows-msvc-install_only_stripped.tar.gz", - ACCURATE} -}; - -static MirrorSite_t -Py_GHRelease_Aliyun = +def_target (pl_uv_github_release, NULL); + +/* 内部 target, 无 CLI 入口, 以下为占位 */ +void pl_uv_github_release_getsrc (char *o) { (void)o; } +void pl_uv_github_release_setsrc (char *o) { (void)o; } +void pl_uv_github_release_resetsrc (char *o) { (void)o; } + +void +pl_uv_github_release_prelude (void) { - IS_DedicatedMirrorSite, - "ali", "Aliyun GitHub Release", "阿里云 GitHub 发布镜像", - "https://mirrors.aliyun.com/github/releases/astral-sh/python-build-standalone", - {NotSkip, NA, NA, - "https://mirrors.aliyun.com/github/releases/astral-sh/python-build-standalone/20260510/cpython-3.14.5+20260510-i686-pc-windows-msvc-install_only_stripped.tar.gz", - ACCURATE} -}; - -/* 内部 target,不注册到 menu,仅用于 chsrc_yield_source 自动测速选取 */ -static Source_t gh_release_sources[] = { - {&UpstreamProvider, "https://github.com/astral-sh/python-build-standalone/releases/download", DelegateToUpstream}, - {&Py_GHRelease_NJU, "https://mirrors.nju.edu.cn/github-release/astral-sh/python-build-standalone", DelegateToMirror}, - {&Py_GHRelease_USTC, "https://mirrors.ustc.edu.cn/github-release/astral-sh/python-build-standalone", DelegateToMirror}, - {&Py_GHRelease_LZU, "https://mirror.lzu.edu.cn/github-release/astral-sh/python-build-standalone", DelegateToMirror}, - {&Py_GHRelease_Aliyun, "https://mirrors.aliyun.com/github/releases/astral-sh/python-build-standalone", DelegateToMirror} -}; - -static Target_t gh_release_target = { - .sources_n = xy_c_array_len (gh_release_sources), - .sources = gh_release_sources, - .inited = true -}; + chef_prep_this (pl_uv_github_release, gsr); + + chef_set_recipe_created_on (this, "2026-05-31"); + chef_set_recipe_last_updated (this, "2026-05-31"); + chef_set_sources_last_updated (this, "2026-05-31"); + + chef_set_chef (this, NULL); + chef_set_cooks (this, 1, "@Mikachu2333"); + chef_set_sauciers (this, 0); + + chef_allow_english (this); + chef_allow_user_define (this); + + def_sources_begin () + {&UpstreamProvider, "https://github.com/astral-sh/python-build-standalone/releases/download", DelegateToUpstream}, + {&Nju, "https://mirrors.nju.edu.cn/github-release/astral-sh/python-build-standalone", FeedByPrelude}, + {&Ustc, "https://mirrors.ustc.edu.cn/github-release/astral-sh/python-build-standalone", FeedByPrelude}, + {&Lzuoss, "https://mirror.lzu.edu.cn/github-release/astral-sh/python-build-standalone", FeedByPrelude}, + {&Ali, "https://mirrors.aliyun.com/github/releases/astral-sh/python-build-standalone", FeedByPrelude} + def_sources_end () + +#define GH_SM_POSTFIX "/20260510/cpython-3.14.5+20260510-i686-pc-windows-msvc-install_only_stripped.tar.gz" + chef_set_smURL_with_postfix (this, &Nju, GH_SM_POSTFIX); + chef_set_smURL_with_postfix (this, &Lzuoss, GH_SM_POSTFIX); + chef_set_smURL_with_postfix (this, &Ali, GH_SM_POSTFIX); +#undef GH_SM_POSTFIX + + /* 2026-5-31: USTC 仅保留 Latest, 只能用 SHA256SUMS 粗略测速 */ + chef_set_smURL_with_postfix (this, &Ustc, "/LatestRelease/SHA256SUMS"); + + /* 中科大仅保留 Latest 且文件内含动态版本号, 使用模糊测速 */ + chef_set_provider_sm_accuracy (&Ustc, ROUGH); +} /** @@ -243,18 +237,20 @@ replace_pypi_index_url (const char *content, const char *url) if (!url_line) { - /* 有 [[index]] 段但没有 url = "..." 行,追加 url 行 */ - size_t len = strlen (content) + strlen (url) + 32; + /* 有 [[index]] 段但没有 url = "..." 行,追加 url 和 default 行 */ + bool has_default = (strstr (search_start, "default = ") != NULL); + size_t len = strlen (content) + strlen (url) + 64; char *ret = xy_malloc0 (len); size_t pos = 0; - /* 在 [[index]] 行的下一行插入 url */ const char *insert_at = search_start; while (*insert_at == '\n') insert_at++; pos += snprintf (ret + pos, len - pos, "%.*s", (int)(insert_at - content), content); pos += snprintf (ret + pos, len - pos, "url = \"%s\"\n", url); + if (!has_default) + pos += snprintf (ret + pos, len - pos, "default = true\n"); strcpy (ret + pos, insert_at); return ret; } @@ -430,7 +426,7 @@ pl_python_uv_setsrc (char *option) /* set: 选取源并写入 */ Source_t source = chsrc_yield_source (&pl_python_group_target, option); - Source_t gh_source = chsrc_yield_source (&gh_release_target, NULL); + Source_t gh_source = chsrc_yield_source (&pl_uv_github_release_target, NULL); if (chsrc_in_standalone_mode()) chsrc_confirm_source (&source); From af585c1cb897a09d4fe9090dd8bac79ba4dcfbbd Mon Sep 17 00:00:00 2001 From: Mikachu2333 Date: Sun, 31 May 2026 14:21:57 +0800 Subject: [PATCH 11/16] =?UTF-8?q?uv=E8=BF=98=E5=8E=9F=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/recipe/lang/Python/uv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/recipe/lang/Python/uv.c b/src/recipe/lang/Python/uv.c index 8d201b94..bfd0d22a 100644 --- a/src/recipe/lang/Python/uv.c +++ b/src/recipe/lang/Python/uv.c @@ -62,6 +62,7 @@ pl_python_find_uv_config (bool mkdir) { if (xy.on_windows) { + /* config path on Windows */ char *appdata = getenv ("APPDATA"); if (!appdata) @@ -81,6 +82,7 @@ pl_python_find_uv_config (bool mkdir) } else { + /* config path on Linux or macOS */ if (mkdir) { chsrc_ensure_dir (PL_Python_uv_User_ConfigPath); From 8ce347259686cda4ab4c264722b3ffd780b1de5e Mon Sep 17 00:00:00 2001 From: Mikachu2333 Date: Sun, 31 May 2026 14:30:22 +0800 Subject: [PATCH 12/16] =?UTF-8?q?=E6=A0=B9=E6=8D=AEgithub=20docs=E7=9A=84?= =?UTF-8?q?=E7=A4=BA=E4=BE=8B=E6=96=87=E4=BB=B6=E4=BF=AE=E6=94=B9copilot?= =?UTF-8?q?=E6=8F=90=E7=A4=BA=E8=AF=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/copilot-instructions.md | 112 +++++++++++++++++++++++++++----- 1 file changed, 95 insertions(+), 17 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 2c099bbf..05108741 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,32 +1,110 @@ -# chsrc Project Rules for AI Assistants +# chsrc — Change Source Everywhere -## 项目概述 +Cross-platform CLI tool and framework for switching software repository mirrors. Written in **C11** (recommend C17+), single-file compilation from `src/chsrc-main.c`. Targets Linux, Windows (native/MSYS2/Cygwin), macOS, BSD, Android. Licensed GPL-3.0-or-later (main) and MIT (`lib/xy.h`). -这是 chsrc 项目,一个用 C 语言编写的跨平台命令行换源工具,帮助用户在不同的镜像之间切换,适用于编程语言、操作系统、其他软件。它的最强大之处在于它是一个框架,能够帮助用户轻松地为不同的目标换源。 +## Build & Test +Single entry point: `src/chsrc-main.c` `#include`s all framework and recipe files. No build orchestration needed. -## 架构 +``` +# Build (just — recommended for native Windows) +just build # DEV mode → chsrc.exe +just bd # DEBUG mode (-DXY_DEBUG) → chsrc-debug.exe +just br # RELEASE mode (-O2) → chsrc-release.exe -- **Framework**: 在目录 `src/framework/` 中,包含了核心实现,支持 recipe +# Build (make — Linux/macOS/MSYS2) +make build / bd / br +make STATIC=1 br # static linking - - `struct.h` 里定义了各种数据结构和宏,这是整个 chsrc 的核心,也是 chef DSL 的核心 - - `chef.c` 里实现了 chef DSL,你可以使用它来确定正确的使用方法 +# Test +just test # xy.h + framework unit tests +just test-cli # CLI integration tests (Perl) +``` -- **Recipes**: 在目录 `src/recipe/` 中,包含了针对不同目标的具体实现 +DEBUG mode (`-DXY_DEBUG`) enables runtime validation — all `_prelude()` functions are called after every recipe operation to verify integrity. - - `lang/` - 编程语言 (Ruby, JavaScript 等等) - - `os/` - 操作系统 (Ubuntu, Arch Linux 等等) - - `ware/` - 软件工具和应用 (Docker, Homebrew 等等) +## Architecture +### Single-file compilation -## Coding Guidelines +Everything funnels through `src/chsrc-main.c` via `#include`. No object files, no link step beyond the initial compilation. -### C Coding Style: +### Directory layout -请阅读 `doc/03-为什么拒绝使用代码格式化工具.md` +| Path | Role | +| ------------------------------ | ----------------------------------------------------------------------------------------------- | +| `src/chsrc-main.c` | Entry point: `main()`, CLI parsing, display | +| `src/framework/` | Core: global state, Chef DSL, type definitions, mirror registry, helpers | +| `src/framework/struct.h` | `Target_t`, `Source_t`, `MirrorSite_t`, Chef DSL macros — **the backbone of chsrc** | +| `src/framework/chef.c` | Chef DSL implementation for recipe authors | +| `src/recipe/lang/` | Programming language recipes (Ruby, Python, Rust, Go, JS, …) | +| `src/recipe/os/` | OS recipes (APT/Debian, YUM/Fedora, pacman/Arch, BSD, …) | +| `src/recipe/ware/` | Software recipes (Homebrew, Docker, Flatpak, …) | +| `src/recipe/menu.c` | Registry wiring all recipes into three category lists (`pl`, `os`, `wr`) | +| `src/recipe/recipe-template.c` | Template for new recipe authors | +| `lib/xy.h` | Standalone C11 utility library (MIT): strings, OS detection, logging, data structures, file I/O | +| `test/` | `xy.c` (xy.h tests), `fw.c` (framework tests), `cli.pl` (CLI integration) | +| `doc/` | Developer and user documentation (Chinese) | -### Important Project Concepts: +### Core abstractions -请阅读 `doc/10-如何编写recipe.md` +- **`Target_t`** — A change-source target. Fields: aliases, function pointers (`getfn`, `setfn`, `resetfn`, `preludefn`), source list, scope capabilities, contributor metadata. +- **`SourceProvider_t` / `MirrorSite_t`** — Mirror or upstream provider. Types: `IS_GeneralMirrorSite`, `IS_DedicatedMirrorSite`, `IS_UpstreamProvider`, `IS_UserDefinedProvider`. +- **Chef DSL** — Macro-based API: `chef_prep_this()`, `chef_set_chef/cooks/sauciers()`, `chef_set_scope_cap()`, `def_sources_begin()/end()`, `chef_set_smURL()`, `chef_set_smURL_with_postfix()`, etc. +- **xy.h** — Cross-platform runtime: `xy_on_windows`, string ops (`xy_str_gsub`, `xy_2strcat`), `XySeq_t` (linked list), `XyMap_t` (hash map), command execution, file I/O. Memory convention: `return caller-free` means caller must free. -## Important: 一定要保持注释,因为它记录了重要的维护信息 +### Execution flow + +1. `main()` → `chsrc_init_framework()` → `chsrc_init_menu()` (populate targets) +2. Parse CLI → search menus for matching target → call `preludefn()` → dispatch to `getfn`/`setfn`/`resetfn` +3. For `set`: auto-measure mirror speeds via `curl`, select fastest, recipe's `_setsrc()` performs the change + +### Recipe pattern + +Each target is a `.c` file with: + +- `_prelude()` — **Required.** Initializes metadata and sources via Chef DSL. +- `_setsrc()` — **Required.** Performs the source change. Must call `chsrc_use_this_source(target)` to inject the selected source. +- `_getsrc()` / `_resetsrc()` — Optional. + +Reference: `src/recipe/recipe-template.c`, `doc/10-如何编写recipe.md`, `doc/11-如何设置换源链接与测速链接.md`. + +### Naming conventions + +- `pl*` = programming **l**anguage → `src/recipe/lang/` +- `os*` = **o**perating **s**ystem → `src/recipe/os/` +- `wr*` = soft**w**a**r**e → `src/recipe/ware/` +- Type names: `PascalCase_t` +- Space between function name and `()` in definitions and calls + +## Code Style + +- Based on GNU style with project-specific refinements (see `doc/03-为什么拒绝使用代码格式化工具.md`). +- **No code formatters.** Deliberate manual formatting (alignment, preprocessor indentation, etc.). +- **Preserve existing comments** — they record important maintenance metadata. +- Convention over Configuration. NO UFO principle: the tool produces no config/data files in user directories. +- Contributors are registered in code via `chef_register_contributor()` in `src/chsrc-main.c`, not just in git history. + +## Memory Management Rules + +**Recipes (`src/recipe/**`):** +Do NOT use `free()`in recipe code. The project's single-invocation CLI model means the OS reclaims memory on exit. Adding`free()`to recipes creates noise without benefit. Code review should not flag missing`free()` in recipe files. + +**Framework (`lib/xy.h`, `src/framework/**`):** +Strictly check all memory allocations for leaks. Functions marked `return caller-free`in xy.h require callers to free the returned pointer. Every`xy_malloc0`, `xy_strdup`, `xy_str_gsub`, `xy_strcat`, `xy_2strcat`, `xy_file_read` result must be freed or returned to a caller that takes ownership. + +## Code Review Checklist + +When reviewing changes, check: + +1. **Pointers, bounds & memory** — NULL dereferences, buffer overflows, use-after-free. +2. **Logic flaws & structural design** — Incorrect assumptions, fragile coupling, missing edge cases. +3. **Race conditions** — The tool is single-threaded; flag any introduced concurrency. +4. **Deadlocks** — Similarly, flag any introduced locking. +5. **Permission issues** — File/directory access, privilege requirements. +6. **Deprecated functions or standards** — Must target C11 (prefer C17). No POSIX-only APIs without `#ifdef` guards. +7. **Comment-code mismatch** — Comments must accurately describe the code they document. +8. **Redundant code** — Dead code, unreachable branches, duplicate logic. +9. **Inadequate error handling** — Silent failures, missing NULL checks on fallible operations. +10. **Undefined behavior** — Integer overflow, out-of-bounds access, use of uninitialized memory, strict aliasing violations. +11. **Compatibility** — Must compile on Linux (GCC/Clang), macOS (Clang), Windows (MinGW). No C11-incompatible constructs. From 9557adf5ddc0e9767fb79a62a42fb1b1e1bac737 Mon Sep 17 00:00:00 2001 From: Mikachu2333 Date: Sun, 31 May 2026 14:34:26 +0800 Subject: [PATCH 13/16] =?UTF-8?q?uv=E6=89=8B=E5=8A=A8=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/copilot-instructions.md | 2 +- src/recipe/lang/Python/uv.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 05108741..f632c8c3 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -95,7 +95,7 @@ Strictly check all memory allocations for leaks. Functions marked `return caller ## Code Review Checklist -When reviewing changes, check: +When performing a code review, check: 1. **Pointers, bounds & memory** — NULL dereferences, buffer overflows, use-after-free. 2. **Logic flaws & structural design** — Incorrect assumptions, fragile coupling, missing edge cases. diff --git a/src/recipe/lang/Python/uv.c b/src/recipe/lang/Python/uv.c index bfd0d22a..85876f22 100644 --- a/src/recipe/lang/Python/uv.c +++ b/src/recipe/lang/Python/uv.c @@ -173,11 +173,11 @@ pl_uv_github_release_prelude (void) chef_allow_user_define (this); def_sources_begin () - {&UpstreamProvider, "https://github.com/astral-sh/python-build-standalone/releases/download", DelegateToUpstream}, - {&Nju, "https://mirrors.nju.edu.cn/github-release/astral-sh/python-build-standalone", FeedByPrelude}, - {&Ustc, "https://mirrors.ustc.edu.cn/github-release/astral-sh/python-build-standalone", FeedByPrelude}, - {&Lzuoss, "https://mirror.lzu.edu.cn/github-release/astral-sh/python-build-standalone", FeedByPrelude}, - {&Ali, "https://mirrors.aliyun.com/github/releases/astral-sh/python-build-standalone", FeedByPrelude} + {&UpstreamProvider, "https://github.com/astral-sh/python-build-standalone/releases/download", DelegateToUpstream}, + {&Nju, "https://mirrors.nju.edu.cn/github-release/astral-sh/python-build-standalone", FeedByPrelude}, + {&Ustc, "https://mirrors.ustc.edu.cn/github-release/astral-sh/python-build-standalone", FeedByPrelude}, + {&Lzuoss, "https://mirror.lzu.edu.cn/github-release/astral-sh/python-build-standalone", FeedByPrelude}, + {&Ali, "https://mirrors.aliyun.com/github/releases/astral-sh/python-build-standalone", FeedByPrelude} def_sources_end () #define GH_SM_POSTFIX "/20260510/cpython-3.14.5+20260510-i686-pc-windows-msvc-install_only_stripped.tar.gz" From b7a23121f0f4340bfb284e2f54b0bae4e43ede99 Mon Sep 17 00:00:00 2001 From: Mikachu2333 Date: Sun, 31 May 2026 10:14:17 +0000 Subject: [PATCH 14/16] =?UTF-8?q?uv=E7=A7=BB=E9=99=A4free()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/recipe/lang/Python/uv.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/recipe/lang/Python/uv.c b/src/recipe/lang/Python/uv.c index 85876f22..355c7eff 100644 --- a/src/recipe/lang/Python/uv.c +++ b/src/recipe/lang/Python/uv.c @@ -77,7 +77,6 @@ pl_python_find_uv_config (bool mkdir) chsrc_ensure_dir (config_dir); } char *result = xy_2strcat (config_dir, PL_Python_uv_ConfigFile); - free (config_dir); return result; } else @@ -104,7 +103,6 @@ pl_python_uv_getsrc (char *option) chsrc_error2 ("无法获取 uv 配置文件路径"); else chsrc_error2 ("未找到 uv 配置文件"); - free (uv_config); return; } @@ -114,14 +112,12 @@ pl_python_uv_getsrc (char *option) /* 在 Windows 上使用 PowerShell 替代 grep */ char *script = xy_str_gsub (RAWSTR_pl_python_get_uv_config_on_windows, "@f@", uv_config); chsrc_run_as_powershell_file (script); - free (script); } else { /* 在类 Unix 系统上使用 grep */ char *cmd = xy_str_gsub (RAWSTR_pl_python_get_uv_config, "@f@", uv_config); chsrc_run (cmd, RunOpt_Default); - free (cmd); } /* 检查 Python 下载镜像 */ @@ -135,10 +131,7 @@ pl_python_uv_getsrc (char *option) if (!end) end = line + strlen (line); printf ("%.*s\n", (int)(end - line), line); } - free (content); } - - free (uv_config); } @@ -367,13 +360,10 @@ pl_python_uv_write_all (const char *uv_config, const char *pypi_url, const char if (!content) content = xy_strdup (""); char *updated = replace_pypi_index_url (content, pypi_url); - free (content); char *final = replace_python_install_mirror (updated, py_dl_url); - free (updated); chsrc_overwrite_file (final, uv_config); - free (final); } @@ -404,7 +394,6 @@ pl_python_uv_setsrc (char *option) if (!chsrc_check_file (uv_config)) { chsrc_info ("没有 uv 配置文件, 无需重置"); - free (uv_config); return; } @@ -413,16 +402,12 @@ pl_python_uv_setsrc (char *option) if (!content) { chsrc_error2 ("无法读取 uv 配置文件"); - free (uv_config); return; } char *cleaned = cleanup_config_for_reset (content); - free (content); chsrc_overwrite_file (cleaned, uv_config); - free (cleaned); - free (uv_config); return; } @@ -436,8 +421,6 @@ pl_python_uv_setsrc (char *option) chsrc_backup (uv_config); pl_python_uv_write_all (uv_config, source.url, gh_source.url); - free (uv_config); - if (chsrc_in_standalone_mode()) { chsrc_determine_chgtype (ChgType_Auto); From 0a253b81ffa18b5dbc74d911320e7ebe6f9baf85 Mon Sep 17 00:00:00 2001 From: Mikachu2333 Date: Mon, 1 Jun 2026 19:02:32 +0800 Subject: [PATCH 15/16] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=82=AE=E7=AE=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/PR-test.yml | 4 ++-- ...5\246\202\344\275\225\347\274\226\345\206\231recipe.md" | 7 +++++-- lib/xy.h | 6 +++--- src/chsrc-main.c | 2 +- src/recipe/menu.c | 6 +++--- src/recipe/recipe-template.c | 4 ++-- test/xy.c | 4 ++-- 7 files changed, 18 insertions(+), 15 deletions(-) diff --git a/.github/workflows/PR-test.yml b/.github/workflows/PR-test.yml index 7ffa5bf8..9ec3ac04 100644 --- a/.github/workflows/PR-test.yml +++ b/.github/workflows/PR-test.yml @@ -1,7 +1,7 @@ # --------------------------------------------------------------- # Workflow File : PR-test.yml -# File Authors : 曾奥然 -# Contributors : Mikachu2333 +# File Authors : 曾奥然 +# Contributors : Mikachu2333 # | # Created On : <2025-06-19> # Last Modified : <2025-08-17> diff --git "a/doc/10-\345\246\202\344\275\225\347\274\226\345\206\231recipe.md" "b/doc/10-\345\246\202\344\275\225\347\274\226\345\206\231recipe.md" index c35aec10..c394de39 100644 --- "a/doc/10-\345\246\202\344\275\225\347\274\226\345\206\231recipe.md" +++ "b/doc/10-\345\246\202\344\275\225\347\274\226\345\206\231recipe.md" @@ -3,8 +3,9 @@ ! ------------------------------------------------------------- ! Doc Type : Markdown ! Doc Name : 10-如何编写recipe.md - ! Doc Authors : 曾奥然 - ! Contributors : Nul None + ! Doc Authors : 曾奥然 + ! Contributors : Nul None + Mikachu2333 ! | ! Created On : <2024-08-19> ! Last Modified : <2026-01-21> @@ -116,6 +117,8 @@ `chsrc` 主程序不提供配置文件,不提供数据文件,干净无污染。那么在实现 `recipe` 的时候,除了备份文件外,也不要污染用户环境。 +4. 逻辑精炼简洁, +
[rawstr4c]: https://github.com/RubyMetric/rawstr4c diff --git a/lib/xy.h b/lib/xy.h index 7da82c42..01998f30 100644 --- a/lib/xy.h +++ b/lib/xy.h @@ -2,9 +2,9 @@ * Copyright © 2023-2026 曾奥然, 郭恒 * SPDX-License-Identifier: MIT * ------------------------------------------------------------- - * Lib Authors : 曾奥然 - * | 郭恒 <2085471348@qq.com> - * Contributors : Mikachu2333 + * Lib Authors : 曾奥然 + * | 郭恒 <2085471348@qq.com> + * Contributors : Mikachu2333 * | juzeon * | BingChunMoLi * | AnonTokio diff --git a/src/chsrc-main.c b/src/chsrc-main.c index bf41f528..ea57f026 100644 --- a/src/chsrc-main.c +++ b/src/chsrc-main.c @@ -69,7 +69,7 @@ chsrc_register_contributors () chef_register_contributor ("@Kattos", "ccy", "icuichengyi@gmail.com", NULL); chef_register_contributor ("@xrgzs", "MadDogOwner", "xiaoran@xrgzs.top", NULL); chef_register_contributor ("@sanchuanhehe", "sanchuanhehe", "wyihe5520@gmail.com", NULL); - chef_register_contributor ("@Mikachu2333", "Mikachu2333", "mikachu.23333@zohomail.com", NULL); + chef_register_contributor ("@Mikachu2333", "Mikachu2333", "linkchou@yandex.com", NULL); chef_register_contributor ("@techoc", "Rui Yang", "techoc@foxmail.com", NULL); chef_register_contributor ("@BingChunMoLi", "BingChunMoLi", "bingchunmoli@bingchunmoli.com", NULL); // 该注释下一行的用户 ID 为 Gitee ID diff --git a/src/recipe/menu.c b/src/recipe/menu.c index c31da9d7..6df5a00b 100644 --- a/src/recipe/menu.c +++ b/src/recipe/menu.c @@ -1,9 +1,9 @@ /** ------------------------------------------------------------ * SPDX-License-Identifier: GPL-3.0-or-later * ------------------------------------------------------------- - * File Authors : 曾奥然 - * Contributors : Mikachu2333 - * | BingChunMoLi + * File Authors : 曾奥然 + * Contributors : Mikachu2333 + * | BingChunMoLi * | * Created On : <2023-09-01> * Major Revision : 5 diff --git a/src/recipe/recipe-template.c b/src/recipe/recipe-template.c index 6a33c710..7a3c23d8 100644 --- a/src/recipe/recipe-template.c +++ b/src/recipe/recipe-template.c @@ -2,8 +2,8 @@ * SPDX-License-Identifier: GPL-3.0-or-later * ------------------------------------------------------------- * File Name : recipe-template.c - * File Authors : 曾奥然 - * Contributors : Mikachu2333 + * File Authors : 曾奥然 + * Contributors : Mikachu2333 * | * Created On : <2024-08-09> * Last Modified : <2026-02-24> diff --git a/test/xy.c b/test/xy.c index 34de9329..9db5f70a 100644 --- a/test/xy.c +++ b/test/xy.c @@ -2,8 +2,8 @@ * SPDX-License-Identifier: MIT * ------------------------------------------------------------- * Test File : xy.c - * Test Authors : 曾奥然 - * Contributors : Mikachu2333 + * Test Authors : 曾奥然 + * Contributors : Mikachu2333 * Created On : <2023-08-30> * Last Modified : <2026-03-22> * From a635331ebde4b8d788a7437f787c44c1cc1b7176 Mon Sep 17 00:00:00 2001 From: Mikachu2333 Date: Mon, 1 Jun 2026 19:06:36 +0800 Subject: [PATCH 16/16] =?UTF-8?q?uv=E4=BF=AE=E5=A4=8D=E9=95=9C=E5=83=8F?= =?UTF-8?q?=E7=AB=99=E6=B5=8B=E9=80=9F=E9=93=BE=E6=8E=A5404?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 删除阿里镜像 --- src/recipe/lang/Python/uv.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/recipe/lang/Python/uv.c b/src/recipe/lang/Python/uv.c index 355c7eff..83fdf7ca 100644 --- a/src/recipe/lang/Python/uv.c +++ b/src/recipe/lang/Python/uv.c @@ -156,7 +156,7 @@ pl_uv_github_release_prelude (void) chef_set_recipe_created_on (this, "2026-05-31"); chef_set_recipe_last_updated (this, "2026-05-31"); - chef_set_sources_last_updated (this, "2026-05-31"); + chef_set_sources_last_updated (this, "2026-06-01"); chef_set_chef (this, NULL); chef_set_cooks (this, 1, "@Mikachu2333"); @@ -169,14 +169,12 @@ pl_uv_github_release_prelude (void) {&UpstreamProvider, "https://github.com/astral-sh/python-build-standalone/releases/download", DelegateToUpstream}, {&Nju, "https://mirrors.nju.edu.cn/github-release/astral-sh/python-build-standalone", FeedByPrelude}, {&Ustc, "https://mirrors.ustc.edu.cn/github-release/astral-sh/python-build-standalone", FeedByPrelude}, - {&Lzuoss, "https://mirror.lzu.edu.cn/github-release/astral-sh/python-build-standalone", FeedByPrelude}, - {&Ali, "https://mirrors.aliyun.com/github/releases/astral-sh/python-build-standalone", FeedByPrelude} + {&Lzuoss, "https://mirror.lzu.edu.cn/github-release/astral-sh/python-build-standalone", FeedByPrelude} def_sources_end () #define GH_SM_POSTFIX "/20260510/cpython-3.14.5+20260510-i686-pc-windows-msvc-install_only_stripped.tar.gz" chef_set_smURL_with_postfix (this, &Nju, GH_SM_POSTFIX); chef_set_smURL_with_postfix (this, &Lzuoss, GH_SM_POSTFIX); - chef_set_smURL_with_postfix (this, &Ali, GH_SM_POSTFIX); #undef GH_SM_POSTFIX /* 2026-5-31: USTC 仅保留 Latest, 只能用 SHA256SUMS 粗略测速 */ @@ -412,8 +410,21 @@ pl_python_uv_setsrc (char *option) } /* set: 选取源并写入 */ - Source_t source = chsrc_yield_source (&pl_python_group_target, option); - Source_t gh_source = chsrc_yield_source (&pl_uv_github_release_target, NULL); + Source_t source = chsrc_yield_source (&pl_python_group_target, option); + + /* 若 option 命中了 GitHub release 源则直接用, 否则自动测速 */ + char *gh_opt = option; + if (gh_opt && !chsrc_in_reset_mode () && !hp_is_url (gh_opt)) + { + bool found = false; + if (!pl_uv_github_release_target.inited) + pl_uv_github_release_target.preludefn (); + for (int i = 0; i < pl_uv_github_release_target.sources_n; i++) + if (xy_streql (pl_uv_github_release_target.sources[i].mirror->code, gh_opt)) + { found = true; break; } + if (!found) gh_opt = NULL; + } + Source_t gh_source = chsrc_yield_source (&pl_uv_github_release_target, gh_opt); if (chsrc_in_standalone_mode()) chsrc_confirm_source (&source);