def considerExtraDlls(self, dist_dir, module): full_name = module.getFullName() if full_name == "uuid" and getOS() == "Linux": uuid_dll_path = locateDLL("uuid") dist_dll_path = os.path.join(dist_dir, os.path.basename(uuid_dll_path)) shutil.copy(uuid_dll_path, dist_dll_path) return ((uuid_dll_path, dist_dll_path, None), ) elif full_name == "iptc" and getOS() == "Linux": import iptc.util # pylint: disable=I0021,import-error xtwrapper_dll = iptc.util.find_library("xtwrapper")[0] xtwrapper_dll_path = xtwrapper_dll._name # pylint: disable=protected-access dist_dll_path = os.path.join(dist_dir, os.path.basename(xtwrapper_dll_path)) shutil.copy(xtwrapper_dll_path, dist_dll_path) return ((xtwrapper_dll_path, dist_dll_path, None), ) return ()
def considerExtraDlls(self, dist_dir, module): full_name = module.getFullName() if getOS() == "Linux" and full_name == "uuid": uuid_dll_path = locateDLL("uuid") dist_dll_path = joinpath(dist_dir, basename(uuid_dll_path)) shutil.copy(uuid_dll_path, dist_dir) return ((dist_dll_path, None), ) return ()
def considerExtraDlls(self, dist_dir, module): full_name = module.getFullName() if full_name == "uuid" and getOS() == "Linux": uuid_dll_path = locateDLL("uuid") dist_dll_path = os.path.join(dist_dir, os.path.basename(uuid_dll_path)) shutil.copy(uuid_dll_path, dist_dll_path) return ((uuid_dll_path, dist_dll_path, None),) return ()
def considerExtraDlls(self, dist_dir, module): # pylint: disable=too-many-branches,too-many-locals,too-many-statements full_name = module.getFullName() elements = full_name.split(".") plugin_dirs = None if elements[0] in ("PyQt4", "PyQt5"): qt_version = int(elements[0][-1]) plugin_dirs = self.getPyQtPluginDirs(qt_version) if full_name in ("PyQt4", "PyQt5"): if not plugin_dirs: sys.exit("Error, failed to detect %s plugin directories." % full_name) target_plugin_dir = os.path.join(dist_dir, full_name, "qt-plugins") plugin_options = set(self.qt_plugins.split(",")) if "sensible" in plugin_options: # Most used ones with low dependencies. plugin_options.update( tuple( family for family in ( "imageformats", "iconengines", "mediaservice", "printsupport", "platforms", ) if self.hasPluginFamily(plugin_dirs, family) ) ) plugin_options.remove("sensible") # Make sure the above didn't detect nothing, which would be # indicating the check to be bad. assert plugin_options self.info( "Copying Qt plug-ins '%s' to '%s'." % ( ",".join(sorted(x for x in plugin_options if x != "xml")), target_plugin_dir, ) ) for plugin_dir in plugin_dirs: copyTree(plugin_dir, target_plugin_dir) if "all" not in plugin_options: for plugin_candidate in getSubDirectories(target_plugin_dir): if os.path.basename(plugin_candidate) not in plugin_options: removeDirectory(plugin_candidate, ignore_errors=False) for plugin_candidate in plugin_options: if plugin_candidate == "qml": continue if not os.path.isdir( os.path.join(target_plugin_dir, plugin_candidate) ): sys.exit( "Error, no such Qt plugin family: %s" % plugin_candidate ) result = [ ( filename, os.path.join( target_plugin_dir, os.path.relpath(filename, plugin_dir) ), full_name, ) for plugin_dir in plugin_dirs for filename in getFileList(plugin_dir) if not filename.endswith(".qml") if os.path.exists( os.path.join( target_plugin_dir, os.path.relpath(filename, plugin_dir) ) ) ] if isWin32Windows(): # Those 2 vars will be used later, just saving some resources # by caching the files list qt_bin_files = sum( ( getFileList(qt_bin_dir) for qt_bin_dir in self._getQtBinDirs(plugin_dirs) ), [], ) self.info("Copying OpenSSL DLLs to %r." % dist_dir) for filename in qt_bin_files: basename = os.path.basename(filename).lower() if basename in ("libeay32.dll", "ssleay32.dll"): shutil.copy(filename, os.path.join(dist_dir, basename)) if "qml" in plugin_options or "all" in plugin_options: for plugin_dir in plugin_dirs: qml_plugin_dir = os.path.normpath( os.path.join(plugin_dir, "..", "qml") ) if os.path.exists(qml_plugin_dir): break else: sys.exit("Error, no such Qt plugin family: qml") qml_target_dir = os.path.normpath( os.path.join(target_plugin_dir, "..", "Qt", "qml") ) self.info("Copying Qt plug-ins 'xml' to '%s'." % (qml_target_dir)) copyTree(qml_plugin_dir, qml_target_dir) # We try to filter here, not for DLLs. result += [ ( filename, os.path.join( qml_target_dir, os.path.relpath(filename, qml_plugin_dir) ), full_name, ) for filename in getFileList(qml_plugin_dir) if not filename.endswith( ( ".qml", ".qmlc", ".qmltypes", ".js", ".jsc", ".png", ".ttf", ".metainfo", ) ) if not os.path.isdir(filename) if not os.path.basename(filename) == "qmldir" ] # Also copy required OpenGL DLLs on Windows if isWin32Windows(): opengl_dlls = ("libegl.dll", "libglesv2.dll", "opengl32sw.dll") self.info("Copying OpenGL DLLs to %r." % dist_dir) for filename in qt_bin_files: basename = os.path.basename(filename).lower() if basename in opengl_dlls or basename.startswith( "d3dcompiler_" ): shutil.copy(filename, os.path.join(dist_dir, basename)) return result elif full_name == "PyQt5.QtNetwork": if not isWin32Windows(): dll_path = locateDLL("crypto") if dll_path is None: dist_dll_path = os.path.join(dist_dir, os.path.basename(dll_path)) shutil.copy(dll_path, dist_dll_path) dll_path = locateDLL("ssl") if dll_path is not None: dist_dll_path = os.path.join(dist_dir, os.path.basename(dll_path)) shutil.copy(dll_path, dist_dll_path) elif ( full_name.startswith(("PyQt4.QtWebEngine", "PyQt5.QtWebEngine")) and not self.webengine_done ): self.webengine_done = True # prevent multiple copies self.info("Copying QtWebEngine components") plugin_parent = os.path.dirname(plugin_dirs[0]) if isWin32Windows(): bin_dir = os.path.join(plugin_parent, "bin") else: # TODO verify this for non-Windows! bin_dir = os.path.join(plugin_parent, "libexec") target_bin_dir = os.path.join(dist_dir) for f in os.listdir(bin_dir): if f.startswith("QtWebEngineProcess"): shutil.copy(os.path.join(bin_dir, f), target_bin_dir) resources_dir = os.path.join(plugin_parent, "resources") target_resources_dir = os.path.join(dist_dir) for f in os.listdir(resources_dir): shutil.copy(os.path.join(resources_dir, f), target_resources_dir) translations_dir = os.path.join(plugin_parent, "translations") pos = len(translations_dir) + 1 target_translations_dir = os.path.join( dist_dir, elements[0], "Qt", "translations" ) for f in getFileList(translations_dir): tar_f = os.path.join(target_translations_dir, f[pos:]) makePath(os.path.dirname(tar_f)) shutil.copyfile(f, tar_f) return ()
def packDistFolderToOnefileLinux(onefile_output_filename, dist_dir, binary_filename): """Pack to onefile binary on Linux. Notes: This is mostly a wrapper around AppImage, which does all the heavy lifting. """ if not locateDLL("fuse"): postprocessing_logger.sysexit( """\ Error, the fuse library (libfuse.so.x from fuse2, *not* fuse3) must be installed for onefile creation to work on Linux.""" ) # This might be possible to avoid being done with --runtime-file. apprun_filename = os.path.join(dist_dir, "AppRun") with open(apprun_filename, "w") as output_file: output_file.write( """\ #!/bin/bash exec -a $ARGV0 $APPDIR/%s $@""" % os.path.basename(binary_filename) ) addFileExecutablePermission(apprun_filename) binary_basename = os.path.basename(getResultBasepath()) icon_paths = getIconPaths() assert icon_paths extension = os.path.splitext(icon_paths[0])[1].lower() shutil.copyfile(icon_paths[0], getResultBasepath() + extension) with open(getResultBasepath() + ".desktop", "w") as output_file: output_file.write( """\ [Desktop Entry] Name=%(binary_basename)s Exec=%(binary_filename)s Icon=%(binary_basename)s Type=Application Categories=Utility;""" % { "binary_basename": binary_basename, "binary_filename": os.path.basename(binary_filename), } ) postprocessing_logger.info( "Creating single file from dist folder, this may take a while." ) stdout_filename = binary_filename + ".appimage.stdout.txt" stderr_filename = binary_filename + ".appimage.stderr.txt" stdout_file = open(stdout_filename, "wb") stderr_file = open(stderr_filename, "wb") # Starting the process while locked, so file handles are not duplicated. appimagetool_process = subprocess.Popen( ( _getAppImageToolPath( for_operation=True, assume_yes_for_downloads=assumeYesForDownloads() ), dist_dir, "--comp", "xz", "-n", onefile_output_filename, ), shell=False, stdin=getNullInput(), stdout=stdout_file, stderr=stderr_file, ) result = appimagetool_process.wait() stdout_file.close() stderr_file.close() if not os.path.exists(onefile_output_filename): postprocessing_logger.sysexit( "Error, expected output file %r not created by AppImage, check its outputs %r and %r." % (onefile_output_filename, stdout_filename, stderr_filename) ) if result != 0: # Useless now. os.unlink(onefile_output_filename) if b"Text file busy" in getFileContents(stderr_filename, mode="rb"): postprocessing_logger.sysexit( "Error, error exit from AppImage because target file is locked." ) postprocessing_logger.sysexit( "Error, error exit from AppImage, check its outputs %r and %r." % (stdout_filename, stderr_filename) ) os.unlink(stdout_filename) os.unlink(stderr_filename) postprocessing_logger.info("Completed onefile creation.")
def locateDLL(cls, dll_name): """Locate a DLL by name.""" return locateDLL(dll_name)
def considerExtraDlls(self, dist_dir, module): # pylint: disable=too-many-branches,too-many-locals,too-many-statements full_name = module.getFullName() if full_name == self.binding_name: if not self.getQtPluginDirs(): self.sysexit( "Error, failed to detect %r plugin directories." % self.binding_name ) target_plugin_dir = os.path.join(dist_dir, full_name.asPath(), "qt-plugins") self.info( "Copying Qt plug-ins '%s' to '%s'." % ( ",".join( sorted(x for x in self.getQtPluginsSelected() if x != "xml") ), target_plugin_dir, ) ) # TODO: Change this to filtering copyTree while it's doing it. for plugin_dir in self.getQtPluginDirs(): copyTree(plugin_dir, target_plugin_dir) if "all" not in self.getQtPluginsSelected(): for plugin_candidate in getSubDirectories(target_plugin_dir): if ( os.path.basename(plugin_candidate) not in self.getQtPluginsSelected() ): removeDirectory(plugin_candidate, ignore_errors=False) for plugin_candidate in self.getQtPluginsSelected(): if plugin_candidate == "qml": continue if not os.path.isdir( os.path.join(target_plugin_dir, plugin_candidate) ): self.sysexit( "Error, no such Qt plugin family: %s" % plugin_candidate ) result = self.findDLLs( full_name=full_name, target_plugin_dir=target_plugin_dir, ) if isWin32Windows(): # Those 2 vars will be used later, just saving some resources # by caching the files list qt_bin_files = sum( (getFileList(qt_bin_dir) for qt_bin_dir in self._getQtBinDirs()), [], ) self.info("Copying OpenSSL DLLs to %r." % dist_dir) for filename in qt_bin_files: basename = os.path.basename(filename).lower() if basename in ("libeay32.dll", "ssleay32.dll"): shutil.copy(filename, os.path.join(dist_dir, basename)) if ( "qml" in self.getQtPluginsSelected() or "all" in self.getQtPluginsSelected() ): result += self.copyQmlFiles( full_name=full_name, target_plugin_dir=target_plugin_dir, ) # Also copy required OpenGL DLLs on Windows if isWin32Windows(): opengl_dlls = ("libegl.dll", "libglesv2.dll", "opengl32sw.dll") self.info("Copying OpenGL DLLs to %r." % dist_dir) for filename in qt_bin_files: basename = os.path.basename(filename).lower() if basename in opengl_dlls or basename.startswith( "d3dcompiler_" ): shutil.copy(filename, os.path.join(dist_dir, basename)) return result elif full_name == self.binding_name + ".QtNetwork": if not isWin32Windows(): dll_path = locateDLL("crypto") if dll_path is not None: dist_dll_path = os.path.join(dist_dir, os.path.basename(dll_path)) shutil.copy(dll_path, dist_dll_path) dll_path = locateDLL("ssl") if dll_path is not None: dist_dll_path = os.path.join(dist_dir, os.path.basename(dll_path)) shutil.copy(dll_path, dist_dll_path) elif ( full_name in ( self.binding_name + ".QtWebEngine", self.binding_name + ".QtWebEngineCore", self.binding_name + ".QtWebEngineWidgets", ) and not self.webengine_done ): self.webengine_done = True # prevent multiple copies self.info("Copying QtWebEngine components") plugin_parent = os.path.dirname(self.getQtPluginDirs()[0]) if isWin32Windows(): bin_dir = os.path.join(plugin_parent, "bin") else: # TODO verify this for non-Windows! bin_dir = os.path.join(plugin_parent, "libexec") target_bin_dir = os.path.join(dist_dir) for f in os.listdir(bin_dir): if f.startswith("QtWebEngineProcess"): shutil.copy(os.path.join(bin_dir, f), target_bin_dir) resources_dir = os.path.join(plugin_parent, "resources") target_resources_dir = os.path.join(dist_dir) for f in os.listdir(resources_dir): shutil.copy(os.path.join(resources_dir, f), target_resources_dir) translations_dir = os.path.join(plugin_parent, "translations") pos = len(translations_dir) + 1 target_translations_dir = os.path.join( dist_dir, full_name.getTopLevelPackageName().asPath(), "Qt", "translations", ) for f in getFileList(translations_dir): tar_f = os.path.join(target_translations_dir, f[pos:]) makePath(os.path.dirname(tar_f)) shutil.copyfile(f, tar_f) return ()
def packDistFolderToOnefileLinux(onefile_output_filename, dist_dir, binary_filename): """Pack to onefile binary on Linux. Notes: This is mostly a wrapper around AppImage, which does all the heavy lifting. """ if not locateDLL("fuse"): postprocessing_logger.sysexit("""\ Error, the fuse library (libfuse.so.x from fuse2, *not* fuse3) must be installed for onefile creation to work on Linux.""") # This might be possible to avoid being done with --runtime-file. apprun_filename = os.path.join(dist_dir, "AppRun") putTextFileContents( apprun_filename, contents="""\ #!/bin/bash exec -a $ARGV0 $APPDIR/%s \"$@\"""" % os.path.basename(binary_filename), ) addFileExecutablePermission(apprun_filename) binary_basename = os.path.basename(getResultBasepath()) icon_paths = getIconPaths() assert icon_paths extension = os.path.splitext(icon_paths[0])[1].lower() copyFile(icon_paths[0], getResultBasepath() + extension) putTextFileContents( getResultBasepath() + ".desktop", contents="""\ [Desktop Entry] Name=%(binary_basename)s Exec=%(binary_filename)s Icon=%(binary_basename)s Type=Application Categories=Utility;""" % { "binary_basename": binary_basename, "binary_filename": os.path.basename(binary_filename), }, ) postprocessing_logger.info( "Creating single file from dist folder, this may take a while.") stdout_filename = binary_filename + ".appimage.stdout.txt" stderr_filename = binary_filename + ".appimage.stderr.txt" stdout_file = openTextFile(stdout_filename, "wb") stderr_file = openTextFile(stderr_filename, "wb") command = ( _getAppImageToolPath(for_operation=True, assume_yes_for_downloads=assumeYesForDownloads()), dist_dir, "--comp", getAppImageCompression(), "-n", onefile_output_filename, ) stderr_file.write(b"Executed %r\n" % " ".join(command)) # Starting the process while locked, so file handles are not duplicated, we # need fine grained control over process here, therefore we cannot use the # Execution.executeProcess() function without making it too complex and not # all Python versions allow using with, pylint: disable=consider-using-with # pylint: disable appimagetool_process = subprocess.Popen( command, shell=False, stdin=getNullInput(), stdout=stdout_file, stderr=stderr_file, ) result = appimagetool_process.wait() stdout_file.close() stderr_file.close() if result != 0: # Useless result if there were errors, so now remove it. deleteFile(onefile_output_filename, must_exist=False) stderr = getFileContents(stderr_filename, mode="rb") if b"Text file busy" in stderr: postprocessing_logger.sysexit( "Error, error exit from AppImage because target file is locked." ) if b"modprobe fuse" in stderr: postprocessing_logger.sysexit( "Error, error exit from AppImage because fuse kernel module was not loaded." ) postprocessing_logger.sysexit( "Error, error exit from AppImage, check its outputs '%s' and '%s'." % (stdout_filename, stderr_filename)) if not os.path.exists(onefile_output_filename): postprocessing_logger.sysexit( "Error, expected output file %r not created by AppImage, check its outputs '%s' and '%s'." % (onefile_output_filename, stdout_filename, stderr_filename)) deleteFile(stdout_filename, must_exist=True) deleteFile(stderr_filename, must_exist=True) postprocessing_logger.info("Completed onefile creation.")
def packDistFolderToOnefileLinux(onefile_output_filename, dist_dir, binary_filename): """Pack to onefile binary on Linux. Notes: This is mostly a wrapper around AppImage, which does all the heavy lifting. """ if not locateDLL("fuse"): postprocessing_logger.sysexit( "Error, the fuse library (libfuse.so.x) must be installed for onefile to work on Linux." ) # This might be possible to avoid being done with --runtime-file. apprun_filename = os.path.join(dist_dir, "AppRun") with open(apprun_filename, "w") as output_file: output_file.write("""\ #!/bin/sh exec $APPDIR/%s $@""" % os.path.basename(binary_filename)) addFileExecutablePermission(apprun_filename) binary_basename = os.path.basename(getResultBasepath()) icon_paths = getIconPaths() assert icon_paths extension = os.path.splitext(icon_paths[0])[1].lower() shutil.copyfile(icon_paths[0], getResultBasepath() + extension) with open(getResultBasepath() + ".desktop", "w") as output_file: output_file.write( """\ [Desktop Entry] Name=%(binary_basename)s Exec=%(binary_filename)s Icon=%(binary_basename)s Type=Application Categories=Utility;""" % { "binary_basename": binary_basename, "binary_filename": os.path.basename(binary_filename), }) postprocessing_logger.info( "Creating single file from dist folder, this may take a while.") # Starting the process while locked, so file handles are not duplicated. appimagetool_process = subprocess.Popen( ( getAppImageToolPath(), dist_dir, "--comp", "xz", "-n", onefile_output_filename, ), shell=False, stdin=getNullInput(), stderr=getNullOutput(), stdout=getNullOutput(), ) # TODO: Exit code should be checked. result = appimagetool_process.wait() if not os.path.exists(onefile_output_filename): postprocessing_logger.sysexit( "Error, expected output file %s not created by AppImage." % onefile_output_filename) postprocessing_logger.info("Completed onefile creation.") assert result == 0, result