import os import shutil import sys import sysconfig from pathlib import Path from zipfile import ZipFile, ZIP_LZMA import cx_Freeze is_64bits = sys.maxsize > 2 ** 32 folder = "exe.{platform}-{version}".format(platform=sysconfig.get_platform(), version=sysconfig.get_python_version()) buildfolder = Path("build", folder) sbuildfolder = str(buildfolder) libfolder = Path(buildfolder, "lib") library = Path(libfolder, "library.zip") print("Outputting to: " + str(buildfolder)) build_resources = "exe_resources" compress = False holoviews = False from hashlib import sha3_512 import base64 base_packages = ["openpyxl", "asyncio", "nacl", "cffi", "PySide2.QtQuick", "passlib", "shiboken2"] if holoviews: excludes = ["OpenGL", "tkinter", "tcl", "mods", "matplotlib", "pygame", "2to3", "PIL", "cython", "zmq", "Cython"] packages = ["numpy", "packaging", "param", "holoviews", "bokeh", ] + base_packages else: excludes = ["OpenGL", "tkinter", "tcl", "mods", "matplotlib", "pygame", "2to3", "PIL", "cython", "zmq", "Cython", "holoviews", "bokeh", "numpy", "pandas"] packages = base_packages def _threaded_hash(filepath): hasher = sha3_512() hasher.update(open(filepath, "rb").read()) return base64.b85encode(hasher.digest()).decode() os.makedirs(buildfolder, exist_ok=True) with open(os.path.join(buildfolder, "qt.conf"), "wt") as f: f.write("""[Paths] Qml2Imports = lib/qml Data = lib ArchData = lib Translations = lib Libraries = lib LibraryExecutables = lib Binaries = lib Plugins = lib Imports = lib Settings = lib """) def manifest_creation(): hashes = {} manifestpath = os.path.join(buildfolder, "manifest.json") from concurrent.futures import ThreadPoolExecutor pool = ThreadPoolExecutor() for dirpath, dirnames, filenames in os.walk(buildfolder): for filename in filenames: path = os.path.join(dirpath, filename) hashes[os.path.relpath(path, start=buildfolder)] = pool.submit(_threaded_hash, path) import json manifest = {"buildtime": buildtime.isoformat(sep=" ", timespec="seconds")} manifest["hashes"] = {path: hash.result() for path, hash in hashes.items()} json.dump(manifest, open(manifestpath, "wt"), indent=4) print("Created Manifest") EXE = cx_Freeze.Executable( script="main.py", targetName="HonorarPlus" if sys.platform == "linux" else "HonorarPlus.exe", base="Win32GUI", icon="icon.ico" ) EXEDEBUG = cx_Freeze.Executable( script="main.py", targetName="HonorarPlus(DEBUG)" if sys.platform == "linux" else "HonorarPlus(DEBUG).exe", base=None, icon="icon.ico" ) EXE2 = cx_Freeze.Executable( script="ConShift.py", targetName="ConShifter" if sys.platform == "linux" else "ConShifter.exe", base="Win32GUI", icon="icon.ico" ) EXEANON = cx_Freeze.Executable( script="Anonymisator.py", targetName="Anonymisator" if sys.platform == "linux" else "Anonymisator.exe", base="Win32GUI", icon="icon.ico" ) EXEANON2 = cx_Freeze.Executable( script="AnonymisatorKeepAdmin.py", targetName="AnonymisatorKeepAdmin" if sys.platform == "linux" else "AnonymisatorKeepAdmin.exe", base="Win32GUI", icon="icon.ico" ) EXESTRIPPER = cx_Freeze.Executable( script="PatientID-Remover.py", targetName="PatientID-Remover" if sys.platform == "linux" else "PatientID-Remover.exe", base="Win32GUI", icon="icon.ico" ) EXEDAK = cx_Freeze.Executable( script="DAK.py", targetName="DAK-KKH" if sys.platform == "linux" else "DAK-KKH.exe", base="Win32GUI", icon="icon.ico" ) import datetime buildtime = datetime.datetime.now(datetime.timezone.utc) cx_Freeze.setup( name="HonorarPlus", version=f"{buildtime.year}.{buildtime.month}.{buildtime.day}.{buildtime.hour}", description="HonorarPlus", executables=[EXE, EXE2, EXEDEBUG, EXEANON, EXEANON2, EXESTRIPPER, EXEDAK], options={ "build_exe": { "excludes": excludes, "packages": packages, "includes": ("PySide2.QtNetwork", "PySide2.QtWebEngineCore", "PySide2.QtWebChannel", "PySide2.QtXml", "PySide2.QtPrintSupport"), "zip_include_packages": ["*"], "zip_exclude_packages": [], "include_files": [], "include_msvcr": True, "replace_paths": [("*", "")], "optimize": 2, "build_exe": buildfolder }, }, ) if sys.platform == "linux": ext = ".so" else: ext = ".pyd" def installfile(path: Path, base: Path = buildfolder): print('copying', path, '->', base) if path.is_dir(): base /= path.name if base.is_dir(): shutil.rmtree(base) shutil.copytree(path, base) elif path.is_file(): shutil.copy(path, base) else: print('Warning,', path, 'not found') extra_data = ["cache", "web"] plugins = ["imageformats", "platforms", "iconengines"] def get_PySide2_Resources(): import PySide2 pyside2 = os.path.dirname(PySide2.__file__) yield os.path.join(pyside2, "resources"), "lib" for plugin in plugins: yield os.path.join(pyside2, "plugins", plugin), "lib" yield os.path.join(pyside2, "qml"), "lib" yield os.path.join(pyside2, "QtWebEngineProcess.exe") for file in os.listdir(pyside2): if file.endswith(".dll"): yield os.path.join(pyside2, file) import shiboken2 shiboken2 = os.path.dirname(shiboken2.__file__) for file in os.listdir(shiboken2): if file.endswith((".dll", ".lib", ".pyd")) and file.startswith("shiboken"): yield os.path.join(shiboken2, file) ###Fix for DLL load failed python_folder = os.path.dirname(sys.executable) yield os.path.join(python_folder, "python3.dll") def clean_pyside2(): lib = buildfolder / "lib" for file in lib.iterdir(): if file.is_file() and (buildfolder / file.name).is_file(): print(f"cleaning {file}") file.unlink() # extra_data.extend(pyside2) for item in extra_data: installfile(Path(item)) for item in get_PySide2_Resources(): if type(item) == tuple: installfile(Path(item[0]), Path(os.path.join(buildfolder, *item[1:]))) else: installfile(Path(item), Path(os.path.join(buildfolder))) if not os.path.isdir(os.path.join(buildfolder, "in")): os.mkdir(os.path.join(buildfolder, "in")) undesirables = ("debug.log", "manifest.json") for filename in undesirables: path = os.path.join(buildfolder, filename) if os.path.isfile(path): os.remove(path) uisrcfolder = os.path.join("UI", "src") with ZipFile(os.path.join(buildfolder, "UI.zip"), "w", compression=ZIP_LZMA) as zip: for file in os.listdir(uisrcfolder): if file.endswith(".ui") or file.endswith(".qml"): zip.write(os.path.join(uisrcfolder, file), file) if holoviews: bokeh_templates = r"C:\Program Files\Python36\Lib\site-packages\bokeh\core\_templates" import zipfile with zipfile.ZipFile(library, "a") as lib: for file in os.listdir(bokeh_templates): lib.write(os.path.join(bokeh_templates, file), os.path.join("bokeh", "core", "_templates", file)) lib.write(r"C:\Program Files\Python36\Lib\site-packages\param\__init__.py", os.path.join("param", "__init__.py")) clean_pyside2() if sys.platform == "win32" and os.path.isfile("upx.exe") and compress: def filter_targets(target): if target.lower() in blacklist: return False if "api-ms-win-crt" in target: return False if target[-4:] in endings: return True endings = {".pyd", ".exe", ".dll"} blacklist = {"vcruntime140.dll", "qwindows.dll", "msvcp140.dll", "python3.dll"} strbuild = str(buildfolder) libbuild = os.path.join(strbuild, "lib") folders = {strbuild, libbuild} targets = "" for folder in folders: newtargets = filter(filter_targets, os.listdir(folder)) targets += " ".join((os.path.join(folder, target) for target in newtargets)) targets += " " command = "upx.exe " + targets print(command) os.system(command) manifest_creation()