4 # Copyright (C) 2011 Patrick "p2k" Schneider <me@p2k-network.org>
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 import subprocess, sys, re, os, shutil, stat, os.path
21 from string import Template
22 from time import sleep
23 from argparse import ArgumentParser
25 # This is ported from the original macdeployqt with modifications
27 class FrameworkInfo(object):
29 self.frameworkDirectory = ""
30 self.frameworkName = ""
31 self.frameworkPath = ""
32 self.binaryDirectory = ""
37 self.deployedInstallName = ""
38 self.sourceFilePath = ""
39 self.destinationDirectory = ""
40 self.sourceResourcesDirectory = ""
41 self.destinationResourcesDirectory = ""
43 def __eq__(self, other):
44 if self.__class__ == other.__class__:
45 return self.__dict__ == other.__dict__
50 return """ Framework name: %s
51 Framework directory: %s
58 Deployed install name: %s
60 Deployed Directory (relative to bundle): %s
61 """ % (self.frameworkName,
62 self.frameworkDirectory,
69 self.deployedInstallName,
71 self.destinationDirectory)
74 return self.frameworkName.endswith(".dylib")
76 def isQtFramework(self):
78 return self.frameworkName.startswith("libQt")
80 return self.frameworkName.startswith("Qt")
82 reOLine = re.compile(r'^(.+) \(compatibility version [0-9.]+, current version [0-9.]+\)$')
83 bundleFrameworkDirectory = "Contents/Frameworks"
84 bundleBinaryDirectory = "Contents/MacOS"
87 def fromOtoolLibraryLine(cls, line):
88 # Note: line must be trimmed
92 # Don't deploy system libraries (exception for libQtuitools and libQtlucene).
93 if line.startswith("/System/Library/") or line.startswith("@executable_path") or (line.startswith("/usr/lib/") and "libQt" not in line):
96 m = cls.reOLine.match(line)
98 raise RuntimeError("otool line could not be parsed: " + line)
103 info.sourceFilePath = path
104 info.installName = path
106 if path.endswith(".dylib"):
107 dirname, filename = os.path.split(path)
108 info.frameworkName = filename
109 info.frameworkDirectory = dirname
110 info.frameworkPath = path
112 info.binaryDirectory = dirname
113 info.binaryName = filename
114 info.binaryPath = path
117 info.installName = path
118 info.deployedInstallName = "@executable_path/../Frameworks/" + info.binaryName
119 info.sourceFilePath = path
120 info.destinationDirectory = cls.bundleFrameworkDirectory
122 parts = path.split("/")
124 # Search for the .framework directory
126 if part.endswith(".framework"):
130 raise RuntimeError("Could not find .framework or .dylib in otool line: " + line)
132 info.frameworkName = parts[i]
133 info.frameworkDirectory = "/".join(parts[:i])
134 info.frameworkPath = os.path.join(info.frameworkDirectory, info.frameworkName)
136 info.binaryName = parts[i+3]
137 info.binaryDirectory = "/".join(parts[i+1:i+3])
138 info.binaryPath = os.path.join(info.binaryDirectory, info.binaryName)
139 info.version = parts[i+2]
141 info.deployedInstallName = "@executable_path/../Frameworks/" + os.path.join(info.frameworkName, info.binaryPath)
142 info.destinationDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, info.binaryDirectory)
144 info.sourceResourcesDirectory = os.path.join(info.frameworkPath, "Resources")
145 info.destinationResourcesDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, "Resources")
149 class ApplicationBundleInfo(object):
150 def __init__(self, path):
152 appName = os.path.splitext(os.path.basename(path))[0]
153 self.binaryPath = os.path.join(path, "Contents", "MacOS", appName)
154 if not os.path.exists(self.binaryPath):
155 raise RuntimeError("Could not find bundle binary for " + path)
156 self.resourcesPath = os.path.join(path, "Contents", "Resources")
157 self.pluginPath = os.path.join(path, "Contents", "PlugIns")
159 class DeploymentInfo(object):
162 self.pluginPath = None
163 self.deployedFrameworks = []
165 def detectQtPath(self, frameworkDirectory):
166 parentDir = os.path.dirname(frameworkDirectory)
167 if os.path.exists(os.path.join(parentDir, "translations")):
168 # Classic layout, e.g. "/usr/local/Trolltech/Qt-4.x.x"
169 self.qtPath = parentDir
170 elif os.path.exists(os.path.join(parentDir, "share", "qt4", "translations")):
171 # MacPorts layout, e.g. "/opt/local/share/qt4"
172 self.qtPath = os.path.join(parentDir, "share", "qt4")
173 elif os.path.exists(os.path.join(os.path.dirname(parentDir), "share", "qt4", "translations")):
174 # Newer Macports layout
175 self.qtPath = os.path.join(os.path.dirname(parentDir), "share", "qt4")
177 self.qtPath = os.getenv("QTDIR", None)
179 if self.qtPath is not None:
180 pluginPath = os.path.join(self.qtPath, "plugins")
181 if os.path.exists(pluginPath):
182 self.pluginPath = pluginPath
184 def usesFramework(self, name):
185 nameDot = "%s." % name
186 libNameDot = "lib%s." % name
187 for framework in self.deployedFrameworks:
188 if framework.endswith(".framework"):
189 if framework.startswith(nameDot):
191 elif framework.endswith(".dylib"):
192 if framework.startswith(libNameDot):
196 def getFrameworks(binaryPath, verbose):
198 print "Inspecting with otool: " + binaryPath
199 otool = subprocess.Popen(["otool", "-L", binaryPath], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
200 o_stdout, o_stderr = otool.communicate()
201 if otool.returncode != 0:
203 sys.stderr.write(o_stderr)
205 raise RuntimeError("otool failed with return code %d" % otool.returncode)
207 otoolLines = o_stdout.split("\n")
208 otoolLines.pop(0) # First line is the inspected binary
209 if ".framework" in binaryPath or binaryPath.endswith(".dylib"):
210 otoolLines.pop(0) # Frameworks and dylibs list themselves as a dependency.
213 for line in otoolLines:
214 info = FrameworkInfo.fromOtoolLibraryLine(line.strip())
217 print "Found framework:"
219 libraries.append(info)
223 def runInstallNameTool(action, *args):
224 subprocess.check_call(["install_name_tool", "-"+action] + list(args))
226 def changeInstallName(oldName, newName, binaryPath, verbose):
228 print "Using install_name_tool:"
229 print " in", binaryPath
230 print " change reference", oldName
232 runInstallNameTool("change", oldName, newName, binaryPath)
234 def changeIdentification(id, binaryPath, verbose):
236 print "Using install_name_tool:"
237 print " change identification in", binaryPath
239 runInstallNameTool("id", id, binaryPath)
241 def runStrip(binaryPath, verbose):
244 print " stripped", binaryPath
245 subprocess.check_call(["strip", "-x", binaryPath])
247 def copyFramework(framework, path, verbose):
248 if framework.sourceFilePath.startswith("Qt"):
249 #standard place for Nokia Qt installer's frameworks
250 fromPath = "/Library/Frameworks/" + framework.sourceFilePath
252 fromPath = framework.sourceFilePath
253 toDir = os.path.join(path, framework.destinationDirectory)
254 toPath = os.path.join(toDir, framework.binaryName)
256 if not os.path.exists(fromPath):
257 raise RuntimeError("No file at " + fromPath)
259 if os.path.exists(toPath):
260 return None # Already there
262 if not os.path.exists(toDir):
265 shutil.copy2(fromPath, toPath)
267 print "Copied:", fromPath
270 permissions = os.stat(toPath)
271 if not permissions.st_mode & stat.S_IWRITE:
272 os.chmod(toPath, permissions.st_mode | stat.S_IWRITE)
274 if not framework.isDylib(): # Copy resources for real frameworks
275 fromResourcesDir = framework.sourceResourcesDirectory
276 if os.path.exists(fromResourcesDir):
277 toResourcesDir = os.path.join(path, framework.destinationResourcesDirectory)
278 shutil.copytree(fromResourcesDir, toResourcesDir)
280 print "Copied resources:", fromResourcesDir
281 print " to:", toResourcesDir
282 elif framework.frameworkName.startswith("libQtGui"): # Copy qt_menu.nib (applies to non-framework layout)
283 qtMenuNibSourcePath = os.path.join(framework.frameworkDirectory, "Resources", "qt_menu.nib")
284 qtMenuNibDestinationPath = os.path.join(path, "Contents", "Resources", "qt_menu.nib")
285 if os.path.exists(qtMenuNibSourcePath) and not os.path.exists(qtMenuNibDestinationPath):
286 shutil.copytree(qtMenuNibSourcePath, qtMenuNibDestinationPath)
288 print "Copied for libQtGui:", qtMenuNibSourcePath
289 print " to:", qtMenuNibDestinationPath
293 def deployFrameworks(frameworks, bundlePath, binaryPath, strip, verbose, deploymentInfo=None):
294 if deploymentInfo is None:
295 deploymentInfo = DeploymentInfo()
297 while len(frameworks) > 0:
298 framework = frameworks.pop(0)
299 deploymentInfo.deployedFrameworks.append(framework.frameworkName)
302 print "Processing", framework.frameworkName, "..."
304 # Get the Qt path from one of the Qt frameworks
305 if deploymentInfo.qtPath is None and framework.isQtFramework():
306 deploymentInfo.detectQtPath(framework.frameworkDirectory)
308 if framework.installName.startswith("@executable_path"):
310 print framework.frameworkName, "already deployed, skipping."
313 # install_name_tool the new id into the binary
314 changeInstallName(framework.installName, framework.deployedInstallName, binaryPath, verbose)
316 # Copy farmework to app bundle.
317 deployedBinaryPath = copyFramework(framework, bundlePath, verbose)
318 # Skip the rest if already was deployed.
319 if deployedBinaryPath is None:
323 runStrip(deployedBinaryPath, verbose)
325 # install_name_tool it a new id.
326 changeIdentification(framework.deployedInstallName, deployedBinaryPath, verbose)
327 # Check for framework dependencies
328 dependencies = getFrameworks(deployedBinaryPath, verbose)
330 for dependency in dependencies:
331 changeInstallName(dependency.installName, dependency.deployedInstallName, deployedBinaryPath, verbose)
333 # Deploy framework if necessary.
334 if dependency.frameworkName not in deploymentInfo.deployedFrameworks and dependency not in frameworks:
335 frameworks.append(dependency)
337 return deploymentInfo
339 def deployFrameworksForAppBundle(applicationBundle, strip, verbose):
340 frameworks = getFrameworks(applicationBundle.binaryPath, verbose)
341 if len(frameworks) == 0 and verbose >= 1:
342 print "Warning: Could not find any external frameworks to deploy in %s." % (applicationBundle.path)
343 return DeploymentInfo()
345 return deployFrameworks(frameworks, applicationBundle.path, applicationBundle.binaryPath, strip, verbose)
347 def deployPlugins(appBundleInfo, deploymentInfo, strip, verbose):
348 # Lookup available plugins, exclude unneeded
350 for dirpath, dirnames, filenames in os.walk(deploymentInfo.pluginPath):
351 pluginDirectory = os.path.relpath(dirpath, deploymentInfo.pluginPath)
352 if pluginDirectory == "designer":
353 # Skip designer plugins
355 elif pluginDirectory == "phonon" or pluginDirectory == "phonon_backend":
356 # Deploy the phonon plugins only if phonon is in use
357 if not deploymentInfo.usesFramework("phonon"):
359 elif pluginDirectory == "sqldrivers":
360 # Deploy the sql plugins only if QtSql is in use
361 if not deploymentInfo.usesFramework("QtSql"):
363 elif pluginDirectory == "script":
364 # Deploy the script plugins only if QtScript is in use
365 if not deploymentInfo.usesFramework("QtScript"):
367 elif pluginDirectory == "qmltooling":
368 # Deploy the qml plugins only if QtDeclarative is in use
369 if not deploymentInfo.usesFramework("QtDeclarative"):
371 elif pluginDirectory == "bearer":
372 # Deploy the bearer plugins only if QtNetwork is in use
373 if not deploymentInfo.usesFramework("QtNetwork"):
376 for pluginName in filenames:
377 pluginPath = os.path.join(pluginDirectory, pluginName)
378 if pluginName.endswith("_debug.dylib"):
381 elif pluginPath == "imageformats/libqsvg.dylib" or pluginPath == "iconengines/libqsvgicon.dylib":
382 # Deploy the svg plugins only if QtSvg is in use
383 if not deploymentInfo.usesFramework("QtSvg"):
385 elif pluginPath == "accessible/libqtaccessiblecompatwidgets.dylib":
386 # Deploy accessibility for Qt3Support only if the Qt3Support is in use
387 if not deploymentInfo.usesFramework("Qt3Support"):
389 elif pluginPath == "graphicssystems/libqglgraphicssystem.dylib":
390 # Deploy the opengl graphicssystem plugin only if QtOpenGL is in use
391 if not deploymentInfo.usesFramework("QtOpenGL"):
394 plugins.append((pluginDirectory, pluginName))
396 for pluginDirectory, pluginName in plugins:
398 print "Processing plugin", os.path.join(pluginDirectory, pluginName), "..."
400 sourcePath = os.path.join(deploymentInfo.pluginPath, pluginDirectory, pluginName)
401 destinationDirectory = os.path.join(appBundleInfo.pluginPath, pluginDirectory)
402 if not os.path.exists(destinationDirectory):
403 os.makedirs(destinationDirectory)
405 destinationPath = os.path.join(destinationDirectory, pluginName)
406 shutil.copy2(sourcePath, destinationPath)
408 print "Copied:", sourcePath
409 print " to:", destinationPath
412 runStrip(destinationPath, verbose)
414 dependencies = getFrameworks(destinationPath, verbose)
416 for dependency in dependencies:
417 changeInstallName(dependency.installName, dependency.deployedInstallName, destinationPath, verbose)
419 # Deploy framework if necessary.
420 if dependency.frameworkName not in deploymentInfo.deployedFrameworks:
421 deployFrameworks([dependency], appBundleInfo.path, destinationPath, strip, verbose, deploymentInfo)
424 translations=Resources
428 ap = ArgumentParser(description="""Improved version of macdeployqt.
430 Outputs a ready-to-deploy app in a folder "dist" and optionally wraps it in a .dmg file.
431 Note, that the "dist" folder will be deleted before deploying on each run.
433 Optionally, Qt translation files (.qm) and additional resources can be added to the bundle.
435 Also optionally signs the .app bundle; set the CODESIGNARGS environment variable to pass arguments
436 to the codesign tool.
437 E.g. CODESIGNARGS='--sign "Developer ID Application: ..." --keychain /encrypted/foo.keychain'""")
439 ap.add_argument("app_bundle", nargs=1, metavar="app-bundle", help="application bundle to be deployed")
440 ap.add_argument("-verbose", type=int, nargs=1, default=[1], metavar="<0-3>", help="0 = no output, 1 = error/warning (default), 2 = normal, 3 = debug")
441 ap.add_argument("-no-plugins", dest="plugins", action="store_false", default=True, help="skip plugin deployment")
442 ap.add_argument("-no-strip", dest="strip", action="store_false", default=True, help="don't run 'strip' on the binaries")
443 ap.add_argument("-sign", dest="sign", action="store_true", default=False, help="sign .app bundle with codesign tool")
444 ap.add_argument("-dmg", nargs="?", const="", metavar="basename", help="create a .dmg disk image; if basename is not specified, a camel-cased version of the app name is used")
445 ap.add_argument("-fancy", nargs=1, metavar="plist", default=[], help="make a fancy looking disk image using the given plist file with instructions; requires -dmg to work")
446 ap.add_argument("-add-qt-tr", nargs=1, metavar="languages", default=[], help="add Qt translation files to the bundle's ressources; the language list must be separated with commas, not with whitespace")
447 ap.add_argument("-add-resources", nargs="+", metavar="path", default=[], help="list of additional files or folders to be copied into the bundle's resources; must be the last argument")
449 config = ap.parse_args()
451 verbose = config.verbose[0]
453 # ------------------------------------------------
455 app_bundle = config.app_bundle[0]
457 if not os.path.exists(app_bundle):
459 sys.stderr.write("Error: Could not find app bundle \"%s\"\n" % (app_bundle))
462 app_bundle_name = os.path.splitext(os.path.basename(app_bundle))[0]
464 # ------------------------------------------------
466 for p in config.add_resources:
468 print "Checking for \"%s\"..." % p
469 if not os.path.exists(p):
471 sys.stderr.write("Error: Could not find additional resource file \"%s\"\n" % (p))
474 # ------------------------------------------------
476 if len(config.fancy) == 1:
478 print "Fancy: Importing plistlib..."
483 sys.stderr.write("Error: Could not import plistlib which is required for fancy disk images.\n")
487 print "Fancy: Importing appscript..."
492 sys.stderr.write("Error: Could not import appscript which is required for fancy disk images.\n")
493 sys.stderr.write("Please install it e.g. with \"sudo easy_install appscript\".\n")
498 print "Fancy: Loading \"%s\"..." % p
499 if not os.path.exists(p):
501 sys.stderr.write("Error: Could not find fancy disk image plist at \"%s\"\n" % (p))
505 fancy = plistlib.readPlist(p)
508 sys.stderr.write("Error: Could not parse fancy disk image plist at \"%s\"\n" % (p))
512 assert not fancy.has_key("window_bounds") or (isinstance(fancy["window_bounds"], list) and len(fancy["window_bounds"]) == 4)
513 assert not fancy.has_key("background_picture") or isinstance(fancy["background_picture"], str)
514 assert not fancy.has_key("icon_size") or isinstance(fancy["icon_size"], int)
515 assert not fancy.has_key("applications_symlink") or isinstance(fancy["applications_symlink"], bool)
516 if fancy.has_key("items_position"):
517 assert isinstance(fancy["items_position"], dict)
518 for key, value in fancy["items_position"].iteritems():
519 assert isinstance(value, list) and len(value) == 2 and isinstance(value[0], int) and isinstance(value[1], int)
522 sys.stderr.write("Error: Bad format of fancy disk image plist at \"%s\"\n" % (p))
525 if fancy.has_key("background_picture"):
526 bp = fancy["background_picture"]
528 print "Fancy: Resolving background picture \"%s\"..." % bp
529 if not os.path.exists(bp):
530 bp = os.path.join(os.path.dirname(p), bp)
531 if not os.path.exists(bp):
533 sys.stderr.write("Error: Could not find background picture at \"%s\" or \"%s\"\n" % (fancy["background_picture"], bp))
536 fancy["background_picture"] = bp
540 # ------------------------------------------------
542 if os.path.exists("dist"):
544 print "+ Removing old dist folder +"
546 shutil.rmtree("dist")
548 # ------------------------------------------------
550 target = os.path.join("dist", app_bundle)
553 print "+ Copying source bundle +"
555 print app_bundle, "->", target
558 shutil.copytree(app_bundle, target)
560 applicationBundle = ApplicationBundleInfo(target)
562 # ------------------------------------------------
565 print "+ Deploying frameworks +"
568 deploymentInfo = deployFrameworksForAppBundle(applicationBundle, config.strip, verbose)
569 if deploymentInfo.qtPath is None:
570 deploymentInfo.qtPath = os.getenv("QTDIR", None)
571 if deploymentInfo.qtPath is None:
573 sys.stderr.write("Warning: Could not detect Qt's path, skipping plugin deployment!\n")
574 config.plugins = False
575 except RuntimeError as e:
577 sys.stderr.write("Error: %s\n" % str(e))
580 # ------------------------------------------------
584 print "+ Deploying plugins +"
587 deployPlugins(applicationBundle, deploymentInfo, config.strip, verbose)
588 except RuntimeError as e:
590 sys.stderr.write("Error: %s\n" % str(e))
593 # ------------------------------------------------
595 if len(config.add_qt_tr) == 0:
598 qt_tr_dir = os.path.join(deploymentInfo.qtPath, "translations")
599 add_qt_tr = ["qt_%s.qm" % lng for lng in config.add_qt_tr[0].split(",")]
600 for lng_file in add_qt_tr:
601 p = os.path.join(qt_tr_dir, lng_file)
603 print "Checking for \"%s\"..." % p
604 if not os.path.exists(p):
606 sys.stderr.write("Error: Could not find Qt translation file \"%s\"\n" % (lng_file))
609 # ------------------------------------------------
612 print "+ Installing qt.conf +"
614 f = open(os.path.join(applicationBundle.resourcesPath, "qt.conf"), "wb")
618 # ------------------------------------------------
620 if len(add_qt_tr) > 0 and verbose >= 2:
621 print "+ Adding Qt translations +"
623 for lng_file in add_qt_tr:
625 print os.path.join(qt_tr_dir, lng_file), "->", os.path.join(applicationBundle.resourcesPath, lng_file)
626 shutil.copy2(os.path.join(qt_tr_dir, lng_file), os.path.join(applicationBundle.resourcesPath, lng_file))
628 # ------------------------------------------------
630 if len(config.add_resources) > 0 and verbose >= 2:
631 print "+ Adding additional resources +"
633 for p in config.add_resources:
634 t = os.path.join(applicationBundle.resourcesPath, os.path.basename(p))
638 shutil.copytree(p, t)
642 # ------------------------------------------------
644 if config.sign and 'CODESIGNARGS' not in os.environ:
645 print "You must set the CODESIGNARGS environment variable. Skipping signing."
648 print "Code-signing app bundle %s"%(target,)
649 subprocess.check_call("codesign --force %s %s"%(os.environ['CODESIGNARGS'], target), shell=True)
651 # ------------------------------------------------
653 if config.dmg is not None:
654 def runHDIUtil(verb, image_basename, **kwargs):
655 hdiutil_args = ["hdiutil", verb, image_basename + ".dmg"]
656 if kwargs.has_key("capture_stdout"):
657 del kwargs["capture_stdout"]
658 run = subprocess.check_output
661 hdiutil_args.append("-quiet")
663 hdiutil_args.append("-verbose")
664 run = subprocess.check_call
666 for key, value in kwargs.iteritems():
667 hdiutil_args.append("-" + key)
668 if not value is True:
669 hdiutil_args.append(str(value))
671 return run(hdiutil_args)
675 print "+ Creating .dmg disk image +"
677 print "+ Preparing .dmg disk image +"
680 dmg_name = config.dmg
682 spl = app_bundle_name.split(" ")
683 dmg_name = spl[0] + "".join(p.capitalize() for p in spl[1:])
687 runHDIUtil("create", dmg_name, srcfolder="dist", format="UDBZ", volname=app_bundle_name, ov=True)
688 except subprocess.CalledProcessError as e:
689 sys.exit(e.returncode)
692 print "Determining size of \"dist\"..."
694 for path, dirs, files in os.walk("dist"):
696 size += os.path.getsize(os.path.join(path, file))
697 size += int(size * 0.1)
700 print "Creating temp image for modification..."
702 runHDIUtil("create", dmg_name + ".temp", srcfolder="dist", format="UDRW", size=size, volname=app_bundle_name, ov=True)
703 except subprocess.CalledProcessError as e:
704 sys.exit(e.returncode)
707 print "Attaching temp image..."
709 output = runHDIUtil("attach", dmg_name + ".temp", readwrite=True, noverify=True, noautoopen=True, capture_stdout=True)
710 except subprocess.CalledProcessError as e:
711 sys.exit(e.returncode)
713 m = re.search("/Volumes/(.+$)", output)
714 disk_root = m.group(0)
715 disk_name = m.group(1)
718 print "+ Applying fancy settings +"
720 if fancy.has_key("background_picture"):
721 bg_path = os.path.join(disk_root, os.path.basename(fancy["background_picture"]))
723 print fancy["background_picture"], "->", bg_path
724 shutil.copy2(fancy["background_picture"], bg_path)
728 if fancy.get("applications_symlink", False):
729 os.symlink("/Applications", os.path.join(disk_root, "Applications"))
731 # The Python appscript package broke with OSX 10.8 and isn't being fixed.
732 # So we now build up an AppleScript string and use the osascript command
733 # to make the .dmg file pretty:
734 appscript = Template( """
736 tell application "Finder"
739 set current view of container window to icon view
740 set toolbar visible of container window to false
741 set statusbar visible of container window to false
742 set the bounds of container window to {$window_bounds}
743 set theViewOptions to the icon view options of container window
744 set arrangement of theViewOptions to not arranged
745 set icon size of theViewOptions to $icon_size
748 close -- close/reopen works around a bug...
750 update without registering applications
758 itemscript = Template('set position of item "${item}" of container window to {${position}}')
760 if fancy.has_key("items_position"):
761 for name, position in fancy["items_position"].iteritems():
762 params = { "item" : name, "position" : ",".join([str(p) for p in position]) }
763 items_positions.append(itemscript.substitute(params))
766 "disk" : "NovaCoin-Qt",
767 "window_bounds" : "300,300,800,620",
769 "background_commands" : "",
770 "items_positions" : "\n ".join(items_positions)
772 if fancy.has_key("window_bounds"):
773 params["window.bounds"] = ",".join([str(p) for p in fancy["window_bounds"]])
774 if fancy.has_key("icon_size"):
775 params["icon_size"] = str(fancy["icon_size"])
776 if bg_path is not None:
777 # Set background file, then call SetFile to make it invisible.
778 # (note: making it invisible first makes set background picture fail)
779 bgscript = Template("""set background picture of theViewOptions to file "$bgpic"
780 do shell script "SetFile -a V /Volumes/$disk/$bgpic" """)
781 params["background_commands"] = bgscript.substitute({"bgpic" : os.path.basename(bg_path), "disk" : params["disk"]})
783 s = appscript.substitute(params)
785 print("Running AppleScript:")
788 p = subprocess.Popen(['osascript', '-'], stdin=subprocess.PIPE)
789 p.communicate(input=s)
791 print("Error running osascript.")
794 print "+ Finalizing .dmg disk image +"
797 runHDIUtil("convert", dmg_name + ".temp", format="UDBZ", o=dmg_name + ".dmg", ov=True)
798 except subprocess.CalledProcessError as e:
799 sys.exit(e.returncode)
801 os.unlink(dmg_name + ".temp.dmg")
803 # ------------------------------------------------