modify: nuitka 支持
This commit is contained in:
+165
-160
@@ -10,9 +10,19 @@ from module_bank import PythonToSQLite
|
||||
from module_bank.encryption import Encryption
|
||||
from sqlite_vfs.core import SQLiteVFS
|
||||
from sqlite_vfs.folder_packer import FolderPacker
|
||||
from ..app.core.config import get_config
|
||||
|
||||
|
||||
PYINSTALLER = "pyinstaller"
|
||||
NUITKA = "nuitka"
|
||||
|
||||
is_windows = platform.system() == "Windows"
|
||||
build_mode = PYINSTALLER
|
||||
if len(sys.argv) == 2:
|
||||
build_mode = sys.argv[1]
|
||||
|
||||
if build_mode not in [PYINSTALLER, NUITKA]:
|
||||
raise ValueError(f"不支持{build_mode}打包模式")
|
||||
|
||||
|
||||
def build_vue_web():
|
||||
@@ -112,6 +122,95 @@ def run_pyarmor_obfuscate():
|
||||
print(result.stdout)
|
||||
print(f"打包文件: {file}")
|
||||
|
||||
def run_nuitka():
|
||||
"""
|
||||
兼容旧版 Nuitka 的打包函数(不使用任何可能不支持的参数)
|
||||
编译后手动 strip + upx 压缩
|
||||
"""
|
||||
import shutil
|
||||
|
||||
config = get_config()
|
||||
build_import_base = config \
|
||||
.get("pyinstaller_build_import", {}) \
|
||||
.get("base", [])
|
||||
linux_extra = config \
|
||||
.get("pyinstaller_build_import", {}) \
|
||||
.get("linux", [])
|
||||
windows_extra = config \
|
||||
.get("pyinstaller_build_import", {}) \
|
||||
.get("windows", [])
|
||||
|
||||
all_imports = list(build_import_base)
|
||||
if is_windows:
|
||||
all_imports.extend(windows_extra)
|
||||
else:
|
||||
all_imports.extend(linux_extra)
|
||||
|
||||
packages = [
|
||||
"module_bank", "sqlite_vfs", "fastapi", "uvicorn", "gunicorn",
|
||||
"diskcache", "bcrypt", "apscheduler", "aiofiles", "aiosqlite",
|
||||
"sqlmodel", "jwt", "python_multipart", "tomli", "typing_extensions"
|
||||
]
|
||||
|
||||
# 最基础的参数(兼容所有 Nuitka 版本)
|
||||
cmd = [
|
||||
sys.executable, "-m", "nuitka",
|
||||
"--standalone",
|
||||
"--onefile",
|
||||
"--follow-imports",
|
||||
"--static-libpython=no",
|
||||
"--remove-output",
|
||||
"--assume-yes-for-downloads",
|
||||
]
|
||||
|
||||
# 尝试添加 LTO(如果不支持,会在此阶段失败,我们在下方会用 try/except 回退)
|
||||
cmd.append("--lto=yes")
|
||||
|
||||
# 尝试启用 UPX 插件(旧版可能不支持)
|
||||
if shutil.which("upx"):
|
||||
cmd.append("--enable-plugin=upx")
|
||||
|
||||
for pkg in packages:
|
||||
cmd.append(f"--include-package={pkg}")
|
||||
|
||||
for mod in all_imports:
|
||||
cmd.append(f"--include-module={mod}")
|
||||
|
||||
cmd.append("--disable-plugin=tk-inter")
|
||||
cmd.append("--disable-plugin=numpy")
|
||||
cmd += ["--output-dir=.", "--output-filename=chongming", "main.py"]
|
||||
cmd.append("--jobs=2")
|
||||
|
||||
if is_windows:
|
||||
icon_path = Path("../../public/chongming.ico")
|
||||
if icon_path.exists():
|
||||
cmd.append(f"--windows-icon-from-ico={icon_path}")
|
||||
|
||||
print("正在运行 Nuitka 编译...")
|
||||
try:
|
||||
subprocess.run(cmd, cwd="./build", check=True, capture_output=False, text=True)
|
||||
except subprocess.CalledProcessError:
|
||||
# 如果因为 --lto=yes 或 --enable-plugin=upx 失败,则用更基础的命令重试
|
||||
print("检测到参数不支持,移除 --lto=yes 和 --enable-plugin=upx 重新编译...")
|
||||
fallback_cmd = [arg for arg in cmd if arg not in ("--lto=yes", "--enable-plugin=upx")]
|
||||
subprocess.run(fallback_cmd, cwd="./build", check=True, capture_output=False, text=True)
|
||||
|
||||
print("Nuitka 编译完成!开始后处理瘦身...")
|
||||
|
||||
exe_path = Path("./build/chongming")
|
||||
if not is_windows and exe_path.exists():
|
||||
# 手动 strip 去掉符号表
|
||||
subprocess.run(["strip", str(exe_path)], check=False)
|
||||
print("已执行 strip,去除调试符号")
|
||||
if shutil.which("upx") and exe_path.exists():
|
||||
# 手动 UPX 极限压缩
|
||||
subprocess.run(["upx", "--best", str(exe_path)], check=False)
|
||||
print("已执行 UPX 压缩")
|
||||
|
||||
# 清理临时入口文件
|
||||
shutil.rmtree("build/utils", ignore_errors=True)
|
||||
os.remove("build/main.py")
|
||||
os.remove("build/server.py")
|
||||
|
||||
def run_pyinstaller():
|
||||
"""
|
||||
@@ -126,141 +225,50 @@ def run_pyinstaller():
|
||||
Returns:
|
||||
None: 无返回值,通过打印信息反馈执行结果
|
||||
"""
|
||||
config = get_config()
|
||||
try:
|
||||
# 构建 PyInstaller 命令
|
||||
build_import_base = config \
|
||||
.get("pyinstaller_build_import", {}) \
|
||||
.get("base", [])
|
||||
cmd = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"PyInstaller",
|
||||
"--onefile",
|
||||
"--noconfirm",
|
||||
"--optimize",
|
||||
"2",
|
||||
"--strip",
|
||||
"--name",
|
||||
"chongming",
|
||||
"--distpath",
|
||||
".",
|
||||
"--workpath",
|
||||
"./build_temp",
|
||||
"--specpath",
|
||||
"./specs",
|
||||
"--clean",
|
||||
# 其他选项
|
||||
"--icon",
|
||||
"../../public/chongming.ico",
|
||||
"main.py",
|
||||
]
|
||||
if is_windows:
|
||||
cmd = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"PyInstaller",
|
||||
"--onefile",
|
||||
"--noconfirm",
|
||||
"--optimize",
|
||||
"2",
|
||||
"--strip",
|
||||
"--name",
|
||||
"chongming",
|
||||
"--distpath",
|
||||
".",
|
||||
"--workpath",
|
||||
"./build_temp",
|
||||
"--specpath",
|
||||
"./specs",
|
||||
"--clean",
|
||||
# 隐藏导入
|
||||
"--hidden-import",
|
||||
"utils",
|
||||
"--hidden-import",
|
||||
"utils.launch",
|
||||
"--hidden-import",
|
||||
"utils.config",
|
||||
"--hidden-import",
|
||||
"server",
|
||||
"--hidden-import",
|
||||
"module_bank",
|
||||
"--hidden-import",
|
||||
"fastapi",
|
||||
"--hidden-import",
|
||||
"fastapi.staticfiles",
|
||||
"--hidden-import",
|
||||
"fastapi.middleware.cors",
|
||||
"--hidden-import",
|
||||
"uvicorn",
|
||||
"--hidden-import",
|
||||
"sqlmodel",
|
||||
"--hidden-import",
|
||||
"aiosqlite",
|
||||
"--hidden-import",
|
||||
"aiofiles",
|
||||
"--hidden-import",
|
||||
"apscheduler",
|
||||
"--hidden-import",
|
||||
"diskcache",
|
||||
"--hidden-import",
|
||||
"bcrypt",
|
||||
"--hidden-import",
|
||||
"sqlite_vfs",
|
||||
"--hidden-import",
|
||||
"python_multipart",
|
||||
"--hidden-import",
|
||||
"sqlite_vfs.core",
|
||||
"--hidden-import",
|
||||
"jwt",
|
||||
# 其他选项
|
||||
"--icon",
|
||||
"../../public/chongming.ico",
|
||||
"main.py",
|
||||
]
|
||||
windows_build_import = config \
|
||||
.get("pyinstaller_build_import", {}) \
|
||||
.get("windows", []) + build_import_base
|
||||
|
||||
for build_import in windows_build_import:
|
||||
cmd.append("--hidden-import")
|
||||
cmd.append(build_import)
|
||||
else:
|
||||
cmd = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"PyInstaller",
|
||||
"--onefile",
|
||||
"--noconfirm",
|
||||
"--optimize",
|
||||
"2",
|
||||
"--strip",
|
||||
"--name",
|
||||
"chongming",
|
||||
"--distpath",
|
||||
".",
|
||||
"--workpath",
|
||||
"./build_temp",
|
||||
"--specpath",
|
||||
"./specs",
|
||||
"--clean",
|
||||
# 隐藏导入
|
||||
"--hidden-import",
|
||||
"utils",
|
||||
"--hidden-import",
|
||||
"utils.launch",
|
||||
"--hidden-import",
|
||||
"utils.config",
|
||||
"--hidden-import",
|
||||
"server",
|
||||
"--hidden-import",
|
||||
"module_bank",
|
||||
"--hidden-import",
|
||||
"fastapi",
|
||||
"--hidden-import",
|
||||
"fastapi.staticfiles",
|
||||
"--hidden-import",
|
||||
"fastapi.middleware.cors",
|
||||
"--hidden-import",
|
||||
"uvicorn",
|
||||
"--hidden-import",
|
||||
"sqlmodel",
|
||||
"--hidden-import",
|
||||
"aiosqlite",
|
||||
"--hidden-import",
|
||||
"aiofiles",
|
||||
"--hidden-import",
|
||||
"apscheduler",
|
||||
"--hidden-import",
|
||||
"diskcache",
|
||||
"--hidden-import",
|
||||
"bcrypt",
|
||||
"--hidden-import",
|
||||
"sqlite_vfs",
|
||||
"--hidden-import",
|
||||
"python_multipart",
|
||||
"--hidden-import",
|
||||
"sqlite_vfs.core",
|
||||
"--hidden-import",
|
||||
"jwt",
|
||||
# gunicorn
|
||||
"--hidden-import",
|
||||
"gunicorn.glogging",
|
||||
"--hidden-import",
|
||||
"gunicorn.app",
|
||||
"--hidden-import",
|
||||
"gunicorn.app.wsgiapp",
|
||||
# 其他选项
|
||||
"--icon",
|
||||
"../../public/chongming.ico",
|
||||
"main.py",
|
||||
]
|
||||
linux_build_import = config \
|
||||
.get("pyinstaller_build_import", {}) \
|
||||
.get("linux", []) + build_import_base
|
||||
for build_import in linux_build_import:
|
||||
cmd.append("--hidden-import")
|
||||
cmd.append(build_import)
|
||||
|
||||
print(f"正在运行 PyInstaller: {' '.join(cmd)}")
|
||||
result = subprocess.run(cmd, cwd="./build", capture_output=True, text=True)
|
||||
@@ -301,7 +309,6 @@ async def init_database():
|
||||
from sqlalchemy.exc import OperationalError
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
|
||||
|
||||
from ..app.core.config import get_config
|
||||
from ..app.task.dev_init_db import init_permission, init_default_roles, dev_init_admin
|
||||
|
||||
database = get_config()["database"]
|
||||
@@ -358,19 +365,18 @@ async def main():
|
||||
None: 无返回值
|
||||
"""
|
||||
# 打包模块
|
||||
key = Encryption.generate_key()
|
||||
resources_path = Path("resources")
|
||||
if not resources_path.exists():
|
||||
resources_path.mkdir()
|
||||
app_packer = PythonToSQLite("./resources/app.mbank", key)
|
||||
app_packer.pack_directory("src/chongming/app", "app")
|
||||
app_packer.verify_package_structure()
|
||||
app_packer.delete_source_code(None)
|
||||
|
||||
plugin_packer = PythonToSQLite("./resources/plugins.mbank", key)
|
||||
plugin_packer.pack_directory("src/plugins", "plugins")
|
||||
plugin_packer.verify_package_structure()
|
||||
plugin_packer.delete_source_code(None)
|
||||
mbank_list = get_config()["production"].get("module_system", {}).get("path", [])
|
||||
for mbank in mbank_list:
|
||||
key = Encryption.generate_key()
|
||||
app_packer = PythonToSQLite(mbank[0], key)
|
||||
app_packer.pack_directory(mbank[1], mbank[2])
|
||||
app_packer.verify_package_structure()
|
||||
app_packer.packer_key(key)
|
||||
app_packer.delete_source_code(None)
|
||||
|
||||
# 创建 build 目录
|
||||
build_path = Path("build")
|
||||
@@ -403,12 +409,6 @@ async def main():
|
||||
# 复制 utils 文件夹到 build 文件夹
|
||||
shutil.copytree("public/utils", "build/utils")
|
||||
|
||||
with open("./build/utils/launch.py", "r", encoding="utf-8") as f:
|
||||
context = f.read()
|
||||
context = context.replace("key = None", f"key = '{key}'")
|
||||
with open("./build/utils/launch.py", "w", encoding="utf-8") as f:
|
||||
f.write(context)
|
||||
|
||||
jwt_key = Encryption.generate_key()
|
||||
with open("./build/config.toml", "r", encoding="utf-8") as f:
|
||||
context = f.read()
|
||||
@@ -416,19 +416,24 @@ async def main():
|
||||
with open("build/config.toml", "w", encoding="utf-8") as f:
|
||||
f.write(context)
|
||||
|
||||
# 混淆
|
||||
run_pyarmor_obfuscate()
|
||||
|
||||
# 运行 PyInstaller
|
||||
run_pyinstaller()
|
||||
|
||||
# 清理临时文件
|
||||
shutil.rmtree(r"build/build_temp", ignore_errors=True)
|
||||
shutil.rmtree(r"build/specs", ignore_errors=True)
|
||||
shutil.rmtree(r"build/utils", ignore_errors=True)
|
||||
shutil.rmtree(r"build/pyarmor_runtime_000000", ignore_errors=True)
|
||||
os.remove(r"build/main.py")
|
||||
os.remove(r"build/server.py")
|
||||
if build_mode == PYINSTALLER:
|
||||
# 混淆
|
||||
run_pyarmor_obfuscate()
|
||||
# 运行 PyInstaller
|
||||
run_pyinstaller()
|
||||
# 清理临时文件
|
||||
shutil.rmtree(r"build/build_temp", ignore_errors=True)
|
||||
shutil.rmtree(r"build/specs", ignore_errors=True)
|
||||
shutil.rmtree(r"build/utils", ignore_errors=True)
|
||||
shutil.rmtree(r"build/pyarmor_runtime_000000", ignore_errors=True)
|
||||
os.remove(r"build/main.py")
|
||||
os.remove(r"build/server.py")
|
||||
elif build_mode == NUITKA:
|
||||
run_nuitka()
|
||||
# Nuitka 编译后清理临时文件
|
||||
shutil.rmtree(r"build/utils", ignore_errors=True)
|
||||
# os.remove(r"build/main.py")
|
||||
# os.remove(r"build/server.py")
|
||||
|
||||
print("构建完成")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user