diff --git a/src/recipe/lang/Python/rawstr4c.h b/src/recipe/lang/Python/rawstr4c.h index 872cb0eb..3e6ceafb 100644 --- a/src/recipe/lang/Python/rawstr4c.h +++ b/src/recipe/lang/Python/rawstr4c.h @@ -4,13 +4,9 @@ * Generated by rawstr4c v1.0.0-2025/08/09 */ -char RAWSTR_pl_python_uv_config_source_content[] = "\x5b\x5b\x69\x6e\x64\x65\x78\x5d\x5d\x0a\x75\x72\x6c\x20\x3d\x20\x22\x40\x75\x72\x6c\x40\x22\x0a\x64\x65\x66\x61\x75\x6c\x74\x20\x3d\x20\x74\x72\x75\x65\x0a"; +char RAWSTR_pl_python_uv_config_uv_format[] = "\x70\x79\x74\x68\x6f\x6e\x2d\x69\x6e\x73\x74\x61\x6c\x6c\x2d\x6d\x69\x72\x72\x6f\x72\x20\x3d\x20\x22\x40\x75\x72\x6c\x41\x40\x22\x5b\x70\x69\x70\x5d\x69\x6e\x64\x65\x78\x2d\x75\x72\x6c\x20\x3d\x20\x22\x40\x75\x72\x6c\x42\x40\x22"; -char RAWSTR_pl_python_get_uv_config[] = "\x67\x72\x65\x70\x20\x2d\x41\x20\x32\x20\x27\x69\x6e\x64\x65\x78\x27\x20\x40\x66\x40\x20\x7c\x20\x73\x65\x64\x20\x2d\x6e\x20\x27\x73\x2f\x5e\x75\x72\x6c\x20\x3d\x20\x22\x5c\x28\x2e\x2a\x5c\x29\x22\x2f\x5c\x31\x2f\x70\x27"; - -char RAWSTR_pl_python_set_uv_config[] = "\x40\x73\x65\x64\x40\x20\x27\x2f\x5e\x5c\x5b\x5c\x5b\x69\x6e\x64\x65\x78\x5c\x5d\x5c\x5d\x24\x2f\x2c\x2f\x5e\x64\x65\x66\x61\x75\x6c\x74\x20\x3d\x20\x74\x72\x75\x65\x24\x2f\x7b\x73\x7c\x5e\x75\x72\x6c\x20\x3d\x20\x22\x2e\x2a\x22\x24\x7c\x75\x72\x6c\x20\x3d\x20\x22\x40\x75\x72\x6c\x40\x22\x7c\x7d\x27\x20\x40\x66\x40"; - -char RAWSTR_pl_python_test_uv_if_set_source[] = "\x67\x72\x65\x70\x20\x2d\x71\x20\x27\x5e\x5c\x5b\x5c\x5b\x69\x6e\x64\x65\x78\x5d\x5d\x24\x27\x20\x40\x66\x40"; +char RAWSTR_pl_python_uv_config_pyproject[] = "\x5b\x74\x6f\x6f\x6c\x2e\x75\x76\x5d\x70\x79\x74\x68\x6f\x6e\x2d\x69\x6e\x73\x74\x61\x6c\x6c\x2d\x6d\x69\x72\x72\x6f\x72\x20\x3d\x20\x22\x40\x75\x72\x6c\x41\x40\x22\x5b\x74\x6f\x6f\x6c\x2e\x75\x76\x2e\x70\x69\x70\x5d\x69\x6e\x64\x65\x78\x2d\x75\x72\x6c\x20\x3d\x20\x22\x40\x75\x72\x6c\x42\x40\x22"; char RAWSTR_pl_python_rye_config[] = "\x5b\x5b\x73\x6f\x75\x72\x63\x65\x73\x5d\x5d\x0a\x6e\x61\x6d\x65\x20\x3d\x20\x22\x40\x31\x40\x22\x0a\x75\x72\x6c\x20\x20\x3d\x20\x22\x40\x32\x40\x22"; diff --git a/src/recipe/lang/Python/rawstr4c.md b/src/recipe/lang/Python/rawstr4c.md index d56a65a8..310f5c5c 100644 --- a/src/recipe/lang/Python/rawstr4c.md +++ b/src/recipe/lang/Python/rawstr4c.md @@ -22,40 +22,27 @@ ## uv -### uv config source content +### uv config source content (`uv.toml`) ```toml -[[index]] -url = "@url@" -default = true +python-install-mirror = "@urlA@" +[pip] +index-url = "@urlB@" ``` +### uv config source content (`pyproject.toml`) -### Get uv config - -```sh -grep -A 2 'index' @f@ | sed -n 's/^url = "\(.*\)"/\1/p' -``` - - -### Set uv config - -```sh -@sed@ '/^\[\[index\]\]$/,/^default = true$/{s|^url = ".*"$|url = "@url@"|}' @f@ -``` - - -### Test uv if set source +```toml +[tool.uv] +python-install-mirror = "@urlA@" -```sh -grep -q '^\[\[index]]$' @f@ +[tool.uv.pip] +index-url = "@urlB@" ```
- - ## Rye - name = `rye_config` diff --git a/src/recipe/lang/Python/uv.c b/src/recipe/lang/Python/uv.c index b2d1e46a..87cf5e54 100644 --- a/src/recipe/lang/Python/uv.c +++ b/src/recipe/lang/Python/uv.c @@ -2,7 +2,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later * ------------------------------------------------------------*/ -def_target(pl_python_uv, "uv"); +def_target (pl_python_uv, "uv"); void pl_python_uv_prelude (void) @@ -10,16 +10,16 @@ pl_python_uv_prelude (void) chef_prep_this (pl_python_uv, gsr); chef_set_created_on (this, "2024-12-11"); - chef_set_last_updated (this, "2025-08-09"); - chef_set_sources_last_updated (this, "2025-08-09"); + chef_set_last_updated (this, "2025-10-11"); + chef_set_sources_last_updated (this, "2025-10-11"); chef_set_chef (this, NULL); - chef_set_cooks (this, 1, "@happy-game"); + chef_set_cooks (this, 2, "@Mikachu2333", "@happy-game"); chef_set_sauciers (this, 2, "@Kattos", "@ccmywish"); chef_allow_local_mode (this, FullyCan, NULL, NULL); - chef_allow_english(this); - chef_allow_user_define(this); + chef_allow_english (this); + chef_allow_user_define (this); chef_use_other_target_sources (this, &pl_python_group_target); } @@ -35,64 +35,677 @@ pl_python_uv_prelude (void) * 4. /etc/uv/uv.toml */ -#define PL_Python_uv_ConfigFile "uv.toml" -#define PL_Python_uv_Local_ConfigPath "./" -#define PL_Python_uv_User_ConfigPath "~/.config/uv/" +#define PL_Python_uv_Proj_uv "uv.toml" +#define PL_Python_uv_Proj_pyproj "pyproject.toml" +#ifdef XY_Build_On_Windows + #define PL_Python_uv_Local_ConfigPath "\\uv\\uv.toml" + #define PL_Python_uv_User_ConfigPath "\\uv\\uv.toml" +#else + #define PL_Python_uv_Local_ConfigPath "~/.config/uv/uv.toml" + #define PL_Python_uv_User_ConfigPath "/etc/uv/uv.toml" +#endif + +typedef struct +{ + bool is_empty; + char *url; + char *file_path; +} +PlPythonUvConfig; + +PlPythonUvConfig +pl_python_uv_config_define_python (char *path) +{ + PlPythonUvConfig config = {true, NULL, NULL}; + + char *content = xy_file_read (path); + if (!content || strlen (content) == 0) + return config; + + char *formatted = xy_str_gsub (content, " ", ""); + formatted = xy_str_gsub (formatted, "'", "\""); + + XyStrFindResult_t result = xy_str_find (formatted, "python-install-mirror"); + if (result.found) + { + char *url = xy_str_next_nonempty_line (formatted + result.end + 1); + url = xy_str_gsub (url, "\"", ""); + url = xy_str_delete_prefix (url, "="); + config.url = url; + config.file_path = xy_strdup (path); + config.is_empty = !config.url; + } + return config; +} + +PlPythonUvConfig +pl_python_uv_config_define_pypi (char *path) +{ + PlPythonUvConfig config = {true, NULL, NULL}; + + char *content = xy_file_read (path); + if (!content || strlen (content) == 0) + return config; + + char *formatted = xy_str_gsub (content, " ", ""); + formatted = xy_str_gsub (formatted, "'", "\""); + + XyStrFindResult_t old_sytle_result = xy_str_find (formatted, "[[index]]"); + XyStrFindResult_t new_sytle_result = xy_str_find (formatted, "index-url"); + if (old_sytle_result.found && new_sytle_result.found) + { + chsrc_error2 (path); + char *msg = ENGLISH ? "UV config conflicted with old sytle and new style." + : "UV 配置文件因同时存在新旧两种格式而冲突,请自行检查。"; + chsrc_error (msg); + exit (Exit_UserCause); + } + + if (old_sytle_result.found) + { + char *current_pos = formatted + old_sytle_result.end + 1; + char *url = NULL; + + /* 循环读取下一行,直到找到包含 "url" 的行 */ + while (*current_pos != '\0') + { + char *line = xy_str_next_nonempty_line (current_pos); + if (line && xy_str_find (line, "url").found) + { + url = line; + break; + } + + /* 移动到下一行的位置 */ + current_pos = strchr (current_pos, '\n'); + if (!current_pos) + break; + current_pos++; + } + + if (url) + { + url = xy_str_gsub (url, "\"", ""); + url = xy_str_delete_prefix (url, "url="); + config.url = url; + config.file_path = xy_strdup (path); + config.is_empty = !config.url; + } + } + else + { + char *url = xy_str_next_nonempty_line (formatted + new_sytle_result.end + 1); + url = xy_str_gsub (url, "\"", ""); + url = xy_str_delete_prefix (url, "="); + config.url = url; + config.file_path = xy_strdup (path); + config.is_empty = !config.url; + } + return config; +} + +typedef struct +{ + bool pypi; + char *pypi_url; + char *pypi_path; + bool python; + char *python_url; + char *python_path; +} +PlPythonUvConfigSummary; + +bool +pl_python_uv_config_summary_struct_is_full (PlPythonUvConfigSummary *config) +{ + return config->pypi == true && + config->python == true; +} + +/** + * @brief 按配置文件优先级依次查找镜像配置 + * + * @return 返回优先级最高的有效配置,其余配置将被忽略。可能为空。 + * + * @note 特殊情况(Local mode)时直接返回项目一级的配置,无论是否为空 + */ +PlPythonUvConfigSummary +pl_python_uv_config_find () +{ + PlPythonUvConfigSummary result = {false, NULL, NULL, false, NULL, NULL}; + + // 第一优先级 + PlPythonUvConfig result1 = pl_python_uv_config_define_python (PL_Python_uv_Proj_uv); + if (!result1.is_empty) + { + result.python = true; + result.python_url = result1.url; + result.python_path = result1.file_path; + } + + PlPythonUvConfig result2 = pl_python_uv_config_define_pypi (PL_Python_uv_Proj_uv); + if (!result2.is_empty) + { + result.pypi = true; + result.pypi_url = result2.url; + result.pypi_path = result2.file_path; + } + if (pl_python_uv_config_summary_struct_is_full (&result) || chsrc_in_local_mode ()) return result; + + PlPythonUvConfig result3 = pl_python_uv_config_define_python (PL_Python_uv_Proj_pyproj); + if (!result3.is_empty) + { + result.python = true; + result.python_url = result3.url; + result.python_path = result3.file_path; + } + if (pl_python_uv_config_summary_struct_is_full (&result)) return result; + + PlPythonUvConfig result4 = pl_python_uv_config_define_pypi (PL_Python_uv_Proj_pyproj); + if (!result4.is_empty) + { + result.pypi = true; + result.pypi_url = result4.url; + result.pypi_path = result4.file_path; + } + if (pl_python_uv_config_summary_struct_is_full (&result)) return result; + + // 第二优先级 + char *uv_local_config_path = PL_Python_uv_Local_ConfigPath; + if (xy.on_windows) + { + uv_local_config_path = xy_2strcat (getenv ("APPDATA"), uv_local_config_path); + } + + PlPythonUvConfig result5 = pl_python_uv_config_define_python (uv_local_config_path); + if (!result5.is_empty) + { + result.python = true; + result.python_path = result5.file_path; + result.python_url = result5.url; + } + if (pl_python_uv_config_summary_struct_is_full (&result)) return result; + + PlPythonUvConfig result6 = pl_python_uv_config_define_pypi (uv_local_config_path); + if (!result6.is_empty) + { + result.pypi = true; + result.pypi_path = result6.file_path; + result.pypi_url = result6.url; + } + if (pl_python_uv_config_summary_struct_is_full (&result)) return result; + // 第三优先级 + char *uv_user_config_path = PL_Python_uv_User_ConfigPath; + if (xy.on_windows) + { + uv_user_config_path = xy_2strcat (getenv ("APPDATA"), uv_user_config_path); + } + PlPythonUvConfig result7 = pl_python_uv_config_define_python (uv_user_config_path); + if (!result7.is_empty) + { + result.python = true; + result.python_path = result7.file_path; + result.python_url = result7.url; + } + if (pl_python_uv_config_summary_struct_is_full (&result)) return result; + + PlPythonUvConfig result8 = pl_python_uv_config_define_pypi (uv_user_config_path); + if (!result8.is_empty) + { + result.pypi = true; + result.pypi_path = result8.file_path; + result.pypi_url = result8.url; + } -char * -pl_python_find_uv_config (bool mkdir) + return result; +} + +void +pl_python_uv_getsrc (char *option) { - if (chsrc_in_local_mode()) + PlPythonUvConfigSummary uv_config = pl_python_uv_config_find (); + + if (uv_config.pypi) + { + chsrc_note2 ("uv pypi:"); + say (xy_2strcat (ENGLISH ? "Config Path: " : "文件路径:", uv_config.pypi_path)); + say (uv_config.pypi_url); + } + else { - return xy_2strcat (PL_Python_uv_Local_ConfigPath, PL_Python_uv_ConfigFile); + char *msg = ENGLISH ? "Can't find uv config for pypi, show default." + : "未找到 uv 关于 pypi 的配置文件,显示默认值"; + chsrc_note2 (msg); + say ("https://pypi.org/simple"); + } + + if (uv_config.python) + { + chsrc_note2 ("uv Python:"); + say (xy_2strcat (ENGLISH ? "Config Path: " : "文件路径:", uv_config.python_path)); + say (uv_config.python_url); } else { - if (xy.on_windows) + char *msg = ENGLISH ? "Can't find uv config for Python, show default." + : "未找到 uv 关于 Python 的配置文件,显示默认值"; + chsrc_note2 (msg); + say ("https://github.com/astral-sh/python-build-standalone/releases/download"); + } +} + + +static char * +pl_python_uv_replace_key_value (const char *content, const char *key, const char *new_value) +{ + if (!content || !key || !new_value) + return NULL; + + char *key_pos = strstr (content, key); + if (!key_pos) + return NULL; + + char *line_end = strchr (key_pos, '\n'); + if (!line_end) + line_end = (char *) content + strlen (content); + + char *equal_pos = strchr (key_pos, '='); + if (!equal_pos || equal_pos > line_end) + return NULL; + + char *value_begin = equal_pos + 1; + while (value_begin < line_end && (*value_begin == ' ' || *value_begin == '\t')) + value_begin++; + + char quote = '\0'; + if (value_begin < line_end && (*value_begin == '"' || *value_begin == '\'')) + { + quote = *value_begin; + value_begin++; + } + + char *value_end = value_begin; + while (value_end < line_end) + { + if (quote) { - /* config path on Windows */ - char *appdata = getenv ("APPDATA"); + if (*value_end == quote) + break; + } + else if (*value_end == ' ' || *value_end == '\t' || *value_end == '#') + { + break; + } + value_end++; + } + + char *after_value = value_end; + if (quote && after_value < line_end && *after_value == quote) + after_value++; - if (!appdata) + size_t prefix_len = value_begin - content; + size_t suffix_len = strlen (after_value); + size_t quote_len = quote ? 1 : 0; + size_t new_len = prefix_len + quote_len + strlen (new_value) + quote_len + suffix_len; + + char *result = xy_malloc0 (new_len + 1); + memcpy (result, content, prefix_len); + + char *cursor = result + prefix_len; + if (quote) + { + *cursor = quote; + cursor++; + } + memcpy (cursor, new_value, strlen (new_value)); + cursor += strlen (new_value); + if (quote) + { + *cursor = quote; + cursor++; + } + + memcpy (cursor, after_value, suffix_len); + result[new_len] = '\0'; + + return result; +} + + +static char * +pl_python_uv_append_line (const char *content, const char *line_with_newline) +{ + if (!content) + content = ""; + if (!line_with_newline) + return NULL; + + size_t len = strlen (content); + bool need_line_break = len > 0 && content[len - 1] != '\n'; + + if (need_line_break) + { + char *tmp = xy_strcat (2, content, "\n"); + char *result = xy_strcat (2, tmp, line_with_newline); + free (tmp); + return result; + } + + return xy_strcat (2, content, line_with_newline); +} + + +static char * +pl_python_uv_insert_into_section (const char *content, const char *section_header, const char *line_with_newline) +{ + if (!content || !section_header || !line_with_newline) + return NULL; + + char *section_pos = strstr (content, section_header); + if (!section_pos) + return NULL; + + char *after_header = strchr (section_pos, '\n'); + if (!after_header) + { + char *line_block = xy_strcat (2, "\n", line_with_newline); + char *result = xy_strcat (2, content, line_block); + free (line_block); + return result; + } + + after_header++; + char *scan = after_header; + while (*scan != '\0') + { + if (*scan == '\n' && scan[1] == '[') + break; + scan++; + } + + char *insert_pos = scan; + size_t prefix_len = insert_pos - content; + size_t suffix_len = strlen (insert_pos); + + bool need_leading_newline = prefix_len > 0 && content[prefix_len - 1] != '\n'; + char *line_block = need_leading_newline ? xy_strcat (2, "\n", line_with_newline) + : xy_strdup (line_with_newline); + + size_t block_len = strlen (line_block); + char *result = xy_malloc0 (prefix_len + block_len + suffix_len + 1); + memcpy (result, content, prefix_len); + memcpy (result + prefix_len, line_block, block_len); + memcpy (result + prefix_len + block_len, insert_pos, suffix_len); + result[prefix_len + block_len + suffix_len] = '\0'; + + free (line_block); + return result; +} + + +static void +pl_python_uv_upsert_python (char **content, + const char *existing_url, + const char *new_url, + bool is_pyproject) +{ + bool updated = false; + + if (existing_url) + { + char *old_literal = xy_strcat (3, "\"", existing_url, "\""); + char *new_literal = xy_strcat (3, "\"", new_url, "\""); + char *replaced = xy_str_gsub (*content, old_literal, new_literal); + bool changed = !xy_streql (replaced, *content); + free (old_literal); + free (new_literal); + if (changed) + { + free (*content); + *content = replaced; + updated = true; + } + else + { + free (replaced); + } + } + + if (!updated) + { + char *replaced = pl_python_uv_replace_key_value (*content, "python-install-mirror", new_url); + if (replaced) + { + free (*content); + *content = replaced; + updated = true; + } + } + + if (!updated) + { + char *line = xy_strcat (4, "python-install-mirror = \"", new_url, "\"", "\n"); + + if (is_pyproject) + { + if (xy_str_find (*content, "[tool.uv]").found) { - chsrc_error2 ("未能获取 APPDATA 环境变量"); - return NULL; + char *inserted = pl_python_uv_insert_into_section (*content, "[tool.uv]", line); + if (inserted) + { + free (*content); + *content = inserted; + updated = true; + } } - char *config_dir = xy_2strcat(appdata, "\\uv\\"); - if (mkdir) + if (!updated) { - chsrc_ensure_dir (config_dir); + char *block = xy_strcat (3, "\n[tool.uv]\n", line, ""); + char *appended = pl_python_uv_append_line (*content, block); + free (block); + if (appended) + { + free (*content); + *content = appended; + updated = true; + } } - return xy_2strcat (config_dir, PL_Python_uv_ConfigFile); } else { - /* config path on Linux or macOS */ - if (mkdir) + char *appended = pl_python_uv_append_line (*content, line); + if (appended) { - chsrc_ensure_dir (PL_Python_uv_User_ConfigPath); + free (*content); + *content = appended; + updated = true; } - return xy_2strcat (PL_Python_uv_User_ConfigPath, PL_Python_uv_ConfigFile); } + + free (line); } } -void -pl_python_uv_getsrc (char *option) + +static void +pl_python_uv_upsert_pypi (char **content, + const char *existing_url, + const char *new_url, + bool is_pyproject) { - char *uv_config = pl_python_find_uv_config (false); + bool updated = false; - if (!chsrc_check_file (uv_config)) + if (existing_url) { - chsrc_error2 ("未找到 uv 配置文件"); + char *old_literal = xy_strcat (3, "\"", existing_url, "\""); + char *new_literal = xy_strcat (3, "\"", new_url, "\""); + char *replaced = xy_str_gsub (*content, old_literal, new_literal); + bool changed = !xy_streql (replaced, *content); + free (old_literal); + free (new_literal); + if (changed) + { + free (*content); + *content = replaced; + updated = true; + } + else + { + free (replaced); + } + } + + if (!updated) + { + char *replaced = pl_python_uv_replace_key_value (*content, "index-url", new_url); + if (replaced) + { + free (*content); + *content = replaced; + updated = true; + } + } + + if (!updated) + { + if (xy_str_find (*content, "[tool.uv.pip]").found) + { + char *line = xy_strcat (4, "index-url = \"", new_url, "\"", "\n"); + char *inserted = pl_python_uv_insert_into_section (*content, "[tool.uv.pip]", line); + free (line); + if (inserted) + { + free (*content); + *content = inserted; + updated = true; + } + } + + if (!updated && xy_str_find (*content, "[pip]").found) + { + char *line = xy_strcat (4, "index-url = \"", new_url, "\"", "\n"); + char *inserted = pl_python_uv_insert_into_section (*content, "[pip]", line); + free (line); + if (inserted) + { + free (*content); + *content = inserted; + updated = true; + } + } + + if (!updated && xy_str_find (*content, "[[index]]").found) + { + char *line = xy_strcat (4, "url = \"", new_url, "\"", "\n"); + char *inserted = pl_python_uv_insert_into_section (*content, "[[index]]", line); + free (line); + if (inserted) + { + free (*content); + *content = inserted; + updated = true; + } + } + } + + if (!updated) + { + char *line = xy_strcat (4, "index-url = \"", new_url, "\"", "\n"); + + if (is_pyproject) + { + char *block = xy_strcat (3, "\n[tool.uv.pip]\n", line, ""); + char *appended = pl_python_uv_append_line (*content, block); + free (block); + if (appended) + { + free (*content); + *content = appended; + updated = true; + } + } + else + { + char *block = xy_strcat (3, "\n[pip]\n", line, ""); + char *appended = pl_python_uv_append_line (*content, block); + free (block); + if (appended) + { + free (*content); + *content = appended; + updated = true; + } + } + + free (line); + } +} + + +typedef struct +{ + char *path; + bool is_pyproject; + bool update_python; + bool update_pypi; + const char *existing_python_url; + const char *existing_pypi_url; +} +PlPythonUvTarget; + +static int +pl_python_uv_target_index (PlPythonUvTarget targets[], int count, const char *path) +{ + for (int i = 0; i < count; i++) + { + if (xy_streql (targets[i].path, path)) + return i; + } + return -1; +} + + +static void +pl_python_uv_target_add (PlPythonUvTarget targets[], + int *count, + const char *path, + bool update_python, + bool update_pypi, + const char *existing_python_url, + const char *existing_pypi_url) +{ + if (!path) + return; + + int index = pl_python_uv_target_index (targets, *count, path); + if (index == -1) + { + index = *count; + targets[index].path = xy_strdup (path); + targets[index].is_pyproject = xy_str_end_with (path, "pyproject.toml"); + targets[index].update_python = update_python; + targets[index].update_pypi = update_pypi; + targets[index].existing_python_url = existing_python_url; + targets[index].existing_pypi_url = existing_pypi_url; + (*count)++; return; } - /* 获取 [[index]] 配置项的 url */ - char *cmd = xy_str_gsub (RAWSTR_pl_python_get_uv_config, "@f@", uv_config); - chsrc_run (cmd, RunOpt_Default); + if (update_python) + { + targets[index].update_python = true; + if (existing_python_url) + targets[index].existing_python_url = existing_python_url; + } + + if (update_pypi) + { + targets[index].update_pypi = true; + if (existing_pypi_url) + targets[index].existing_pypi_url = existing_pypi_url; + } } @@ -104,62 +717,146 @@ void pl_python_uv_setsrc (char *option) { chsrc_ensure_program ("uv"); + PlPythonUvConfigSummary uv_config = pl_python_uv_config_find (); + chsrc_use_this_source (pl_python_uv); + + const char *PL_CPYTHON_URL = "https://mirror.nju.edu.cn/github-release/astral-sh/python-build-standalone/"; - Source_t source = chsrc_yield_source (&pl_python_group_target, option); - if (chsrc_in_standalone_mode()) - chsrc_confirm_source(&source); + char *summary_python_path = uv_config.python_path ? xy_normalize_path (uv_config.python_path) : NULL; + char *summary_pypi_path = uv_config.pypi_path ? xy_normalize_path (uv_config.pypi_path) : NULL; - char *uv_config = pl_python_find_uv_config (true); - if (NULL==uv_config) + char *uv_local_config_path = NULL; + if (xy.on_windows) { - chsrc_error2 ("无法获取 uv 配置文件路径"); - return; + uv_local_config_path = xy_2strcat (getenv ("APPDATA"), PL_Python_uv_Local_ConfigPath); + } + else + { + uv_local_config_path = xy_strdup (PL_Python_uv_Local_ConfigPath); } - chsrc_backup (uv_config); + uv_local_config_path = xy_normalize_path (uv_local_config_path); - const char *source_content = xy_str_gsub (RAWSTR_pl_python_uv_config_source_content, "@url@", source.url); + char *proj_uv_path = xy_normalize_path (PL_Python_uv_Proj_uv); + char *proj_pyproj_path = xy_normalize_path (PL_Python_uv_Proj_pyproj); -#if defined(XY_Build_On_macOS) || defined(XY_Build_On_BSD) - char *sed_cmd = "sed -i '' "; -#else - char *sed_cmd = "sed -i "; -#endif + bool proj_uv_exist = xy_file_exist (proj_uv_path); + bool proj_pyproj_exist = xy_file_exist (proj_pyproj_path); + bool local_mode = chsrc_in_local_mode (); - /** - * 将 [[index]] 到 default = true 之间的 url = ".*" 替换为 url = "source.url" - */ - 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); + PlPythonUvTarget targets[3] = {0}; + int target_count = 0; - if (!xy_file_exist (uv_config)) + if (local_mode) { - /* 当 uv_config 不存在,直接写入文件 */ - chsrc_append_to_file (source_content, uv_config); + const char *existing_python_url = (summary_python_path && xy_streql (summary_python_path, proj_uv_path)) + ? uv_config.python_url + : NULL; + const char *existing_pypi_url = (summary_pypi_path && xy_streql (summary_pypi_path, proj_uv_path)) + ? uv_config.pypi_url + : NULL; + pl_python_uv_target_add (targets, &target_count, proj_uv_path, true, true, + existing_python_url, existing_pypi_url); } else { - /* 当 uv_config 存在,如果存在 [[index]] 则更新,否则追加到文件末尾 */ - char *cmd = xy_str_gsub (RAWSTR_pl_python_test_uv_if_set_source, "@f@", uv_config); - chsrc_ensure_program ("grep"); - int status = xy_run_get_status (cmd); - if (0==status) + if (summary_python_path) + { + pl_python_uv_target_add (targets, &target_count, summary_python_path, true, false, + uv_config.python_url, NULL); + } + else if (proj_uv_exist) { - chsrc_run (update_config_cmd, RunOpt_Default); + pl_python_uv_target_add (targets, &target_count, proj_uv_path, true, false, NULL, NULL); + } + else if (proj_pyproj_exist) + { + pl_python_uv_target_add (targets, &target_count, proj_pyproj_path, true, false, NULL, NULL); } else { - chsrc_append_to_file (source_content, uv_config); + pl_python_uv_target_add (targets, &target_count, uv_local_config_path, true, false, NULL, NULL); + } + + if (summary_pypi_path) + { + pl_python_uv_target_add (targets, &target_count, summary_pypi_path, false, true, + NULL, uv_config.pypi_url); + } + else if (summary_python_path) + { + pl_python_uv_target_add (targets, &target_count, summary_python_path, false, true, + NULL, NULL); + } + else if (proj_uv_exist) + { + pl_python_uv_target_add (targets, &target_count, proj_uv_path, false, true, NULL, NULL); + } + else if (proj_pyproj_exist) + { + pl_python_uv_target_add (targets, &target_count, proj_pyproj_path, false, true, NULL, NULL); + } + else + { + pl_python_uv_target_add (targets, &target_count, uv_local_config_path, false, true, NULL, NULL); } } - if (chsrc_in_standalone_mode()) + for (int i = 0; i < target_count; i++) { - chsrc_determine_chgtype (ChgType_Auto); - chsrc_conclude (&source); + char *path = targets[i].path; + bool file_exists = xy_file_exist (path); + char *content = file_exists ? xy_file_read (path) : NULL; + if (!content) + content = xy_strdup (""); + + if (file_exists) + { + chsrc_backup (path); + } + else + { + char *dir = xy_parent_dir (path); + if (dir) + { + chsrc_ensure_dir (dir); + free (dir); + } + } + + if (targets[i].update_python) + { + const char *existing_python_url = targets[i].existing_python_url; + if (!existing_python_url && summary_python_path && xy_streql (summary_python_path, path)) + existing_python_url = uv_config.python_url; + pl_python_uv_upsert_python (&content, existing_python_url, PL_CPYTHON_URL, + targets[i].is_pyproject); + } + + if (targets[i].update_pypi) + { + const char *existing_pypi_url = targets[i].existing_pypi_url; + if (!existing_pypi_url && summary_pypi_path && xy_streql (summary_pypi_path, path)) + existing_pypi_url = uv_config.pypi_url; + pl_python_uv_upsert_pypi (&content, existing_pypi_url, source.url, + targets[i].is_pyproject); + } + + chsrc_overwrite_file (content, path); + free (content); } -} + for (int i = 0; i < target_count; i++) + free (targets[i].path); + + free (summary_python_path); + free (summary_pypi_path); + free (uv_local_config_path); + free (proj_uv_path); + free (proj_pyproj_path); + + chsrc_determine_chgtype (ChgType_Auto); + chsrc_conclude (&source); +} void pl_python_uv_resetsrc (char *option)