mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-06-29 17:45:08 +08:00
274 lines
12 KiB
Django/Jinja
274 lines
12 KiB
Django/Jinja
# -*- mode: python ; coding: utf-8 -*-
|
|
import os
|
|
from pathlib import Path
|
|
from PyInstaller.utils.hooks import collect_all
|
|
|
|
|
|
datas = {{ datas }}
|
|
binaries = {{ binaries }}
|
|
|
|
hiddenimports = {{ hiddenimports }}
|
|
|
|
{% for value in collect_all %}tmp_ret = collect_all('{{ value }}')
|
|
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
|
|
{% endfor %}
|
|
|
|
# Add dynamic libs in the venv bin/Script Path. This is needed because we might copy some additional libs
|
|
# e.q.: OpenSSL 1.1.1l in that directory with a separate:
|
|
# `conan install openssl@1.1.1l -g deploy && cp openssl/bin/*.so cura_inst/bin`
|
|
binaries.extend([(str(bin), ".") for bin in Path(r"{{ venv_script_path }}").glob("*.so*")])
|
|
binaries.extend([(str(bin), ".") for bin in Path(r"{{ venv_script_path }}").glob("*.dll")])
|
|
binaries.extend([(str(bin), ".") for bin in Path(r"{{ venv_script_path }}").glob("*.dylib")])
|
|
|
|
block_cipher = None
|
|
|
|
a = Analysis(
|
|
[{{ entrypoint }}],
|
|
pathex=[],
|
|
binaries=binaries,
|
|
datas=datas,
|
|
hiddenimports=hiddenimports,
|
|
hookspath=[],
|
|
hooksconfig={},
|
|
runtime_hooks=[],
|
|
excludes=[],
|
|
win_no_prefer_redirects=False,
|
|
win_private_assemblies=False,
|
|
cipher=block_cipher,
|
|
noarchive=False
|
|
)
|
|
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
|
|
|
exe = EXE(
|
|
pyz,
|
|
a.scripts,
|
|
[],
|
|
exclude_binaries=True,
|
|
name=r'{{ name }}',
|
|
debug=False,
|
|
bootloader_ignore_signals=False,
|
|
strip={{ strip }},
|
|
upx={{ upx }},
|
|
console=False,
|
|
disable_windowed_traceback=False,
|
|
argv_emulation=False,
|
|
target_arch={{ target_arch }},
|
|
codesign_identity=os.getenv('CODESIGN_IDENTITY', None),
|
|
entitlements_file={{ entitlements_file }},
|
|
icon={{ icon }}
|
|
)
|
|
|
|
coll = COLLECT(
|
|
exe,
|
|
a.binaries,
|
|
a.zipfiles,
|
|
a.datas,
|
|
strip=False,
|
|
upx=True,
|
|
upx_exclude=[],
|
|
name=r'{{ name }}'
|
|
)
|
|
|
|
{% if macos == true %}
|
|
# PyInstaller seems to copy everything in the resource folder for the MacOS, this causes issues with codesigning and notarizing
|
|
# The folder structure should adhere to the one specified in Table 2-5
|
|
# https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html#//apple_ref/doc/uid/10000123i-CH101-SW1
|
|
# The class below is basically ducktyping the BUNDLE class of PyInstaller and using our own `assemble` method for more fine-grain and specific
|
|
# control. Some code of the method below is copied from:
|
|
# https://github.com/pyinstaller/pyinstaller/blob/22d1d2a5378228744cc95f14904dae1664df32c4/PyInstaller/building/osx.py#L115
|
|
#-----------------------------------------------------------------------------
|
|
# Copyright (c) 2005-2022, PyInstaller Development Team.
|
|
#
|
|
# Distributed under the terms of the GNU General Public License (version 2
|
|
# or later) with exception for distributing the bootloader.
|
|
#
|
|
# The full license is in the file COPYING.txt, distributed with this software.
|
|
#
|
|
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
|
|
#-----------------------------------------------------------------------------
|
|
|
|
import plistlib
|
|
import shutil
|
|
import PyInstaller.utils.osx as osxutils
|
|
from pathlib import Path
|
|
from PyInstaller.building.osx import BUNDLE
|
|
from PyInstaller.building.utils import (_check_path_overlap, _rmtree, add_suffix_to_extension, checkCache)
|
|
from PyInstaller.building.datastruct import logger
|
|
from PyInstaller.building.icon import normalize_icon_type
|
|
|
|
|
|
class UMBUNDLE(BUNDLE):
|
|
def assemble(self):
|
|
from PyInstaller.config import CONF
|
|
|
|
if _check_path_overlap(self.name) and os.path.isdir(self.name):
|
|
_rmtree(self.name)
|
|
logger.info("Building BUNDLE %s", self.tocbasename)
|
|
|
|
# Create a minimal Mac bundle structure.
|
|
macos_path = Path(self.name, "Contents", "MacOS")
|
|
resources_path = Path(self.name, "Contents", "Resources")
|
|
frameworks_path = Path(self.name, "Contents", "Frameworks")
|
|
os.makedirs(macos_path)
|
|
os.makedirs(resources_path)
|
|
os.makedirs(frameworks_path)
|
|
|
|
# Makes sure the icon exists and attempts to convert to the proper format if applicable
|
|
self.icon = normalize_icon_type(self.icon, ("icns",), "icns", CONF["workpath"])
|
|
|
|
# Ensure icon path is absolute
|
|
self.icon = os.path.abspath(self.icon)
|
|
|
|
# Copy icns icon to Resources directory.
|
|
shutil.copy(self.icon, os.path.join(self.name, 'Contents', 'Resources'))
|
|
|
|
# Key/values for a minimal Info.plist file
|
|
info_plist_dict = {
|
|
"CFBundleDisplayName": self.appname,
|
|
"CFBundleName": self.appname,
|
|
|
|
# Required by 'codesign' utility.
|
|
# The value for CFBundleIdentifier is used as the default unique name of your program for Code Signing
|
|
# purposes. It even identifies the APP for access to restricted OS X areas like Keychain.
|
|
#
|
|
# The identifier used for signing must be globally unique. The usual form for this identifier is a
|
|
# hierarchical name in reverse DNS notation, starting with the toplevel domain, followed by the company
|
|
# name, followed by the department within the company, and ending with the product name. Usually in the
|
|
# form: com.mycompany.department.appname
|
|
# CLI option --osx-bundle-identifier sets this value.
|
|
"CFBundleIdentifier": self.bundle_identifier,
|
|
"CFBundleExecutable": os.path.basename(self.exename),
|
|
"CFBundleIconFile": os.path.basename(self.icon),
|
|
"CFBundleInfoDictionaryVersion": "6.0",
|
|
"CFBundlePackageType": "APPL",
|
|
"CFBundleShortVersionString": self.version,
|
|
}
|
|
|
|
# Set some default values. But they still can be overwritten by the user.
|
|
if self.console:
|
|
# Setting EXE console=True implies LSBackgroundOnly=True.
|
|
info_plist_dict['LSBackgroundOnly'] = True
|
|
else:
|
|
# Let's use high resolution by default.
|
|
info_plist_dict['NSHighResolutionCapable'] = True
|
|
|
|
# Merge info_plist settings from spec file
|
|
if isinstance(self.info_plist, dict) and self.info_plist:
|
|
info_plist_dict.update(self.info_plist)
|
|
|
|
plist_filename = os.path.join(self.name, "Contents", "Info.plist")
|
|
with open(plist_filename, "wb") as plist_fh:
|
|
plistlib.dump(info_plist_dict, plist_fh)
|
|
|
|
links = []
|
|
_QT_BASE_PATH = {'PySide2', 'PySide6', 'PyQt5', 'PyQt6', 'PySide6'}
|
|
for inm, fnm, typ in self.toc:
|
|
# Adjust name for extensions, if applicable
|
|
inm, fnm, typ = add_suffix_to_extension(inm, fnm, typ)
|
|
inm = Path(inm)
|
|
fnm = Path(fnm)
|
|
# Copy files from cache. This ensures that are used files with relative paths to dynamic library
|
|
# dependencies (@executable_path)
|
|
if typ in ('EXTENSION', 'BINARY') or (typ == 'DATA' and inm.suffix == '.so'):
|
|
if any(['.' in p for p in inm.parent.parts]):
|
|
inm = Path(inm.name)
|
|
fnm = Path(checkCache(
|
|
str(fnm),
|
|
strip = self.strip,
|
|
upx = self.upx,
|
|
upx_exclude = self.upx_exclude,
|
|
dist_nm = str(inm),
|
|
target_arch = self.target_arch,
|
|
codesign_identity = self.codesign_identity,
|
|
entitlements_file = self.entitlements_file,
|
|
strict_arch_validation = (typ == 'EXTENSION'),
|
|
))
|
|
frame_dst = frameworks_path.joinpath(inm)
|
|
if not frame_dst.exists():
|
|
if frame_dst.is_dir():
|
|
os.makedirs(frame_dst, exist_ok = True)
|
|
else:
|
|
os.makedirs(frame_dst.parent, exist_ok = True)
|
|
shutil.copy(fnm, frame_dst, follow_symlinks = True)
|
|
macos_dst = macos_path.joinpath(inm)
|
|
if not macos_dst.exists():
|
|
if macos_dst.is_dir():
|
|
os.makedirs(macos_dst, exist_ok = True)
|
|
else:
|
|
os.makedirs(macos_dst.parent, exist_ok = True)
|
|
|
|
# Create relative symlink to the framework
|
|
symlink_to = Path(*[".." for p in macos_dst.relative_to(macos_path).parts], "Frameworks").joinpath(
|
|
frame_dst.relative_to(frameworks_path))
|
|
try:
|
|
macos_dst.symlink_to(symlink_to)
|
|
except FileExistsError:
|
|
pass
|
|
else:
|
|
if typ == 'DATA':
|
|
if any(['.' in p for p in inm.parent.parts]) or inm.suffix == '.so':
|
|
# Skip info dist egg and some not needed folders in tcl and tk, since they all contain dots in their files
|
|
logger.warning(f"Skipping DATA file {inm}")
|
|
continue
|
|
res_dst = resources_path.joinpath(inm)
|
|
if not res_dst.exists():
|
|
if res_dst.is_dir():
|
|
os.makedirs(res_dst, exist_ok = True)
|
|
else:
|
|
os.makedirs(res_dst.parent, exist_ok = True)
|
|
shutil.copy(fnm, res_dst, follow_symlinks = True)
|
|
macos_dst = macos_path.joinpath(inm)
|
|
if not macos_dst.exists():
|
|
if macos_dst.is_dir():
|
|
os.makedirs(macos_dst, exist_ok = True)
|
|
else:
|
|
os.makedirs(macos_dst.parent, exist_ok = True)
|
|
|
|
# Create relative symlink to the resource
|
|
symlink_to = Path(*[".." for p in macos_dst.relative_to(macos_path).parts], "Resources").joinpath(
|
|
res_dst.relative_to(resources_path))
|
|
try:
|
|
macos_dst.symlink_to(symlink_to)
|
|
except FileExistsError:
|
|
pass
|
|
else:
|
|
macos_dst = macos_path.joinpath(inm)
|
|
if not macos_dst.exists():
|
|
if macos_dst.is_dir():
|
|
os.makedirs(macos_dst, exist_ok = True)
|
|
else:
|
|
os.makedirs(macos_dst.parent, exist_ok = True)
|
|
shutil.copy(fnm, macos_dst, follow_symlinks = True)
|
|
|
|
# Sign the bundle
|
|
logger.info('Signing the BUNDLE...')
|
|
try:
|
|
osxutils.sign_binary(self.name, self.codesign_identity, self.entitlements_file, deep = True)
|
|
except Exception as e:
|
|
logger.warning(f"Error while signing the bundle: {e}")
|
|
logger.warning("You will need to sign the bundle manually!")
|
|
|
|
logger.info(f"Building BUNDLE {self.tocbasename} completed successfully.")
|
|
|
|
app = UMBUNDLE(
|
|
coll,
|
|
name='{{ name }}.app',
|
|
icon={{ icon }},
|
|
bundle_identifier={{ osx_bundle_identifier }},
|
|
version={{ version }},
|
|
info_plist={
|
|
'CFBundleDisplayName': '{{ display_name }}',
|
|
'NSPrincipalClass': 'NSApplication',
|
|
'CFBundleDevelopmentRegion': 'English',
|
|
'CFBundleExecutable': '{{ name }}',
|
|
'CFBundleInfoDictionaryVersion': '6.0',
|
|
'CFBundlePackageType': 'APPL',
|
|
'CFBundleShortVersionString': {{ short_version }},
|
|
'CFBundleDocumentTypes': [{
|
|
'CFBundleTypeRole': 'Viewer',
|
|
'CFBundleTypeExtensions': ['*'],
|
|
'CFBundleTypeName': 'Model Files',
|
|
}]
|
|
},
|
|
){% endif %}
|