449 lines
14 KiB
Python
449 lines
14 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
优化的EXE加密工具构建脚本
|
||
支持多种构建方式,自动处理依赖和错误恢复
|
||
"""
|
||
|
||
import os
|
||
import sys
|
||
import shutil
|
||
import subprocess
|
||
import platform
|
||
from pathlib import Path
|
||
import json
|
||
|
||
|
||
class BuildTool:
|
||
def __init__(self):
|
||
self.root_dir = Path.cwd()
|
||
self.dist_dir = self.root_dir / "dist"
|
||
self.build_dir = self.root_dir / "build"
|
||
self.temp_files = []
|
||
|
||
def cleanup(self):
|
||
"""清理临时文件和目录"""
|
||
print("🧹 清理临时文件...")
|
||
|
||
# 清理目录
|
||
dirs_to_clean = [self.build_dir, self.dist_dir]
|
||
for dir_path in dirs_to_clean:
|
||
if dir_path.exists():
|
||
try:
|
||
shutil.rmtree(dir_path)
|
||
print(f" 删除目录: {dir_path}")
|
||
except Exception as e:
|
||
print(f" 删除目录失败 {dir_path}: {e}")
|
||
|
||
# 清理临时文件
|
||
patterns = ["*.spec", "setup_*.py", "*.pyc", "*.pyo"]
|
||
for pattern in patterns:
|
||
for file_path in self.root_dir.glob(pattern):
|
||
try:
|
||
file_path.unlink()
|
||
print(f" 删除文件: {file_path}")
|
||
except Exception as e:
|
||
print(f" 删除文件失败 {file_path}: {e}")
|
||
|
||
# 清理__pycache__目录
|
||
for pycache_dir in self.root_dir.rglob("__pycache__"):
|
||
try:
|
||
shutil.rmtree(pycache_dir)
|
||
print(f" 删除缓存: {pycache_dir}")
|
||
except:
|
||
pass
|
||
|
||
def check_dependencies(self):
|
||
"""检查依赖包"""
|
||
print("📦 检查依赖包...")
|
||
|
||
required_packages = [
|
||
"mysql-connector-python",
|
||
"cryptography",
|
||
"pyperclip"
|
||
]
|
||
|
||
missing_packages = []
|
||
for package in required_packages:
|
||
try:
|
||
__import__(package.replace('-', '_'))
|
||
print(f" ✓ {package}")
|
||
except ImportError:
|
||
missing_packages.append(package)
|
||
print(f" ✗ {package}")
|
||
|
||
if missing_packages:
|
||
print(f"❌ 缺少依赖包: {', '.join(missing_packages)}")
|
||
print("正在安装缺少的依赖包...")
|
||
for package in missing_packages:
|
||
try:
|
||
subprocess.run([sys.executable, "-m", "pip", "install", package],
|
||
check=True, capture_output=True)
|
||
print(f" ✓ 安装成功: {package}")
|
||
except subprocess.CalledProcessError as e:
|
||
print(f" ✗ 安装失败: {package} - {e}")
|
||
return False
|
||
|
||
return True
|
||
|
||
def create_setup_file(self, script_name, exe_name, base_type="Console"):
|
||
"""创建setup文件"""
|
||
packages = [
|
||
"mysql.connector",
|
||
"cryptography",
|
||
"uuid",
|
||
"hashlib",
|
||
"datetime",
|
||
"platform",
|
||
"subprocess",
|
||
"json",
|
||
"tempfile",
|
||
"atexit",
|
||
"ctypes"
|
||
]
|
||
|
||
# 主程序需要额外的包
|
||
if script_name == "main.py":
|
||
packages.extend(["tkinter", "pyperclip"])
|
||
|
||
setup_content = f'''#!/usr/bin/env python3
|
||
from cx_Freeze import setup, Executable
|
||
import sys
|
||
import os
|
||
|
||
# 构建选项
|
||
build_exe_options = {{
|
||
"packages": {packages},
|
||
"include_files": [],
|
||
"excludes": ["unittest", "test", "distutils", "email"],
|
||
"build_exe": "dist_{exe_name.replace('.exe', '')}",
|
||
"optimize": 0,
|
||
"include_msvcrt": True
|
||
}}
|
||
|
||
# 如果存在配置文件,包含它
|
||
if os.path.exists("db_config.json"):
|
||
build_exe_options["include_files"].append("db_config.json")
|
||
|
||
# 可执行文件配置
|
||
executables = [
|
||
Executable(
|
||
"{script_name}",
|
||
base="{base_type}" if sys.platform == "win32" else None,
|
||
target_name="{exe_name}",
|
||
icon=None
|
||
)
|
||
]
|
||
|
||
setup(
|
||
name="{exe_name.replace('.exe', '')}",
|
||
version="2.0",
|
||
description="EXE Encryption Tool Component",
|
||
options={{"build_exe": build_exe_options}},
|
||
executables=executables
|
||
)
|
||
'''
|
||
return setup_content
|
||
|
||
def build_with_cx_freeze(self):
|
||
"""使用cx_Freeze构建"""
|
||
print("🏗️ 使用cx_Freeze构建...")
|
||
|
||
# 检查cx_Freeze
|
||
try:
|
||
import cx_Freeze
|
||
print(f" cx_Freeze版本: {cx_Freeze.version}")
|
||
except ImportError:
|
||
print(" 安装cx_Freeze...")
|
||
try:
|
||
subprocess.run([sys.executable, "-m", "pip", "install", "cx_Freeze"], check=True)
|
||
except subprocess.CalledProcessError:
|
||
print(" ❌ cx_Freeze安装失败")
|
||
return False
|
||
|
||
# 构建配置
|
||
builds = [
|
||
("validator.py", "validator.exe", "Console"),
|
||
("main.py", "EXE加密工具.exe", "Win32GUI")
|
||
]
|
||
|
||
success_count = 0
|
||
|
||
for script, exe_name, base_type in builds:
|
||
print(f" 构建 {script} -> {exe_name}")
|
||
|
||
# 创建setup文件
|
||
setup_filename = f"setup_{exe_name.replace('.exe', '').replace('EXE加密工具', 'main')}.py"
|
||
setup_content = self.create_setup_file(script, exe_name, base_type)
|
||
|
||
try:
|
||
with open(setup_filename, 'w', encoding='utf-8') as f:
|
||
f.write(setup_content)
|
||
|
||
self.temp_files.append(setup_filename)
|
||
|
||
# 执行构建
|
||
result = subprocess.run(
|
||
[sys.executable, setup_filename, "build"],
|
||
capture_output=True,
|
||
text=True,
|
||
cwd=self.root_dir
|
||
)
|
||
|
||
if result.returncode == 0:
|
||
print(f" ✓ {exe_name} 构建成功")
|
||
success_count += 1
|
||
else:
|
||
print(f" ✗ {exe_name} 构建失败:")
|
||
print(f" 错误: {result.stderr}")
|
||
|
||
except Exception as e:
|
||
print(f" ✗ {exe_name} 构建异常: {e}")
|
||
|
||
return success_count == len(builds)
|
||
|
||
def build_with_pyinstaller(self):
|
||
"""使用PyInstaller构建"""
|
||
print("🏗️ 使用PyInstaller构建...")
|
||
|
||
# 检查PyInstaller
|
||
try:
|
||
import PyInstaller
|
||
except ImportError:
|
||
print(" 安装PyInstaller...")
|
||
try:
|
||
subprocess.run([sys.executable, "-m", "pip", "install", "pyinstaller"], check=True)
|
||
except subprocess.CalledProcessError:
|
||
print(" ❌ PyInstaller安装失败")
|
||
return False
|
||
|
||
# 构建命令
|
||
commands = [
|
||
[
|
||
sys.executable, "-m", "PyInstaller",
|
||
"--onefile",
|
||
"--console",
|
||
"--name=validator",
|
||
"validator.py"
|
||
],
|
||
[
|
||
sys.executable, "-m", "PyInstaller",
|
||
"--onefile",
|
||
"--windowed",
|
||
"--name=EXE加密工具",
|
||
"main.py"
|
||
]
|
||
]
|
||
|
||
success_count = 0
|
||
|
||
for cmd in commands:
|
||
print(f" 执行: {' '.join(cmd)}")
|
||
try:
|
||
result = subprocess.run(cmd, capture_output=True, text=True, cwd=self.root_dir)
|
||
if result.returncode == 0:
|
||
print(f" ✓ 构建成功")
|
||
success_count += 1
|
||
else:
|
||
print(f" ✗ 构建失败: {result.stderr}")
|
||
except Exception as e:
|
||
print(f" ✗ 构建异常: {e}")
|
||
|
||
return success_count == len(commands)
|
||
|
||
def organize_cx_freeze_output(self):
|
||
"""整理cx_Freeze输出"""
|
||
print("📁 整理cx_Freeze构建输出...")
|
||
|
||
# 创建最终输出目录
|
||
final_dir = self.root_dir / "dist_final"
|
||
final_dir.mkdir(exist_ok=True)
|
||
|
||
# 查找所有构建目录
|
||
dist_dirs = [d for d in self.root_dir.iterdir()
|
||
if d.is_dir() and d.name.startswith("dist_")]
|
||
|
||
if not dist_dirs:
|
||
print(" ❌ 没有找到构建输出目录")
|
||
return False
|
||
|
||
# 收集所有文件
|
||
all_files = {}
|
||
for dist_dir in dist_dirs:
|
||
print(f" 处理目录: {dist_dir}")
|
||
for file_path in dist_dir.rglob("*"):
|
||
if file_path.is_file():
|
||
# 避免重复的DLL文件,保留EXE文件
|
||
if file_path.name in all_files and not file_path.suffix == '.exe':
|
||
continue
|
||
all_files[file_path.name] = file_path
|
||
|
||
# 复制文件到最终目录
|
||
for filename, filepath in all_files.items():
|
||
dest_path = final_dir / filename
|
||
try:
|
||
shutil.copy2(filepath, dest_path)
|
||
print(f" 复制: {filename}")
|
||
except Exception as e:
|
||
print(f" 复制失败 {filename}: {e}")
|
||
|
||
# 清理临时构建目录
|
||
for dist_dir in dist_dirs:
|
||
try:
|
||
shutil.rmtree(dist_dir)
|
||
except:
|
||
pass
|
||
|
||
print(f" ✓ 文件已整理到: {final_dir}")
|
||
return True
|
||
|
||
def organize_pyinstaller_output(self):
|
||
"""整理PyInstaller输出"""
|
||
print("📁 整理PyInstaller构建输出...")
|
||
|
||
dist_dir = self.root_dir / "dist"
|
||
if not dist_dir.exists():
|
||
print(" ❌ dist目录不存在")
|
||
return False
|
||
|
||
exe_files = list(dist_dir.glob("*.exe"))
|
||
if not exe_files:
|
||
print(" ❌ 没有找到可执行文件")
|
||
return False
|
||
|
||
print(f" ✓ 找到 {len(exe_files)} 个可执行文件")
|
||
for exe_file in exe_files:
|
||
print(f" {exe_file.name}")
|
||
|
||
return True
|
||
|
||
def cleanup_temp_files(self):
|
||
"""清理临时文件"""
|
||
for temp_file in self.temp_files:
|
||
try:
|
||
if os.path.exists(temp_file):
|
||
os.remove(temp_file)
|
||
except:
|
||
pass
|
||
|
||
def validate_build_output(self):
|
||
"""验证构建输出"""
|
||
print("✅ 验证构建结果...")
|
||
|
||
# 查找可执行文件
|
||
exe_files = []
|
||
|
||
# 检查cx_Freeze输出
|
||
final_dir = self.root_dir / "dist_final"
|
||
if final_dir.exists():
|
||
exe_files.extend(final_dir.glob("*.exe"))
|
||
|
||
# 检查PyInstaller输出
|
||
if not exe_files:
|
||
dist_dir = self.root_dir / "dist"
|
||
if dist_dir.exists():
|
||
exe_files.extend(dist_dir.glob("*.exe"))
|
||
|
||
if not exe_files:
|
||
print(" ❌ 没有找到可执行文件")
|
||
return False
|
||
|
||
required_exes = ["validator.exe", "EXE加密工具.exe"]
|
||
found_exes = [exe.name for exe in exe_files]
|
||
|
||
success = True
|
||
for required in required_exes:
|
||
if required in found_exes:
|
||
print(f" ✓ {required}")
|
||
else:
|
||
print(f" ✗ {required} - 未找到")
|
||
success = False
|
||
|
||
if success:
|
||
print(f" ✓ 构建验证通过,共 {len(exe_files)} 个文件")
|
||
for exe_file in exe_files:
|
||
size_mb = exe_file.stat().st_size / (1024 * 1024)
|
||
print(f" {exe_file.name}: {size_mb:.1f} MB")
|
||
|
||
return success
|
||
|
||
def build(self):
|
||
"""主构建流程"""
|
||
print("🚀 开始构建EXE加密工具...")
|
||
print(f"Python版本: {sys.version}")
|
||
print(f"操作系统: {platform.system()} {platform.release()}")
|
||
print(f"工作目录: {self.root_dir}")
|
||
|
||
try:
|
||
# 1. 清理环境
|
||
self.cleanup()
|
||
|
||
# 2. 检查依赖
|
||
if not self.check_dependencies():
|
||
return False
|
||
|
||
# 3. 尝试cx_Freeze构建
|
||
cx_success = self.build_with_cx_freeze()
|
||
if cx_success:
|
||
if self.organize_cx_freeze_output():
|
||
if self.validate_build_output():
|
||
print("🎉 cx_Freeze构建成功!")
|
||
return True
|
||
|
||
print("⚠️ cx_Freeze构建失败,尝试PyInstaller...")
|
||
self.cleanup()
|
||
|
||
# 4. 尝试PyInstaller构建
|
||
py_success = self.build_with_pyinstaller()
|
||
if py_success:
|
||
if self.organize_pyinstaller_output():
|
||
if self.validate_build_output():
|
||
print("🎉 PyInstaller构建成功!")
|
||
return True
|
||
|
||
print("❌ 所有构建方法都失败了")
|
||
return False
|
||
|
||
except KeyboardInterrupt:
|
||
print("\n⏹️ 构建被用户中断")
|
||
return False
|
||
except Exception as e:
|
||
print(f"❌ 构建过程出现异常: {e}")
|
||
return False
|
||
finally:
|
||
# 清理临时文件
|
||
self.cleanup_temp_files()
|
||
|
||
def show_usage_info(self):
|
||
"""显示使用说明"""
|
||
print("\n📖 使用说明:")
|
||
print("1. 确保所有Python文件在同一目录")
|
||
print("2. 运行此构建脚本")
|
||
print("3. 构建完成后,可执行文件将在dist或dist_final目录中")
|
||
print("4. validator.exe是验证程序,用于加密过程")
|
||
print("5. EXE加密工具.exe是主程序界面")
|
||
print("\n⚠️ 注意事项:")
|
||
print("- 确保有稳定的网络连接以下载依赖包")
|
||
print("- 首次构建可能需要较长时间")
|
||
print("- 如果遇到权限问题,请以管理员身份运行")
|
||
|
||
|
||
def main():
|
||
builder = BuildTool()
|
||
|
||
if len(sys.argv) > 1 and sys.argv[1] == "--help":
|
||
builder.show_usage_info()
|
||
return
|
||
|
||
success = builder.build()
|
||
|
||
if success:
|
||
print("\n🎊 构建完成!")
|
||
builder.show_usage_info()
|
||
else:
|
||
print("\n💥 构建失败!")
|
||
print("请检查错误信息并解决相关问题")
|
||
sys.exit(1)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main() |