From c9ab41598bc13be188d26c73f95368957be2451d Mon Sep 17 00:00:00 2001 From: Fuzzbawls Date: Sat, 11 May 2024 23:20:18 -0700 Subject: [PATCH] [Build] Add distribution packaging functionality Adds code to the `.spec` file used to create the distribution binaries. Requires `pyinstaller`. The macOS binaries further require the node.js `appdmg` package for `.dmg` creation, and a valid Apple issued developer ID certificate for code signing. --- SecurePivxMasternodeTool.spec | 93 +++++++++++++++++++++------- contrib/macdeploy/appdmg.json.in | 13 ++++ contrib/macdeploy/background.svg | 34 ++++++++++ contrib/macdeploy/entitlements.plist | 18 ++++++ 4 files changed, 137 insertions(+), 21 deletions(-) create mode 100644 contrib/macdeploy/appdmg.json.in create mode 100644 contrib/macdeploy/background.svg create mode 100644 contrib/macdeploy/entitlements.plist diff --git a/SecurePivxMasternodeTool.spec b/SecurePivxMasternodeTool.spec index 0ce7d91..d73421c 100644 --- a/SecurePivxMasternodeTool.spec +++ b/SecurePivxMasternodeTool.spec @@ -1,7 +1,9 @@ # -*- mode: python -*- import sys +import platform import os.path as os_path import simplejson as json +import subprocess os_type = sys.platform block_cipher = None @@ -11,9 +13,30 @@ def libModule(module, source, dest): m = __import__(module) module_path = os_path.dirname(m.__file__) del m - print(f"libModule {(os.path.join(module_path, source), dest)}") + print(f"libModule {(os_path.join(module_path, source), dest)}") return ( os_path.join(module_path, source), dest ) +def is_tool(prog): + for dir in os.environ['PATH'].split(os.pathsep): + if os.path.exists(os.path.join(dir, prog)): + try: + subprocess.call([os.path.join(dir, prog)], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + except(OSError, e): + return False + return True + return False + +# Set this to True if codesigning macOS bundles. Requires an Apple issued developer ID certificate. +code_sign = False +codesigner = 'fuzzbawls@pivx.org' + +# detect CPU architecture +cpu_arch = platform.processor() +if os_type == 'darwin': + if cpu_arch == 'arm': cpu_arch = 'arm64' + if cpu_arch == 'i386': cpu_arch = 'x86_64' # look for version string version_str = '' @@ -89,22 +112,23 @@ exe = EXE(pyz, strip=False, upx=False, console=False, - icon = os.path.join(base_dir, 'img', f'spmt.{"icns" if os_type == "darwin" else "ico"}')) - -#coll = COLLECT(exe, -# a.binaries, -# a.zipfiles, -# a.datas, -# strip=False, -# upx=True, -# name='app') + target_arch=f'{cpu_arch}', + entitlements_file='contrib/macdeploy/entitlements.plist', + codesign_identity=f'{codesigner if code_sign == True else ""}', + icon = os_path.join(base_dir, 'img', f'spmt.{"icns" if os_type == "darwin" else "ico"}')) if os_type == 'darwin': app = BUNDLE(exe, name='SecurePivxMasternodeTool.app', icon=os_path.join(base_dir, 'img', 'spmt.icns'), - bundle_identifier=None, - info_plist={'NSHighResolutionCapable': 'True'}) + bundle_identifier='io.pivx.spmt', + info_plist={ + 'NSHighResolutionCapable': 'True', + 'CFBundleVersion': version_str, + 'CFBundleShortVersionString': version_str, + 'NSPrincipalClass': 'NSApplication', + 'LSApplicationCategoryType': 'public.app-category.finance' + }) # Prepare bundles @@ -121,33 +145,60 @@ os.chdir(dist_path) if os_type == 'win32': os.chdir(base_dir) # Rename dist Dir - dist_path_win = os_path.join(base_dir, 'SPMT-v' + version_str + '-Win64') + dist_path_win = os_path.join(base_dir, f'SPMT-v{version_str}-Win64') os.rename(dist_path, dist_path_win) - # Create NSIS compressed installer - print('Creating Windows installer (requires NSIS)') - os.system(f'"{os.path.join("c:", "program files (x86)", "NSIS", "makensis.exe")}" {os.path.join(base_dir, "setup.nsi")}') + # Check for NSIS + prog_path = os.environ["ProgramFiles(x86)"] + nsis_bin = os_path.join(prog_path, "NSIS", "makensis.exe") + if os_path.exists(nsis_bin): + # Create NSIS compressed installer + print('Creating Windows installer') + os.system(f'"{nsis_bin}" {os_path.join(base_dir, "setup.nsi")}') + else: + print('NSIS not found, cannot build windows installer.') if os_type == 'linux': os.chdir(base_dir) # Rename dist Dir - dist_path_linux = os_path.join(base_dir, 'SPMT-v' + version_str + '-gnu_linux') + dist_path_linux = os_path.join(base_dir, f'SPMT-v{version_str}-{cpu_arch}-gnu_linux') os.rename(dist_path, dist_path_linux) # Compress dist Dir print('Compressing Linux App Folder') - os.system(f'tar -zcvf SPMT-v{version_str}-x86_64-gnu_linux.tar.gz -C {base_dir} SPMT-v{version_str}-gnu_linux') + os.system(f'tar -zcvf SPMT-v{version_str}-{cpu_arch}-gnu_linux.tar.gz -C {base_dir} SPMT-v{version_str}-{cpu_arch}-gnu_linux') if os_type == 'darwin': os.chdir(base_dir) # Rename dist Dir - dist_path_mac = os_path.join(base_dir, 'SPMT-v' + version_str + '-MacOSX') + dist_path_mac = os_path.join(base_dir, f'SPMT-v{version_str}-{cpu_arch}-MacOS') os.rename(dist_path, dist_path_mac) # Remove 'app' folder - print("Removin 'app' folder") + print("Removing 'app' folder") os.chdir(dist_path_mac) os.system('rm -rf app') os.chdir(base_dir) # Compress dist Dir print('Compressing Mac App Folder') - os.system(f'tar -zcvf SPMT-v{version_str}-MacOSX.tar.gz -C {base_dir} SPMT-v{version_str}-MacOSX') + os.system(f'tar -zcvf SPMT-v{version_str}-{cpu_arch}-MacOS.tar.gz -C {base_dir} SPMT-v{version_str}-{cpu_arch}-MacOS') + + # dmg image creation uses the node.js appdmg package + if is_tool("appdmg"): + # Prepare dmg + print("Preparing distribution dmg installer") + os.chdir(dist_path_mac) + with open(os_path.join(base_dir, 'contrib/macdeploy', 'appdmg.json.in')) as conf: + confdata = conf.read() + confdata = confdata.replace('%version%', version_str) + confdata = confdata.replace('%signer%', f'{codesigner if code_sign == True else ""}') + with open('appdmg.json', 'w') as newconf: + newconf.write(confdata) + + os.system(f'sed \"s/PACKAGE_NAME/SPMT {version_str}/\" < \"../contrib/macdeploy/background.svg\" | rsvg-convert -f png -d 72 -p 72 | convert - background.tiff@2x.png') + os.system('convert background.tiff@2x.png -resize 500x320 background.tiff.png') + os.system('tiffutil -cathidpicheck background.tiff.png background.tiff@2x.png -out background.tiff') + os.remove('background.tiff.png') + os.system(f'appdmg appdmg.json ../SPMT-v{version_str}-{cpu_arch}-MacOS.dmg') + os.remove('background.tiff@2x.png') + else: + print("appdmg not found, skipping DMG creation") diff --git a/contrib/macdeploy/appdmg.json.in b/contrib/macdeploy/appdmg.json.in new file mode 100644 index 0000000..2a3861c --- /dev/null +++ b/contrib/macdeploy/appdmg.json.in @@ -0,0 +1,13 @@ +{ + "title": "SPMT %version%", + "icon": "../img/spmt.icns", + "icon-size": 96, + "background": "background.tiff", + "contents": [ + { "x": 370, "y": 156, "type": "link", "path": "/Applications" }, + { "x": 128, "y": 156, "type": "file", "path": "SecurePivxMasternodeTool.app" } + ], + "code-sign": { + "signing-identity": "%signer%" + } +} diff --git a/contrib/macdeploy/background.svg b/contrib/macdeploy/background.svg new file mode 100644 index 0000000..2104a56 --- /dev/null +++ b/contrib/macdeploy/background.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + PACKAGE_NAME + + + + + diff --git a/contrib/macdeploy/entitlements.plist b/contrib/macdeploy/entitlements.plist new file mode 100644 index 0000000..6198c3a --- /dev/null +++ b/contrib/macdeploy/entitlements.plist @@ -0,0 +1,18 @@ + + + + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.disable-library-validation + + com.apple.security.automation.apple-events + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-dyld-environment-variables + + com.apple.security.device.camera + + +