Changeset View
Changeset View
Standalone View
Standalone View
contrib/macdeploy/macdeployqtplus
Show All 34 Lines | def __init__(self): | ||||
self.deployedInstallName = "" | self.deployedInstallName = "" | ||||
self.sourceFilePath = "" | self.sourceFilePath = "" | ||||
self.destinationDirectory = "" | self.destinationDirectory = "" | ||||
self.sourceResourcesDirectory = "" | self.sourceResourcesDirectory = "" | ||||
self.sourceVersionContentsDirectory = "" | self.sourceVersionContentsDirectory = "" | ||||
self.sourceContentsDirectory = "" | self.sourceContentsDirectory = "" | ||||
self.destinationResourcesDirectory = "" | self.destinationResourcesDirectory = "" | ||||
self.destinationVersionContentsDirectory = "" | self.destinationVersionContentsDirectory = "" | ||||
def __eq__(self, other): | def __eq__(self, other): | ||||
if self.__class__ == other.__class__: | if self.__class__ == other.__class__: | ||||
return self.__dict__ == other.__dict__ | return self.__dict__ == other.__dict__ | ||||
else: | else: | ||||
return False | return False | ||||
def __str__(self): | def __str__(self): | ||||
return """ Framework name: %s | return """ Framework name: %s | ||||
Framework directory: %s | Framework directory: %s | ||||
Framework path: %s | Framework path: %s | ||||
Binary name: %s | Binary name: %s | ||||
Binary directory: %s | Binary directory: %s | ||||
Binary path: %s | Binary path: %s | ||||
Version: %s | Version: %s | ||||
Install name: %s | Install name: %s | ||||
Deployed install name: %s | Deployed install name: %s | ||||
Source file Path: %s | Source file Path: %s | ||||
Deployed Directory (relative to bundle): %s | Deployed Directory (relative to bundle): %s | ||||
""" % (self.frameworkName, | """ % (self.frameworkName, | ||||
self.frameworkDirectory, | self.frameworkDirectory, | ||||
self.frameworkPath, | self.frameworkPath, | ||||
self.binaryName, | self.binaryName, | ||||
self.binaryDirectory, | self.binaryDirectory, | ||||
self.binaryPath, | self.binaryPath, | ||||
self.version, | self.version, | ||||
self.installName, | self.installName, | ||||
self.deployedInstallName, | self.deployedInstallName, | ||||
self.sourceFilePath, | self.sourceFilePath, | ||||
self.destinationDirectory) | self.destinationDirectory) | ||||
def isDylib(self): | def isDylib(self): | ||||
return self.frameworkName.endswith(".dylib") | return self.frameworkName.endswith(".dylib") | ||||
def isQtFramework(self): | def isQtFramework(self): | ||||
if self.isDylib(): | if self.isDylib(): | ||||
return self.frameworkName.startswith("libQt") | return self.frameworkName.startswith("libQt") | ||||
else: | else: | ||||
return self.frameworkName.startswith("Qt") | return self.frameworkName.startswith("Qt") | ||||
reOLine = re.compile(r'^(.+) \(compatibility version [0-9.]+, current version [0-9.]+\)$') | reOLine = re.compile(r'^(.+) \(compatibility version [0-9.]+, current version [0-9.]+\)$') | ||||
bundleFrameworkDirectory = "Contents/Frameworks" | bundleFrameworkDirectory = "Contents/Frameworks" | ||||
bundleBinaryDirectory = "Contents/MacOS" | bundleBinaryDirectory = "Contents/MacOS" | ||||
@classmethod | @classmethod | ||||
def fromOtoolLibraryLine(cls, line): | def fromOtoolLibraryLine(cls, line): | ||||
# Note: line must be trimmed | # Note: line must be trimmed | ||||
if line == "": | if line == "": | ||||
return None | return None | ||||
# Don't deploy system libraries (exception for libQtuitools and libQtlucene). | # Don't deploy system libraries (exception for libQtuitools and libQtlucene). | ||||
if line.startswith("/System/Library/") or line.startswith("@executable_path") or (line.startswith("/usr/lib/") and "libQt" not in line): | if line.startswith("/System/Library/") or line.startswith("@executable_path") or (line.startswith("/usr/lib/") and "libQt" not in line): | ||||
return None | return None | ||||
m = cls.reOLine.match(line) | m = cls.reOLine.match(line) | ||||
if m is None: | if m is None: | ||||
raise RuntimeError("otool line could not be parsed: " + line) | raise RuntimeError("otool line could not be parsed: " + line) | ||||
path = m.group(1) | path = m.group(1) | ||||
info = cls() | info = cls() | ||||
info.sourceFilePath = path | info.sourceFilePath = path | ||||
info.installName = path | info.installName = path | ||||
if path.endswith(".dylib"): | if path.endswith(".dylib"): | ||||
dirname, filename = os.path.split(path) | dirname, filename = os.path.split(path) | ||||
info.frameworkName = filename | info.frameworkName = filename | ||||
info.frameworkDirectory = dirname | info.frameworkDirectory = dirname | ||||
info.frameworkPath = path | info.frameworkPath = path | ||||
info.binaryDirectory = dirname | info.binaryDirectory = dirname | ||||
info.binaryName = filename | info.binaryName = filename | ||||
info.binaryPath = path | info.binaryPath = path | ||||
info.version = "-" | info.version = "-" | ||||
info.installName = path | info.installName = path | ||||
info.deployedInstallName = "@executable_path/../Frameworks/" + info.binaryName | info.deployedInstallName = "@executable_path/../Frameworks/" + info.binaryName | ||||
info.sourceFilePath = path | info.sourceFilePath = path | ||||
info.destinationDirectory = cls.bundleFrameworkDirectory | info.destinationDirectory = cls.bundleFrameworkDirectory | ||||
else: | else: | ||||
parts = path.split("/") | parts = path.split("/") | ||||
i = 0 | i = 0 | ||||
# Search for the .framework directory | # Search for the .framework directory | ||||
for part in parts: | for part in parts: | ||||
if part.endswith(".framework"): | if part.endswith(".framework"): | ||||
break | break | ||||
i += 1 | i += 1 | ||||
if i == len(parts): | if i == len(parts): | ||||
raise RuntimeError("Could not find .framework or .dylib in otool line: " + line) | raise RuntimeError("Could not find .framework or .dylib in otool line: " + line) | ||||
info.frameworkName = parts[i] | info.frameworkName = parts[i] | ||||
info.frameworkDirectory = "/".join(parts[:i]) | info.frameworkDirectory = "/".join(parts[:i]) | ||||
info.frameworkPath = os.path.join(info.frameworkDirectory, info.frameworkName) | info.frameworkPath = os.path.join(info.frameworkDirectory, info.frameworkName) | ||||
info.binaryName = parts[i+3] | info.binaryName = parts[i+3] | ||||
info.binaryDirectory = "/".join(parts[i+1:i+3]) | info.binaryDirectory = "/".join(parts[i+1:i+3]) | ||||
info.binaryPath = os.path.join(info.binaryDirectory, info.binaryName) | info.binaryPath = os.path.join(info.binaryDirectory, info.binaryName) | ||||
info.version = parts[i+2] | info.version = parts[i+2] | ||||
info.deployedInstallName = "@executable_path/../Frameworks/" + os.path.join(info.frameworkName, info.binaryPath) | info.deployedInstallName = "@executable_path/../Frameworks/" + os.path.join(info.frameworkName, info.binaryPath) | ||||
info.destinationDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, info.binaryDirectory) | info.destinationDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, info.binaryDirectory) | ||||
info.sourceResourcesDirectory = os.path.join(info.frameworkPath, "Resources") | info.sourceResourcesDirectory = os.path.join(info.frameworkPath, "Resources") | ||||
info.sourceContentsDirectory = os.path.join(info.frameworkPath, "Contents") | info.sourceContentsDirectory = os.path.join(info.frameworkPath, "Contents") | ||||
info.sourceVersionContentsDirectory = os.path.join(info.frameworkPath, "Versions", info.version, "Contents") | info.sourceVersionContentsDirectory = os.path.join(info.frameworkPath, "Versions", info.version, "Contents") | ||||
info.destinationResourcesDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, "Resources") | info.destinationResourcesDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, "Resources") | ||||
info.destinationContentsDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, "Contents") | info.destinationContentsDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, "Contents") | ||||
info.destinationVersionContentsDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, "Versions", info.version, "Contents") | info.destinationVersionContentsDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, "Versions", info.version, "Contents") | ||||
return info | return info | ||||
class ApplicationBundleInfo(object): | class ApplicationBundleInfo(object): | ||||
def __init__(self, path): | def __init__(self, path): | ||||
self.path = path | self.path = path | ||||
appName = "BitcoinABC-Qt" | appName = "BitcoinABC-Qt" | ||||
self.binaryPath = os.path.join(path, "Contents", "MacOS", appName) | self.binaryPath = os.path.join(path, "Contents", "MacOS", appName) | ||||
if not os.path.exists(self.binaryPath): | if not os.path.exists(self.binaryPath): | ||||
raise RuntimeError("Could not find bundle binary for " + path) | raise RuntimeError("Could not find bundle binary for " + path) | ||||
self.resourcesPath = os.path.join(path, "Contents", "Resources") | self.resourcesPath = os.path.join(path, "Contents", "Resources") | ||||
self.pluginPath = os.path.join(path, "Contents", "PlugIns") | self.pluginPath = os.path.join(path, "Contents", "PlugIns") | ||||
class DeploymentInfo(object): | class DeploymentInfo(object): | ||||
def __init__(self): | def __init__(self): | ||||
self.qtPath = None | self.qtPath = None | ||||
self.pluginPath = None | self.pluginPath = None | ||||
self.deployedFrameworks = [] | self.deployedFrameworks = [] | ||||
def detectQtPath(self, frameworkDirectory): | def detectQtPath(self, frameworkDirectory): | ||||
parentDir = os.path.dirname(frameworkDirectory) | parentDir = os.path.dirname(frameworkDirectory) | ||||
if os.path.exists(os.path.join(parentDir, "translations")): | if os.path.exists(os.path.join(parentDir, "translations")): | ||||
# Classic layout, e.g. "/usr/local/Trolltech/Qt-4.x.x" | # Classic layout, e.g. "/usr/local/Trolltech/Qt-4.x.x" | ||||
self.qtPath = parentDir | self.qtPath = parentDir | ||||
elif os.path.exists(os.path.join(parentDir, "share", "qt4", "translations")): | elif os.path.exists(os.path.join(parentDir, "share", "qt4", "translations")): | ||||
# MacPorts layout, e.g. "/opt/local/share/qt4" | # MacPorts layout, e.g. "/opt/local/share/qt4" | ||||
self.qtPath = os.path.join(parentDir, "share", "qt4") | self.qtPath = os.path.join(parentDir, "share", "qt4") | ||||
elif os.path.exists(os.path.join(os.path.dirname(parentDir), "share", "qt4", "translations")): | elif os.path.exists(os.path.join(os.path.dirname(parentDir), "share", "qt4", "translations")): | ||||
# Newer Macports layout | # Newer Macports layout | ||||
self.qtPath = os.path.join(os.path.dirname(parentDir), "share", "qt4") | self.qtPath = os.path.join(os.path.dirname(parentDir), "share", "qt4") | ||||
else: | else: | ||||
self.qtPath = os.getenv("QTDIR", None) | self.qtPath = os.getenv("QTDIR", None) | ||||
if self.qtPath is not None: | if self.qtPath is not None: | ||||
pluginPath = os.path.join(self.qtPath, "plugins") | pluginPath = os.path.join(self.qtPath, "plugins") | ||||
if os.path.exists(pluginPath): | if os.path.exists(pluginPath): | ||||
self.pluginPath = pluginPath | self.pluginPath = pluginPath | ||||
def usesFramework(self, name): | def usesFramework(self, name): | ||||
nameDot = "%s." % name | nameDot = "%s." % name | ||||
libNameDot = "lib%s." % name | libNameDot = "lib%s." % name | ||||
for framework in self.deployedFrameworks: | for framework in self.deployedFrameworks: | ||||
if framework.endswith(".framework"): | if framework.endswith(".framework"): | ||||
if framework.startswith(nameDot): | if framework.startswith(nameDot): | ||||
return True | return True | ||||
elif framework.endswith(".dylib"): | elif framework.endswith(".dylib"): | ||||
Show All 12 Lines | if otool.returncode != 0: | ||||
sys.stderr.write(o_stderr) | sys.stderr.write(o_stderr) | ||||
sys.stderr.flush() | sys.stderr.flush() | ||||
raise RuntimeError("otool failed with return code %d" % otool.returncode) | raise RuntimeError("otool failed with return code %d" % otool.returncode) | ||||
otoolLines = o_stdout.split("\n") | otoolLines = o_stdout.split("\n") | ||||
otoolLines.pop(0) # First line is the inspected binary | otoolLines.pop(0) # First line is the inspected binary | ||||
if ".framework" in binaryPath or binaryPath.endswith(".dylib"): | if ".framework" in binaryPath or binaryPath.endswith(".dylib"): | ||||
otoolLines.pop(0) # Frameworks and dylibs list themselves as a dependency. | otoolLines.pop(0) # Frameworks and dylibs list themselves as a dependency. | ||||
libraries = [] | libraries = [] | ||||
for line in otoolLines: | for line in otoolLines: | ||||
line = line.replace("@loader_path", os.path.dirname(binaryPath)) | line = line.replace("@loader_path", os.path.dirname(binaryPath)) | ||||
info = FrameworkInfo.fromOtoolLibraryLine(line.strip()) | info = FrameworkInfo.fromOtoolLibraryLine(line.strip()) | ||||
if info is not None: | if info is not None: | ||||
if verbose >= 3: | if verbose >= 3: | ||||
print("Found framework:") | print("Found framework:") | ||||
print(info) | print(info) | ||||
libraries.append(info) | libraries.append(info) | ||||
return libraries | return libraries | ||||
def runInstallNameTool(action, *args): | def runInstallNameTool(action, *args): | ||||
installnametoolbin=os.getenv("INSTALLNAMETOOL", "install_name_tool") | installnametoolbin=os.getenv("INSTALLNAMETOOL", "install_name_tool") | ||||
subprocess.check_call([installnametoolbin, "-"+action] + list(args)) | subprocess.check_call([installnametoolbin, "-"+action] + list(args)) | ||||
def changeInstallName(oldName, newName, binaryPath, verbose): | def changeInstallName(oldName, newName, binaryPath, verbose): | ||||
if verbose >= 3: | if verbose >= 3: | ||||
Show All 20 Lines | |||||
def copyFramework(framework, path, verbose): | def copyFramework(framework, path, verbose): | ||||
if framework.sourceFilePath.startswith("Qt"): | if framework.sourceFilePath.startswith("Qt"): | ||||
#standard place for Nokia Qt installer's frameworks | #standard place for Nokia Qt installer's frameworks | ||||
fromPath = "/Library/Frameworks/" + framework.sourceFilePath | fromPath = "/Library/Frameworks/" + framework.sourceFilePath | ||||
else: | else: | ||||
fromPath = framework.sourceFilePath | fromPath = framework.sourceFilePath | ||||
toDir = os.path.join(path, framework.destinationDirectory) | toDir = os.path.join(path, framework.destinationDirectory) | ||||
toPath = os.path.join(toDir, framework.binaryName) | toPath = os.path.join(toDir, framework.binaryName) | ||||
if not os.path.exists(fromPath): | if not os.path.exists(fromPath): | ||||
raise RuntimeError("No file at " + fromPath) | raise RuntimeError("No file at " + fromPath) | ||||
if os.path.exists(toPath): | if os.path.exists(toPath): | ||||
return None # Already there | return None # Already there | ||||
if not os.path.exists(toDir): | if not os.path.exists(toDir): | ||||
os.makedirs(toDir) | os.makedirs(toDir) | ||||
shutil.copy2(fromPath, toPath) | shutil.copy2(fromPath, toPath) | ||||
if verbose >= 3: | if verbose >= 3: | ||||
print("Copied:", fromPath) | print("Copied:", fromPath) | ||||
print(" to:", toPath) | print(" to:", toPath) | ||||
permissions = os.stat(toPath) | permissions = os.stat(toPath) | ||||
if not permissions.st_mode & stat.S_IWRITE: | if not permissions.st_mode & stat.S_IWRITE: | ||||
os.chmod(toPath, permissions.st_mode | stat.S_IWRITE) | os.chmod(toPath, permissions.st_mode | stat.S_IWRITE) | ||||
Show All 26 Lines | def copyFramework(framework, path, verbose): | ||||
elif framework.frameworkName.startswith("libQtGui"): # Copy qt_menu.nib (applies to non-framework layout) | elif framework.frameworkName.startswith("libQtGui"): # Copy qt_menu.nib (applies to non-framework layout) | ||||
qtMenuNibSourcePath = os.path.join(framework.frameworkDirectory, "Resources", "qt_menu.nib") | qtMenuNibSourcePath = os.path.join(framework.frameworkDirectory, "Resources", "qt_menu.nib") | ||||
qtMenuNibDestinationPath = os.path.join(path, "Contents", "Resources", "qt_menu.nib") | qtMenuNibDestinationPath = os.path.join(path, "Contents", "Resources", "qt_menu.nib") | ||||
if os.path.exists(qtMenuNibSourcePath) and not os.path.exists(qtMenuNibDestinationPath): | if os.path.exists(qtMenuNibSourcePath) and not os.path.exists(qtMenuNibDestinationPath): | ||||
shutil.copytree(qtMenuNibSourcePath, qtMenuNibDestinationPath, symlinks=True) | shutil.copytree(qtMenuNibSourcePath, qtMenuNibDestinationPath, symlinks=True) | ||||
if verbose >= 3: | if verbose >= 3: | ||||
print("Copied for libQtGui:", qtMenuNibSourcePath) | print("Copied for libQtGui:", qtMenuNibSourcePath) | ||||
print(" to:", qtMenuNibDestinationPath) | print(" to:", qtMenuNibDestinationPath) | ||||
return toPath | return toPath | ||||
def deployFrameworks(frameworks, bundlePath, binaryPath, strip, verbose, deploymentInfo=None): | def deployFrameworks(frameworks, bundlePath, binaryPath, strip, verbose, deploymentInfo=None): | ||||
if deploymentInfo is None: | if deploymentInfo is None: | ||||
deploymentInfo = DeploymentInfo() | deploymentInfo = DeploymentInfo() | ||||
while len(frameworks) > 0: | while len(frameworks) > 0: | ||||
framework = frameworks.pop(0) | framework = frameworks.pop(0) | ||||
deploymentInfo.deployedFrameworks.append(framework.frameworkName) | deploymentInfo.deployedFrameworks.append(framework.frameworkName) | ||||
if verbose >= 2: | if verbose >= 2: | ||||
print("Processing", framework.frameworkName, "...") | print("Processing", framework.frameworkName, "...") | ||||
# Get the Qt path from one of the Qt frameworks | # Get the Qt path from one of the Qt frameworks | ||||
if deploymentInfo.qtPath is None and framework.isQtFramework(): | if deploymentInfo.qtPath is None and framework.isQtFramework(): | ||||
deploymentInfo.detectQtPath(framework.frameworkDirectory) | deploymentInfo.detectQtPath(framework.frameworkDirectory) | ||||
if framework.installName.startswith("@executable_path") or framework.installName.startswith(bundlePath): | if framework.installName.startswith("@executable_path") or framework.installName.startswith(bundlePath): | ||||
if verbose >= 2: | if verbose >= 2: | ||||
print(framework.frameworkName, "already deployed, skipping.") | print(framework.frameworkName, "already deployed, skipping.") | ||||
continue | continue | ||||
# install_name_tool the new id into the binary | # install_name_tool the new id into the binary | ||||
changeInstallName(framework.installName, framework.deployedInstallName, binaryPath, verbose) | changeInstallName(framework.installName, framework.deployedInstallName, binaryPath, verbose) | ||||
# Copy framework to app bundle. | # Copy framework to app bundle. | ||||
deployedBinaryPath = copyFramework(framework, bundlePath, verbose) | deployedBinaryPath = copyFramework(framework, bundlePath, verbose) | ||||
# Skip the rest if already was deployed. | # Skip the rest if already was deployed. | ||||
if deployedBinaryPath is None: | if deployedBinaryPath is None: | ||||
continue | continue | ||||
if strip: | if strip: | ||||
runStrip(deployedBinaryPath, verbose) | runStrip(deployedBinaryPath, verbose) | ||||
# install_name_tool it a new id. | # install_name_tool it a new id. | ||||
changeIdentification(framework.deployedInstallName, deployedBinaryPath, verbose) | changeIdentification(framework.deployedInstallName, deployedBinaryPath, verbose) | ||||
# Check for framework dependencies | # Check for framework dependencies | ||||
dependencies = getFrameworks(deployedBinaryPath, verbose) | dependencies = getFrameworks(deployedBinaryPath, verbose) | ||||
for dependency in dependencies: | for dependency in dependencies: | ||||
changeInstallName(dependency.installName, dependency.deployedInstallName, deployedBinaryPath, verbose) | changeInstallName(dependency.installName, dependency.deployedInstallName, deployedBinaryPath, verbose) | ||||
# Deploy framework if necessary. | # Deploy framework if necessary. | ||||
if dependency.frameworkName not in deploymentInfo.deployedFrameworks and dependency not in frameworks: | if dependency.frameworkName not in deploymentInfo.deployedFrameworks and dependency not in frameworks: | ||||
frameworks.append(dependency) | frameworks.append(dependency) | ||||
return deploymentInfo | return deploymentInfo | ||||
def deployFrameworksForAppBundle(applicationBundle, strip, verbose): | def deployFrameworksForAppBundle(applicationBundle, strip, verbose): | ||||
frameworks = getFrameworks(applicationBundle.binaryPath, verbose) | frameworks = getFrameworks(applicationBundle.binaryPath, verbose) | ||||
if len(frameworks) == 0 and verbose >= 1: | if len(frameworks) == 0 and verbose >= 1: | ||||
print("Warning: Could not find any external frameworks to deploy in %s." % (applicationBundle.path)) | print("Warning: Could not find any external frameworks to deploy in %s." % (applicationBundle.path)) | ||||
return DeploymentInfo() | return DeploymentInfo() | ||||
else: | else: | ||||
▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | for dirpath, dirnames, filenames in os.walk(deploymentInfo.pluginPath): | ||||
if not deploymentInfo.usesFramework("QtOpenGL"): | if not deploymentInfo.usesFramework("QtOpenGL"): | ||||
continue | continue | ||||
elif pluginPath == "accessible/libqtaccessiblequick.dylib": | elif pluginPath == "accessible/libqtaccessiblequick.dylib": | ||||
# Deploy the accessible qtquick plugin only if QtQuick is in use | # Deploy the accessible qtquick plugin only if QtQuick is in use | ||||
if not deploymentInfo.usesFramework("QtQuick"): | if not deploymentInfo.usesFramework("QtQuick"): | ||||
continue | continue | ||||
plugins.append((pluginDirectory, pluginName)) | plugins.append((pluginDirectory, pluginName)) | ||||
for pluginDirectory, pluginName in plugins: | for pluginDirectory, pluginName in plugins: | ||||
if verbose >= 2: | if verbose >= 2: | ||||
print("Processing plugin", os.path.join(pluginDirectory, pluginName), "...") | print("Processing plugin", os.path.join(pluginDirectory, pluginName), "...") | ||||
sourcePath = os.path.join(deploymentInfo.pluginPath, pluginDirectory, pluginName) | sourcePath = os.path.join(deploymentInfo.pluginPath, pluginDirectory, pluginName) | ||||
destinationDirectory = os.path.join(appBundleInfo.pluginPath, pluginDirectory) | destinationDirectory = os.path.join(appBundleInfo.pluginPath, pluginDirectory) | ||||
if not os.path.exists(destinationDirectory): | if not os.path.exists(destinationDirectory): | ||||
os.makedirs(destinationDirectory) | os.makedirs(destinationDirectory) | ||||
destinationPath = os.path.join(destinationDirectory, pluginName) | destinationPath = os.path.join(destinationDirectory, pluginName) | ||||
shutil.copy2(sourcePath, destinationPath) | shutil.copy2(sourcePath, destinationPath) | ||||
if verbose >= 3: | if verbose >= 3: | ||||
print("Copied:", sourcePath) | print("Copied:", sourcePath) | ||||
print(" to:", destinationPath) | print(" to:", destinationPath) | ||||
if strip: | if strip: | ||||
runStrip(destinationPath, verbose) | runStrip(destinationPath, verbose) | ||||
dependencies = getFrameworks(destinationPath, verbose) | dependencies = getFrameworks(destinationPath, verbose) | ||||
for dependency in dependencies: | for dependency in dependencies: | ||||
changeInstallName(dependency.installName, dependency.deployedInstallName, destinationPath, verbose) | changeInstallName(dependency.installName, dependency.deployedInstallName, destinationPath, verbose) | ||||
# Deploy framework if necessary. | # Deploy framework if necessary. | ||||
if dependency.frameworkName not in deploymentInfo.deployedFrameworks: | if dependency.frameworkName not in deploymentInfo.deployedFrameworks: | ||||
deployFrameworks([dependency], appBundleInfo.path, destinationPath, strip, verbose, deploymentInfo) | deployFrameworks([dependency], appBundleInfo.path, destinationPath, strip, verbose, deploymentInfo) | ||||
qt_conf="""[Paths] | qt_conf="""[Paths] | ||||
Translations=Resources | Translations=Resources | ||||
Plugins=PlugIns | Plugins=PlugIns | ||||
""" | """ | ||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | if len(config.fancy) == 1: | ||||
if verbose >= 3: | if verbose >= 3: | ||||
print("Fancy: Importing plistlib...") | print("Fancy: Importing plistlib...") | ||||
try: | try: | ||||
import plistlib | import plistlib | ||||
except ImportError: | except ImportError: | ||||
if verbose >= 1: | if verbose >= 1: | ||||
sys.stderr.write("Error: Could not import plistlib which is required for fancy disk images.\n") | sys.stderr.write("Error: Could not import plistlib which is required for fancy disk images.\n") | ||||
sys.exit(1) | sys.exit(1) | ||||
p = config.fancy[0] | p = config.fancy[0] | ||||
if verbose >= 3: | if verbose >= 3: | ||||
print("Fancy: Loading \"%s\"..." % p) | print("Fancy: Loading \"%s\"..." % p) | ||||
if not os.path.exists(p): | if not os.path.exists(p): | ||||
if verbose >= 1: | if verbose >= 1: | ||||
sys.stderr.write("Error: Could not find fancy disk image plist at \"%s\"\n" % (p)) | sys.stderr.write("Error: Could not find fancy disk image plist at \"%s\"\n" % (p)) | ||||
sys.exit(1) | sys.exit(1) | ||||
try: | try: | ||||
fancy = plistlib.readPlist(p) | fancy = plistlib.readPlist(p) | ||||
except: | except: | ||||
if verbose >= 1: | if verbose >= 1: | ||||
sys.stderr.write("Error: Could not parse fancy disk image plist at \"%s\"\n" % (p)) | sys.stderr.write("Error: Could not parse fancy disk image plist at \"%s\"\n" % (p)) | ||||
sys.exit(1) | sys.exit(1) | ||||
try: | try: | ||||
assert "window_bounds" not in fancy or (isinstance(fancy["window_bounds"], list) and len(fancy["window_bounds"]) == 4) | assert "window_bounds" not in fancy or (isinstance(fancy["window_bounds"], list) and len(fancy["window_bounds"]) == 4) | ||||
assert "background_picture" not in fancy or isinstance(fancy["background_picture"], str) | assert "background_picture" not in fancy or isinstance(fancy["background_picture"], str) | ||||
assert "icon_size" not in fancy or isinstance(fancy["icon_size"], int) | assert "icon_size" not in fancy or isinstance(fancy["icon_size"], int) | ||||
assert "applications_symlink" not in fancy or isinstance(fancy["applications_symlink"], bool) | assert "applications_symlink" not in fancy or isinstance(fancy["applications_symlink"], bool) | ||||
if "items_position" in fancy: | if "items_position" in fancy: | ||||
assert isinstance(fancy["items_position"], dict) | assert isinstance(fancy["items_position"], dict) | ||||
for key, value in fancy["items_position"].items(): | for key, value in fancy["items_position"].items(): | ||||
assert isinstance(value, list) and len(value) == 2 and isinstance(value[0], int) and isinstance(value[1], int) | assert isinstance(value, list) and len(value) == 2 and isinstance(value[0], int) and isinstance(value[1], int) | ||||
except: | except: | ||||
if verbose >= 1: | if verbose >= 1: | ||||
sys.stderr.write("Error: Bad format of fancy disk image plist at \"%s\"\n" % (p)) | sys.stderr.write("Error: Bad format of fancy disk image plist at \"%s\"\n" % (p)) | ||||
sys.exit(1) | sys.exit(1) | ||||
if "background_picture" in fancy: | if "background_picture" in fancy: | ||||
bp = fancy["background_picture"] | bp = fancy["background_picture"] | ||||
if verbose >= 3: | if verbose >= 3: | ||||
print("Fancy: Resolving background picture \"%s\"..." % bp) | print("Fancy: Resolving background picture \"%s\"..." % bp) | ||||
if not os.path.exists(bp): | if not os.path.exists(bp): | ||||
bp = os.path.join(os.path.dirname(p), bp) | bp = os.path.join(os.path.dirname(p), bp) | ||||
if not os.path.exists(bp): | if not os.path.exists(bp): | ||||
if verbose >= 1: | if verbose >= 1: | ||||
sys.stderr.write("Error: Could not find background picture at \"%s\" or \"%s\"\n" % (fancy["background_picture"], bp)) | sys.stderr.write("Error: Could not find background picture at \"%s\" or \"%s\"\n" % (fancy["background_picture"], bp)) | ||||
sys.exit(1) | sys.exit(1) | ||||
else: | else: | ||||
fancy["background_picture"] = bp | fancy["background_picture"] = bp | ||||
else: | else: | ||||
fancy = None | fancy = None | ||||
# ------------------------------------------------ | # ------------------------------------------------ | ||||
if os.path.exists("dist"): | if os.path.exists("dist"): | ||||
if verbose >= 2: | if verbose >= 2: | ||||
print("+ Removing old dist folder +") | print("+ Removing old dist folder +") | ||||
shutil.rmtree("dist") | shutil.rmtree("dist") | ||||
# ------------------------------------------------ | # ------------------------------------------------ | ||||
if len(config.volname) == 1: | if len(config.volname) == 1: | ||||
volname = config.volname[0] | volname = config.volname[0] | ||||
else: | else: | ||||
volname = app_bundle_name | volname = app_bundle_name | ||||
Show All 30 Lines | if verbose >= 1: | ||||
sys.stderr.write("Error: %s\n" % str(e)) | sys.stderr.write("Error: %s\n" % str(e)) | ||||
sys.exit(1) | sys.exit(1) | ||||
# ------------------------------------------------ | # ------------------------------------------------ | ||||
if config.plugins: | if config.plugins: | ||||
if verbose >= 2: | if verbose >= 2: | ||||
print("+ Deploying plugins +") | print("+ Deploying plugins +") | ||||
try: | try: | ||||
deployPlugins(applicationBundle, deploymentInfo, config.strip, verbose) | deployPlugins(applicationBundle, deploymentInfo, config.strip, verbose) | ||||
except RuntimeError as e: | except RuntimeError as e: | ||||
if verbose >= 1: | if verbose >= 1: | ||||
sys.stderr.write("Error: %s\n" % str(e)) | sys.stderr.write("Error: %s\n" % str(e)) | ||||
sys.exit(1) | sys.exit(1) | ||||
# ------------------------------------------------ | # ------------------------------------------------ | ||||
▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | def runHDIUtil(verb, image_basename, **kwargs): | ||||
del kwargs["capture_stdout"] | del kwargs["capture_stdout"] | ||||
run = subprocess.check_output | run = subprocess.check_output | ||||
else: | else: | ||||
if verbose < 2: | if verbose < 2: | ||||
hdiutil_args.append("-quiet") | hdiutil_args.append("-quiet") | ||||
elif verbose >= 3: | elif verbose >= 3: | ||||
hdiutil_args.append("-verbose") | hdiutil_args.append("-verbose") | ||||
run = subprocess.check_call | run = subprocess.check_call | ||||
for key, value in kwargs.items(): | for key, value in kwargs.items(): | ||||
hdiutil_args.append("-" + key) | hdiutil_args.append("-" + key) | ||||
if not value is True: | if not value is True: | ||||
hdiutil_args.append(str(value)) | hdiutil_args.append(str(value)) | ||||
return run(hdiutil_args, universal_newlines=True) | return run(hdiutil_args, universal_newlines=True) | ||||
if verbose >= 2: | if verbose >= 2: | ||||
if fancy is None: | if fancy is None: | ||||
print("+ Creating .dmg disk image +") | print("+ Creating .dmg disk image +") | ||||
else: | else: | ||||
print("+ Preparing .dmg disk image +") | print("+ Preparing .dmg disk image +") | ||||
if config.dmg != "": | if config.dmg != "": | ||||
dmg_name = config.dmg | dmg_name = config.dmg | ||||
else: | else: | ||||
spl = app_bundle_name.split(" ") | spl = app_bundle_name.split(" ") | ||||
dmg_name = spl[0] + "".join(p.capitalize() for p in spl[1:]) | dmg_name = spl[0] + "".join(p.capitalize() for p in spl[1:]) | ||||
if fancy is None: | if fancy is None: | ||||
try: | try: | ||||
runHDIUtil("create", dmg_name, srcfolder="dist", format="UDBZ", volname=volname, ov=True) | runHDIUtil("create", dmg_name, srcfolder="dist", format="UDBZ", volname=volname, ov=True) | ||||
except subprocess.CalledProcessError as e: | except subprocess.CalledProcessError as e: | ||||
sys.exit(e.returncode) | sys.exit(e.returncode) | ||||
else: | else: | ||||
if verbose >= 3: | if verbose >= 3: | ||||
print("Determining size of \"dist\"...") | print("Determining size of \"dist\"...") | ||||
size = 0 | size = 0 | ||||
for path, dirs, files in os.walk("dist"): | for path, dirs, files in os.walk("dist"): | ||||
for file in files: | for file in files: | ||||
size += os.path.getsize(os.path.join(path, file)) | size += os.path.getsize(os.path.join(path, file)) | ||||
size += int(size * 0.15) | size += int(size * 0.15) | ||||
if verbose >= 3: | if verbose >= 3: | ||||
print("Creating temp image for modification...") | print("Creating temp image for modification...") | ||||
try: | try: | ||||
runHDIUtil("create", dmg_name + ".temp", srcfolder="dist", format="UDRW", size=size, volname=volname, ov=True) | runHDIUtil("create", dmg_name + ".temp", srcfolder="dist", format="UDRW", size=size, volname=volname, ov=True) | ||||
except subprocess.CalledProcessError as e: | except subprocess.CalledProcessError as e: | ||||
sys.exit(e.returncode) | sys.exit(e.returncode) | ||||
if verbose >= 3: | if verbose >= 3: | ||||
print("Attaching temp image...") | print("Attaching temp image...") | ||||
try: | try: | ||||
output = runHDIUtil("attach", dmg_name + ".temp", readwrite=True, noverify=True, noautoopen=True, capture_stdout=True) | output = runHDIUtil("attach", dmg_name + ".temp", readwrite=True, noverify=True, noautoopen=True, capture_stdout=True) | ||||
except subprocess.CalledProcessError as e: | except subprocess.CalledProcessError as e: | ||||
sys.exit(e.returncode) | sys.exit(e.returncode) | ||||
m = re.search("/Volumes/(.+$)", output) | m = re.search("/Volumes/(.+$)", output) | ||||
disk_root = m.group(0) | disk_root = m.group(0) | ||||
disk_name = m.group(1) | disk_name = m.group(1) | ||||
if verbose >= 2: | if verbose >= 2: | ||||
print("+ Applying fancy settings +") | print("+ Applying fancy settings +") | ||||
if "background_picture" in fancy: | if "background_picture" in fancy: | ||||
bg_path = os.path.join(disk_root, ".background", os.path.basename(fancy["background_picture"])) | bg_path = os.path.join(disk_root, ".background", os.path.basename(fancy["background_picture"])) | ||||
os.mkdir(os.path.dirname(bg_path)) | os.mkdir(os.path.dirname(bg_path)) | ||||
if verbose >= 3: | if verbose >= 3: | ||||
print(fancy["background_picture"], "->", bg_path) | print(fancy["background_picture"], "->", bg_path) | ||||
shutil.copy2(fancy["background_picture"], bg_path) | shutil.copy2(fancy["background_picture"], bg_path) | ||||
else: | else: | ||||
bg_path = None | bg_path = None | ||||
if fancy.get("applications_symlink", False): | if fancy.get("applications_symlink", False): | ||||
os.symlink("/Applications", os.path.join(disk_root, "Applications")) | os.symlink("/Applications", os.path.join(disk_root, "Applications")) | ||||
# The Python appscript package broke with OSX 10.8 and isn't being fixed. | # The Python appscript package broke with OSX 10.8 and isn't being fixed. | ||||
# So we now build up an AppleScript string and use the osascript command | # So we now build up an AppleScript string and use the osascript command | ||||
# to make the .dmg file pretty: | # to make the .dmg file pretty: | ||||
appscript = Template( """ | appscript = Template( """ | ||||
on run argv | on run argv | ||||
tell application "Finder" | tell application "Finder" | ||||
tell disk "$disk" | tell disk "$disk" | ||||
open | open | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | else: | ||||
p = subprocess.Popen(['osascript', '-'], stdin=subprocess.PIPE) | p = subprocess.Popen(['osascript', '-'], stdin=subprocess.PIPE) | ||||
p.communicate(input=s.encode('utf-8')) | p.communicate(input=s.encode('utf-8')) | ||||
if p.returncode: | if p.returncode: | ||||
print("Error running osascript.") | print("Error running osascript.") | ||||
if verbose >= 2: | if verbose >= 2: | ||||
print("+ Finalizing .dmg disk image +") | print("+ Finalizing .dmg disk image +") | ||||
time.sleep(5) | time.sleep(5) | ||||
try: | try: | ||||
runHDIUtil("convert", dmg_name + ".temp", format="UDBZ", o=dmg_name + ".dmg", ov=True) | runHDIUtil("convert", dmg_name + ".temp", format="UDBZ", o=dmg_name + ".dmg", ov=True) | ||||
except subprocess.CalledProcessError as e: | except subprocess.CalledProcessError as e: | ||||
sys.exit(e.returncode) | sys.exit(e.returncode) | ||||
os.unlink(dmg_name + ".temp.dmg") | os.unlink(dmg_name + ".temp.dmg") | ||||
# ------------------------------------------------ | # ------------------------------------------------ | ||||
if verbose >= 2: | if verbose >= 2: | ||||
print("+ Done +") | print("+ Done +") | ||||
sys.exit(0) | sys.exit(0) |