Esempio n. 1
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)
Esempio n. 2
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)