def _check_guts(self, data, last_build): if not os.path.exists(self.name): logger.info("Rebuilding %s because %s missing", self.tocbasename, os.path.basename(self.name)) return 1 if not self.append_pkg and not os.path.exists(self.pkgname): logger.info("Rebuilding because %s missing", os.path.basename(self.pkgname)) return 1 if Target._check_guts(self, data, last_build): return True if (data['versrsrc'] or data['resources']) and not is_win: # todo: really ignore :-) logger.warning( 'ignoring version, manifest and resources, platform not capable' ) if data['icon'] and not (is_win or is_darwin): logger.warning('ignoring icon, platform not capable') mtm = data['mtm'] if mtm != misc.mtime(self.name): logger.info("Rebuilding %s because mtimes don't match", self.tocbasename) return True if mtm < misc.mtime(self.pkg.tocfilename): logger.info("Rebuilding %s because pkg is more recent", self.tocbasename) return True return False
def _check_guts(self, data, last_build): if not os.path.exists(self.name): logger.info("Rebuilding %s because %s missing", self.tocbasename, os.path.basename(self.name)) return 1 if not self.append_pkg and not os.path.exists(self.pkgname): logger.info("Rebuilding because %s missing", os.path.basename(self.pkgname)) return 1 if Target._check_guts(self, data, last_build): return True if (data['versrsrc'] or data['resources']) and not is_win: # todo: really ignore :-) logger.warn('ignoring version, manifest and resources, platform not capable') if data['icon'] and not (is_win or is_darwin): logger.warn('ignoring icon, platform not capable') mtm = data['mtm'] if mtm != misc.mtime(self.name): logger.info("Rebuilding %s because mtimes don't match", self.tocbasename) return True if mtm < misc.mtime(self.pkg.tocfilename): logger.info("Rebuilding %s because pkg is more recent", self.tocbasename) return True return False
def _check_guts_toc_mtime(_attr, old, _toc, last_build, pyc=0): """ Rebuild is required if mtimes of files listed in old toc are newer than last_build. If pyc=1, check for .py files as well. Use this for calculated/analysed values read from cache. """ for nm, fnm, typ in old: if misc.mtime(fnm) > last_build: logger.info("Building because %s changed", fnm) return True elif pyc and misc.mtime(fnm[:-1]) > last_build: logger.info("Building because %s changed", fnm[:-1]) return True return False
def __postinit__(self): """ Check if the target need to be rebuild and if so, re-assemble. `__postinit__` is to be called at the end of `__init__` of every subclass of Target. `__init__` is meant to setup the parameters and `__postinit__` is checking if rebuild is required and in case calls `assemble()` """ logger.info("checking %s", self.__class__.__name__) data = None last_build = misc.mtime(self.tocfilename) if last_build == 0: logger.info("Building %s because %s is non existent", self.__class__.__name__, self.tocbasename) else: try: data = load_py_data_struct(self.tocfilename) except: logger.info("Building because %s is bad", self.tocbasename) else: # create a dict for easier access data = dict(zip((g[0] for g in self._GUTS), data)) # assemble if previous data was not found or is outdated if not data or self._check_guts(data, last_build): self.assemble() self._save_guts()
def _check_guts(self, data, last_build): if Target._check_guts(self, data, last_build): return True # Walk the collected directories as check if they have been changed - which means files have been added or # removed. There is no need to check for the files, since `Tree` is only about the directory contents (which is # the list of files). stack = [data['root']] while stack: d = stack.pop() if misc.mtime(d) > last_build: logger.info("Building %s because directory %s changed", self.tocbasename, d) return True for nm in os.listdir(d): path = os.path.join(d, nm) if os.path.isdir(path): stack.append(path) self[:] = data['data'] # collected files return False
def _check_guts(self, data, last_build): if Target._check_guts(self, data, last_build): return True # Walk the collected directories as check if they have been # changed - which means files have been added or removed. # There is no need to check for the files, since `Tree` is # only about the directory contents (which is the list of # files). stack = [data["root"]] while stack: d = stack.pop() if misc.mtime(d) > last_build: logger.info("Building %s because directory %s changed", self.tocbasename, d) return True for nm in os.listdir(d): path = os.path.join(d, nm) if os.path.isdir(path): stack.append(path) self[:] = data["data"] # collected files return False
def _check_guts(self, data, last_build): if Target._check_guts(self, data, last_build): return True for fnm in self.inputs: if mtime(fnm) > last_build: logger.info("Building because %s changed", fnm) return True # Now we know that none of the input parameters and none of the input files has changed. So take the values # calculated resp. analysed in the last run and store them in `self`. self.scripts = TOC(data['scripts']) self.pure = TOC(data['pure']) self.binaries = TOC(data['binaries']) self.zipfiles = TOC(data['zipfiles']) self.zipped_data = TOC(data['zipped_data']) self.datas = TOC(data['datas']) # Store previously found binding redirects in CONF for later use by PKG/COLLECT from PyInstaller.config import CONF self.binding_redirects = CONF['binding_redirects'] = data[ 'binding_redirects'] return False
def assemble(self): logger.info("Building EXE from %s", self.tocbasename) trash = [] if os.path.exists(self.name): os.remove(self.name) if not os.path.exists(os.path.dirname(self.name)): os.makedirs(os.path.dirname(self.name)) exe = self.exefiles[0][1] # pathname of bootloader if not os.path.exists(exe): raise SystemExit(_MISSING_BOOTLOADER_ERRORMSG) if is_win and (self.icon or self.versrsrc or self.resources): tmpnm = tempfile.mktemp() self._copyfile(exe, tmpnm) os.chmod(tmpnm, 0o755) if self.icon: icon.CopyIcons(tmpnm, self.icon) if self.versrsrc: versioninfo.SetVersion(tmpnm, self.versrsrc) for res in self.resources: res = res.split(",") for i in range(1, len(res)): try: res[i] = int(res[i]) except ValueError: pass resfile = res[0] restype = resname = reslang = None if len(res) > 1: restype = res[1] if len(res) > 2: resname = res[2] if len(res) > 3: reslang = res[3] try: winresource.UpdateResourcesFromResFile( tmpnm, resfile, [restype or "*"], [resname or "*"], [reslang or "*"]) except winresource.pywintypes.error as exc: if exc.args[0] != winresource.ERROR_BAD_EXE_FORMAT: logger.error( "Error while updating resources in %s" " from resource file %s", tmpnm, resfile, exc_info=1) continue # Handle the case where the file contains no resources, and is # intended as a single resource to be added to the exe. if not restype or not resname: logger.error("resource type and/or name not specified") continue if "*" in (restype, resname): logger.error("no wildcards allowed for resource type " "and name when source file does not " "contain resources") continue try: winresource.UpdateResourcesFromDataFile( tmpnm, resfile, restype, [resname], [reslang or 0]) except winresource.pywintypes.error: logger.error( "Error while updating resource %s %s in %s" " from data file %s", restype, resname, tmpnm, resfile, exc_info=1) trash.append(tmpnm) exe = tmpnm # NOTE: Do not look up for bootloader file in the cache because it might # get corrupted by UPX when UPX is available. See #1863 for details. if not self.append_pkg: logger.info("Copying bootloader exe to %s", self.name) self._copyfile(exe, self.name) logger.info("Copying archive to %s", self.pkgname) self._copyfile(self.pkg.name, self.pkgname) elif is_linux: self._copyfile(exe, self.name) logger.info("Appending archive to ELF section in EXE %s", self.name) retcode, stdout, stderr = exec_command_all( 'objcopy', '--add-section', 'pydata=%s' % self.pkg.name, self.name) logger.debug("objcopy returned %i", retcode) if stdout: logger.debug(stdout) if stderr: logger.debug(stderr) if retcode != 0: raise SystemError("objcopy Failure: %s" % stderr) else: # Fall back to just append on end of file logger.info("Appending archive to EXE %s", self.name) with open(self.name, 'wb') as outf: # write the bootloader data with open(exe, 'rb') as infh: shutil.copyfileobj(infh, outf, length=64 * 1024) # write the archive data with open(self.pkg.name, 'rb') as infh: shutil.copyfileobj(infh, outf, length=64 * 1024) if is_darwin: # Fix Mach-O header for codesigning on OS X. logger.info("Fixing EXE for code signing %s", self.name) import PyInstaller.utils.osx as osxutils osxutils.fix_exe_for_code_signing(self.name) os.chmod(self.name, 0o755) # get mtime for storing into the guts self.mtm = misc.mtime(self.name) for item in trash: os.remove(item) logger.info("Building EXE from %s completed successfully.", self.tocbasename)
def assemble(self): logger.info("Building EXE from %s", self.tocbasename) trash = [] if not os.path.exists(os.path.dirname(self.name)): os.makedirs(os.path.dirname(self.name)) outf = open(self.name, 'wb') exe = self.exefiles[0][1] # pathname of bootloader if not os.path.exists(exe): raise SystemExit(_MISSING_BOOTLOADER_ERRORMSG) if is_win and (self.icon or self.versrsrc or self.resources): tmpnm = tempfile.mktemp() shutil.copy2(exe, tmpnm) os.chmod(tmpnm, 0o755) if self.icon: icon.CopyIcons(tmpnm, self.icon) if self.versrsrc: versioninfo.SetVersion(tmpnm, self.versrsrc) for res in self.resources: res = res.split(",") for i in range(1, len(res)): try: res[i] = int(res[i]) except ValueError: pass resfile = res[0] restype = resname = reslang = None if len(res) > 1: restype = res[1] if len(res) > 2: resname = res[2] if len(res) > 3: reslang = res[3] try: winresource.UpdateResourcesFromResFile(tmpnm, resfile, [restype or "*"], [resname or "*"], [reslang or "*"]) except winresource.pywintypes.error as exc: if exc.args[0] != winresource.ERROR_BAD_EXE_FORMAT: logger.exception(exc) continue if not restype or not resname: logger.error("resource type and/or name not specified") continue if "*" in (restype, resname): logger.error("no wildcards allowed for resource type " "and name when source file does not " "contain resources") continue try: winresource.UpdateResourcesFromDataFile(tmpnm, resfile, restype, [resname], [reslang or 0]) except winresource.pywintypes.error as exc: logger.exception(exc) trash.append(tmpnm) exe = tmpnm exe = checkCache(exe, strip=self.strip, upx=self.upx) self.copy(exe, outf) if self.append_pkg: logger.info("Appending archive to EXE %s", self.name) self.copy(self.pkg.name, outf) else: logger.info("Copying archive to %s", self.pkgname) shutil.copy2(self.pkg.name, self.pkgname) outf.close() if is_darwin: # Fix Mach-O header for codesigning on OS X. logger.info("Fixing EXE for code signing %s", self.name) import PyInstaller.utils.osx as osxutils osxutils.fix_exe_for_code_signing(self.name) pass os.chmod(self.name, 0o755) # get mtime for storing into the guts self.mtm = misc.mtime(self.name) for item in trash: os.remove(item)
def assemble(self): logger.info("Building EXE from %s", self.tocbasename) trash = [] if os.path.exists(self.name): os.remove(self.name) if not os.path.exists(os.path.dirname(self.name)): os.makedirs(os.path.dirname(self.name)) exe = self.exefiles[0][1] # pathname of bootloader if not os.path.exists(exe): raise SystemExit(_MISSING_BOOTLOADER_ERRORMSG) if is_win and (self.icon or self.versrsrc or self.resources): tmpnm = tempfile.mktemp() self._copyfile(exe, tmpnm) os.chmod(tmpnm, 0o755) if self.icon: icon.CopyIcons(tmpnm, self.icon) if self.versrsrc: versioninfo.SetVersion(tmpnm, self.versrsrc) for res in self.resources: res = res.split(",") for i in range(1, len(res)): try: res[i] = int(res[i]) except ValueError: pass resfile = res[0] restype = resname = reslang = None if len(res) > 1: restype = res[1] if len(res) > 2: resname = res[2] if len(res) > 3: reslang = res[3] try: winresource.UpdateResourcesFromResFile(tmpnm, resfile, [restype or "*"], [resname or "*"], [reslang or "*"]) except winresource.pywintypes.error as exc: if exc.args[0] != winresource.ERROR_BAD_EXE_FORMAT: logger.error("Error while updating resources in %s" " from resource file %s", tmpnm, resfile, exc_info=1) continue # Handle the case where the file contains no resources, and is # intended as a single resource to be added to the exe. if not restype or not resname: logger.error("resource type and/or name not specified") continue if "*" in (restype, resname): logger.error("no wildcards allowed for resource type " "and name when source file does not " "contain resources") continue try: winresource.UpdateResourcesFromDataFile(tmpnm, resfile, restype, [resname], [reslang or 0]) except winresource.pywintypes.error: logger.error("Error while updating resource %s %s in %s" " from data file %s", restype, resname, tmpnm, resfile, exc_info=1) trash.append(tmpnm) exe = tmpnm # NOTE: Do not look up for bootloader file in the cache because it might # get corrupted by UPX when UPX is available. See #1863 for details. if not self.append_pkg: logger.info("Copying bootloader exe to %s", self.name) self._copyfile(exe, self.name) logger.info("Copying archive to %s", self.pkgname) self._copyfile(self.pkg.name, self.pkgname) elif is_linux: self._copyfile(exe, self.name) logger.info("Appending archive to ELF section in EXE %s", self.name) retcode, stdout, stderr = exec_command_all( 'objcopy', '--add-section', 'pydata=%s' % self.pkg.name, self.name) logger.debug("objcopy returned %i", retcode) if stdout: logger.debug(stdout) if stderr: logger.debug(stderr) if retcode != 0: raise SystemError("objcopy Failure: %s" % stderr) else: # Fall back to just append on end of file logger.info("Appending archive to EXE %s", self.name) with open(self.name, 'wb') as outf: # write the bootloader data with open(exe, 'rb') as infh: shutil.copyfileobj(infh, outf, length=64*1024) # write the archive data with open(self.pkg.name, 'rb') as infh: shutil.copyfileobj(infh, outf, length=64*1024) if is_darwin: # Fix Mach-O header for codesigning on OS X. logger.info("Fixing EXE for code signing %s", self.name) import PyInstaller.utils.osx as osxutils osxutils.fix_exe_for_code_signing(self.name) os.chmod(self.name, 0o755) # get mtime for storing into the guts self.mtm = misc.mtime(self.name) for item in trash: os.remove(item) logger.info("Building EXE from %s completed successfully.", self.tocbasename)
def assemble(self): from PyInstaller.config import CONF logger.info("Building EXE from %s", self.tocbasename) if os.path.exists(self.name): os.remove(self.name) if not os.path.exists(os.path.dirname(self.name)): os.makedirs(os.path.dirname(self.name)) exe = self.exefiles[0][1] # pathname of bootloader if not os.path.exists(exe): raise SystemExit(_MISSING_BOOTLOADER_ERRORMSG) # Step 1: copy the bootloader file, and perform any operations that need to be done prior to appending the PKG. logger.info("Copying bootloader EXE to %s", self.name) self._copyfile(exe, self.name) os.chmod(self.name, 0o755) if is_win: # First, remove all resources from the file. This ensures that no manifest is embedded, even if bootloader # was compiled with a toolchain that forcibly embeds a default manifest (e.g., mingw toolchain from msys2). winresource.RemoveAllResources(self.name) # Embed icon. if self.icon != "NONE": logger.info("Copying icon to EXE") icon.CopyIcons(self.name, self.icon) # Embed version info. if self.versrsrc: logger.info("Copying version information to EXE") versioninfo.SetVersion(self.name, self.versrsrc) # Embed other resources. logger.info("Copying %d resources to EXE", len(self.resources)) for res in self.resources: res = res.split(",") for i in range(1, len(res)): try: res[i] = int(res[i]) except ValueError: pass resfile = res[0] if not os.path.isabs(resfile): resfile = os.path.join(CONF['specpath'], resfile) restype = resname = reslang = None if len(res) > 1: restype = res[1] if len(res) > 2: resname = res[2] if len(res) > 3: reslang = res[3] try: winresource.UpdateResourcesFromResFile( self.name, resfile, [restype or "*"], [resname or "*"], [reslang or "*"] ) except winresource.pywintypes.error as exc: if exc.args[0] != winresource.ERROR_BAD_EXE_FORMAT: logger.error( "Error while updating resources in %s from resource file %s!", self.name, resfile, exc_info=1 ) continue # Handle the case where the file contains no resources, and is intended as a single resource to be # added to the exe. if not restype or not resname: logger.error("Resource type and/or name not specified!") continue if "*" in (restype, resname): logger.error( "No wildcards allowed for resource type and name when the source file does not contain " "any resources!" ) continue try: winresource.UpdateResourcesFromDataFile(self.name, resfile, restype, [resname], [reslang or 0]) except winresource.pywintypes.error: logger.error( "Error while updating resource %s %s in %s from data file %s!", restype, resname, self.name, resfile, exc_info=1 ) # Embed the manifest into the executable. if self.embed_manifest: logger.info("Emedding manifest in EXE") self.manifest.update_resources(self.name, [1]) elif is_darwin: # Convert bootloader to the target arch logger.info("Converting EXE to target arch (%s)", self.target_arch) osxutils.binary_to_target_arch(self.name, self.target_arch, display_name='Bootloader EXE') # Step 2: append the PKG, if necessary if not self.append_pkg: # In onefile mode, copy the stand-alone pkg next to the executable. In onedir, this will be done by the # COLLECT() target. if not self.exclude_binaries: pkg_dst = os.path.join(os.path.dirname(self.name), os.path.basename(self.pkgname)) logger.info("Copying stand-alone PKG archive from %s to %s", self.pkg.name, pkg_dst) self._copyfile(self.pkg.name, pkg_dst) else: logger.info("Stand-alone PKG archive will be handled by COLLECT") elif is_linux: # Linux: append PKG into ELF section using objcopy logger.info("Appending PKG archive to ELF section in EXE") retcode, stdout, stderr = exec_command_all( 'objcopy', '--add-section', 'pydata=%s' % self.pkg.name, self.name ) logger.debug("objcopy returned %i", retcode) if stdout: logger.debug(stdout) if stderr: logger.debug(stderr) if retcode != 0: raise SystemError("objcopy Failure: %s" % stderr) elif is_darwin: # macOS: remove signature, append PKG, and fix-up headers so that PKG appears to be part of the executable. # Strip signatures from all arch slices. Strictly speaking, we need to remove signature (if present) from # the last slice, because we will be appending data to it. When building universal2 bootloaders natively on # macOS, only arm64 slices have a (dummy) signature. However, when cross-compiling with osxcross, we seem to # get dummy signatures on both x86_64 and arm64 slices. While the former should not have any impact, it does # seem to cause issues with further binary signing using real identity. Therefore, we remove all signatures # and re-sign the binary using dummy signature once the data is appended. logger.info("Removing signature(s) from EXE") osxutils.remove_signature_from_binary(self.name) # Append the PKG data logger.info("Appending PKG archive to EXE") with open(self.name, 'ab') as outf: with open(self.pkg.name, 'rb') as inf: shutil.copyfileobj(inf, outf, length=64 * 1024) # Fix Mach-O header for code signing logger.info("Fixing EXE headers for code signing") osxutils.fix_exe_for_code_signing(self.name) else: # Fall back to just appending PKG at the end of the file logger.info("Appending PKG archive to EXE") with open(self.name, 'ab') as outf: with open(self.pkg.name, 'rb') as inf: shutil.copyfileobj(inf, outf, length=64 * 1024) # Step 3: post-processing if is_win: # Set checksum to appease antiviral software. set_exe_checksum(self.name) elif is_darwin: # If the version of macOS SDK used to build bootloader exceeds that of macOS SDK used to built Python # library (and, by extension, bundled Tcl/Tk libraries), force the version declared by the frozen executable # to match that of the Python library. # Having macOS attempt to enable new features (based on SDK version) for frozen application has no benefit # if the Python library does not support them as well. # On the other hand, there seem to be UI issues in tkinter due to failed or partial enablement of dark mode # (i.e., the bootloader executable being built against SDK 10.14 or later, which causes macOS to enable dark # mode, and Tk libraries being built against an earlier SDK version that does not support the dark mode). # With python.org Intel macOS installers, this manifests as black Tk windows and UI elements (see issue # #5827), while in Anaconda python, it may result in white text on bright background. pylib_version = osxutils.get_macos_sdk_version(bindepend.get_python_library_path()) exe_version = osxutils.get_macos_sdk_version(self.name) if pylib_version < exe_version: logger.info( "Rewriting the executable's macOS SDK version (%d.%d.%d) to match the SDK version of the Python " "library (%d.%d.%d) in order to avoid inconsistent behavior and potential UI issues in the " "frozen application.", *exe_version, *pylib_version ) osxutils.set_macos_sdk_version(self.name, *pylib_version) # Re-sign the binary (either ad-hoc or using real identity, if provided). logger.info("Re-signing the EXE") osxutils.sign_binary(self.name, self.codesign_identity, self.entitlements_file) # Ensure executable flag is set os.chmod(self.name, 0o755) # Get mtime for storing into the guts self.mtm = misc.mtime(self.name) logger.info("Building EXE from %s completed successfully.", self.tocbasename)
def assemble(self): from PyInstaller.config import CONF logger.info("Building EXE from %s", self.tocbasename) trash = [] if os.path.exists(self.name): os.remove(self.name) if not os.path.exists(os.path.dirname(self.name)): os.makedirs(os.path.dirname(self.name)) exe = self.exefiles[0][1] # pathname of bootloader if not os.path.exists(exe): raise SystemExit(_MISSING_BOOTLOADER_ERRORMSG) if is_win: fd, tmpnm = tempfile.mkstemp(prefix=os.path.basename(exe) + ".", dir=CONF['workpath']) # need to close the file, otherwise copying resources will fail # with "the file [...] is being used by another process" os.close(fd) self._copyfile(exe, tmpnm) os.chmod(tmpnm, 0o755) if self.icon != "NONE": icon.CopyIcons(tmpnm, self.icon) if self.versrsrc: versioninfo.SetVersion(tmpnm, self.versrsrc) for res in self.resources: res = res.split(",") for i in range(1, len(res)): try: res[i] = int(res[i]) except ValueError: pass resfile = res[0] if not os.path.isabs(resfile): resfile = os.path.join(CONF['specpath'], resfile) restype = resname = reslang = None if len(res) > 1: restype = res[1] if len(res) > 2: resname = res[2] if len(res) > 3: reslang = res[3] try: winresource.UpdateResourcesFromResFile( tmpnm, resfile, [restype or "*"], [resname or "*"], [reslang or "*"]) except winresource.pywintypes.error as exc: if exc.args[0] != winresource.ERROR_BAD_EXE_FORMAT: logger.error( "Error while updating resources in %s" " from resource file %s", tmpnm, resfile, exc_info=1) continue # Handle the case where the file contains no resources, and is # intended as a single resource to be added to the exe. if not restype or not resname: logger.error("resource type and/or name not specified") continue if "*" in (restype, resname): logger.error("no wildcards allowed for resource type " "and name when source file does not " "contain resources") continue try: winresource.UpdateResourcesFromDataFile( tmpnm, resfile, restype, [resname], [reslang or 0]) except winresource.pywintypes.error: logger.error( "Error while updating resource %s %s in %s" " from data file %s", restype, resname, tmpnm, resfile, exc_info=1) if self.manifest and not self.exclude_binaries: self.manifest.update_resources(tmpnm, [1]) trash.append(tmpnm) exe = tmpnm # NOTE: Do not look up for bootloader file in the cache because it might # get corrupted by UPX when UPX is available. See #1863 for details. if not self.append_pkg: logger.info("Copying bootloader exe to %s", self.name) self._copyfile(exe, self.name) logger.info("Copying archive to %s", self.pkgname) self._copyfile(self.pkg.name, self.pkgname) elif is_linux: self._copyfile(exe, self.name) logger.info("Appending archive to ELF section in EXE %s", self.name) retcode, stdout, stderr = exec_command_all( 'objcopy', '--add-section', 'pydata=%s' % self.pkg.name, self.name) logger.debug("objcopy returned %i", retcode) if stdout: logger.debug(stdout) if stderr: logger.debug(stderr) if retcode != 0: raise SystemError("objcopy Failure: %s" % stderr) elif is_darwin: import PyInstaller.utils.osx as osxutils # Copy bootloader logger.info("Copying bootloader exe to %s", self.name) with open(self.name, 'wb') as outf: with open(exe, 'rb') as inf: shutil.copyfileobj(inf, outf, length=64 * 1024) # Convert bootloader to target arch logger.info("Converting EXE to target arch (%s)", self.target_arch) osxutils.binary_to_target_arch(self.name, self.target_arch, display_name='Bootloader EXE') # Strip signatures from all arch slices. Strictly speaking, # we need to remove signature (if present) from the last # slice, because we will be appending data to it. When # building universal2 bootloaders natively on macOS, only # arm64 slices have a (dummy) signature. However, when # cross-compiling with osxcross, we seem to get dummy # signatures on both x86_64 and arm64 slices. While the former # should not have any impact, it does seem to cause issues # with further binary signing using real identity. Therefore, # we remove all signatures and re-sign the binary using # dummy signature once the data is appended. logger.info("Removing signature(s) from EXE") osxutils.remove_signature_from_binary(self.name) # Append the data with open(self.name, 'ab') as outf: with open(self.pkg.name, 'rb') as inf: shutil.copyfileobj(inf, outf, length=64 * 1024) # If the version of macOS SDK used to build bootloader exceeds # that of macOS SDK used to built Python library (and, by # extension, bundled Tcl/Tk libraries), force the version # declared by the frozen executable to match that of the Python # library. # Having macOS attempt to enable new features (based on SDK # version) for frozen application has no benefit if the Python # library does not support them as well. # On the other hand, there seem to be UI issues in tkinter # due to failed or partial enablement of dark mode (i.e., the # bootloader executable being built against SDK 10.14 or later, # which causes macOS to enable dark mode, and Tk libraries being # built against an earlier SDK version that does not support the # dark mode). With python.org Intel macOS installers, this # manifests as black Tk windows and UI elements (see issue #5827), # while in Anaconda python, it may result in white text on bright # background. pylib_version = osxutils.get_macos_sdk_version( bindepend.get_python_library_path()) exe_version = osxutils.get_macos_sdk_version(self.name) if pylib_version < exe_version: logger.info( "Rewriting executable's macOS SDK version (%d.%d.%d) to " "match the SDK version of the Python library (%d.%d.%d) " "in order to avoid inconsistent behavior and potential UI " "issues in the frozen application.", *exe_version, *pylib_version) osxutils.set_macos_sdk_version(self.name, *pylib_version) # Fix Mach-O header for codesigning on OS X. logger.info("Fixing EXE for code signing %s", self.name) osxutils.fix_exe_for_code_signing(self.name) # Re-sign the binary (either ad-hoc or using real identity, # if provided) logger.info("Re-signing the EXE") osxutils.sign_binary(self.name, self.codesign_identity, self.entitlements_file) else: # Fall back to just append on end of file logger.info("Appending archive to EXE %s", self.name) with open(self.name, 'wb') as outf: # write the bootloader data with open(exe, 'rb') as infh: shutil.copyfileobj(infh, outf, length=64 * 1024) # write the archive data with open(self.pkg.name, 'rb') as infh: shutil.copyfileobj(infh, outf, length=64 * 1024) if is_win: # Set checksum to appease antiviral software. from PyInstaller.utils.win32.winutils import set_exe_checksum set_exe_checksum(self.name) os.chmod(self.name, 0o755) # get mtime for storing into the guts self.mtm = misc.mtime(self.name) for item in trash: os.remove(item) logger.info("Building EXE from %s completed successfully.", self.tocbasename)