@@ -35,132 +35,280 @@ pl_python_uv_prelude (void)
3535 * 4. /etc/uv/uv.toml
3636 */
3737
38- #define PL_Python_uv_ConfigFile "uv.toml"
39- #define PL_Python_uv_Local_ConfigPath "./"
40- #define PL_Python_uv_User_ConfigPath "~/.config/uv/"
38+ #define PL_Python_uv_Proj_uv "uv.toml"
39+ #define PL_Python_uv_Proj_pyproj "pyproject.toml"
40+ #ifdef XY_Build_On_Windows
41+ #define PL_Python_uv_Local_ConfigPath "\\uv\\uv.toml"
42+ #define PL_Python_uv_User_ConfigPath "\\uv\\uv.toml"
43+ #else
44+ #define PL_Python_uv_Local_ConfigPath "~/.config/uv/"
45+ #define PL_Python_uv_User_ConfigPath "/etc/uv/uv.toml"
46+ #endif
4147
48+ typedef struct
49+ {
50+ bool is_empty ;
51+ char * url ;
52+ char * file_path ;
53+ }
54+ PlPythonUvConfig ;
4255
43- char *
44- pl_python_find_uv_config ( bool mkdir )
56+ PlPythonUvConfig
57+ pl_python_uv_config_define_python ( char * path )
4558{
46- if (chsrc_in_local_mode ())
59+ PlPythonUvConfig config = {true, NULL , NULL };
60+
61+ char * content = xy_file_read (path );
62+ if (!content || strlen (content ) == 0 )
63+ return config ;
64+
65+ char * formatted = xy_str_gsub (content , " " , "" );
66+ formatted = xy_str_gsub (formatted , "'" , "\"" );
67+
68+ XyStrFindResult_t result = xy_str_find (formatted , "python-install-mirror" );
69+ if (result .found )
4770 {
48- return xy_2strcat (PL_Python_uv_Local_ConfigPath , PL_Python_uv_ConfigFile );
71+ char * url = xy_str_next_nonempty_line (formatted + result .end + 1 );
72+ url = xy_str_gsub (url , "\"" , "" );
73+ url = xy_str_delete_prefix (url , "=" );
74+ config .url = url ;
75+ config .file_path = xy_strdup (path );
76+ config .is_empty = !config .url ;
4977 }
50- else
78+ return config ;
79+ }
80+
81+ PlPythonUvConfig
82+ pl_python_uv_config_define_pypi (char * path )
83+ {
84+ PlPythonUvConfig config = {true, NULL , NULL };
85+
86+ char * content = xy_file_read (path );
87+ if (!content || strlen (content ) == 0 )
88+ return config ;
89+
90+ char * formatted = xy_str_gsub (content , " " , "" );
91+ formatted = xy_str_gsub (formatted , "'" , "\"" );
92+
93+ XyStrFindResult_t old_sytle_result = xy_str_find (formatted , "[[index]]" );
94+ XyStrFindResult_t new_sytle_result = xy_str_find (formatted , "index-url" );
95+ if (old_sytle_result .found && new_sytle_result .found )
5196 {
52- if (xy .on_windows )
53- {
54- /* config path on Windows */
55- char * appdata = getenv ("APPDATA" );
97+ chsrc_error2 (path );
98+ char * msg = ENGLISH ? "UV config conflicted with old sytle and new style."
99+ : "UV 配置文件因同时存在新旧两种格式而冲突,请自行检查。" ;
100+ chsrc_error (msg );
101+ exit (Exit_UserCause );
102+ }
56103
57- if (!appdata )
58- {
59- chsrc_error2 ("未能获取 APPDATA 环境变量" );
60- return NULL ;
61- }
104+ if (old_sytle_result .found )
105+ {
106+ char * current_pos = formatted + old_sytle_result .end + 1 ;
107+ char * url = NULL ;
62108
63- char * config_dir = xy_2strcat (appdata , "\\uv\\" );
64- if (mkdir )
109+ /* 循环读取下一行,直到找到包含 "url" 的行 */
110+ while (* current_pos != '\0' )
111+ {
112+ char * line = xy_str_next_nonempty_line (current_pos );
113+ if (line && xy_str_find (line , "url" ).found )
65114 {
66- chsrc_ensure_dir (config_dir );
115+ url = line ;
116+ break ;
67117 }
68- return xy_2strcat (config_dir , PL_Python_uv_ConfigFile );
118+
119+ /* 移动到下一行的位置 */
120+ current_pos = strchr (current_pos , '\n' );
121+ if (!current_pos )
122+ break ;
123+ current_pos ++ ;
69124 }
70- else
125+
126+ if (url )
71127 {
72- /* config path on Linux or macOS */
73- if (mkdir )
74- {
75- chsrc_ensure_dir (PL_Python_uv_User_ConfigPath );
76- }
77- return xy_2strcat (PL_Python_uv_User_ConfigPath , PL_Python_uv_ConfigFile );
128+ url = xy_str_gsub (url , "\"" , "" );
129+ url = xy_str_delete_prefix (url , "url=" );
130+ config .url = url ;
131+ config .file_path = xy_strdup (path );
132+ config .is_empty = !config .url ;
78133 }
79134 }
80- }
81-
82- void
83- pl_python_uv_getsrc (char * option )
84- {
85- char * uv_config = pl_python_find_uv_config (false);
86-
87- if (!chsrc_check_file (uv_config ))
135+ else
88136 {
89- chsrc_error2 ("未找到 uv 配置文件" );
90- return ;
137+ char * url = xy_str_next_nonempty_line (formatted + new_sytle_result .end + 1 );
138+ url = xy_str_gsub (url , "\"" , "" );
139+ url = xy_str_delete_prefix (url , "=" );
140+ config .url = url ;
141+ config .file_path = xy_strdup (path );
142+ config .is_empty = !config .url ;
91143 }
144+ return config ;
145+ }
92146
93- /* 获取 [[index]] 配置项的 url */
94- char * cmd = xy_str_gsub (RAWSTR_pl_python_get_uv_config , "@f@" , uv_config );
95- chsrc_run (cmd , RunOpt_Default );
147+ typedef struct
148+ {
149+ bool pypi ;
150+ char * pypi_url ;
151+ char * pypi_path ;
152+ bool python ;
153+ char * python_url ;
154+ char * python_path ;
96155}
156+ PlPythonUvConfigSummary ;
97157
158+ bool
159+ pl_python_uv_config_summary_struct_is_full (PlPythonUvConfigSummary * config )
160+ {
161+ return config -> pypi == true &&
162+ config -> python == true;
163+ }
98164
99165/**
100- * @consult https://docs.astral.sh/uv/configuration/files/
101- * https://github.com/RubyMetric/chsrc/issues/139
166+ * @brief 按配置文件优先级依次查找镜像配置
167+ *
168+ * @return 返回优先级最高的有效配置,其余配置将被忽略。可能为空。
169+ *
170+ * @note 特殊情况(Local mode)时直接返回项目一级的配置,无论是否为空
102171 */
103- void
104- pl_python_uv_setsrc ( char * option )
172+ PlPythonUvConfigSummary
173+ pl_python_uv_config_find ( )
105174{
106- chsrc_ensure_program ("uv" );
175+ PlPythonUvConfigSummary result = {false, NULL , NULL , false, NULL , NULL };
176+
177+ // 第一优先级
178+ PlPythonUvConfig result1 = pl_python_uv_config_define_python (PL_Python_uv_Proj_uv );
179+ if (!result1 .is_empty )
180+ {
181+ result .python = true;
182+ result .python_url = result1 .url ;
183+ result .python_path = result1 .file_path ;
184+ }
185+
186+ PlPythonUvConfig result2 = pl_python_uv_config_define_pypi (PL_Python_uv_Proj_uv );
187+ if (!result2 .is_empty )
188+ {
189+ result .pypi = true;
190+ result .pypi_url = result2 .url ;
191+ result .pypi_path = result2 .file_path ;
192+ }
193+ if (pl_python_uv_config_summary_struct_is_full (& result ) || chsrc_in_local_mode ()) return result ;
194+
195+ PlPythonUvConfig result3 = pl_python_uv_config_define_python (PL_Python_uv_Proj_pyproj );
196+ if (!result3 .is_empty )
197+ {
198+ result .python = true;
199+ result .python_url = result3 .url ;
200+ result .python_path = result3 .file_path ;
201+ }
202+ if (pl_python_uv_config_summary_struct_is_full (& result )) return result ;
107203
108- Source_t source = chsrc_yield_source (& pl_python_group_target , option );
109- if (chsrc_in_standalone_mode ())
110- chsrc_confirm_source (& source );
204+ PlPythonUvConfig result4 = pl_python_uv_config_define_pypi (PL_Python_uv_Proj_pyproj );
205+ if (!result4 .is_empty )
206+ {
207+ result .pypi = true;
208+ result .pypi_url = result4 .url ;
209+ result .pypi_path = result4 .file_path ;
210+ }
211+ if (pl_python_uv_config_summary_struct_is_full (& result )) return result ;
111212
112- char * uv_config = pl_python_find_uv_config (true);
113- if (NULL == uv_config )
213+ // 第二优先级
214+ char * uv_local_config_path = PL_Python_uv_Local_ConfigPath ;
215+ if (xy .on_windows )
114216 {
115- chsrc_error2 ("无法获取 uv 配置文件路径" );
116- return ;
217+ uv_local_config_path = xy_2strcat (getenv ("APPDATA" ), uv_local_config_path );
117218 }
118- chsrc_backup (uv_config );
119219
120- const char * source_content = xy_str_gsub (RAWSTR_pl_python_uv_config_source_content , "@url@" , source .url );
220+ PlPythonUvConfig result5 = pl_python_uv_config_define_python (uv_local_config_path );
221+ if (!result5 .is_empty )
222+ {
223+ result .python = true;
224+ result .python_path = result5 .file_path ;
225+ result .python_url = result5 .url ;
226+ }
227+ if (pl_python_uv_config_summary_struct_is_full (& result )) return result ;
121228
122- #if defined(XY_Build_On_macOS ) || defined(XY_Build_On_BSD )
123- char * sed_cmd = "sed -i '' " ;
124- #else
125- char * sed_cmd = "sed -i " ;
126- #endif
229+ PlPythonUvConfig result6 = pl_python_uv_config_define_pypi (uv_local_config_path );
230+ if (!result6 .is_empty )
231+ {
232+ result .pypi = true;
233+ result .pypi_path = result6 .file_path ;
234+ result .pypi_url = result6 .url ;
235+ }
236+ if (pl_python_uv_config_summary_struct_is_full (& result )) return result ;
237+
238+ // 第三优先级
239+ char * uv_user_config_path = PL_Python_uv_User_ConfigPath ;
240+ if (xy .on_windows )
241+ {
242+ uv_user_config_path = xy_2strcat (getenv ("APPDATA" ), uv_user_config_path );
243+ }
244+ PlPythonUvConfig result7 = pl_python_uv_config_define_python (uv_user_config_path );
245+ if (!result7 .is_empty )
246+ {
247+ result .python = true;
248+ result .python_path = result7 .file_path ;
249+ result .python_url = result7 .url ;
250+ }
251+ if (pl_python_uv_config_summary_struct_is_full (& result )) return result ;
252+
253+ PlPythonUvConfig result8 = pl_python_uv_config_define_pypi (uv_user_config_path );
254+ if (!result8 .is_empty )
255+ {
256+ result .pypi = true;
257+ result .pypi_path = result8 .file_path ;
258+ result .pypi_url = result8 .url ;
259+ }
260+
261+ return result ;
262+ }
127263
128- /**
129- * 将 [[index]] 到 default = true 之间的 url = ".*" 替换为 url = "source.url"
130- */
131- char * update_config_cmd = xy_str_gsub (RAWSTR_pl_python_set_uv_config , "@sed@" , sed_cmd );
132- update_config_cmd = xy_str_gsub (update_config_cmd , "@f@" , uv_config );
133- update_config_cmd = xy_str_gsub (update_config_cmd , "@url@" , source .url );
264+ void
265+ pl_python_uv_getsrc (char * option )
266+ {
267+ PlPythonUvConfigSummary uv_config = pl_python_uv_config_find ();
134268
135- if (! xy_file_exist ( uv_config ) )
269+ if (uv_config . pypi )
136270 {
137- /* 当 uv_config 不存在,直接写入文件 */
138- chsrc_append_to_file (source_content , uv_config );
271+ chsrc_note2 ("uv pypi:" );
272+ say (xy_2strcat (ENGLISH ? "Config Path: " : "文件路径:" , uv_config .pypi_path ));
273+ say (uv_config .pypi_url );
139274 }
140275 else
141276 {
142- /* 当 uv_config 存在,如果存在 [[index]] 则更新,否则追加到文件末尾 */
143- char * cmd = xy_str_gsub (RAWSTR_pl_python_test_uv_if_set_source , "@f@" , uv_config );
144- chsrc_ensure_program ("grep" );
145- int status = xy_run_get_status (cmd );
146- if (0 == status )
147- {
148- chsrc_run (update_config_cmd , RunOpt_Default );
149- }
150- else
151- {
152- chsrc_append_to_file (source_content , uv_config );
153- }
277+ char * msg = ENGLISH ? "Can't find uv config for pypi, show default."
278+ : "未找到 uv 关于 pypi 的配置文件,显示默认值" ;
279+ chsrc_note2 (msg );
280+ say ("https://pypi.org/simple" );
154281 }
155282
156- if (chsrc_in_standalone_mode ())
283+ if (uv_config .python )
284+ {
285+ chsrc_note2 ("uv Python:" );
286+ say (xy_2strcat (ENGLISH ? "Config Path: " : "文件路径:" , uv_config .python_path ));
287+ say (uv_config .python_url );
288+ }
289+ else
157290 {
158- chsrc_determine_chgtype (ChgType_Auto );
159- chsrc_conclude (& source );
291+ char * msg = ENGLISH ? "Can't find uv config for Python, show default."
292+ : "未找到 uv 关于 Python 的配置文件,显示默认值" ;
293+ chsrc_note2 (msg );
294+ say ("https://github.com/astral-sh/python-build-standalone/releases/download" );
160295 }
161296}
162297
163298
299+ /**
300+ * @consult https://docs.astral.sh/uv/configuration/files/
301+ * https://github.com/RubyMetric/chsrc/issues/139
302+ */
303+ void
304+ pl_python_uv_setsrc (char * option )
305+ {
306+ chsrc_ensure_program ("uv" );
307+
308+
309+ }
310+
311+
164312void
165313pl_python_uv_resetsrc (char * option )
166314{
0 commit comments