def checkDigest(self, downloadRetriesLeft=3): CraftCore.log.debug("ArchiveSource.checkDigest called") filenames = self.localFileNames() if self.subinfo.hasTargetDigestUrls(): CraftCore.log.debug("check digests urls") if not CraftHash.checkFilesDigests(self.__downloadDir, filenames): CraftCore.log.error("invalid digest file") redownload = downloadRetriesLeft and CraftChoicePrompt.promptForChoice( "Do you want to delete the files and redownload them?", [("Yes", True), ("No", False)], default="Yes") if redownload: for filename in filenames: CraftCore.log.info( f"Deleting downloaded file: {filename}") utils.deleteFile( os.path.join(self.__downloadDir, filename)) for digestAlgorithm, digestFileEnding in CraftHash.HashAlgorithm.fileEndings( ).items(): digestFileName = filename + digestFileEnding if os.path.exists( os.path.join(self.__downloadDir, digestFileName)): CraftCore.log.info( f"Deleting downloaded file: {digestFileName}" ) utils.deleteFile( os.path.join(self.__downloadDir, digestFileName)) return self.fetch() and self.checkDigest( downloadRetriesLeft - 1) return False elif self.subinfo.hasTargetDigests(): CraftCore.log.debug("check digests") digests, algorithm = self.subinfo.targetDigest() if not CraftHash.checkFilesDigests(self.__downloadDir, filenames, digests, algorithm): CraftCore.log.error("invalid digest file") redownload = downloadRetriesLeft and CraftChoicePrompt.promptForChoice( "Do you want to delete the files and redownload them?", [("Yes", True), ("No", False)], default="Yes") if redownload: for filename in filenames: CraftCore.log.info( f"Deleting downloaded file: {filename}") utils.deleteFile( os.path.join(self.__downloadDir, filename)) return self.fetch() and self.checkDigest( downloadRetriesLeft - 1) return False else: CraftCore.log.debug("print source file digests") CraftHash.printFilesDigests( self.__downloadDir, filenames, self.subinfo.buildTarget, algorithm=CraftHash.HashAlgorithm.SHA256) return True
def checkDigest(self): craftDebug.log.debug("ArchiveSource.checkDigest called") filenames = self.localFileNames() if self.subinfo.hasTargetDigestUrls(): craftDebug.log.debug("check digests urls") if not CraftHash.checkFilesDigests(CraftStandardDirs.downloadDir(), filenames): craftDebug.log.error("invalid digest file") return False elif self.subinfo.hasTargetDigests(): craftDebug.log.debug("check digests") digests, algorithm = self.subinfo.targetDigest() if not CraftHash.checkFilesDigests(CraftStandardDirs.downloadDir(), filenames, digests, algorithm): craftDebug.log.error("invalid digest file") return False else: craftDebug.log.debug("print source file digests") CraftHash.printFilesDigests( CraftStandardDirs.downloadDir(), filenames, self.subinfo.buildTarget, algorithm=CraftHash.HashAlgorithm.SHA256) return True
def createPackage(self): """ create a package """ CraftCore.log.debug("packaging using the MacDMGPackager") defines = self.setDefaults(self.defines) # TODO: provide an image with dbg files if not self.internalCreatePackage(defines): return False appPath = self.getMacAppPath(defines) if not appPath: return False archive = os.path.normpath(self.archiveDir()) CraftCore.log.info(f"Packaging {appPath}") dmgDest = defines["setupname"] if os.path.exists(dmgDest): utils.deleteFile(dmgDest) appName = defines['appname'] + ".app" if not utils.system(["create-dmg", "--volname", os.path.basename(dmgDest), # Add a drop link to /Applications: "--icon", appName, "140", "150", "--app-drop-link", "350", "150", dmgDest, appPath]): return False if not CodeSign.signMacPackage(dmgDest): return False CraftHash.createDigestFiles(dmgDest) return True
def _createArchive(self, archiveName, sourceDir, destDir, createDigests=True) -> bool: archiveName = str((Path(destDir) / archiveName)) if not utils.compress(archiveName, sourceDir): return False if createDigests: if CraftCore.settings.getboolean( "Packager", "CreateCache") and CraftCore.settings.get( "Packager", "PackageType") == "SevenZipPackager": if CraftCore.settings.getboolean("ContinuousIntegration", "UpdateRepository", False): manifestUrls = [self.cacheRepositoryUrls()[0]] else: CraftCore.log.warning( f"Creating new cache, if you want to extend an existing cache, set \"[ContinuousIntegration]UpdateRepository = True\"" ) manifestUrls = None self._generateManifest(destDir, archiveName, manifestLocation=self.cacheLocation(), manifestUrls=manifestUrls) else: self._generateManifest(destDir, archiveName) CraftHash.createDigestFiles(archiveName) return True
def _compress(self, archiveName, sourceDir, destDir): utils.deleteFile(archiveName) app = CraftCore.cache.findApplication("7za") kw = {} progressFlags = [] if CraftCore.cache.checkCommandOutputFor(app, "-bs"): progressFlags = ["-bso2", "-bsp1"] kw["stderr"] = subprocess.PIPE archive = os.path.join(destDir, archiveName) if os.path.isfile(archive): utils.deleteFile(archive) cmd = [app, "a", "-r", archive, os.path.join(sourceDir, "*")] + progressFlags if not utils.system(cmd, displayProgress=True, **kw): CraftCore.log.critical(f"while packaging. cmd: {cmd}") if not CraftCore.settings.getboolean("Packager", "CreateCache"): self._generateManifest(destDir, archiveName) CraftHash.createDigestFiles(archive) else: if CraftCore.settings.getboolean("ContinuousIntegration", "UpdateRepository", False): manifestUrls = [self.cacheRepositoryUrls()[0]] else: manifestUrls = None self._generateManifest(destDir, archiveName, manifestLocation=self.cacheLocation(), manifestUrls=manifestUrls)
def _compress(self, archiveName, sourceDir, destDir, createDigests=True) -> bool: archive = os.path.join(destDir, archiveName) utils.createDir(os.path.dirname(archive)) if os.path.isfile(archive): utils.deleteFile(archive) if OsUtils.isUnix(): if not self.__xz(archive, sourceDir): return False else: if not self.__7z(archive, sourceDir): return False if createDigests: if not CraftCore.settings.getboolean("Packager", "CreateCache"): self._generateManifest(destDir, archiveName) CraftHash.createDigestFiles(archive) else: if CraftCore.settings.getboolean("ContinuousIntegration", "UpdateRepository", False): manifestUrls = [self.cacheRepositoryUrls()[0]] else: manifestUrls = None self._generateManifest(destDir, archiveName, manifestLocation=self.cacheLocation(), manifestUrls=manifestUrls) return True
def _createArchive(self, archiveName, sourceDir, destDir, createDigests=True, extention=None) -> bool: if extention is None: extention = "." + CraftCore.settings.get("Packager", "7ZipArchiveType", "7z") if extention == ".7z" and CraftCore.compiler.isUnix: if self.package.path == "dev-utils/7zip" or not CraftCore.cache.findApplication( "7za"): extention = ".tar.xz" else: extention = ".tar.7z" archiveName = str((Path(destDir) / archiveName)) + extention if not utils.compress(archiveName, sourceDir): return False if createDigests: if not CraftCore.settings.getboolean("Packager", "CreateCache"): self._generateManifest(destDir, archiveName) CraftHash.createDigestFiles(archiveName) else: if CraftCore.settings.getboolean("ContinuousIntegration", "UpdateRepository", False): manifestUrls = [self.cacheRepositoryUrls()[0]] else: manifestUrls = None self._generateManifest(destDir, archiveName, manifestLocation=self.cacheLocation(), manifestUrls=manifestUrls) return True
def checkDigest(self, downloadRetriesLeft=3): CraftCore.log.debug("ArchiveSource.checkDigest called") filenames = self.localFileNames() if self.subinfo.hasTargetDigestUrls(): CraftCore.log.debug("check digests urls") if not CraftHash.checkFilesDigests(self.__downloadDir, filenames): CraftCore.log.error("invalid digest file") return self.__retry(downloadRetriesLeft, self.__redownload, filenames=filenames) elif self.subinfo.hasTargetDigests(): CraftCore.log.debug("check digests") digests, algorithm = self.subinfo.targetDigest() if not CraftHash.checkFilesDigests(self.__downloadDir, filenames, digests, algorithm): CraftCore.log.error("invalid digest file") return self.__retry(downloadRetriesLeft, self.__redownload, filenames=filenames) else: CraftCore.log.debug("print source file digests") CraftHash.printFilesDigests( self.__downloadDir, filenames, self.subinfo.buildTarget, algorithm=CraftHash.HashAlgorithm.SHA256) return True
def _compress(self, archiveName, sourceDir, destDir): utils.deleteFile(archiveName) app = utils.utilsCache.findApplication("7za") kw = {} progressFlags = "" if utils.utilsCache.checkCommandOutputFor(app, "-bs"): progressFlags = " -bso2 -bsp1" kw["stderr"] = subprocess.PIPE archive = os.path.join(destDir, archiveName) if os.path.isfile(archive): utils.deleteFile(archive) cmd = f"\"{app}\" a {progressFlags} -r \"{archive}\" \"{sourceDir}/*\"" if not utils.system(cmd, displayProgress=True, **kw): craftDebug.log.critical(f"while packaging. cmd: {cmd}") if not craftSettings.getboolean("Packager", "CreateCache"): CraftHash.createDigestFiles(archive) else: cacheFilePath = os.path.join(self.cacheLocation(), "manifest.json") if os.path.exists(cacheFilePath): with open(cacheFilePath, "rt+") as cacheFile: cache = json.load(cacheFile) else: cache = {} if not str(self) in cache: cache[str(self)] = {} cache[str(self)][archiveName] = { "checksum": CraftHash.digestFile(archive, CraftHash.HashAlgorithm.SHA256) } with open(cacheFilePath, "wt+") as cacheFile: json.dump(cache, cacheFile, sort_keys=True, indent=2)
def test_printFilesDigests(self): path, name = os.path.split(self.tmpFile) log = io.StringIO() oldLog = CraftCore.debug._handler.stream CraftCore.debug._handler.stream = log CraftHash.printFilesDigests(path, [name], "test", CraftHash.HashAlgorithm.SHA256) self.assertEquals( "Digests for test: (['4fc1e96dc5ecf625efe228fce1b0964b6302cfa4d4fb2bb8d16c665d23f6ff30'], CraftHash.HashAlgorithm.SHA256)\n", log.getvalue()) CraftCore.debug._handler.stream = oldLog
def createPackage(self): """ create a package """ if not self.isNsisInstalled(): return False craftDebug.log.debug("packaging using the NullsoftInstallerPackager") self.internalCreatePackage() self.preArchive() self.generateNSISInstaller() CraftHash.createDigestFiles(self.defines["setupname"]) return True
def createPackage(self): """ create a package """ CraftCore.log.debug("packaging using the MacDMGPackager") self.internalCreatePackage() self.preArchive() self._setDefaults() archive = self.archiveDir() appPath = os.path.join(archive, self.defines['apppath'], f"{self.defines['appname']}.app") if os.path.exists(os.path.join(archive, "lib/plugins")): utils.mergeTree(os.path.join(archive, "lib/plugins"), os.path.join(appPath, "Contents/PlugIns/")) targetLibdir = os.path.join(appPath, "Contents/Frameworks/") if not os.path.exists(targetLibdir): os.makedirs(targetLibdir) if os.path.exists(os.path.join(archive, "lib")): utils.mergeTree(os.path.join(archive, "lib"), targetLibdir) if os.path.exists(os.path.join(archive, "share")): utils.mergeTree(os.path.join(archive, "share"), os.path.join(appPath, "Contents/Resources/")) utils.mergeTree(os.path.join(archive, "bin"), os.path.join(appPath, "Contents/MacOS/")) env = os.environ env['DYLD_LIBRARY_PATH'] = os.path.join(CraftStandardDirs.craftRoot(), "lib") if not utils.systemWithoutShell([ "dylibbundler", "-of", "-b", "-p", "@executable_path/../Frameworks", "-d", targetLibdir, "-x", f"{appPath}/Contents/MacOS/{self.defines['appname']}" ], env=env): CraftCore.log.warning("Failed to run dylibbundler") if not utils.systemWithoutShell([ "macdeployqt", appPath, "-always-overwrite", "-dmg", "-verbose=2" ], env=env): CraftCore.log.warning("Failed to run macdeployqt!") dmgSrc = appPath.replace(".app", ".dmg") dmgDest = os.path.join(self.packageDestinationDir(), os.path.basename(dmgSrc)) utils.copyFile(dmgSrc, dmgDest, linkOnly=False) CraftHash.createDigestFiles(dmgDest) return True
def createPackage(self): """ create a package """ if not self.isNsisInstalled(): return False CraftCore.log.debug("packaging using the NullsoftInstallerPackager") if not super().createPackage(): return False if not self.generateNSISInstaller(): return False destDir, archiveName = os.path.split(self.setupName) self._generateManifest(destDir, archiveName) CraftHash.createDigestFiles(self.setupName) return True
def fetchBinary(self) -> bool: if self.subinfo.options.package.disableBinaryCache: return False archiveName = self.binaryArchiveName(includePackagePath=True) archvieFolder, localArchiveName = self.binaryArchiveName(includePackagePath=True).rsplit("/", 1) downloadFolder = os.path.join(self.cacheLocation(), archvieFolder) print(downloadFolder) if not os.path.exists(downloadFolder): os.makedirs(downloadFolder) for url in [self.cacheLocation()] + self.cacheRepositoryUrls(): craftDebug.log.debug(f"Trying to restore {archiveName} from cache: {url}.") cache = utils.utilsCache.cacheJsonFromUrl(f"{url}/manifest.json") if not cache or not str(self) in cache or not archiveName in cache[str(self)]: continue if url != self.cacheLocation(): if not os.path.exists(os.path.join(downloadFolder, localArchiveName)): if not utils.getFile(f"{url}/{archiveName}", downloadFolder, localArchiveName): return False return CraftHash.checkFilesDigests(downloadFolder, [localArchiveName], digests=cache[str(self)][archiveName]["checksum"], digestAlgorithm=CraftHash.HashAlgorithm.SHA256) and \ self.cleanImage() \ and utils.unpackFile(downloadFolder, localArchiveName, self.imageDir()) \ and self.qmerge() return False
def fetchBinary(self) -> bool: if self.subinfo.options.package.disableBinaryCache: return False for url in [self.cacheLocation()] + self.cacheRepositoryUrls(): CraftCore.log.debug(f"Trying to restore {self} from cache: {url}.") if url == self.cacheLocation(): fileUrl = f"{url}/manifest.json" if os.path.exists(fileUrl): with open(fileUrl, "rt", encoding="UTF-8") as f: manifest = CraftManifest.fromJson(json.load(f)) else: continue else: manifest = CraftManifest.fromJson(CraftCore.cache.cacheJsonFromUrl(f"{url}/manifest.json")) fileEntry = manifest.get(str(self)).files files = [] for f in fileEntry: if f.version == self.version: files.append(f) latest = None if not files: CraftCore.log.debug(f"Could not find {self}={self.version} in {url}") continue latest = files[0] if url != self.cacheLocation(): downloadFolder = self.cacheLocation(os.path.join(CraftCore.standardDirs.downloadDir(), "cache")) else: downloadFolder = self.cacheLocation() localArchiveAbsPath = OsUtils.toNativePath(os.path.join(downloadFolder, latest.fileName)) localArchivePath, localArchiveName = os.path.split(localArchiveAbsPath) if url != self.cacheLocation(): if not os.path.exists(localArchiveAbsPath): os.makedirs(localArchivePath, exist_ok=True) fUrl = f"{url}/{latest.fileName}" if not utils.getFile(fUrl, localArchivePath, localArchiveName): CraftCore.log.warning(f"Failed to fetch {fUrl}") return False elif not os.path.isfile(localArchiveAbsPath): continue if not CraftHash.checkFilesDigests(localArchivePath, [localArchiveName], digests=latest.checksum, digestAlgorithm=CraftHash.HashAlgorithm.SHA256): CraftCore.log.warning(f"Hash did not match, {localArchiveName} might be corrupted") return False self.subinfo.buildPrefix = latest.buildPrefix if not (self.cleanImage() and utils.unpackFile(localArchivePath, localArchiveName, self.imageDir()) and self.internalPostInstall() and self.postInstall() and self.qmerge() and self.internalPostQmerge() and self.postQmerge()): return False return True return False
def createPackage(self): """ create a package """ if not self.isNsisInstalled(): return False CraftCore.log.debug("packaging using the NullsoftInstallerPackager") if CraftCore.compiler.isMSVC(): # we use the redist installer self.ignoredPackages.append("libs/runtime") self.internalCreatePackage() self.generateNSISInstaller() destDir, archiveName = os.path.split(self.defines["setupname"]) self._generateManifest(destDir, archiveName) CraftHash.createDigestFiles(self.defines["setupname"]) return True
def unmergeFileList(rootdir, fileList): """ delete files in the fileList if has matches """ for filename, filehash in fileList: fullPath = os.path.join(rootdir, os.path.normcase(filename)) if os.path.isfile(fullPath) or os.path.islink(fullPath): if filehash: algorithm = CraftHash.HashAlgorithm.getAlgorithmFromPrefix( filehash) currentHash = algorithm.stringPrefix( ) + CraftHash.digestFile(fullPath, algorithm) if not filehash or currentHash == filehash: OsUtils.rm(fullPath, True) else: CraftCore.log.warning( f"We can't remove {fullPath} as its hash has changed," f" that usually implies that the file was modified or replaced" ) elif not os.path.isdir(fullPath) and os.path.lexists(fullPath): CraftCore.log.debug(f"Remove a dead symlink {fullPath}") OsUtils.rm(fullPath, True) elif not os.path.isdir(fullPath): CraftCore.log.warning("file %s does not exist" % fullPath) containingDir = os.path.dirname(fullPath) if os.path.exists(containingDir) and not os.listdir(containingDir): CraftCore.log.debug(f"Delete empty dir {containingDir}") utils.rmtree(containingDir)
def createPackage(self): """ create a package """ CraftCore.log.debug("packaging using the MacDMGPackager") if not CraftCore.cache.findApplication("packagesutil"): CraftCore.log.critical( "Craft requires dev-utils/packagesdev to create a package, please install dev-utils/packagesdev\n" "\t'craft dev-utils/packagesdev'") return False defines = self.setDefaults(self.defines) if not "pkgproj" in defines: CraftCore.log.error( "Cannot not create .pkg because no .pkgproj was defined.") return False if not self.internalCreatePackage( defines, seperateSymbolFiles=True, packageSymbols=True): return False packageDest = Path(defines["setupname"]) if packageDest.exists(): utils.deleteFile(packageDest) pkgprojPath = defines["pkgproj"] # set output file basename packagesutil = CraftCore.cache.findApplication("packagesutil") if not utils.system([ packagesutil, '--file', pkgprojPath, 'set', 'project', 'name', packageDest.stem ]): return False packagesbuild = CraftCore.cache.findApplication("packagesbuild") if not utils.system([ packagesbuild, "-v", '--reference-folder', os.path.dirname(self.getMacAppPath(defines)), '--build-folder', packageDest.parent, pkgprojPath ]): return False if not CodeSign.signMacPackage(packageDest): return False CraftHash.createDigestFiles(packageDest) return True
def unmergeFileList(rootdir, fileList): """ delete files in the fileList if has matches """ for filename, filehash in fileList: fullPath = os.path.join(rootdir, os.path.normcase(filename)) if os.path.isfile(fullPath): algorithm = CraftHash.HashAlgorithm.getAlgorithmFromPrefix(filehash) if not algorithm: currentHash = CraftHash.digestFile(fullPath, CraftHash.HashAlgorithm.MD5) else: currentHash = algorithm.stringPrefix() + CraftHash.digestFile(fullPath, algorithm) if currentHash == filehash or filehash == "": OsUtils.rm(fullPath, True) else: craftDebug.log.warning( f"We can't remove {fullPath} as its hash has changed," f" that usually implies that the fiel was modified or replaced") elif not os.path.isdir(fullPath): craftDebug.log.warning("file %s does not exist" % fullPath)
def createPackage(self): """ create a package """ if not self.isInnoInstalled(): return False CraftCore.log.debug("packaging using the InnoSetupPackager") defines = self.setDefaults(self.defines) if not self.internalCreatePackage(defines, True): return False if not self.generateInnoInstaller(defines): return False destDir, archiveName = os.path.split(defines["setupname"]) self._generateManifest(destDir, archiveName) CraftHash.createDigestFiles(defines["setupname"]) return True
def getFileListFromDirectory(imagedir, filePaths): """ create a file list containing hashes """ ret = [] algorithm = CraftHash.HashAlgorithm.SHA256 for filePath in filePaths: relativeFilePath = os.path.relpath(filePath, imagedir) digest = algorithm.stringPrefix() + CraftHash.digestFile(filePath, algorithm) ret.append((relativeFilePath, digest)) return ret
def _generateManifest(self, destDir, archiveName, manifestLocation=None, manifestUrls=None): if not manifestLocation: manifestLocation = destDir manifestLocation = os.path.join(manifestLocation, "manifest.json") archiveFile = os.path.join(destDir, archiveName) name = archiveName if not os.path.isabs(archiveName) else os.path.relpath(archiveName, destDir) manifest = CraftManifest.load(manifestLocation, urls=manifestUrls) entry = manifest.get(str(self)) entry.addFile(name, CraftHash.digestFile(archiveFile, CraftHash.HashAlgorithm.SHA256), version=self.version) manifest.dump(manifestLocation)
def __fetchFromArchiveCache(self, downloadRetriesLeft : int=3): for url, files in self._getFileInfoFromArchiveCache(): self.__downloadDir.mkdir(parents=True, exist_ok=True) for entry in files: if entry.version != self.buildTarget: continue if not GetFiles.getFile(utils.urljoin(url, entry.fileName), self.__archiveDir, entry.fileName): self.__retry(downloadRetriesLeft, self.__fetchFromArchiveCache) if not CraftHash.checkFilesDigests(self.__archiveDir, [entry.fileName], digests=entry.checksum, digestAlgorithm=CraftHash.HashAlgorithm.SHA256): return self.__retry(downloadRetriesLeft, lambda downloadRetriesLeft: utils.deleteFile(self.__archiveDir / entry.fileName) and self.__fetchFromArchiveCache(downloadRetriesLeft)) if self.__checkFilesPresent(self.localFileNames()): return True return False
def generateSrcManifest(self) -> bool: archiveNames = self.localFileNames() if self.subinfo.hasTargetDigestUrls(): url, alg = self.subinfo.targetDigestUrl() archiveNames.append(self.subinfo.archiveName()[0] + CraftHash.HashAlgorithm.fileEndings().get(alg)) manifestLocation = os.path.join(self.__archiveDir, "manifest.json") manifest = CraftManifest.load(manifestLocation, urls=CraftCore.settings.getList("Packager", "ArchiveRepositoryUrl")) entry = manifest.get(str(self), compiler="all") entry.files.clear() for archiveName in archiveNames: name = (Path(self.package.path) / archiveName).as_posix() archiveFile = self.__downloadDir / archiveName if not archiveFile.is_file(): continue digests = CraftHash.digestFile(archiveFile, CraftHash.HashAlgorithm.SHA256) entry.addFile(name, digests, version=self.version) manifest.dump(manifestLocation) return True
def hashTest(self, hash, algorithm): path, name = os.path.split(self.tmpFile) self.assertEquals(CraftHash.checkFilesDigests(path, [name], hash, algorithm), True)
def createPackage(self): """ create a package """ CraftCore.log.debug("packaging using the MacDMGPackager") if not self.internalCreatePackage(): return False self._setDefaults() archive = os.path.normpath(self.archiveDir()) appPath = self.defines['apppath'] if not appPath: apps = glob.glob(os.path.join(archive, f"**/{self.defines['appname']}.app"), recursive=True) if len(apps) != 1: CraftCore.log.error( f"Failed to detect {self.defines['appname']}.app for {self}, please provide a correct self.defines['apppath'] or a relative path to the app as self.defines['apppath']" ) return False appPath = apps[0] appPath = os.path.join(archive, appPath) appPath = os.path.normpath(appPath) CraftCore.log.info(f"Packaging {appPath}") targetLibdir = os.path.join(appPath, "Contents", "Frameworks") utils.createDir(targetLibdir) moveTargets = [(os.path.join(archive, "lib", "plugins"), os.path.join(appPath, "Contents", "PlugIns")), (os.path.join(archive, "plugins"), os.path.join(appPath, "Contents", "PlugIns")), (os.path.join(archive, "lib"), targetLibdir), (os.path.join(archive, "share"), os.path.join(appPath, "Contents", "Resources"))] if not appPath.startswith(archive): moveTargets += [(os.path.join(archive, "bin"), os.path.join(appPath, "Contents", "MacOS"))] for src, dest in moveTargets: if os.path.exists(src): if not utils.mergeTree(src, dest): return False dylibbundler = MacDylibBundler(appPath) with utils.ScopedEnv({ 'DYLD_FALLBACK_LIBRARY_PATH': targetLibdir + ":" + os.path.join(CraftStandardDirs.craftRoot(), "lib") }): CraftCore.log.info("Bundling main binary dependencies...") mainBinary = Path(appPath, "Contents", "MacOS", self.defines['appname']) if not dylibbundler.bundleLibraryDependencies(mainBinary): return False # Fix up the library dependencies of files in Contents/Frameworks/ CraftCore.log.info("Bundling library dependencies...") if not dylibbundler.fixupAndBundleLibsRecursively( "Contents/Frameworks"): return False CraftCore.log.info("Bundling plugin dependencies...") if not dylibbundler.fixupAndBundleLibsRecursively( "Contents/PlugIns"): return False if not utils.system( ["macdeployqt", appPath, "-always-overwrite", "-verbose=1"]): return False # macdeployqt might just have added some explicitly blacklisted files blackList = Path(self.packageDir(), "mac_blacklist.txt") if blackList.exists(): blackList = [self.read_blacklist(str(blackList))] # use it as whitelist as we want only matches, ignore all others matches = utils.filterDirectoryContent( appPath, whitelist=lambda x, root: self.blacklisted(x, blackList), blacklist=lambda x, root: True) for f in matches: CraftCore.log.info(f"Remove blacklisted file: {f}") utils.deleteFile(f) # macdeployqt adds some more plugins so we fix the plugins after calling macdeployqt dylibbundler.checkedLibs = set( ) # ensure we check all libs again (but # we should not need to make any changes) CraftCore.log.info( "Fixing plugin dependencies after macdeployqt...") if not dylibbundler.fixupAndBundleLibsRecursively( "Contents/PlugIns"): return False CraftCore.log.info( "Fixing library dependencies after macdeployqt...") if not dylibbundler.fixupAndBundleLibsRecursively( "Contents/Frameworks"): return False # Finally sanity check that we don't depend on absolute paths from the builder CraftCore.log.info( "Checking for absolute library paths in package...") found_bad_dylib = False # Don't exit immeditately so that we log all the bad libraries before failing: if not dylibbundler.areLibraryDepsOkay(mainBinary): found_bad_dylib = True CraftCore.log.error( "Found bad library dependency in main binary %s", mainBinary) if not dylibbundler.checkLibraryDepsRecursively( "Contents/Frameworks"): CraftCore.log.error( "Found bad library dependency in bundled libraries") found_bad_dylib = True if not dylibbundler.checkLibraryDepsRecursively( "Contents/PlugIns"): CraftCore.log.error( "Found bad library dependency in bundled plugins") found_bad_dylib = True if found_bad_dylib: CraftCore.log.error( "Cannot not create .dmg since the .app contains a bad library depenency!" ) return False name = self.binaryArchiveName(fileType="", includeRevision=True) dmgDest = os.path.join(self.packageDestinationDir(), f"{name}.dmg") if os.path.exists(dmgDest): utils.deleteFile(dmgDest) appName = self.defines['appname'] + ".app" if not utils.system([ "create-dmg", "--volname", name, # Add a drop link to /Applications: "--icon", appName, "140", "150", "--app-drop-link", "350", "150", dmgDest, appPath ]): return False CraftHash.createDigestFiles(dmgDest) return True
def fetchBinary(self, downloadRetriesLeft=3) -> bool: if self.subinfo.options.package.disableBinaryCache: return False for url in [self.cacheLocation()] + self.cacheRepositoryUrls(): CraftCore.log.debug(f"Trying to restore {self} from cache: {url}.") if url == self.cacheLocation(): fileUrl = f"{url}/manifest.json" if os.path.exists(fileUrl): with open(fileUrl, "rt", encoding="UTF-8") as f: manifest = CraftManifest.fromJson(json.load(f)) else: continue else: manifest = CraftManifest.fromJson( CraftCore.cache.cacheJsonFromUrl(f"{url}/manifest.json")) fileEntry = manifest.get(str(self)).files files = [] for f in fileEntry: if f.version == self.version: files.append(f) if not files: CraftCore.log.info( f"Could not find {self}={self.version} in {url}") continue latest = files[0] if not self.subinfo.options.dynamic.compatible( latest.config, latest.configHash): CraftCore.log.info( "Failed to restore package, configuration missmatch") CraftCore.debug.debug_line() CraftCore.log.info("Cached config: {}".format(", ".join( f"{k}={v}" for k, v in latest.config.items()))) CraftCore.log.info( f"Local config: {self.subinfo.options.dynamic}") CraftCore.debug.debug_line() # try next cache continue # if we are creating the cache, a rebuild on a failed fetch would be suboptimal createingCache = CraftCore.settings.getboolean( "Packager", "CreateCache", False) if url != self.cacheLocation(): downloadFolder = self.cacheLocation( os.path.join(CraftCore.standardDirs.downloadDir(), "cache")) else: downloadFolder = self.cacheLocation() localArchiveAbsPath = OsUtils.toNativePath( os.path.join(downloadFolder, latest.fileName)) localArchivePath, localArchiveName = os.path.split( localArchiveAbsPath) if url != self.cacheLocation(): if not os.path.exists(localArchiveAbsPath): os.makedirs(localArchivePath, exist_ok=True) fileName = latest.fileName if CraftCore.compiler.isWindows: fileName = fileName.replace("\\", "/") fUrl = f"{url}/{fileName}" # try it up to 3 times retries = 3 while True: if GetFiles.getFile(fUrl, localArchivePath, localArchiveName): break msg = f"Failed to fetch {fUrl}" retries -= 1 if not retries: if createingCache: raise BlueprintException(msg, self.package) else: CraftCore.log.warning(msg) return False elif not os.path.isfile(localArchiveAbsPath): continue if not CraftHash.checkFilesDigests( localArchivePath, [localArchiveName], digests=latest.checksum, digestAlgorithm=CraftHash.HashAlgorithm.SHA256): msg = f"Hash did not match, {localArchiveName} might be corrupted" CraftCore.log.warning(msg) if downloadRetriesLeft and CraftChoicePrompt.promptForChoice( "Do you want to delete the files and redownload them?", [("Yes", True), ("No", False)], default="Yes"): return utils.deleteFile( localArchiveAbsPath) and self.fetchBinary( downloadRetriesLeft=downloadRetriesLeft - 1) if createingCache: raise BlueprintException(msg, self.package) return False self.subinfo.buildPrefix = latest.buildPrefix self.subinfo.isCachedBuild = True if not (self.cleanImage() and utils.unpackFile( localArchivePath, localArchiveName, self.imageDir()) and self.internalPostInstall() and self.postInstall() and self.qmerge() and self.internalPostQmerge() and self.postQmerge()): return False return True return False
def createPackage(self): """ create a package """ CraftCore.log.debug("packaging using the MacDMGPackager") if not self.internalCreatePackage(): return False self._setDefaults() archive = os.path.normpath(self.archiveDir()) appPath = self.defines['apppath'] if not appPath: apps = glob.glob(os.path.join(archive, f"**/{self.defines['appname']}.app"), recursive=True) if len(apps) != 1: CraftCore.log.error(f"Failed to detect {self.defines['appname']}.app for {self}, please provide a correct self.defines['apppath'] or a relative path to the app as self.defines['apppath']") return False appPath = apps[0] appPath = os.path.join(archive, appPath) appPath = os.path.normpath(appPath) CraftCore.log.info(f"Packaging {appPath}") targetLibdir = os.path.join(appPath, "Contents", "Frameworks") utils.createDir(targetLibdir) moveTargets = [ (os.path.join(archive, "lib", "plugins"), os.path.join(appPath, "Contents", "PlugIns")), (os.path.join(archive, "plugins"), os.path.join(appPath, "Contents", "PlugIns")), (os.path.join(archive, "lib"), targetLibdir), (os.path.join(archive, "share"), os.path.join(appPath, "Contents", "Resources"))] if not appPath.startswith(archive): moveTargets += [(os.path.join(archive, "bin"), os.path.join(appPath, "Contents", "MacOS"))] for src, dest in moveTargets: if os.path.exists(src): if not utils.mergeTree(src, dest): return False with utils.ScopedEnv({'DYLD_FALLBACK_LIBRARY_PATH' : os.path.join(CraftStandardDirs.craftRoot(), "lib")}): if not utils.system(["dylibbundler", "--overwrite-files", "--bundle-deps", "--install-path", "@executable_path/../Frameworks", "--dest-dir", targetLibdir, "--fix-file", os.path.join(appPath, "Contents", "MacOS", self.defines['appname'])]): return False if not utils.system(["macdeployqt", appPath, "-always-overwrite", "-verbose=1"]): return False name = self.binaryArchiveName(fileType="", includeRevision=True) dmgDest = os.path.join(self.packageDestinationDir(), f"{name}.dmg") if os.path.exists(dmgDest): utils.deleteFile(dmgDest) if not utils.system(["create-dmg", "--volname", name, dmgDest, appPath]): return False CraftHash.createDigestFiles(dmgDest) return True
def test_createDigestFiles(self): # TODO: check file content algorithms = CraftHash.HashAlgorithm.__members__.values() CraftHash.createDigestFiles(self.tmpFile, algorithms=algorithms) for algorithm in algorithms: self.assertEquals(os.path.exists(self.tmpFile + algorithm.fileEnding()), True)