88import uuid
99import shutil
1010
11+ # 自定义字典类,用于支持默认值为 None 的键
12+ class MyDict (dict ):
13+ def get (self , key , default = None ):
14+ # 如果键存在且值为 None,返回默认值
15+ if key in self and self [key ] is None :
16+ return default
17+ # 其他情况调用原生 get 方法
18+ return super ().get (key , default )
19+
1120def normalize_path (path_str ):
1221 """将路径字符串中的反斜杠转换为当前系统的分隔符"""
1322 return Path (path_str .replace ('\\ ' , os .sep ))
1423
24+ def get_dir_name (directory ):
25+ """获取目录名称"""
26+ # 处理根目录和路径结尾带分隔符的情况(如 "dist/")
27+ base = os .path .basename (directory )
28+ if not base : # 当路径以分隔符结尾时(如 "dist/")
29+ return os .path .basename (os .path .dirname (directory ))
30+ return base
31+
1532def delete_folders (directory ):
16- """删除指定目录下的所有文件夹,但保留文件"""
17- if os .path .basename (directory ) == '' or os .path .basename (directory ):
18- dir_name = os .path .basename (os .path .dirname (directory ))
19- else :
20- dir_name = os .path .basename (directory )
33+ """directory is str,删除指定目录下的所有文件夹,但保留文件"""
34+ dir_name = get_dir_name (directory )
2135 print (f'清理文件夹({ dir_name } )下的文件夹' )
2236 try :
2337 # 确保目录存在
2438 if not os .path .exists (directory ):
2539 print (f"目录不存在: { directory } " )
2640 return True
2741
42+ deleted_dirs = [] # 新增:用于记录删除的目录
2843 # 遍历目录中的所有项
2944 for item in os .listdir (directory ):
3045 item_path = os .path .join (directory , item )
3146 # 如果是文件夹,则删除
3247 if os .path .isdir (item_path ):
33- # print(f"删除文件夹: {item_path}")
34- rm_item_dir = ',' .join (item_path )
48+ deleted_dirs .append (get_dir_name (item_path )) # 记录被删除的路径
3549 shutil .rmtree (item_path )
36- # else:
37- # print(f"保留文件: {item_path}")
38- print (f'清理文件夹({ dir_name } )下的文件夹完成: { rm_item_dir } ' )
50+
51+ # 修改打印语句
52+ if deleted_dirs :
53+ # print(f'清理文件夹({dir_name})下的文件夹完成: {", ".join(deleted_dirs)}')
54+ print (f'清理文件夹({ dir_name } )下的文件夹完成: { deleted_dirs } ' )
55+ else :
56+ print (f'未发现需要清理的文件夹({ dir_name } )' )
3957 print ("操作完成" )
4058 return True
4159 except Exception as e :
@@ -89,6 +107,7 @@ def main():
89107
90108 # 遍历所有打包任务
91109 for i , task in enumerate (config , start = 1 ):
110+ task = MyDict (task )
92111 try :
93112 print (f"\n { '=' * 40 } " )
94113 print (f"开始打包任务: [{ i } /{ len (config )} ] { task ['name' ]} " )
@@ -101,17 +120,29 @@ def main():
101120 enable_plugins = task .get ('enable-plugins' , [])
102121
103122 icon = task .get ('icon' )
104- windows_disable_console = task .get ('windows-disable-console' , False )
105123 name = task .get ('name' )
106124 version = task .get ('version' )
107125 timeout = task .get ('timeout' , 60 * 45 )
126+
127+ # c-compiler
128+ # c_compiler_clang = task.get('c-compiler', {}).get('clang', False)
129+ # c_compiler_mingw64 = task.get('c-compiler', {}).get('mingw64', False)
130+ c_compiler = MyDict (task .get ('c-compiler' , {}))
131+ c_compiler_lto = c_compiler .get ('lto' , 'auto' )
132+ c_compiler_static_libpython = c_compiler .get ('static-libpython' , 'auto' )
108133
134+ # windows-specific-controls
135+ windows_specific_controls = MyDict (task .get ('windows-specific-controls' , {}))
136+ windows_console_mode = windows_specific_controls .get ('console-mode' )
137+ windows_icon = windows_specific_controls .get ('icon' )
138+ windows_uac_admin = windows_specific_controls .get ('uac-admin' ,False )
139+
109140 # 处理输出文件名模板
110141 output_name_template = task .get ('output-name-template' , '{{name}}_{{version}}_nuitka_{{os}}_{{arch}}{{exe_suffix}}' )
111142
112143 # 处理架构名称统一
113144 normalized_arch = "x64" if arch == "AMD64" else arch
114- exe_suffix = '.exe' if system_os == 'Windows' else ''
145+ exe_suffix = '.exe' if system_os == 'Windows' else '.bin '
115146
116147 output_name = output_name_template .replace ('{{name}}' , name ) \
117148 .replace ('{{version}}' , version ) \
@@ -122,14 +153,13 @@ def main():
122153 custom_command = task .get ('custom-command' )
123154 only_linux_command = task .get ('only-linux-command' )
124155 only_windows_command = task .get ('only-windows-command' )
156+ clean_cache = task .get ('clean-cache' )
125157
126158 # 检查Python文件是否存在
127159 if not python_file .exists ():
128160 print (f"错误: Python文件不存在 { python_file } " )
129161 continue
130162
131- # 创建输出目录
132- dist_path .mkdir (parents = True , exist_ok = True )
133163
134164 # 安装依赖
135165 if requirements :
@@ -139,8 +169,6 @@ def main():
139169 # 构建Nuitka命令
140170 cmd = [
141171 sys .executable , '-m' , 'nuitka' ,
142- f'--output-filename={ output_name } ' ,
143- f'--output-dir={ dist_path } ' , # 输出目录
144172 '--onefile' , # 单文件
145173 '--standalone' ,
146174 f'--jobs={ os .cpu_count ()} ' , # 多线程
@@ -150,15 +178,16 @@ def main():
150178 # Windows特定参数
151179 if system_os == 'Windows' :
152180 cmd .append ('--mingw64' )
153- if windows_disable_console :
154- cmd .append ('--windows-disable-console' )
155-
156- if icon :
157- icon_path = base_dir / normalize_path (icon )
181+ if windows_console_mode in ['force' ,'disable' ,'attach' ,'hide' ]:
182+ cmd .append (f'--windows-console-mode={ windows_console_mode } ' )
183+ if windows_icon :
184+ icon_path = base_dir / normalize_path (windows_icon )
158185 if icon_path .exists ():
159186 cmd .append (f'--windows-icon-from-ico={ icon_path } ' )
160187 else :
161188 print (f"警告: 图标文件不存在 { icon_path } " )
189+ if windows_uac_admin :
190+ cmd .append ('--windows-uac-admin' )
162191 if only_windows_command :
163192 if isinstance (only_windows_command , str ):
164193 cmd .extend (only_windows_command .split ())
@@ -173,8 +202,26 @@ def main():
173202 cmd .append ('--clang' )
174203 else :
175204 # Linux/macOS 使用 clang
176- cmd . append ( '--clang' )
205+ pass
177206
207+ # c-compiler
208+ # if c_compiler_clang and c_compiler_mingw64:
209+ # print('不能同时选择 clang mingw64')
210+ # else:
211+ # if c_compiler_clang:
212+ # cmd.append('--clang')
213+ # if c_compiler_mingw64:
214+ # cmd.append('--mingw64')
215+ if c_compiler_lto in ['auto' ,'yes' ,'no' ]:
216+ cmd .append (f'--lto={ c_compiler_lto } ' )
217+ else :
218+ print (f"警告: 无效的lto选项 '{ c_compiler_lto } ',将忽略" )
219+ if c_compiler_static_libpython in ['auto' ,'yes' ,'no' ]:
220+ cmd .append (f'--static-libpython={ c_compiler_static_libpython } ' )
221+ else :
222+ print (f"警告: 无效的static-libpython选项 '{ c_compiler_static_libpython } ',将忽略" )
223+
224+
178225 # 启用插件
179226 if enable_plugins :
180227 plugin_list = ',' .join (enable_plugins )
@@ -198,9 +245,28 @@ def main():
198245 elif isinstance (custom_command , list ):
199246 cmd .extend (custom_command )
200247
248+ # 禁用缓存
249+ if clean_cache :
250+ if not clean_cache in ['all' ,'bytecode' ,'ccache' ,'compression' ,'dll-dependencies' ]:
251+ print (f"警告: 无效的clean-cache选项 '{ clean_cache } ',将忽略" )
252+ else :
253+ cmd .append (f'--clean-cache={ clean_cache } ' )
254+
201255 # 创建临时文件避免中文路径问题
202- temp_file = base_dir / f"{ uuid .uuid4 ().hex } .py"
256+ temp_uuid = uuid .uuid4 ().hex
257+ temp_file = base_dir / f"{ temp_uuid } .py"
258+ temp_output_path = base_dir / temp_uuid
259+
203260 shutil .copy (python_file , temp_file )
261+ # 创建目录
262+ dist_path .mkdir (parents = True , exist_ok = True )
263+ temp_output_path .mkdir (parents = True , exist_ok = True )
264+
265+ cmd .append (f'--output-filename={ output_name } ' )
266+ # 使用临时目录
267+ cmd .append (f'--output-dir={ temp_output_path } ' )
268+
269+ # 添加py文件
204270 cmd .append (str (temp_file ))
205271
206272 # 打印并执行命令
@@ -216,7 +282,8 @@ def main():
216282
217283 print (f"打包成功: { dist_path / output_name } " )
218284 success_count += 1
219-
285+ # 移动打包结果到dist目录
286+ shutil .move (temp_output_path / output_name , dist_path / output_name )
220287 except subprocess .TimeoutExpired :
221288 print (f"任务[{ i } /{ len (config )} { task ['name' ]} ] 执行超时 { timeout } 秒" )
222289 task_error_list .append (task ['name' ])
@@ -228,6 +295,8 @@ def main():
228295 # 确保临时文件被删除
229296 if temp_file .exists ():
230297 temp_file .unlink ()
298+ if temp_output_path .exists ():
299+ shutil .rmtree (temp_output_path )
231300
232301 except Exception as e :
233302 print (f"任务[{ i } /{ len (config )} { task ['name' ]} ]失败: { str (e )} " )
@@ -243,18 +312,18 @@ def main():
243312 files_list = []
244313
245314 # 遍历目录下的所有条目
246- for entry in os .listdir (str (base_dir / 'dist' )):
315+ for entry in os .listdir (str (dist_path )):
247316 # 拼接完整的文件路径
248- full_path = os .path .join (str (base_dir / 'dist' ), entry )
317+ full_path = os .path .join (str (dist_path ), entry )
249318 # 检查该路径是否为文件
250319 if os .path .isfile (full_path ):
251320 files_list .append (entry )
252321
253322 # 输出文件列表
254323 print (f'Dist: { str (files_list )} ' )
255324 # 清理文件夹
256- if delete_folders (dist_path ):
257- print (f"已清理dist目录下文件夹: { dist_path } " )
325+ # if delete_folders(dist_path):
326+ # print(f"已清理dist目录下文件夹: {dist_path}")
258327
259328 if task_error_list :
260329 print (f"失败的任务: { ', ' .join (task_error_list )} " )
0 commit comments