Ejemplo n.º 1
0
    def copyFiles(self, srcDir, destDir) -> bool:
        """
            Copy the binaries for the Package from srcDir to the imageDir
            directory
        """
        CraftCore.log.debug("Copying %s -> %s" % (srcDir, destDir))

        filesToSign = []
        # Only sign all files on Windows. On MacOS we recursively sign the whole .app Folder
        doSign = CraftCore.compiler.isWindows and CraftCore.settings.getboolean("CodeSigning", "Enabled", False)
        if doSign and CraftCore.settings.getboolean("CodeSigning", "SignCache", False):
            # files from the cache are already signed
            doSign = os.path.samefile(srcDir, self.imageDir())

        for entry in utils.filterDirectoryContent(srcDir, self.whitelisted, self.blacklisted, handleAppBundleAsFile=True):
            if not self._filterQtBuildType(entry):
                continue
            entry_target = os.path.join(destDir, os.path.relpath(entry, srcDir))
            if os.path.isfile(entry) or os.path.islink(entry):
                if not utils.copyFile(entry, entry_target, linkOnly=False):
                    return False
                if utils.isBinary(entry_target):
                    if doSign:
                        filesToSign.append(entry_target)
            else:
                # .app or .dSYM
                assert CraftCore.compiler.isMacOS
                if not utils.copyDir(entry, entry_target, linkOnly=False):
                    return False
        if filesToSign:
            if not CodeSign.signWindows(filesToSign):
                return False
        return True
Ejemplo n.º 2
0
    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
Ejemplo n.º 3
0
    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
Ejemplo n.º 4
0
    def __createSideloadAppX(self, defines) -> bool:
        def appendToPublisherString(publisher: [str], field: str, key: str) -> None:
            data = CraftCore.settings.get("CodeSigning", key, "")
            if data:
                publisher += [f"{field}={data}"]

        publisher = []
        appendToPublisherString(publisher, "CN", "CommonName")
        appendToPublisherString(publisher, "O", "Organization")
        appendToPublisherString(publisher, "STREET", "Street")
        appendToPublisherString(publisher, "L", "Locality")
        appendToPublisherString(publisher, "S", "State")
        appendToPublisherString(publisher, "PostalCode", "PostalCode")
        appendToPublisherString(publisher, "C", "Country")
        defines["publisher"] = ", ".join(publisher)
        setupName = os.path.join(self.packageDestinationDir(), "{0}-sideload{1}".format(*os.path.splitext(os.path.basename(defines["setupname"]))))
        defines["setupname"] =  setupName
        return self.__createAppX(defines) and CodeSign.signWindows([setupName])
Ejemplo n.º 5
0
    def generateNSISInstaller(self, defines):
        """ runs makensis to generate the installer itself """
        defines["dataPath"] = defines["setupname"]
        defines["dataName"] = os.path.basename(defines["dataPath"])
        defines["setupname"] = str(Path(defines["setupname"]).with_suffix(".exe"))
        defines["7za"] = CraftCore.cache.findApplication("7za") if CraftCore.compiler.isX64() else CraftCore.cache.findApplication("7za_32")
        # provide the actual installation size in kb, ignore the 7z size as it gets removed after the install
        defines["installSize"] = str(int((self.folderSize(self.archiveDir()) - os.path.getsize(defines["dataPath"])) / 1000))
        defines["estimated_size"] = str(int(int(os.path.getsize(defines["dataPath"])) / 1000))

        defines["installerIcon"] = f"""!define MUI_ICON "{defines["icon"]}" """
        defines["iconname"] = os.path.basename(defines["icon"])
        if not defines["license"] == "":
            defines["license"] = f"""!insertmacro MUI_PAGE_LICENSE "{defines["license"]}" """
        if not defines["readme"] == "":
            defines["readme"] = f"""!insertmacro MUI_FINISHPAGE_SHOWREADME "{defines["readme"]}" """

        shortcuts = []
        if "executable" in defines:
            shortcuts.append(self._createShortcut(defines["productname"], defines["executable"]))
            del defines["executable"]

        for short in defines["shortcuts"]:
            shortcuts.append(self._createShortcut(**short))
        if shortcuts:
            defines["shortcuts"] = NullsoftInstallerPackager.SHORTCUT_SECTION.format(shortcuts = "".join(shortcuts))


        if defines.get("sections", None):
            defines["sections_page"] = "!insertmacro MUI_PAGE_COMPONENTS"

        uninstallDirs = set()
        uninstallFiles = ["\\uninstall.exe", f"\\{defines['iconname']}"]
        prefixLength = len(self.archiveDir())
        for f in utils.filterDirectoryContent(self.archiveDir()):
            f = Path(f[prefixLength:])
            uninstallFiles.append(f)
            d = f.parent
            while d not in uninstallDirs:
                uninstallDirs.add(d)
                d = d.parent

        defines["uninstallFiles"] = "\n".join([f"Delete \"$INSTDIR{f}\"" for f in uninstallFiles])
        defines["uninstallDirs"] = "\n".join([f"RMDir \"$INSTDIR{x}\"" for x in sorted(uninstallDirs, reverse=True)])

        CraftCore.debug.new_line()
        CraftCore.log.debug(f"generating installer {defines['setupname']}")

        verboseString = "/V4" if CraftCore.debug.verbose() > 0 else "/V3"

        defines.setdefault("nsis_include", f"!addincludedir {os.path.dirname(self.scriptname)}")
        defines["nsis_include_internal"] = f"!addincludedir {os.path.join(os.path.dirname(__file__), 'Nsis')}"
        cmdDefines = []
        configuredScrip = os.path.join(self.workDir(), f"{self.package.name}.nsi")
        if not utils.configureFile(self.scriptname, configuredScrip, defines):
            configuredScrip = self.scriptname
            # this script uses the old behaviour, using defines
            for key, value in defines.items():
                if value is not None:
                    cmdDefines.append(f"/D{key}={value}")

        if not utils.systemWithoutShell([self.nsisExe, verboseString] + cmdDefines + [configuredScrip],
                                        cwd=os.path.abspath(self.packageDir())):
            CraftCore.log.critical("Error in makensis execution")
            return False
        return CodeSign.signWindows([defines["setupname"]])
Ejemplo n.º 6
0
    def internalCreatePackage(self,
                              defines,
                              seperateSymbolFiles=False,
                              packageSymbols=False):
        """ create a package """
        CraftCore.log.debug("packaging using the MacDMGPackager")

        # TODO: provide an image with dbg files
        if not super().internalCreatePackage(
                defines,
                seperateSymbolFiles=seperateSymbolFiles,
                packageSymbols=packageSymbols):
            return False

        appPath = self.getMacAppPath(defines)
        if not appPath:
            return False
        archive = Path(self.archiveDir())
        CraftCore.log.info(f"Packaging {appPath}")

        CraftCore.log.info("Clean up frameworks")
        for framework in utils.filterDirectoryContent(
                archive / "lib",
                handleAppBundleAsFile=True,
                whitelist=lambda x, root: x.name.endswith(".framework"),
                blacklist=lambda x, root: True):
            rubbish = []
            framework = Path(framework)
            rubbish += glob.glob(str(framework / "*.prl"))
            rubbish += glob.glob(str(framework / "Headers"))
            for r in rubbish:
                r = Path(r)
                if r.is_symlink():
                    if framework not in r.parents:
                        raise Exception(f"Evil symlink detected: {r}")
                    utils.deleteFile(r)
                    r = r.resolve()
                if r.is_dir():
                    utils.rmtree(r)
                else:
                    utils.deleteFile(r)

        targetLibdir = os.path.join(appPath, "Contents", "Frameworks")
        utils.createDir(targetLibdir)

        moveTargets = [
            (archive / "lib/plugins", appPath / "Contents/PlugIns"),
            (archive / "plugins", appPath / "Contents/PlugIns"),
            (archive / "share", appPath / "Contents/Resources"),
            (archive / "qml", appPath / "Contents/Resources/qml"),
            (archive / "translations",
             appPath / "Contents/Resources/Translations"),
            (archive / "bin", appPath / "Contents/MacOS"),
            (archive / "libexec", appPath / "Contents/MacOS"),
            (archive / "lib/libexec/kf5", appPath / "Contents/MacOS"),
            (archive / "lib/libexec", appPath / "Contents/MacOS"),
            (archive / "lib", targetLibdir),
        ]

        if archive not in appPath.parents:
            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", defines['appname'])
            if not dylibbundler.bundleLibraryDependencies(mainBinary):
                return False

            binaries = list(
                utils.filterDirectoryContent(
                    os.path.join(appPath, "Contents", "MacOS"),
                    whitelist=lambda x, root: utils.isBinary(
                        x.path) and x.name != defines["appname"],
                    blacklist=lambda x, root: True))

            for binary in binaries:
                CraftCore.log.info(f"Bundling dependencies for {binary}...")
                binaryPath = Path(binary)
                if not dylibbundler.bundleLibraryDependencies(binaryPath):
                    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

            macdeployqt_multiple_executables_command = [
                "macdeployqt", appPath, "-always-overwrite", "-verbose=1"
            ]
            for binary in binaries:
                macdeployqt_multiple_executables_command.append(
                    f"-executable={binary}")
            if "qmldirs" in self.defines.keys() and isinstance(
                    self.defines["qmldirs"], list):
                for qmldir in self.defines["qmldirs"]:
                    macdeployqt_multiple_executables_command.append(
                        f"-qmldir={qmldir}")
            if not utils.system(macdeployqt_multiple_executables_command):
                return False

            # macdeployqt might just have added some explicitly blacklisted files
            blackList = Path(self.packageDir(), "mac_blacklist.txt")
            if blackList.exists():
                pattern = [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: utils.regexFileFilter(
                        x, root, pattern),
                    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)
            for binary in binaries:
                binaryPath = Path(binary)
                if not dylibbundler.areLibraryDepsOkay(binaryPath):
                    found_bad_dylib = True
                    CraftCore.log.error(
                        "Found bad library dependency in binary %s",
                        binaryPath)
            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
            return CodeSign.signMacApp(appPath)
Ejemplo n.º 7
0
    def generateInnoInstaller(self, defines):
        """ runs ISCC to generate the installer itself """
        defines["setupname"] = str(Path(defines["setupname"]).with_suffix(".exe"))
        if not defines["license"] == "":
            defines["license"] = f"""LicenseFile="{defines["license"]}" """
        if not defines["readme"] == "":
            defines["readme"] = f"""InfoBeforeFile="{defines["readme"]}" """

        shortcuts = []
        if "executable" in defines:
            shortcuts.append(self._createShortcut(defines["productname"], defines["executable"]))
        else:
            # Needed below, for registering file associations
            # This will error out, if neither executable, nor any shortcuts have been set, but that is certainly an error, anyway
            defines["executable"] = defines["shortcuts"][0]["target"]

        for short in defines["shortcuts"]:
            shortcuts.append(self._createShortcut(**short))
        if shortcuts:
            defines["shortcuts"] = "".join(shortcuts)

        tasks = []
        registry_keys = []
        for key in defines["registry_keys"]:
            registry_keys.append(f"""Root: HKA; Subkey: ; ValueType: string; ValueName: "{key["name"]}"; ValueData: "{key["value"]}" """)
        for ftype in defines["file_types"]:
            ftype_id = self.subinfo.displayName + ftype
            ftype_id = ftype_id.replace(".", "_")
            tasks.append(f"""Name: "{ftype_id}"; Description: "{{cm:AssocFileExtension,{self.subinfo.displayName},{ftype}}}";""")

            registry_keys.append(f"""Root: HKA; Subkey: "Software\\Classes\\{ftype}\\OpenWithProgids"; ValueType: string; ValueName: "{ftype_id}"; ValueData: ""; Flags: uninsdeletevalue ; Tasks: {ftype_id}""")
            registry_keys.append(f"""Root: HKA; Subkey: "Software\\Classes\\{ftype_id}"; ValueType: string; ValueName: ""; ValueData: "{ftype} file"; Flags: uninsdeletekey ; Tasks: {ftype_id}""")
            registry_keys.append(f"""Root: HKA; Subkey: "Software\\Classes\\{ftype_id}\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{{app}}\\{defines["executable"]},0" ; Tasks: {ftype_id}""")
            registry_keys.append(f"""Root: HKA; Subkey: "Software\\Classes\\{ftype_id}\\shell\\open\\command"; ValueType: string; ValueName: ""; ValueData: \"""{{app}}\\{defines["executable"]}"" ""%1""\" ; Tasks: {ftype_id}""")
            registry_keys.append(f"""Root: HKA; Subkey: "Software\\Classes\\Applications\\{os.path.basename(defines["executable"])}\\SupportedTypes"; ValueType: string; ValueName: "{ftype}"; ValueData: "" ; Tasks: {ftype_id}""")
        if registry_keys:
            defines["registry"] = "[Registry]\n" + "\n".join(registry_keys)
            defines["associations"] = "ChangesAssociations=yes"
        else:
            defines["registry"] = ""
            defines["associations"] = ""

        if tasks:
            defines["tasks"] = "[TASKS]\n" + "\n".join(tasks)
        else:
            defines["tasks"] = ""

        CraftCore.debug.new_line()
        CraftCore.log.debug(f"generating installer {defines['setupname']}")

        verboseString = "/V4" if CraftCore.debug.verbose() > 0 else "/V1"

        cmdDefines = []
        cmdDefines.append(f"""/O{os.path.dirname(defines["setupname"])}""")
        cmdDefines.append(f"""/F{str(Path(os.path.basename(defines["setupname"])).with_suffix(""))}""")
        configuredScrip = os.path.join(self.workDir(), f"{self.package.name}.iss")
        utils.configureFile(self.scriptname, configuredScrip, defines)

        if not utils.systemWithoutShell([self.innoExe, verboseString] + cmdDefines + [configuredScrip],
                                        cwd=os.path.abspath(self.packageDir())):
            CraftCore.log.critical("Error in ISCC execution")
            return False
        return CodeSign.signWindows([defines["setupname"]])
Ejemplo n.º 8
0
    def internalPostInstall(self):
        if not super().internalPostInstall():
            return False
        # fix absolute symlinks
        for sym in utils.filterDirectoryContent(self.installDir(),
                                                lambda x, root: x.is_symlink(),
                                                lambda x, root: True,
                                                allowBadSymlinks=True):
            target = Path(os.readlink(sym))
            if target.is_absolute():
                sym = Path(sym)
                target = Path(self.imageDir()) / target.relative_to(
                    CraftCore.standardDirs.craftRoot())
                sym.unlink()
                # we can't use relative_to here
                sym.symlink_to(os.path.relpath(target, sym.parent))

        # a post install routine to fix the prefix (make things relocatable)
        newPrefix = OsUtils.toUnixPath(CraftCore.standardDirs.craftRoot())
        oldPrefixes = [self.subinfo.buildPrefix]
        if CraftCore.compiler.isWindows:
            oldPrefixes += [OsUtils.toMSysPath(self.subinfo.buildPrefix)]

        files = utils.filterDirectoryContent(
            self.installDir(),
            whitelist=lambda x, root: Path(
                x).suffix in BuildSystemBase.PatchableFile,
            blacklist=lambda x, root: True)

        if not self.patchInstallPrefix(files, oldPrefixes, newPrefix):
            return False

        binaryFiles = list(
            utils.filterDirectoryContent(
                self.installDir(), lambda x, root: utils.isBinary(x.path),
                lambda x, root: True))
        if (CraftCore.compiler.isMacOS and os.path.isdir(self.installDir())):
            for f in binaryFiles:
                if os.path.islink(f):
                    continue
                # replace the old prefix or add it if missing
                craft_rpath = os.path.join(newPrefix, "lib")
                if not utils.system([
                        "install_name_tool", "-rpath",
                        os.path.join(self.subinfo.buildPrefix, "lib"),
                        craft_rpath, f
                ],
                                    logCommand=False,
                                    stdout=subprocess.DEVNULL,
                                    stderr=subprocess.DEVNULL):
                    CraftCore.log.info(f"Adding rpath {craft_rpath} to {f}")
                    utils.system(
                        ["install_name_tool", "-add_rpath", craft_rpath, f],
                        logCommand=False)

                # update prefix
                if self.subinfo.buildPrefix != newPrefix:
                    if os.path.splitext(f)[1] in {".dylib", ".so"}:
                        # fix dylib id
                        with io.StringIO() as log:
                            utils.system(["otool", "-D", f],
                                         stdout=log,
                                         logCommand=False)
                            oldId = log.getvalue().strip().split("\n")
                        # the first line is the file name
                        # the second the id, if we only get one line, there is no id to fix
                        if len(oldId) == 2:
                            oldId = oldId[1].strip()
                            newId = oldId.replace(self.subinfo.buildPrefix,
                                                  newPrefix)
                            if newId != oldId:
                                if not utils.system(
                                    ["install_name_tool", "-id", newId, f],
                                        logCommand=False):
                                    return False

                    # fix dependencies
                    for dep in utils.getLibraryDeps(f):
                        if dep.startswith(self.subinfo.buildPrefix):
                            newDep = dep.replace(self.subinfo.buildPrefix,
                                                 newPrefix)
                            if newDep != dep:
                                if not utils.system([
                                        "install_name_tool", "-change", dep,
                                        newDep, f
                                ],
                                                    logCommand=False):
                                    return False

        # Install pdb files on MSVC if they are not found next to the dll
        # skip if we are a release build or from cache
        if not self.subinfo.isCachedBuild:
            if self.buildType() in {"RelWithDebInfo", "Debug"}:
                if CraftCore.compiler.isMSVC():
                    for f in binaryFiles:
                        if not os.path.exists(f"{os.path.splitext(f)[0]}.pdb"):
                            pdb = utils.getPDBForBinary(f)
                            if not pdb:
                                CraftCore.log.warning(
                                    f"Could not find a PDB for {f}")
                                continue
                            if not os.path.exists(pdb):
                                CraftCore.log.warning(
                                    f"PDB {pdb} for {f} does not exist")
                                continue
                            pdbDestination = os.path.join(
                                os.path.dirname(f), os.path.basename(pdb))

                            CraftCore.log.info(
                                f"Install pdb: {pdbDestination} for {os.path.basename(f)}"
                            )
                            utils.copyFile(pdb, pdbDestination, linkOnly=False)
                else:
                    if not self.subinfo.options.package.disableStriping:
                        for f in binaryFiles:
                            utils.strip(f)

            # sign the binaries if we can
            if CraftCore.compiler.isWindows and CraftCore.settings.getboolean(
                    "CodeSigning", "SignCache", False):
                if not CodeSign.signWindows(binaryFiles):
                    return False
        return True
Ejemplo n.º 9
0
    def internalCreatePackage(self,
                              defines,
                              seperateSymbolFiles=False,
                              packageSymbols=False):
        """ create a package """
        CraftCore.log.debug("packaging using the MacDMGPackager")

        # TODO: provide an image with dbg files
        if not super().internalCreatePackage(
                defines,
                seperateSymbolFiles=seperateSymbolFiles,
                packageSymbols=packageSymbols):
            return False

        appPath = self.getMacAppPath(defines)
        if not appPath:
            return False
        archive = Path(self.archiveDir())
        CraftCore.log.info(f"Packaging {appPath}")

        CraftCore.log.info("Clean up frameworks")
        for framework in utils.filterDirectoryContent(
                archive / "lib",
                handleAppBundleAsFile=True,
                whitelist=lambda x, root: x.name.endswith(".framework"),
                blacklist=lambda x, root: True):
            rubbish = []
            framework = Path(framework)
            rubbish += framework.rglob("*.prl")
            rubbish += framework.rglob("Headers")
            for r in rubbish:
                r = Path(r)
                if r.is_symlink():
                    if framework not in r.parents:
                        raise Exception(f"Evil symlink detected: {r}")
                    utils.deleteFile(r)
                    r = r.resolve()
                if r.is_dir():
                    utils.rmtree(r)
                else:
                    utils.deleteFile(r)

        targetLibdir = os.path.join(appPath, "Contents", "Frameworks")
        utils.createDir(targetLibdir)

        moveTargets = [
            (archive / "lib/plugins", appPath / "Contents/PlugIns"),
            (archive / "plugins", appPath / "Contents/PlugIns"),
            (archive / "share", appPath / "Contents/Resources"),
            (archive / "qml", appPath / "Contents/Resources/qml"),
            (archive / "translations",
             appPath / "Contents/Resources/Translations"),
            (archive / "bin", appPath / "Contents/MacOS"),
            (archive / "libexec", appPath / "Contents/MacOS"),
            (archive / "lib/libexec/kf5", appPath / "Contents/MacOS"),
            (archive / "lib/libexec", appPath / "Contents/MacOS"),
            (archive / "lib", targetLibdir),
        ]
        self._addQtConf(appPath)

        if archive not in appPath.parents:
            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, self.externalLibs)
        CraftCore.log.info("Bundling main binary dependencies...")
        binaries = list(
            utils.filterDirectoryContent(
                os.path.join(appPath, "Contents"),
                whitelist=lambda x, root: utils.isBinary(x.path),
                blacklist=lambda x, root: True))

        for binary in binaries:
            CraftCore.log.info(f"Bundling dependencies for {binary}...")
            binaryPath = Path(binary)
            if not dylibbundler.bundleLibraryDependencies(binaryPath):
                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:
        for binary in binaries:
            binaryPath = Path(binary)
            if not dylibbundler.areLibraryDepsOkay(binaryPath):
                found_bad_dylib = True
                CraftCore.log.error(
                    "Found bad library dependency in binary %s", binaryPath)
        if found_bad_dylib:
            CraftCore.log.error(
                "Cannot not create .dmg since the .app contains a bad library depenency!"
            )
            return False
        return CodeSign.signMacApp(appPath)