mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-05-09 22:19:03 +08:00
commit
a8e3d51fea
@ -55,7 +55,8 @@ exe = EXE(
|
|||||||
target_arch={{ target_arch }},
|
target_arch={{ target_arch }},
|
||||||
codesign_identity=os.getenv('CODESIGN_IDENTITY', None),
|
codesign_identity=os.getenv('CODESIGN_IDENTITY', None),
|
||||||
entitlements_file={{ entitlements_file }},
|
entitlements_file={{ entitlements_file }},
|
||||||
icon={{ icon }}
|
icon={{ icon }},
|
||||||
|
contents_directory='.'
|
||||||
)
|
)
|
||||||
|
|
||||||
coll = COLLECT(
|
coll = COLLECT(
|
||||||
@ -70,188 +71,7 @@ coll = COLLECT(
|
|||||||
)
|
)
|
||||||
|
|
||||||
{% if macos == true %}
|
{% if macos == true %}
|
||||||
# PyInstaller seems to copy everything in the resource folder for the MacOS, this causes issues with codesigning and notarizing
|
app = BUNDLE(
|
||||||
# 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",
|
|
||||||
"CFBundleVersionString": self.version,
|
|
||||||
"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,
|
coll,
|
||||||
name='{{ display_name }}.app',
|
name='{{ display_name }}.app',
|
||||||
icon={{ icon }},
|
icon={{ icon }},
|
||||||
@ -271,9 +91,10 @@ app = UMBUNDLE(
|
|||||||
'CFBundleURLSchemes': ['cura', 'slicer'],
|
'CFBundleURLSchemes': ['cura', 'slicer'],
|
||||||
}],
|
}],
|
||||||
'CFBundleDocumentTypes': [{
|
'CFBundleDocumentTypes': [{
|
||||||
'CFBundleTypeRole': 'Viewer',
|
'CFBundleTypeRole': 'Viewer',
|
||||||
'CFBundleTypeExtensions': ['*'],
|
'CFBundleTypeExtensions': ['*'],
|
||||||
'CFBundleTypeName': 'Model Files',
|
'CFBundleTypeName': 'Model Files',
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
){% endif %}
|
)
|
||||||
|
{% endif %}
|
||||||
|
@ -118,7 +118,6 @@ pyinstaller:
|
|||||||
- "sqlite3"
|
- "sqlite3"
|
||||||
- "trimesh"
|
- "trimesh"
|
||||||
- "win32ctypes"
|
- "win32ctypes"
|
||||||
- "PyQt6"
|
|
||||||
- "PyQt6.QtNetwork"
|
- "PyQt6.QtNetwork"
|
||||||
- "PyQt6.sip"
|
- "PyQt6.sip"
|
||||||
- "stl"
|
- "stl"
|
||||||
|
@ -374,9 +374,15 @@ class CuraApplication(QtApplication):
|
|||||||
Resources.addExpectedDirNameInData(dir_name)
|
Resources.addExpectedDirNameInData(dir_name)
|
||||||
|
|
||||||
app_root = os.path.abspath(os.path.join(os.path.dirname(sys.executable)))
|
app_root = os.path.abspath(os.path.join(os.path.dirname(sys.executable)))
|
||||||
Resources.addSecureSearchPath(os.path.join(app_root, "share", "cura", "resources"))
|
|
||||||
|
|
||||||
Resources.addSecureSearchPath(os.path.join(self._app_install_dir, "share", "cura", "resources"))
|
if platform.system() == "Darwin":
|
||||||
|
Resources.addSecureSearchPath(os.path.join(app_root, "Resources", "share", "cura", "resources"))
|
||||||
|
Resources.addSecureSearchPath(
|
||||||
|
os.path.join(self._app_install_dir, "Resources", "share", "cura", "resources"))
|
||||||
|
else:
|
||||||
|
Resources.addSecureSearchPath(os.path.join(app_root, "share", "cura", "resources"))
|
||||||
|
Resources.addSecureSearchPath(os.path.join(self._app_install_dir, "share", "cura", "resources"))
|
||||||
|
|
||||||
if not hasattr(sys, "frozen"):
|
if not hasattr(sys, "frozen"):
|
||||||
cura_data_root = os.environ.get('CURA_DATA_ROOT', None)
|
cura_data_root = os.environ.get('CURA_DATA_ROOT', None)
|
||||||
if cura_data_root:
|
if cura_data_root:
|
||||||
|
@ -76,6 +76,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||||||
self._default_engine_location = executable_name
|
self._default_engine_location = executable_name
|
||||||
|
|
||||||
search_path = [
|
search_path = [
|
||||||
|
os.path.abspath(os.path.join(os.path.dirname(sys.executable), "..", "Resources")),
|
||||||
os.path.abspath(os.path.dirname(sys.executable)),
|
os.path.abspath(os.path.dirname(sys.executable)),
|
||||||
os.path.abspath(os.path.join(os.path.dirname(sys.executable), "bin")),
|
os.path.abspath(os.path.join(os.path.dirname(sys.executable), "bin")),
|
||||||
os.path.abspath(os.path.join(os.path.dirname(sys.executable), "..")),
|
os.path.abspath(os.path.join(os.path.dirname(sys.executable), "..")),
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
pytest
|
pytest
|
||||||
pyinstaller==5.8.0
|
pyinstaller==6.3.0
|
||||||
pyinstaller-hooks-contrib
|
pyinstaller-hooks-contrib
|
||||||
pyyaml
|
pyyaml
|
||||||
sip==6.5.1
|
sip==6.5.1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user