diff --git a/Makefile.am b/Makefile.am index 6a8c1b761b..fd805b7be9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,239 +1,239 @@ # Copyright (c) 2013-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. ACLOCAL_AMFLAGS = -I build-aux/m4 SUBDIRS = src if ENABLE_MAN SUBDIRS += doc/man endif .PHONY: deploy FORCE GZIP_ENV="-9n" export PYTHONPATH if BUILD_BITCOIN_LIBS pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libbitcoinconsensus.pc endif BITCOIND_BIN=$(top_builddir)/src/$(BITCOIN_DAEMON_NAME)$(EXEEXT) BITCOIN_QT_BIN=$(top_builddir)/src/qt/$(BITCOIN_GUI_NAME)$(EXEEXT) BITCOIN_CLI_BIN=$(top_builddir)/src/$(BITCOIN_CLI_NAME)$(EXEEXT) BITCOIN_WIN_INSTALLER=$(PACKAGE)-$(PACKAGE_VERSION)-win$(WINDOWS_BITS)-setup$(EXEEXT) empty := space := $(empty) $(empty) -OSX_APP=Bitcoin-Qt.app +OSX_APP=BitcoinABC-Qt.app OSX_VOLNAME = $(subst $(space),-,$(PACKAGE_NAME)) OSX_DMG = $(OSX_VOLNAME).dmg OSX_BACKGROUND_SVG=background.svg OSX_BACKGROUND_IMAGE=background.tiff OSX_BACKGROUND_IMAGE_DPIS=36 72 OSX_DSSTORE_GEN=$(top_srcdir)/contrib/macdeploy/custom_dsstore.py OSX_DEPLOY_SCRIPT=$(top_srcdir)/contrib/macdeploy/macdeployqtplus OSX_FANCY_PLIST=$(top_srcdir)/contrib/macdeploy/fancy.plist OSX_INSTALLER_ICONS=$(top_srcdir)/src/qt/res/icons/bitcoin.icns OSX_PLIST=$(top_builddir)/share/qt/Info.plist #not installed OSX_QT_TRANSLATIONS = da,de,es,hu,ru,uk,zh_CN,zh_TW DIST_DOCS = $(wildcard doc/*.md) $(wildcard doc/release-notes/*.md) DIST_CONTRIB = $(top_srcdir)/contrib/bitcoin-cli.bash-completion \ $(top_srcdir)/contrib/bitcoin-tx.bash-completion \ $(top_srcdir)/contrib/bitcoind.bash-completion \ $(top_srcdir)/contrib/init \ $(top_srcdir)/contrib/rpm BIN_CHECKS=$(top_srcdir)/contrib/devtools/symbol-check.py \ $(top_srcdir)/contrib/devtools/security-check.py WINDOWS_PACKAGING = $(top_srcdir)/share/pixmaps/bitcoin.ico \ $(top_srcdir)/share/pixmaps/nsis-header.bmp \ $(top_srcdir)/share/pixmaps/nsis-wizard.bmp \ $(top_srcdir)/doc/README_windows.txt OSX_PACKAGING = $(OSX_DEPLOY_SCRIPT) $(OSX_FANCY_PLIST) $(OSX_INSTALLER_ICONS) \ $(top_srcdir)/contrib/macdeploy/$(OSX_BACKGROUND_SVG) \ $(OSX_DSSTORE_GEN) \ $(top_srcdir)/contrib/macdeploy/detached-sig-apply.sh \ $(top_srcdir)/contrib/macdeploy/detached-sig-create.sh COVERAGE_INFO = baseline_filtered_combined.info baseline.info \ leveldb_baseline.info test_bitcoin_filtered.info total_coverage.info \ baseline_filtered.info rpc_test.info rpc_test_filtered.info \ leveldb_baseline_filtered.info test_bitcoin_coverage.info test_bitcoin.info dist-hook: -$(GIT) archive --format=tar HEAD -- src/clientversion.cpp | $(AMTAR) -C $(top_distdir) -xf - $(BITCOIN_WIN_INSTALLER): all-recursive $(MKDIR_P) $(top_builddir)/release STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIND_BIN) $(top_builddir)/release STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_QT_BIN) $(top_builddir)/release STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_CLI_BIN) $(top_builddir)/release @test -f $(MAKENSIS) && $(MAKENSIS) -V2 $(top_builddir)/share/setup.nsi || \ echo error: could not build $@ @echo built $@ $(if $(findstring src/,$(MAKECMDGOALS)),$(MAKECMDGOALS), none): FORCE $(MAKE) -C src $(patsubst src/%,%,$@) $(OSX_APP)/Contents/PkgInfo: $(MKDIR_P) $(@D) @echo "APPL????" > $@ $(OSX_APP)/Contents/Resources/empty.lproj: $(MKDIR_P) $(@D) @touch $@ $(OSX_APP)/Contents/Info.plist: $(OSX_PLIST) $(MKDIR_P) $(@D) $(INSTALL_DATA) $< $@ $(OSX_APP)/Contents/Resources/bitcoin.icns: $(OSX_INSTALLER_ICONS) $(MKDIR_P) $(@D) $(INSTALL_DATA) $< $@ -$(OSX_APP)/Contents/MacOS/Bitcoin-Qt: $(BITCOIN_QT_BIN) +$(OSX_APP)/Contents/MacOS/BitcoinABC-Qt: $(BITCOIN_QT_BIN) $(MKDIR_P) $(@D) STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $< $@ $(OSX_APP)/Contents/Resources/Base.lproj/InfoPlist.strings: $(MKDIR_P) $(@D) echo '{ CFBundleDisplayName = "$(PACKAGE_NAME)"; CFBundleName = "$(PACKAGE_NAME)"; }' > $@ OSX_APP_BUILT=$(OSX_APP)/Contents/PkgInfo $(OSX_APP)/Contents/Resources/empty.lproj \ $(OSX_APP)/Contents/Resources/bitcoin.icns $(OSX_APP)/Contents/Info.plist \ - $(OSX_APP)/Contents/MacOS/Bitcoin-Qt $(OSX_APP)/Contents/Resources/Base.lproj/InfoPlist.strings + $(OSX_APP)/Contents/MacOS/BitcoinABC-Qt $(OSX_APP)/Contents/Resources/Base.lproj/InfoPlist.strings osx_volname: echo $(OSX_VOLNAME) >$@ if BUILD_DARWIN $(OSX_DMG): $(OSX_APP_BUILT) $(OSX_PACKAGING) $(OSX_BACKGROUND_IMAGE) $(PYTHON) $(OSX_DEPLOY_SCRIPT) $(OSX_APP) -add-qt-tr $(OSX_QT_TRANSLATIONS) -translations-dir=$(QT_TRANSLATION_DIR) -dmg -fancy $(OSX_FANCY_PLIST) -verbose 2 -volname $(OSX_VOLNAME) $(OSX_BACKGROUND_IMAGE).png: contrib/macdeploy/$(OSX_BACKGROUND_SVG) sed 's/PACKAGE_NAME/$(PACKAGE_NAME)/' < "$<" | $(RSVG_CONVERT) -f png -d 36 -p 36 -o $@ $(OSX_BACKGROUND_IMAGE)@2x.png: contrib/macdeploy/$(OSX_BACKGROUND_SVG) sed 's/PACKAGE_NAME/$(PACKAGE_NAME)/' < "$<" | $(RSVG_CONVERT) -f png -d 72 -p 72 -o $@ $(OSX_BACKGROUND_IMAGE): $(OSX_BACKGROUND_IMAGE).png $(OSX_BACKGROUND_IMAGE)@2x.png tiffutil -cathidpicheck $^ -out $@ deploydir: $(OSX_DMG) else APP_DIST_DIR=$(top_builddir)/dist APP_DIST_EXTRAS=$(APP_DIST_DIR)/.background/$(OSX_BACKGROUND_IMAGE) $(APP_DIST_DIR)/.DS_Store $(APP_DIST_DIR)/Applications $(APP_DIST_DIR)/Applications: @rm -f $@ @cd $(@D); $(LN_S) /Applications $(@F) -$(APP_DIST_EXTRAS): $(APP_DIST_DIR)/$(OSX_APP)/Contents/MacOS/Bitcoin-Qt +$(APP_DIST_EXTRAS): $(APP_DIST_DIR)/$(OSX_APP)/Contents/MacOS/BitcoinABC-Qt $(OSX_DMG): $(APP_DIST_EXTRAS) $(GENISOIMAGE) -no-cache-inodes -D -l -probe -V "$(OSX_VOLNAME)" -no-pad -r -dir-mode 0755 -apple -o $@ dist dpi%.$(OSX_BACKGROUND_IMAGE): contrib/macdeploy/$(OSX_BACKGROUND_SVG) sed 's/PACKAGE_NAME/$(PACKAGE_NAME)/' < "$<" | $(RSVG_CONVERT) -f png -d $* -p $* | $(IMAGEMAGICK_CONVERT) - $@ OSX_BACKGROUND_IMAGE_DPIFILES := $(foreach dpi,$(OSX_BACKGROUND_IMAGE_DPIS),dpi$(dpi).$(OSX_BACKGROUND_IMAGE)) $(APP_DIST_DIR)/.background/$(OSX_BACKGROUND_IMAGE): $(OSX_BACKGROUND_IMAGE_DPIFILES) $(MKDIR_P) $(@D) $(TIFFCP) -c none $(OSX_BACKGROUND_IMAGE_DPIFILES) $@ $(APP_DIST_DIR)/.DS_Store: $(OSX_DSSTORE_GEN) $(PYTHON) $< "$@" "$(OSX_VOLNAME)" -$(APP_DIST_DIR)/$(OSX_APP)/Contents/MacOS/Bitcoin-Qt: $(OSX_APP_BUILT) $(OSX_PACKAGING) +$(APP_DIST_DIR)/$(OSX_APP)/Contents/MacOS/BitcoinABC-Qt: $(OSX_APP_BUILT) $(OSX_PACKAGING) INSTALLNAMETOOL=$(INSTALLNAMETOOL) OTOOL=$(OTOOL) STRIP=$(STRIP) $(PYTHON) $(OSX_DEPLOY_SCRIPT) $(OSX_APP) -translations-dir=$(QT_TRANSLATION_DIR) -add-qt-tr $(OSX_QT_TRANSLATIONS) -verbose 2 deploydir: $(APP_DIST_EXTRAS) endif if TARGET_DARWIN appbundle: $(OSX_APP_BUILT) deploy: $(OSX_DMG) endif if TARGET_WINDOWS deploy: $(BITCOIN_WIN_INSTALLER) endif $(BITCOIN_QT_BIN): FORCE $(MAKE) -C src qt/$(@F) $(BITCOIND_BIN): FORCE $(MAKE) -C src $(@F) $(BITCOIN_CLI_BIN): FORCE $(MAKE) -C src $(@F) if USE_LCOV baseline.info: $(LCOV) -c -i -d $(abs_builddir)/src -o $@ baseline_filtered.info: baseline.info $(LCOV) -r $< "/usr/include/*" -o $@ leveldb_baseline.info: baseline_filtered.info $(LCOV) -c -i -d $(abs_builddir)/src/leveldb -b $(abs_builddir)/src/leveldb -o $@ leveldb_baseline_filtered.info: leveldb_baseline.info $(LCOV) -r $< "/usr/include/*" -o $@ baseline_filtered_combined.info: leveldb_baseline_filtered.info baseline_filtered.info $(LCOV) -a leveldb_baseline_filtered.info -a baseline_filtered.info -o $@ test_bitcoin.info: baseline_filtered_combined.info $(MAKE) -C src/ check $(LCOV) -c -d $(abs_builddir)/src -t test_bitcoin -o $@ $(LCOV) -z -d $(abs_builddir)/src $(LCOV) -z -d $(abs_builddir)/src/leveldb test_bitcoin_filtered.info: test_bitcoin.info $(LCOV) -r $< "/usr/include/*" -o $@ rpc_test.info: test_bitcoin_filtered.info -@TIMEOUT=15 python qa/pull-tester/rpc-tests.py $(EXTENDED_RPC_TESTS) $(LCOV) -c -d $(abs_builddir)/src --t rpc-tests -o $@ $(LCOV) -z -d $(abs_builddir)/src $(LCOV) -z -d $(abs_builddir)/src/leveldb rpc_test_filtered.info: rpc_test.info $(LCOV) -r $< "/usr/include/*" -o $@ test_bitcoin_coverage.info: baseline_filtered_combined.info test_bitcoin_filtered.info $(LCOV) -a baseline_filtered.info -a leveldb_baseline_filtered.info -a test_bitcoin_filtered.info -o $@ total_coverage.info: baseline_filtered_combined.info test_bitcoin_filtered.info rpc_test_filtered.info $(LCOV) -a baseline_filtered.info -a leveldb_baseline_filtered.info -a test_bitcoin_filtered.info -a rpc_test_filtered.info -o $@ | $(GREP) "\%" | $(AWK) '{ print substr($$3,2,50) "/" $$5 }' > coverage_percent.txt test_bitcoin.coverage/.dirstamp: test_bitcoin_coverage.info $(GENHTML) -s $< -o $(@D) @touch $@ total.coverage/.dirstamp: total_coverage.info $(GENHTML) -s $< -o $(@D) @touch $@ cov: test_bitcoin.coverage/.dirstamp total.coverage/.dirstamp endif dist_noinst_SCRIPTS = autogen.sh EXTRA_DIST = $(top_srcdir)/share/genbuild.sh qa/pull-tester/rpc-tests.py qa/rpc-tests $(DIST_CONTRIB) $(DIST_DOCS) $(WINDOWS_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS) CLEANFILES = $(OSX_DMG) $(BITCOIN_WIN_INSTALLER) # This file is problematic for out-of-tree builds if it exists. DISTCLEANFILES = qa/pull-tester/tests_config.pyc .INTERMEDIATE: $(COVERAGE_INFO) DISTCHECK_CONFIGURE_FLAGS = --enable-man clean-local: rm -rf coverage_percent.txt test_bitcoin.coverage/ total.coverage/ qa/tmp/ cache/ $(OSX_APP) rm -rf qa/pull-tester/__pycache__ diff --git a/contrib/macdeploy/custom_dsstore.py b/contrib/macdeploy/custom_dsstore.py index e6ecabace1..90496e716a 100755 --- a/contrib/macdeploy/custom_dsstore.py +++ b/contrib/macdeploy/custom_dsstore.py @@ -1,60 +1,62 @@ #!/usr/bin/env python # Copyright (c) 2013-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -from __future__ import division,print_function,unicode_literals +from __future__ import division, print_function, unicode_literals import biplist from ds_store import DSStore from mac_alias import Alias import sys output_file = sys.argv[1] package_name_ns = sys.argv[2] ds = DSStore.open(output_file, 'w+') ds['.']['bwsp'] = { 'ShowStatusBar': False, 'WindowBounds': b'{{300, 280}, {500, 343}}', 'ContainerShowSidebar': False, 'SidebarWidth': 0, 'ShowTabView': False, 'PreviewPaneVisibility': False, 'ShowToolbar': False, 'ShowSidebar': False, 'ShowPathbar': True } icvp = { 'gridOffsetX': 0.0, 'textSize': 12.0, 'viewOptionsVersion': 1, 'backgroundImageAlias': b'\x00\x00\x00\x00\x02\x1e\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd1\x94\\\xb0H+\x00\x05\x00\x00\x00\x98\x0fbackground.tiff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x99\xd19\xb0\xf8\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\r\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b.background\x00\x00\x10\x00\x08\x00\x00\xd1\x94\\\xb0\x00\x00\x00\x11\x00\x08\x00\x00\xd19\xb0\xf8\x00\x00\x00\x01\x00\x04\x00\x00\x00\x98\x00\x0e\x00 \x00\x0f\x00b\x00a\x00c\x00k\x00g\x00r\x00o\x00u\x00n\x00d\x00.\x00t\x00i\x00f\x00f\x00\x0f\x00\x02\x00\x00\x00\x12\x00\x1c/.background/background.tiff\x00\x14\x01\x06\x00\x00\x00\x00\x01\x06\x00\x02\x00\x00\x0cMacintosh HD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xce\x97\xab\xc3H+\x00\x00\x01\x88[\x88\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02u\xab\x8d\xd1\x94\\\xb0devrddsk\xff\xff\xff\xff\x00\x00\t \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07bitcoin\x00\x00\x10\x00\x08\x00\x00\xce\x97\xab\xc3\x00\x00\x00\x11\x00\x08\x00\x00\xd1\x94\\\xb0\x00\x00\x00\x01\x00\x14\x01\x88[\x88\x00\x16\xa9\t\x00\x08\xfaR\x00\x08\xfaQ\x00\x02d\x8e\x00\x0e\x00\x02\x00\x00\x00\x0f\x00\x1a\x00\x0c\x00M\x00a\x00c\x00i\x00n\x00t\x00o\x00s\x00h\x00 \x00H\x00D\x00\x13\x00\x01/\x00\x00\x15\x00\x02\x00\x14\xff\xff\x00\x00\xff\xff\x00\x00', 'backgroundColorBlue': 1.0, 'iconSize': 96.0, 'backgroundColorGreen': 1.0, 'arrangeBy': 'none', 'showIconPreview': True, 'gridSpacing': 100.0, 'gridOffsetY': 0.0, 'showItemInfo': False, 'labelOnBottom': True, 'backgroundType': 2, 'backgroundColorRed': 1.0 } alias = Alias.from_bytes(icvp['backgroundImageAlias']) alias.volume.name = package_name_ns alias.volume.posix_path = '/Volumes/' + package_name_ns alias.volume.disk_image_alias.target.filename = package_name_ns + '.temp.dmg' -alias.volume.disk_image_alias.target.carbon_path = 'Macintosh HD:Users:\x00bitcoinuser:\x00Documents:\x00bitcoin:\x00bitcoin:\x00' + package_name_ns + '.temp.dmg' -alias.volume.disk_image_alias.target.posix_path = 'Users/bitcoinuser/Documents/bitcoin/bitcoin/' + package_name_ns + '.temp.dmg' +alias.volume.disk_image_alias.target.carbon_path = 'Macintosh HD:Users:\x00bitcoinuser:\x00Documents:\x00bitcoin:\x00bitcoin:\x00' + \ + package_name_ns + '.temp.dmg' +alias.volume.disk_image_alias.target.posix_path = 'Users/bitcoinuser/Documents/bitcoin/bitcoin/' + \ + package_name_ns + '.temp.dmg' alias.target.carbon_path = package_name_ns + ':.background:\x00background.tiff' icvp['backgroundImageAlias'] = biplist.Data(alias.to_bytes()) ds['.']['icvp'] = icvp ds['.']['vSrn'] = ('long', 1) ds['Applications']['Iloc'] = (370, 156) -ds['Bitcoin-Qt.app']['Iloc'] = (128, 156) +ds['BitcoinABC-Qt.app']['Iloc'] = (128, 156) ds.flush() ds.close() diff --git a/contrib/macdeploy/detached-sig-create.sh b/contrib/macdeploy/detached-sig-create.sh index 5022ea88bc..5d4f96e86c 100755 --- a/contrib/macdeploy/detached-sig-create.sh +++ b/contrib/macdeploy/detached-sig-create.sh @@ -1,51 +1,51 @@ #!/bin/sh # Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. set -e ROOTDIR=dist -BUNDLE="${ROOTDIR}/Bitcoin-Qt.app" +BUNDLE="${ROOTDIR}/BitcoinABC-Qt.app" CODESIGN=codesign TEMPDIR=sign.temp TEMPLIST=${TEMPDIR}/signatures.txt OUT=signature.tar.gz OUTROOT=osx if [ ! -n "$1" ]; then echo "usage: $0 " echo "example: $0 -s MyIdentity" exit 1 fi rm -rf ${TEMPDIR} ${TEMPLIST} mkdir -p ${TEMPDIR} ${CODESIGN} -f --file-list ${TEMPLIST} "$@" "${BUNDLE}" grep -v CodeResources < "${TEMPLIST}" | while read i; do TARGETFILE="${BUNDLE}/`echo "${i}" | sed "s|.*${BUNDLE}/||"`" SIZE=`pagestuff "$i" -p | tail -2 | grep size | sed 's/[^0-9]*//g'` OFFSET=`pagestuff "$i" -p | tail -2 | grep offset | sed 's/[^0-9]*//g'` SIGNFILE="${TEMPDIR}/${OUTROOT}/${TARGETFILE}.sign" DIRNAME="`dirname "${SIGNFILE}"`" mkdir -p "${DIRNAME}" echo "Adding detached signature for: ${TARGETFILE}. Size: ${SIZE}. Offset: ${OFFSET}" dd if="$i" of="${SIGNFILE}" bs=1 skip=${OFFSET} count=${SIZE} 2>/dev/null done grep CodeResources < "${TEMPLIST}" | while read i; do TARGETFILE="${BUNDLE}/`echo "${i}" | sed "s|.*${BUNDLE}/||"`" RESOURCE="${TEMPDIR}/${OUTROOT}/${TARGETFILE}" DIRNAME="`dirname "${RESOURCE}"`" mkdir -p "${DIRNAME}" echo "Adding resource for: "${TARGETFILE}"" cp "${i}" "${RESOURCE}" done rm ${TEMPLIST} tar -C "${TEMPDIR}" -czf "${OUT}" . rm -rf "${TEMPDIR}" echo "Created ${OUT}" diff --git a/contrib/macdeploy/fancy.plist b/contrib/macdeploy/fancy.plist index ef277a7f14..e6ffa2e034 100644 --- a/contrib/macdeploy/fancy.plist +++ b/contrib/macdeploy/fancy.plist @@ -1,32 +1,32 @@ window_bounds 300 300 800 620 background_picture background.tiff icon_size 96 applications_symlink items_position Applications 370 156 - Bitcoin-Qt.app + BitcoinABC-Qt.app 128 156 diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus index 5995f9f438..62ca70983f 100755 --- a/contrib/macdeploy/macdeployqtplus +++ b/contrib/macdeploy/macdeployqtplus @@ -1,891 +1,891 @@ #!/usr/bin/env python from __future__ import division, print_function, unicode_literals # # Copyright (C) 2011 Patrick "p2k" Schneider # # 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 . # import subprocess, sys, re, os, shutil, stat, os.path, time from string import Template from argparse import ArgumentParser # This is ported from the original macdeployqt with modifications class FrameworkInfo(object): def __init__(self): self.frameworkDirectory = "" self.frameworkName = "" self.frameworkPath = "" self.binaryDirectory = "" self.binaryName = "" self.binaryPath = "" self.version = "" self.installName = "" self.deployedInstallName = "" self.sourceFilePath = "" self.destinationDirectory = "" self.sourceResourcesDirectory = "" self.sourceVersionContentsDirectory = "" self.sourceContentsDirectory = "" self.destinationResourcesDirectory = "" self.destinationVersionContentsDirectory = "" def __eq__(self, other): if self.__class__ == other.__class__: return self.__dict__ == other.__dict__ else: return False def __str__(self): return """ Framework name: %s Framework directory: %s Framework path: %s Binary name: %s Binary directory: %s Binary path: %s Version: %s Install name: %s Deployed install name: %s Source file Path: %s Deployed Directory (relative to bundle): %s """ % (self.frameworkName, self.frameworkDirectory, self.frameworkPath, self.binaryName, self.binaryDirectory, self.binaryPath, self.version, self.installName, self.deployedInstallName, self.sourceFilePath, self.destinationDirectory) def isDylib(self): return self.frameworkName.endswith(".dylib") def isQtFramework(self): if self.isDylib(): return self.frameworkName.startswith("libQt") else: return self.frameworkName.startswith("Qt") reOLine = re.compile(r'^(.+) \(compatibility version [0-9.]+, current version [0-9.]+\)$') bundleFrameworkDirectory = "Contents/Frameworks" bundleBinaryDirectory = "Contents/MacOS" @classmethod def fromOtoolLibraryLine(cls, line): # Note: line must be trimmed if line == "": return None # 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): return None m = cls.reOLine.match(line) if m is None: raise RuntimeError("otool line could not be parsed: " + line) path = m.group(1) info = cls() info.sourceFilePath = path info.installName = path if path.endswith(".dylib"): dirname, filename = os.path.split(path) info.frameworkName = filename info.frameworkDirectory = dirname info.frameworkPath = path info.binaryDirectory = dirname info.binaryName = filename info.binaryPath = path info.version = "-" info.installName = path info.deployedInstallName = "@executable_path/../Frameworks/" + info.binaryName info.sourceFilePath = path info.destinationDirectory = cls.bundleFrameworkDirectory else: parts = path.split("/") i = 0 # Search for the .framework directory for part in parts: if part.endswith(".framework"): break i += 1 if i == len(parts): raise RuntimeError("Could not find .framework or .dylib in otool line: " + line) info.frameworkName = parts[i] info.frameworkDirectory = "/".join(parts[:i]) info.frameworkPath = os.path.join(info.frameworkDirectory, info.frameworkName) info.binaryName = parts[i+3] info.binaryDirectory = "/".join(parts[i+1:i+3]) info.binaryPath = os.path.join(info.binaryDirectory, info.binaryName) info.version = parts[i+2] info.deployedInstallName = "@executable_path/../Frameworks/" + os.path.join(info.frameworkName, info.binaryPath) info.destinationDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, info.binaryDirectory) info.sourceResourcesDirectory = os.path.join(info.frameworkPath, "Resources") info.sourceContentsDirectory = os.path.join(info.frameworkPath, "Contents") info.sourceVersionContentsDirectory = os.path.join(info.frameworkPath, "Versions", info.version, "Contents") info.destinationResourcesDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, "Resources") info.destinationContentsDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, "Contents") info.destinationVersionContentsDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, "Versions", info.version, "Contents") return info class ApplicationBundleInfo(object): def __init__(self, path): self.path = path - appName = "Bitcoin-Qt" + appName = "BitcoinABC-Qt" self.binaryPath = os.path.join(path, "Contents", "MacOS", appName) if not os.path.exists(self.binaryPath): raise RuntimeError("Could not find bundle binary for " + path) self.resourcesPath = os.path.join(path, "Contents", "Resources") self.pluginPath = os.path.join(path, "Contents", "PlugIns") class DeploymentInfo(object): def __init__(self): self.qtPath = None self.pluginPath = None self.deployedFrameworks = [] def detectQtPath(self, frameworkDirectory): parentDir = os.path.dirname(frameworkDirectory) if os.path.exists(os.path.join(parentDir, "translations")): # Classic layout, e.g. "/usr/local/Trolltech/Qt-4.x.x" self.qtPath = parentDir elif os.path.exists(os.path.join(parentDir, "share", "qt4", "translations")): # MacPorts layout, e.g. "/opt/local/share/qt4" self.qtPath = os.path.join(parentDir, "share", "qt4") elif os.path.exists(os.path.join(os.path.dirname(parentDir), "share", "qt4", "translations")): # Newer Macports layout self.qtPath = os.path.join(os.path.dirname(parentDir), "share", "qt4") else: self.qtPath = os.getenv("QTDIR", None) if self.qtPath is not None: pluginPath = os.path.join(self.qtPath, "plugins") if os.path.exists(pluginPath): self.pluginPath = pluginPath def usesFramework(self, name): nameDot = "%s." % name libNameDot = "lib%s." % name for framework in self.deployedFrameworks: if framework.endswith(".framework"): if framework.startswith(nameDot): return True elif framework.endswith(".dylib"): if framework.startswith(libNameDot): return True return False def getFrameworks(binaryPath, verbose): if verbose >= 3: print("Inspecting with otool: " + binaryPath) otoolbin=os.getenv("OTOOL", "otool") otool = subprocess.Popen([otoolbin, "-L", binaryPath], stdout=subprocess.PIPE, stderr=subprocess.PIPE) o_stdout, o_stderr = otool.communicate() if otool.returncode != 0: if verbose >= 1: sys.stderr.write(o_stderr) sys.stderr.flush() raise RuntimeError("otool failed with return code %d" % otool.returncode) otoolLines = o_stdout.decode().split("\n") otoolLines.pop(0) # First line is the inspected binary if ".framework" in binaryPath or binaryPath.endswith(".dylib"): otoolLines.pop(0) # Frameworks and dylibs list themselves as a dependency. libraries = [] for line in otoolLines: line = line.replace("@loader_path", os.path.dirname(binaryPath)) info = FrameworkInfo.fromOtoolLibraryLine(line.strip()) if info is not None: if verbose >= 3: print("Found framework:") print(info) libraries.append(info) return libraries def runInstallNameTool(action, *args): installnametoolbin=os.getenv("INSTALLNAMETOOL", "install_name_tool") subprocess.check_call([installnametoolbin, "-"+action] + list(args)) def changeInstallName(oldName, newName, binaryPath, verbose): if verbose >= 3: print("Using install_name_tool:") print(" in", binaryPath) print(" change reference", oldName) print(" to", newName) runInstallNameTool("change", oldName, newName, binaryPath) def changeIdentification(id, binaryPath, verbose): if verbose >= 3: print("Using install_name_tool:") print(" change identification in", binaryPath) print(" to", id) runInstallNameTool("id", id, binaryPath) def runStrip(binaryPath, verbose): stripbin=os.getenv("STRIP", "strip") if verbose >= 3: print("Using strip:") print(" stripped", binaryPath) subprocess.check_call([stripbin, "-x", binaryPath]) def copyFramework(framework, path, verbose): if framework.sourceFilePath.startswith("Qt"): #standard place for Nokia Qt installer's frameworks fromPath = "/Library/Frameworks/" + framework.sourceFilePath else: fromPath = framework.sourceFilePath toDir = os.path.join(path, framework.destinationDirectory) toPath = os.path.join(toDir, framework.binaryName) if not os.path.exists(fromPath): raise RuntimeError("No file at " + fromPath) if os.path.exists(toPath): return None # Already there if not os.path.exists(toDir): os.makedirs(toDir) shutil.copy2(fromPath, toPath) if verbose >= 3: print("Copied:", fromPath) print(" to:", toPath) permissions = os.stat(toPath) if not permissions.st_mode & stat.S_IWRITE: os.chmod(toPath, permissions.st_mode | stat.S_IWRITE) if not framework.isDylib(): # Copy resources for real frameworks linkfrom = os.path.join(path, "Contents","Frameworks", framework.frameworkName, "Versions", "Current") linkto = framework.version if not os.path.exists(linkfrom): os.symlink(linkto, linkfrom) if verbose >= 2: print("Linked:", linkfrom, "->", linkto) fromResourcesDir = framework.sourceResourcesDirectory if os.path.exists(fromResourcesDir): toResourcesDir = os.path.join(path, framework.destinationResourcesDirectory) shutil.copytree(fromResourcesDir, toResourcesDir, symlinks=True) if verbose >= 3: print("Copied resources:", fromResourcesDir) print(" to:", toResourcesDir) fromContentsDir = framework.sourceVersionContentsDirectory if not os.path.exists(fromContentsDir): fromContentsDir = framework.sourceContentsDirectory if os.path.exists(fromContentsDir): toContentsDir = os.path.join(path, framework.destinationVersionContentsDirectory) shutil.copytree(fromContentsDir, toContentsDir, symlinks=True) contentslinkfrom = os.path.join(path, framework.destinationContentsDirectory) if verbose >= 3: print("Copied Contents:", fromContentsDir) print(" to:", toContentsDir) elif framework.frameworkName.startswith("libQtGui"): # Copy qt_menu.nib (applies to non-framework layout) qtMenuNibSourcePath = os.path.join(framework.frameworkDirectory, "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): shutil.copytree(qtMenuNibSourcePath, qtMenuNibDestinationPath, symlinks=True) if verbose >= 3: print("Copied for libQtGui:", qtMenuNibSourcePath) print(" to:", qtMenuNibDestinationPath) return toPath def deployFrameworks(frameworks, bundlePath, binaryPath, strip, verbose, deploymentInfo=None): if deploymentInfo is None: deploymentInfo = DeploymentInfo() while len(frameworks) > 0: framework = frameworks.pop(0) deploymentInfo.deployedFrameworks.append(framework.frameworkName) if verbose >= 2: print("Processing", framework.frameworkName, "...") # Get the Qt path from one of the Qt frameworks if deploymentInfo.qtPath is None and framework.isQtFramework(): deploymentInfo.detectQtPath(framework.frameworkDirectory) if framework.installName.startswith("@executable_path") or framework.installName.startswith(bundlePath): if verbose >= 2: print(framework.frameworkName, "already deployed, skipping.") continue # install_name_tool the new id into the binary changeInstallName(framework.installName, framework.deployedInstallName, binaryPath, verbose) # Copy framework to app bundle. deployedBinaryPath = copyFramework(framework, bundlePath, verbose) # Skip the rest if already was deployed. if deployedBinaryPath is None: continue if strip: runStrip(deployedBinaryPath, verbose) # install_name_tool it a new id. changeIdentification(framework.deployedInstallName, deployedBinaryPath, verbose) # Check for framework dependencies dependencies = getFrameworks(deployedBinaryPath, verbose) for dependency in dependencies: changeInstallName(dependency.installName, dependency.deployedInstallName, deployedBinaryPath, verbose) # Deploy framework if necessary. if dependency.frameworkName not in deploymentInfo.deployedFrameworks and dependency not in frameworks: frameworks.append(dependency) return deploymentInfo def deployFrameworksForAppBundle(applicationBundle, strip, verbose): frameworks = getFrameworks(applicationBundle.binaryPath, verbose) if len(frameworks) == 0 and verbose >= 1: print("Warning: Could not find any external frameworks to deploy in %s." % (applicationBundle.path)) return DeploymentInfo() else: return deployFrameworks(frameworks, applicationBundle.path, applicationBundle.binaryPath, strip, verbose) def deployPlugins(appBundleInfo, deploymentInfo, strip, verbose): # Lookup available plugins, exclude unneeded plugins = [] if deploymentInfo.pluginPath is None: return for dirpath, dirnames, filenames in os.walk(deploymentInfo.pluginPath): pluginDirectory = os.path.relpath(dirpath, deploymentInfo.pluginPath) if pluginDirectory == "designer": # Skip designer plugins continue elif pluginDirectory == "phonon" or pluginDirectory == "phonon_backend": # Deploy the phonon plugins only if phonon is in use if not deploymentInfo.usesFramework("phonon"): continue elif pluginDirectory == "sqldrivers": # Deploy the sql plugins only if QtSql is in use if not deploymentInfo.usesFramework("QtSql"): continue elif pluginDirectory == "script": # Deploy the script plugins only if QtScript is in use if not deploymentInfo.usesFramework("QtScript"): continue elif pluginDirectory == "qmltooling" or pluginDirectory == "qml1tooling": # Deploy the qml plugins only if QtDeclarative is in use if not deploymentInfo.usesFramework("QtDeclarative"): continue elif pluginDirectory == "bearer": # Deploy the bearer plugins only if QtNetwork is in use if not deploymentInfo.usesFramework("QtNetwork"): continue elif pluginDirectory == "position": # Deploy the position plugins only if QtPositioning is in use if not deploymentInfo.usesFramework("QtPositioning"): continue elif pluginDirectory == "sensors" or pluginDirectory == "sensorgestures": # Deploy the sensor plugins only if QtSensors is in use if not deploymentInfo.usesFramework("QtSensors"): continue elif pluginDirectory == "audio" or pluginDirectory == "playlistformats": # Deploy the audio plugins only if QtMultimedia is in use if not deploymentInfo.usesFramework("QtMultimedia"): continue elif pluginDirectory == "mediaservice": # Deploy the mediaservice plugins only if QtMultimediaWidgets is in use if not deploymentInfo.usesFramework("QtMultimediaWidgets"): continue for pluginName in filenames: pluginPath = os.path.join(pluginDirectory, pluginName) if pluginName.endswith("_debug.dylib"): # Skip debug plugins continue elif pluginPath == "imageformats/libqsvg.dylib" or pluginPath == "iconengines/libqsvgicon.dylib": # Deploy the svg plugins only if QtSvg is in use if not deploymentInfo.usesFramework("QtSvg"): continue elif pluginPath == "accessible/libqtaccessiblecompatwidgets.dylib": # Deploy accessibility for Qt3Support only if the Qt3Support is in use if not deploymentInfo.usesFramework("Qt3Support"): continue elif pluginPath == "graphicssystems/libqglgraphicssystem.dylib": # Deploy the opengl graphicssystem plugin only if QtOpenGL is in use if not deploymentInfo.usesFramework("QtOpenGL"): continue elif pluginPath == "accessible/libqtaccessiblequick.dylib": # Deploy the accessible qtquick plugin only if QtQuick is in use if not deploymentInfo.usesFramework("QtQuick"): continue plugins.append((pluginDirectory, pluginName)) for pluginDirectory, pluginName in plugins: if verbose >= 2: print("Processing plugin", os.path.join(pluginDirectory, pluginName), "...") sourcePath = os.path.join(deploymentInfo.pluginPath, pluginDirectory, pluginName) destinationDirectory = os.path.join(appBundleInfo.pluginPath, pluginDirectory) if not os.path.exists(destinationDirectory): os.makedirs(destinationDirectory) destinationPath = os.path.join(destinationDirectory, pluginName) shutil.copy2(sourcePath, destinationPath) if verbose >= 3: print("Copied:", sourcePath) print(" to:", destinationPath) if strip: runStrip(destinationPath, verbose) dependencies = getFrameworks(destinationPath, verbose) for dependency in dependencies: changeInstallName(dependency.installName, dependency.deployedInstallName, destinationPath, verbose) # Deploy framework if necessary. if dependency.frameworkName not in deploymentInfo.deployedFrameworks: deployFrameworks([dependency], appBundleInfo.path, destinationPath, strip, verbose, deploymentInfo) qt_conf="""[Paths] Translations=Resources Plugins=PlugIns """ ap = ArgumentParser(description="""Improved version of macdeployqt. 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. Also optionally signs the .app bundle; set the CODESIGNARGS environment variable to pass arguments to the codesign tool. E.g. CODESIGNARGS='--sign "Developer ID Application: ..." --keychain /encrypted/foo.keychain'""") 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("-sign", dest="sign", action="store_true", default=False, help="sign .app bundle with codesign tool") 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 resources; the language list must be separated with commas, not with whitespace") ap.add_argument("-translations-dir", nargs=1, metavar="path", default=None, help="Path to Qt's translation files") 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") ap.add_argument("-volname", nargs=1, metavar="volname", default=[], help="custom volume name for dmg") 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] # ------------------------------------------------ translations_dir = None if config.translations_dir and config.translations_dir[0]: if os.path.exists(config.translations_dir[0]): translations_dir = config.translations_dir[0] else: if verbose >= 1: sys.stderr.write("Error: Could not find translation dir \"%s\"\n" % (translations_dir)) sys.exit(1) # ------------------------------------------------ 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.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) 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 "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 "icon_size" not in fancy or isinstance(fancy["icon_size"], int) assert "applications_symlink" not in fancy or isinstance(fancy["applications_symlink"], bool) if "items_position" in fancy: assert isinstance(fancy["items_position"], dict) 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) except: if verbose >= 1: sys.stderr.write("Error: Bad format of fancy disk image plist at \"%s\"\n" % (p)) sys.exit(1) if "background_picture" in fancy: 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") # ------------------------------------------------ if len(config.volname) == 1: volname = config.volname[0] else: volname = app_bundle_name # ------------------------------------------------ -target = os.path.join("dist", "Bitcoin-Qt.app") +target = os.path.join("dist", "BitcoinABC-Qt.app") if verbose >= 2: print("+ Copying source bundle +") if verbose >= 3: print(app_bundle, "->", target) os.mkdir("dist") shutil.copytree(app_bundle, target, symlinks=True) applicationBundle = ApplicationBundleInfo(target) # ------------------------------------------------ if verbose >= 2: print("+ Deploying frameworks +") try: deploymentInfo = deployFrameworksForAppBundle(applicationBundle, config.strip, verbose) if deploymentInfo.qtPath is None: deploymentInfo.qtPath = os.getenv("QTDIR", None) if deploymentInfo.qtPath is None: if verbose >= 1: sys.stderr.write("Warning: Could not detect Qt's path, skipping plugin deployment!\n") config.plugins = False except RuntimeError as e: if verbose >= 1: sys.stderr.write("Error: %s\n" % str(e)) sys.exit(1) # ------------------------------------------------ if config.plugins: if verbose >= 2: print("+ Deploying plugins +") try: deployPlugins(applicationBundle, deploymentInfo, config.strip, verbose) except RuntimeError as e: if verbose >= 1: sys.stderr.write("Error: %s\n" % str(e)) sys.exit(1) # ------------------------------------------------ if len(config.add_qt_tr) == 0: add_qt_tr = [] else: if translations_dir is not None: qt_tr_dir = translations_dir else: if deploymentInfo.qtPath is not None: qt_tr_dir = os.path.join(deploymentInfo.qtPath, "translations") else: sys.stderr.write("Error: Could not find Qt translation path\n") sys.exit(1) 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 verbose >= 2: print("+ Installing qt.conf +") f = open(os.path.join(applicationBundle.resourcesPath, "qt.conf"), "wb") f.write(qt_conf.encode()) 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(applicationBundle.resourcesPath, lng_file)) shutil.copy2(os.path.join(qt_tr_dir, lng_file), os.path.join(applicationBundle.resourcesPath, 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(applicationBundle.resourcesPath, os.path.basename(p)) if verbose >= 3: print(p, "->", t) if os.path.isdir(p): shutil.copytree(p, t, symlinks=True) else: shutil.copy2(p, t) # ------------------------------------------------ if config.sign and 'CODESIGNARGS' not in os.environ: print("You must set the CODESIGNARGS environment variable. Skipping signing.") elif config.sign: if verbose >= 1: print("Code-signing app bundle %s"%(target,)) subprocess.check_call("codesign --force %s %s"%(os.environ['CODESIGNARGS'], target), shell=True) # ------------------------------------------------ if config.dmg is not None: #Patch in check_output for Python 2.6 if "check_output" not in dir( subprocess ): def f(*popenargs, **kwargs): if 'stdout' in kwargs: raise ValueError('stdout argument not allowed, it will be overridden.') process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs) output, unused_err = process.communicate() retcode = process.poll() if retcode: cmd = kwargs.get("args") if cmd is None: cmd = popenargs[0] raise CalledProcessError(retcode, cmd) return output subprocess.check_output = f def runHDIUtil(verb, image_basename, **kwargs): hdiutil_args = ["hdiutil", verb, image_basename + ".dmg"] if "capture_stdout" in kwargs: 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.items(): 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=volname, 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.15) if verbose >= 3: print("Creating temp image for modification...") try: runHDIUtil("create", dmg_name + ".temp", srcfolder="dist", format="UDRW", size=size, volname=volname, 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.decode()) disk_root = m.group(0) disk_name = m.group(1) if verbose >= 2: print("+ Applying fancy settings +") if "background_picture" in fancy: bg_path = os.path.join(disk_root, ".background", os.path.basename(fancy["background_picture"])) os.mkdir(os.path.dirname(bg_path)) 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")) # 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 # to make the .dmg file pretty: appscript = Template( """ on run argv tell application "Finder" tell disk "$disk" open set current view of container window to icon view set toolbar visible of container window to false set statusbar visible of container window to false set the bounds of container window to {$window_bounds} set theViewOptions to the icon view options of container window set arrangement of theViewOptions to not arranged set icon size of theViewOptions to $icon_size $background_commands $items_positions close -- close/reopen works around a bug... open update without registering applications delay 5 eject end tell end tell end run """) itemscript = Template('set position of item "${item}" of container window to {${position}}') items_positions = [] if "items_position" in fancy: for name, position in fancy["items_position"].items(): params = { "item" : name, "position" : ",".join([str(p) for p in position]) } items_positions.append(itemscript.substitute(params)) params = { "disk" : volname, "window_bounds" : "300,300,800,620", "icon_size" : "96", "background_commands" : "", "items_positions" : "\n ".join(items_positions) } if "window_bounds" in fancy: params["window_bounds"] = ",".join([str(p) for p in fancy["window_bounds"]]) if "icon_size" in fancy: params["icon_size"] = str(fancy["icon_size"]) if bg_path is not None: # Set background file, then call SetFile to make it invisible. # (note: making it invisible first makes set background picture fail) bgscript = Template("""set background picture of theViewOptions to file ".background:$bgpic" do shell script "SetFile -a V /Volumes/$disk/.background/$bgpic" """) params["background_commands"] = bgscript.substitute({"bgpic" : os.path.basename(bg_path), "disk" : params["disk"]}) s = appscript.substitute(params) if verbose >= 2: print("Running AppleScript:") print(s) p = subprocess.Popen(['osascript', '-'], stdin=subprocess.PIPE) p.communicate(input=s.encode('utf-8')) if p.returncode: print("Error running osascript.") if verbose >= 2: print("+ Finalizing .dmg disk image +") time.sleep(5) 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) diff --git a/share/qt/Info.plist.in b/share/qt/Info.plist.in index e48a82b8d2..14c7b0a066 100644 --- a/share/qt/Info.plist.in +++ b/share/qt/Info.plist.in @@ -1,106 +1,106 @@ LSMinimumSystemVersion 10.8.0 LSArchitecturePriority x86_64 CFBundleIconFile bitcoin.icns CFBundlePackageType APPL CFBundleGetInfoString @CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@.@CLIENT_VERSION_REVISION@, Copyright © 2009-@COPYRIGHT_YEAR@ @COPYRIGHT_HOLDERS_FINAL@ CFBundleShortVersionString @CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@.@CLIENT_VERSION_REVISION@ CFBundleVersion @CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@.@CLIENT_VERSION_REVISION@ CFBundleSignature ???? CFBundleExecutable - Bitcoin-Qt + BitcoinABC-Qt CFBundleName - Bitcoin-Qt + BitcoinABC-Qt LSHasLocalizedDisplayName CFBundleIdentifier - org.bitcoincash.Bitcoin-Qt + org.bitcoincash.BitcoinABC-Qt CFBundleURLTypes CFBundleTypeRole Editor CFBundleURLName org.bitcoincash.BitcoinPayment CFBundleURLSchemes bitcoincash UTExportedTypeDeclarations UTTypeIdentifier org.bitcoincash.paymentrequest UTTypeDescription Bitcoin Cash payment request UTTypeConformsTo public.data UTTypeTagSpecification public.mime-type application/bitcoincash-paymentrequest public.filename-extension bitcoincashpaymentrequest CFBundleDocumentTypes CFBundleTypeRole Editor LSItemContentTypes org.bitcoincash.paymentrequest LSHandlerRank Owner NSPrincipalClass NSApplication NSHighResolutionCapable True LSAppNapIsDisabled True LSApplicationCategoryType public.app-category.finance diff --git a/src/qt/macnotificationhandler.mm b/src/qt/macnotificationhandler.mm index 4c96d08c8a..7dd80ca929 100644 --- a/src/qt/macnotificationhandler.mm +++ b/src/qt/macnotificationhandler.mm @@ -1,91 +1,91 @@ // Copyright (c) 2011-2013 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "macnotificationhandler.h" #undef slots #import #include // Add an obj-c category (extension) to return the expected bundle identifier @implementation NSBundle(returnCorrectIdentifier) - (NSString *)__bundleIdentifier { if (self == [NSBundle mainBundle]) { - return @"org.bitcoinfoundation.Bitcoin-Qt"; + return @"org.bitcoincash.BitcoinABC-Qt"; } else { return [self __bundleIdentifier]; } } @end void MacNotificationHandler::showNotification(const QString &title, const QString &text) { // check if users OS has support for NSUserNotification if(this->hasUserNotificationCenterSupport()) { // okay, seems like 10.8+ QByteArray utf8 = title.toUtf8(); char* cString = (char *)utf8.constData(); NSString *titleMac = [[NSString alloc] initWithUTF8String:cString]; utf8 = text.toUtf8(); cString = (char *)utf8.constData(); NSString *textMac = [[NSString alloc] initWithUTF8String:cString]; // do everything weak linked (because we will keep <10.8 compatibility) id userNotification = [[NSClassFromString(@"NSUserNotification") alloc] init]; [userNotification performSelector:@selector(setTitle:) withObject:titleMac]; [userNotification performSelector:@selector(setInformativeText:) withObject:textMac]; id notificationCenterInstance = [NSClassFromString(@"NSUserNotificationCenter") performSelector:@selector(defaultUserNotificationCenter)]; [notificationCenterInstance performSelector:@selector(deliverNotification:) withObject:userNotification]; [titleMac release]; [textMac release]; [userNotification release]; } } // sendAppleScript just take a QString and executes it as apple script void MacNotificationHandler::sendAppleScript(const QString &script) { QByteArray utf8 = script.toUtf8(); char* cString = (char *)utf8.constData(); NSString *scriptApple = [[NSString alloc] initWithUTF8String:cString]; NSAppleScript *as = [[NSAppleScript alloc] initWithSource:scriptApple]; NSDictionary *err = nil; [as executeAndReturnError:&err]; [as release]; [scriptApple release]; } bool MacNotificationHandler::hasUserNotificationCenterSupport(void) { Class possibleClass = NSClassFromString(@"NSUserNotificationCenter"); // check if users OS has support for NSUserNotification if(possibleClass!=nil) { return true; } return false; } MacNotificationHandler *MacNotificationHandler::instance() { static MacNotificationHandler *s_instance = nullptr; if (!s_instance) { s_instance = new MacNotificationHandler(); Class aPossibleClass = objc_getClass("NSBundle"); if (aPossibleClass) { // change NSBundle -bundleIdentifier method to return a correct bundle identifier // a bundle identifier is required to use OSXs User Notification Center method_exchangeImplementations(class_getInstanceMethod(aPossibleClass, @selector(bundleIdentifier)), class_getInstanceMethod(aPossibleClass, @selector(__bundleIdentifier))); } } return s_instance; }