Browse Source
See notes.txt in contrib/macdeploy. Also added a dash to the application name in src/qt/bitcoin.cppmiguelfreitas
p2k
13 years ago
6 changed files with 400 additions and 1 deletions
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
@ -0,0 +1,32 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
||||||
|
<plist version="1.0"> |
||||||
|
<dict> |
||||||
|
<key>window_bounds</key> |
||||||
|
<array> |
||||||
|
<integer>300</integer> |
||||||
|
<integer>300</integer> |
||||||
|
<integer>800</integer> |
||||||
|
<integer>620</integer> |
||||||
|
</array> |
||||||
|
<key>background_picture</key> |
||||||
|
<string>background.png</string> |
||||||
|
<key>icon_size</key> |
||||||
|
<integer>96</integer> |
||||||
|
<key>applications_symlink</key> |
||||||
|
<true/> |
||||||
|
<key>items_position</key> |
||||||
|
<dict> |
||||||
|
<key>Applications</key> |
||||||
|
<array> |
||||||
|
<integer>370</integer> |
||||||
|
<integer>156</integer> |
||||||
|
</array> |
||||||
|
<key>Bitcoin-Qt.app</key> |
||||||
|
<array> |
||||||
|
<integer>128</integer> |
||||||
|
<integer>156</integer> |
||||||
|
</array> |
||||||
|
</dict> |
||||||
|
</dict> |
||||||
|
</plist> |
@ -0,0 +1,341 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
|
||||||
|
# |
||||||
|
# Copyright (C) 2011 Patrick "p2k" Schneider <me@p2k-network.org> |
||||||
|
# |
||||||
|
# This program is free software: you can redistribute it and/or modify |
||||||
|
# it under the terms of the GNU General Public License as published by |
||||||
|
# the Free Software Foundation, either version 3 of the License, or |
||||||
|
# (at your option) any later version. |
||||||
|
# |
||||||
|
# This program is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||||
|
# |
||||||
|
|
||||||
|
import subprocess, sys, re, os, shutil, os.path |
||||||
|
from time import sleep |
||||||
|
from argparse import ArgumentParser |
||||||
|
|
||||||
|
qt_conf="""[Paths] |
||||||
|
translations=Resources |
||||||
|
plugins=PlugIns |
||||||
|
""" |
||||||
|
|
||||||
|
ap = ArgumentParser(description="""Front-end to macdeployqt with some additional functions. |
||||||
|
|
||||||
|
Outputs a ready-to-deploy app in a folder "dist" and optionally wraps it in a .dmg file. |
||||||
|
Note, that the "dist" folder will be deleted before deploying on each run. |
||||||
|
|
||||||
|
Optionally, Qt translation files (.qm) and additional resources can be added to the bundle.""") |
||||||
|
|
||||||
|
ap.add_argument("app_bundle", nargs=1, metavar="app-bundle", help="application bundle to be deployed") |
||||||
|
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") |
||||||
|
ap.add_argument("-no-plugins", dest="plugins", action="store_false", default=True, help="skip plugin deployment") |
||||||
|
ap.add_argument("-no-strip", dest="strip", action="store_false", default=True, help="don't run 'strip' on the binaries") |
||||||
|
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") |
||||||
|
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") |
||||||
|
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") |
||||||
|
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") |
||||||
|
|
||||||
|
config = ap.parse_args() |
||||||
|
|
||||||
|
verbose = config.verbose[0] |
||||||
|
|
||||||
|
# ------------------------------------------------ |
||||||
|
|
||||||
|
app_bundle = config.app_bundle[0] |
||||||
|
|
||||||
|
if not os.path.exists(app_bundle): |
||||||
|
if verbose >= 1: |
||||||
|
sys.stderr.write("Error: Could not find app bundle \"%s\"\n" % (app_bundle)) |
||||||
|
sys.exit(1) |
||||||
|
|
||||||
|
app_bundle_name = os.path.splitext(os.path.basename(app_bundle))[0] |
||||||
|
|
||||||
|
# ------------------------------------------------ |
||||||
|
|
||||||
|
for p in config.add_resources: |
||||||
|
if verbose >= 3: |
||||||
|
print "Checking for \"%s\"..." % p |
||||||
|
if not os.path.exists(p): |
||||||
|
if verbose >= 1: |
||||||
|
sys.stderr.write("Error: Could not find additional resource file \"%s\"\n" % (p)) |
||||||
|
sys.exit(1) |
||||||
|
|
||||||
|
# ------------------------------------------------ |
||||||
|
|
||||||
|
if len(config.add_qt_tr) == 0: |
||||||
|
add_qt_tr = [] |
||||||
|
else: |
||||||
|
qt_tr_dir = os.path.join(os.getenv("QTDIR", ""), "translations") |
||||||
|
add_qt_tr = ["qt_%s.qm" % lng for lng in config.add_qt_tr[0].split(",")] |
||||||
|
for lng_file in add_qt_tr: |
||||||
|
p = os.path.join(qt_tr_dir, lng_file) |
||||||
|
if verbose >= 3: |
||||||
|
print "Checking for \"%s\"..." % p |
||||||
|
if not os.path.exists(p): |
||||||
|
if verbose >= 1: |
||||||
|
sys.stderr.write("Error: Could not find Qt translation file \"%s\"\n" % (lng_file)) |
||||||
|
sys.exit(1) |
||||||
|
|
||||||
|
# ------------------------------------------------ |
||||||
|
|
||||||
|
if len(config.fancy) == 1: |
||||||
|
if verbose >= 3: |
||||||
|
print "Fancy: Importing plistlib..." |
||||||
|
try: |
||||||
|
import plistlib |
||||||
|
except ImportError: |
||||||
|
if verbose >= 1: |
||||||
|
sys.stderr.write("Error: Could not import plistlib which is required for fancy disk images.\n") |
||||||
|
sys.exit(1) |
||||||
|
|
||||||
|
if verbose >= 3: |
||||||
|
print "Fancy: Importing appscript..." |
||||||
|
try: |
||||||
|
import appscript |
||||||
|
except ImportError: |
||||||
|
if verbose >= 1: |
||||||
|
sys.stderr.write("Error: Could not import appscript which is required for fancy disk images.\n") |
||||||
|
sys.stderr.write("Please install it e.g. with \"sudo easy_install appscript\".\n") |
||||||
|
sys.exit(1) |
||||||
|
|
||||||
|
p = config.fancy[0] |
||||||
|
if verbose >= 3: |
||||||
|
print "Fancy: Loading \"%s\"..." % p |
||||||
|
if not os.path.exists(p): |
||||||
|
if verbose >= 1: |
||||||
|
sys.stderr.write("Error: Could not find fancy disk image plist at \"%s\"\n" % (p)) |
||||||
|
sys.exit(1) |
||||||
|
|
||||||
|
try: |
||||||
|
fancy = plistlib.readPlist(p) |
||||||
|
except: |
||||||
|
if verbose >= 1: |
||||||
|
sys.stderr.write("Error: Could not parse fancy disk image plist at \"%s\"\n" % (p)) |
||||||
|
sys.exit(1) |
||||||
|
|
||||||
|
try: |
||||||
|
assert not fancy.has_key("window_bounds") or (isinstance(fancy["window_bounds"], list) and len(fancy["window_bounds"]) == 4) |
||||||
|
assert not fancy.has_key("background_picture") or isinstance(fancy["background_picture"], str) |
||||||
|
assert not fancy.has_key("icon_size") or isinstance(fancy["icon_size"], int) |
||||||
|
assert not fancy.has_key("applications_symlink") or isinstance(fancy["applications_symlink"], bool) |
||||||
|
if fancy.has_key("items_position"): |
||||||
|
assert isinstance(fancy["items_position"], dict) |
||||||
|
for key, value in fancy["items_position"].iteritems(): |
||||||
|
assert isinstance(value, list) and len(value) == 2 and isinstance(value[0], int) and isinstance(value[1], int) |
||||||
|
except: |
||||||
|
if verbose >= 1: |
||||||
|
sys.stderr.write("Error: Bad format of fancy disk image plist at \"%s\"\n" % (p)) |
||||||
|
sys.exit(1) |
||||||
|
|
||||||
|
if fancy.has_key("background_picture"): |
||||||
|
bp = fancy["background_picture"] |
||||||
|
if verbose >= 3: |
||||||
|
print "Fancy: Resolving background picture \"%s\"..." % bp |
||||||
|
if not os.path.exists(bp): |
||||||
|
bp = os.path.join(os.path.dirname(p), bp) |
||||||
|
if not os.path.exists(bp): |
||||||
|
if verbose >= 1: |
||||||
|
sys.stderr.write("Error: Could not find background picture at \"%s\" or \"%s\"\n" % (fancy["background_picture"], bp)) |
||||||
|
sys.exit(1) |
||||||
|
else: |
||||||
|
fancy["background_picture"] = bp |
||||||
|
else: |
||||||
|
fancy = None |
||||||
|
|
||||||
|
# ------------------------------------------------ |
||||||
|
|
||||||
|
if os.path.exists("dist"): |
||||||
|
if verbose >= 2: |
||||||
|
print "+ Removing old dist folder +" |
||||||
|
|
||||||
|
shutil.rmtree("dist") |
||||||
|
|
||||||
|
# ------------------------------------------------ |
||||||
|
|
||||||
|
target = os.path.join("dist", app_bundle) |
||||||
|
target_res = os.path.join(target, "Contents", "Resources") |
||||||
|
|
||||||
|
if verbose >= 2: |
||||||
|
print "+ Copying source bundle +" |
||||||
|
if verbose >= 3: |
||||||
|
print app_bundle, "->", target |
||||||
|
|
||||||
|
os.mkdir("dist") |
||||||
|
shutil.copytree(app_bundle, target) |
||||||
|
|
||||||
|
# ------------------------------------------------ |
||||||
|
|
||||||
|
macdeployqt_args = ["macdeployqt", target, "-verbose=%d" % verbose] |
||||||
|
if not config.plugins: |
||||||
|
macdeployqt_args.append("-no-plugins") |
||||||
|
if not config.strip: |
||||||
|
macdeployqt_args.append("-no-strip") |
||||||
|
|
||||||
|
if verbose >= 2: |
||||||
|
print "+ Running macdeployqt +" |
||||||
|
|
||||||
|
ret = subprocess.call(macdeployqt_args) |
||||||
|
if ret != 0: |
||||||
|
sys.exit(ret) |
||||||
|
|
||||||
|
# ------------------------------------------------ |
||||||
|
|
||||||
|
if verbose >= 2: |
||||||
|
print "+ Installing qt.conf +" |
||||||
|
|
||||||
|
f = open(os.path.join(target_res, "qt.conf"), "wb") |
||||||
|
f.write(qt_conf) |
||||||
|
f.close() |
||||||
|
|
||||||
|
# ------------------------------------------------ |
||||||
|
|
||||||
|
if len(add_qt_tr) > 0 and verbose >= 2: |
||||||
|
print "+ Adding Qt translations +" |
||||||
|
|
||||||
|
for lng_file in add_qt_tr: |
||||||
|
if verbose >= 3: |
||||||
|
print os.path.join(qt_tr_dir, lng_file), "->", os.path.join(target_res, lng_file) |
||||||
|
shutil.copy2(os.path.join(qt_tr_dir, lng_file), os.path.join(target_res, lng_file)) |
||||||
|
|
||||||
|
# ------------------------------------------------ |
||||||
|
|
||||||
|
if len(config.add_resources) > 0 and verbose >= 2: |
||||||
|
print "+ Adding additional resources +" |
||||||
|
|
||||||
|
for p in config.add_resources: |
||||||
|
t = os.path.join(target_res, os.path.basename(p)) |
||||||
|
if verbose >= 3: |
||||||
|
print p, "->", t |
||||||
|
if os.path.isdir(p): |
||||||
|
shutil.copytree(p, t) |
||||||
|
else: |
||||||
|
shutil.copy2(p, t) |
||||||
|
|
||||||
|
# ------------------------------------------------ |
||||||
|
|
||||||
|
if config.dmg is not None: |
||||||
|
def runHDIUtil(verb, image_basename, **kwargs): |
||||||
|
hdiutil_args = ["hdiutil", verb, image_basename + ".dmg"] |
||||||
|
if kwargs.has_key("capture_stdout"): |
||||||
|
del kwargs["capture_stdout"] |
||||||
|
run = subprocess.check_output |
||||||
|
else: |
||||||
|
if verbose < 2: |
||||||
|
hdiutil_args.append("-quiet") |
||||||
|
elif verbose >= 3: |
||||||
|
hdiutil_args.append("-verbose") |
||||||
|
run = subprocess.check_call |
||||||
|
|
||||||
|
for key, value in kwargs.iteritems(): |
||||||
|
hdiutil_args.append("-" + key) |
||||||
|
if not value is True: |
||||||
|
hdiutil_args.append(str(value)) |
||||||
|
|
||||||
|
return run(hdiutil_args) |
||||||
|
|
||||||
|
if verbose >= 2: |
||||||
|
if fancy is None: |
||||||
|
print "+ Creating .dmg disk image +" |
||||||
|
else: |
||||||
|
print "+ Preparing .dmg disk image +" |
||||||
|
|
||||||
|
if config.dmg != "": |
||||||
|
dmg_name = config.dmg |
||||||
|
else: |
||||||
|
spl = app_bundle_name.split(" ") |
||||||
|
dmg_name = spl[0] + "".join(p.capitalize() for p in spl[1:]) |
||||||
|
|
||||||
|
if fancy is None: |
||||||
|
try: |
||||||
|
runHDIUtil("create", dmg_name, srcfolder="dist", format="UDBZ", volname=app_bundle_name, ov=True) |
||||||
|
except subprocess.CalledProcessError as e: |
||||||
|
sys.exit(e.returncode) |
||||||
|
else: |
||||||
|
if verbose >= 3: |
||||||
|
print "Determining size of \"dist\"..." |
||||||
|
size = 0 |
||||||
|
for path, dirs, files in os.walk("dist"): |
||||||
|
for file in files: |
||||||
|
size += os.path.getsize(os.path.join(path, file)) |
||||||
|
size += int(size * 0.1) |
||||||
|
|
||||||
|
if verbose >= 3: |
||||||
|
print "Creating temp image for modification..." |
||||||
|
try: |
||||||
|
runHDIUtil("create", dmg_name + ".temp", srcfolder="dist", format="UDRW", size=size, volname=app_bundle_name, ov=True) |
||||||
|
except subprocess.CalledProcessError as e: |
||||||
|
sys.exit(e.returncode) |
||||||
|
|
||||||
|
if verbose >= 3: |
||||||
|
print "Attaching temp image..." |
||||||
|
try: |
||||||
|
output = runHDIUtil("attach", dmg_name + ".temp", readwrite=True, noverify=True, noautoopen=True, capture_stdout=True) |
||||||
|
except subprocess.CalledProcessError as e: |
||||||
|
sys.exit(e.returncode) |
||||||
|
|
||||||
|
m = re.search("/Volumes/(.+$)", output) |
||||||
|
disk_root = m.group(0) |
||||||
|
disk_name = m.group(1) |
||||||
|
|
||||||
|
if verbose >= 2: |
||||||
|
print "+ Applying fancy settings +" |
||||||
|
|
||||||
|
if fancy.has_key("background_picture"): |
||||||
|
bg_path = os.path.join(disk_root, os.path.basename(fancy["background_picture"])) |
||||||
|
if verbose >= 3: |
||||||
|
print fancy["background_picture"], "->", bg_path |
||||||
|
shutil.copy2(fancy["background_picture"], bg_path) |
||||||
|
else: |
||||||
|
bg_path = None |
||||||
|
|
||||||
|
if fancy.get("applications_symlink", False): |
||||||
|
os.symlink("/Applications", os.path.join(disk_root, "Applications")) |
||||||
|
|
||||||
|
finder = appscript.app("Finder") |
||||||
|
disk = finder.disks[disk_name] |
||||||
|
disk.open() |
||||||
|
window = disk.container_window |
||||||
|
window.current_view.set(appscript.k.icon_view) |
||||||
|
window.toolbar_visible.set(False) |
||||||
|
window.statusbar_visible.set(False) |
||||||
|
if fancy.has_key("window_bounds"): |
||||||
|
window.bounds.set(fancy["window_bounds"]) |
||||||
|
view_options = window.icon_view_options |
||||||
|
view_options.arrangement.set(appscript.k.not_arranged) |
||||||
|
if fancy.has_key("icon_size"): |
||||||
|
view_options.icon_size.set(fancy["icon_size"]) |
||||||
|
if bg_path is not None: |
||||||
|
view_options.background_picture.set(disk.files[os.path.basename(bg_path)]) |
||||||
|
if fancy.has_key("items_position"): |
||||||
|
for name, position in fancy["items_position"].iteritems(): |
||||||
|
window.items[name].position.set(position) |
||||||
|
disk.close() |
||||||
|
if bg_path is not None: |
||||||
|
subprocess.call(["SetFile", "-a", "V", bg_path]) |
||||||
|
disk.update(registering_applications=False) |
||||||
|
sleep(2) |
||||||
|
disk.eject() |
||||||
|
|
||||||
|
if verbose >= 2: |
||||||
|
print "+ Finalizing .dmg disk image +" |
||||||
|
|
||||||
|
try: |
||||||
|
runHDIUtil("convert", dmg_name + ".temp", format="UDBZ", o=dmg_name + ".dmg", ov=True) |
||||||
|
except subprocess.CalledProcessError as e: |
||||||
|
sys.exit(e.returncode) |
||||||
|
|
||||||
|
os.unlink(dmg_name + ".temp.dmg") |
||||||
|
|
||||||
|
# ------------------------------------------------ |
||||||
|
|
||||||
|
if verbose >= 2: |
||||||
|
print "+ Done +" |
||||||
|
|
||||||
|
sys.exit(0) |
@ -0,0 +1,26 @@ |
|||||||
|
|
||||||
|
macdeployqtplus works best on OS X Lion, for Snow Leopard you'd need to install |
||||||
|
Python 2.7 and make it your default Python installation. |
||||||
|
|
||||||
|
You will need the appscript package for the fancy disk image creation to work. |
||||||
|
Install it by invoking "sudo easy_install appscript". |
||||||
|
|
||||||
|
Ths script should be invoked in the target directory like this: |
||||||
|
$source_dir/contrib/macdeploy/macdeployqtplus Bitcoin-Qt.app -add-qt-tr de,ru -dmg -fancy $source_dir/contrib/macdeploy/fancy.plist |
||||||
|
|
||||||
|
During the process, the disk image window will pop up briefly where the fancy |
||||||
|
settings are applied. This is normal, please do not interfere. |
||||||
|
|
||||||
|
You can also set up Qt Creator for invoking the script. For this, go to the |
||||||
|
"Projects" tab on the left side, switch to "Run Settings" above and add a |
||||||
|
deploy configuration. Next add a deploy step choosing "Custom Process Step". |
||||||
|
Fill in the following. |
||||||
|
|
||||||
|
Enable custom process step: [x] |
||||||
|
Command: %{sourceDir}/contrib/macdeploy/macdeployqtplus |
||||||
|
Working directory: %{buildDir} |
||||||
|
Command arguments: Bitcoin-Qt.app -add-qt-tr de,ru -dmg -fancy %{sourceDir}/contrib/macdeploy/fancy.plist |
||||||
|
|
||||||
|
After that you can start the deployment process through the menu with |
||||||
|
Build -> Deploy Project "bitcoin-qt" |
||||||
|
|
Loading…
Reference in new issue