267 lines
7.7 KiB
Python
267 lines
7.7 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
完全清理环境并重新构建的脚本
|
||
"""
|
||
|
||
import os
|
||
import sys
|
||
import shutil
|
||
import glob
|
||
import subprocess
|
||
from pathlib import Path
|
||
|
||
|
||
def deep_cleanup():
|
||
"""深度清理所有相关文件"""
|
||
print("开始深度清理...")
|
||
|
||
# 要清理的目录
|
||
dirs_to_clean = [
|
||
"build", "dist", "__pycache__",
|
||
".pytest_cache", "*.egg-info"
|
||
]
|
||
|
||
# 要清理的文件模式
|
||
files_to_clean = [
|
||
"*.exe", "*.spec", "*_encrypted*",
|
||
"setup_main.py", "setup_validator.py",
|
||
"*.pyc", "*.pyo"
|
||
]
|
||
|
||
# 清理目录
|
||
for dir_pattern in dirs_to_clean:
|
||
for dir_path in glob.glob(dir_pattern):
|
||
if os.path.exists(dir_path):
|
||
print(f"删除目录: {dir_path}")
|
||
try:
|
||
shutil.rmtree(dir_path)
|
||
except Exception as e:
|
||
print(f"删除 {dir_path} 失败: {e}")
|
||
|
||
# 清理文件
|
||
for file_pattern in files_to_clean:
|
||
for file_path in glob.glob(file_pattern):
|
||
if os.path.exists(file_path):
|
||
print(f"删除文件: {file_path}")
|
||
try:
|
||
os.remove(file_path)
|
||
except Exception as e:
|
||
print(f"删除 {file_path} 失败: {e}")
|
||
|
||
# 清理Python缓存
|
||
for root, dirs, files in os.walk("."):
|
||
for dir_name in dirs[:]:
|
||
if dir_name == "__pycache__":
|
||
pycache_path = os.path.join(root, dir_name)
|
||
print(f"删除缓存: {pycache_path}")
|
||
try:
|
||
shutil.rmtree(pycache_path)
|
||
dirs.remove(dir_name)
|
||
except Exception as e:
|
||
print(f"删除缓存失败: {e}")
|
||
|
||
print("清理完成")
|
||
|
||
|
||
def create_single_file_setup(script_name, output_name, base_type="Console"):
|
||
"""创建单文件构建的setup.py"""
|
||
setup_content = f'''
|
||
import sys
|
||
from cx_Freeze import setup, Executable
|
||
|
||
# 基础构建选项
|
||
build_exe_options = {{
|
||
"packages": [
|
||
"mysql.connector", "cryptography", "uuid", "hashlib",
|
||
"datetime", "platform", "subprocess", "json",
|
||
"tempfile", "atexit", "ctypes"
|
||
],
|
||
"build_exe": "dist_{output_name.replace('.exe', '')}",
|
||
"excludes": ["unittest", "test"]
|
||
}}
|
||
|
||
# 如果是主程序,添加tkinter和pyperclip
|
||
if "{script_name}" == "main.py":
|
||
build_exe_options["packages"].extend(["tkinter", "pyperclip"])
|
||
|
||
executables = [
|
||
Executable("{script_name}", base="{base_type}")
|
||
]
|
||
|
||
setup(
|
||
name="{output_name.replace('.exe', '')}",
|
||
version="1.0",
|
||
options={{"build_exe": build_exe_options}},
|
||
executables=executables
|
||
)
|
||
'''
|
||
return setup_content
|
||
|
||
|
||
def build_single_executable(script_name, output_name, base_type="Console"):
|
||
"""构建单个可执行文件"""
|
||
print(f"构建 {script_name} -> {output_name}")
|
||
|
||
# 创建临时setup文件
|
||
setup_filename = f"setup_{output_name.replace('.exe', '')}.py"
|
||
setup_content = create_single_file_setup(script_name, output_name, base_type)
|
||
|
||
try:
|
||
with open(setup_filename, "w", encoding="utf-8") as f:
|
||
f.write(setup_content)
|
||
|
||
# 运行构建
|
||
result = subprocess.run(
|
||
[sys.executable, setup_filename, "build"],
|
||
capture_output=True, text=True, cwd="."
|
||
)
|
||
|
||
success = result.returncode == 0
|
||
if success:
|
||
print(f"{output_name} 构建成功")
|
||
else:
|
||
print(f"{output_name} 构建失败:")
|
||
print(result.stderr)
|
||
|
||
return success
|
||
|
||
except Exception as e:
|
||
print(f"构建 {output_name} 时发生异常: {e}")
|
||
return False
|
||
finally:
|
||
# 清理临时文件
|
||
if os.path.exists(setup_filename):
|
||
try:
|
||
os.remove(setup_filename)
|
||
except:
|
||
pass
|
||
|
||
|
||
def organize_final_dist():
|
||
"""整理最终的发布目录"""
|
||
print("整理最终发布目录...")
|
||
|
||
final_dir = Path("dist_final")
|
||
final_dir.mkdir(exist_ok=True)
|
||
|
||
# 收集所有构建的文件
|
||
dist_dirs = [d for d in Path(".").iterdir() if d.is_dir() and d.name.startswith("dist_")]
|
||
|
||
all_files = {}
|
||
for dist_dir in dist_dirs:
|
||
for file_path in dist_dir.rglob("*"):
|
||
if file_path.is_file():
|
||
all_files[file_path.name] = file_path
|
||
|
||
# 复制文件到最终目录
|
||
for filename, filepath in all_files.items():
|
||
dest_path = final_dir / filename
|
||
if not dest_path.exists() or filename.endswith('.exe'):
|
||
try:
|
||
shutil.copy2(filepath, dest_path)
|
||
print(f"复制: {filename}")
|
||
except Exception as e:
|
||
print(f"复制 {filename} 失败: {e}")
|
||
|
||
# 重命名主程序
|
||
main_exe = final_dir / "main.exe"
|
||
target_exe = final_dir / "EXE加密工具.exe"
|
||
if main_exe.exists() and not target_exe.exists():
|
||
try:
|
||
main_exe.rename(target_exe)
|
||
print("主程序重命名为: EXE加密工具.exe")
|
||
except Exception as e:
|
||
print(f"重命名失败: {e}")
|
||
|
||
# 清理临时构建目录
|
||
for dist_dir in dist_dirs:
|
||
try:
|
||
shutil.rmtree(dist_dir)
|
||
except:
|
||
pass
|
||
|
||
print(f"最终文件已整理到: {final_dir.absolute()}")
|
||
return final_dir
|
||
|
||
|
||
def try_pyinstaller_fallback():
|
||
"""尝试使用PyInstaller作为备选方案"""
|
||
print("尝试使用PyInstaller作为备选方案...")
|
||
|
||
try:
|
||
# 检查PyInstaller是否可用
|
||
result = subprocess.run([sys.executable, "-c", "import PyInstaller"],
|
||
capture_output=True)
|
||
if result.returncode != 0:
|
||
print("PyInstaller未安装,正在安装...")
|
||
subprocess.run([sys.executable, "-m", "pip", "install", "pyinstaller"],
|
||
check=True)
|
||
|
||
# 使用PyInstaller构建
|
||
commands = [
|
||
[sys.executable, "-m", "PyInstaller", "--onefile", "--windowed",
|
||
"--name=EXE加密工具", "main.py"],
|
||
[sys.executable, "-m", "PyInstaller", "--onefile", "--console",
|
||
"validator.py"]
|
||
]
|
||
|
||
for cmd in commands:
|
||
print(f"运行: {' '.join(cmd)}")
|
||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||
if result.returncode != 0:
|
||
print(f"PyInstaller构建失败: {result.stderr}")
|
||
return False
|
||
|
||
print("PyInstaller构建成功!文件位于 dist/ 目录")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"PyInstaller构建异常: {e}")
|
||
return False
|
||
|
||
|
||
def main():
|
||
"""主函数"""
|
||
print("=== EXE加密工具构建脚本 ===")
|
||
|
||
# 1. 深度清理
|
||
deep_cleanup()
|
||
|
||
# 2. 尝试使用cx_Freeze分别构建
|
||
print("\n尝试使用cx_Freeze构建...")
|
||
|
||
builds = [
|
||
("main.py", "main.exe", "Win32GUI"),
|
||
("validator.py", "validator.exe", "Console")
|
||
]
|
||
|
||
all_success = True
|
||
for script, output, base in builds:
|
||
if not build_single_executable(script, output, base):
|
||
all_success = False
|
||
break
|
||
|
||
if all_success:
|
||
final_dir = organize_final_dist()
|
||
print(f"\n✅ cx_Freeze构建成功!")
|
||
print(f"📁 文件位置: {final_dir.absolute()}")
|
||
return True
|
||
|
||
# 3. 如果cx_Freeze失败,尝试PyInstaller
|
||
print("\n❌ cx_Freeze构建失败,尝试PyInstaller...")
|
||
deep_cleanup() # 再次清理
|
||
|
||
if try_pyinstaller_fallback():
|
||
print("\n✅ PyInstaller构建成功!")
|
||
print("📁 文件位置: dist/")
|
||
return True
|
||
|
||
print("\n❌ 所有构建方法都失败了")
|
||
print("建议手动检查依赖和环境配置")
|
||
return False
|
||
|
||
|
||
if __name__ == "__main__":
|
||
success = main()
|
||
if not success:
|
||
print("失败") |