diff --git a/misc/qutebrowser.spec b/misc/qutebrowser.spec new file mode 100644 index 000000000..d9e53e13c --- /dev/null +++ b/misc/qutebrowser.spec @@ -0,0 +1,73 @@ +# -*- mode: python -*- + +import sys +import os + +sys.path.insert(0, os.getcwd()) +from scripts import setupcommon + +block_cipher = None + + +def get_data_files(): + data_files = [ + ('../qutebrowser/html', 'html'), + ('../qutebrowser/img', 'img'), + ('../qutebrowser/javascript', 'javascript'), + ('../qutebrowser/html/doc', 'html/doc'), + ('../qutebrowser/git-commit-id', '') + ] + + if os.path.exists(os.path.join('qutebrowser', '3rdparty', 'pdfjs')): + data_files.append(('../qutebrowser/3rdparty/pdfjs', '3rdparty/pdfjs')) + else: + print("Warning: excluding pdfjs as it's not present!") + + return data_files + + +setupcommon.write_git_file() + + +if os.name == 'nt': + icon = 'icons/qutebrowser.ico' +elif sys.platform == 'darwin': + icon = 'icons/qutebrowser.icns' +else: + icon = None + + +a = Analysis(['../qutebrowser.py'], + pathex=['misc'], + binaries=None, + datas=get_data_files(), + hiddenimports=[], + hookspath=[], + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher) +pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) +exe = EXE(pyz, + a.scripts, + exclude_binaries=True, + name='qutebrowser', + icon=icon, + debug=False, + strip=False, + upx=True, + console=False ) +coll = COLLECT(exe, + a.binaries, + a.zipfiles, + a.datas, + strip=False, + upx=True, + name='qutebrowser') + +app = BUNDLE(coll, + name='qutebrowser.app', + icon=icon, + bundle_identifier=None) diff --git a/scripts/dev/Makefile-dmg b/scripts/dev/Makefile-dmg new file mode 100644 index 000000000..1ecdb1514 --- /dev/null +++ b/scripts/dev/Makefile-dmg @@ -0,0 +1,76 @@ +# +# Build file for creating DMG files. +# +# The DMG packager looks for a template.dmg.bz2 for using as its +# DMG template. If it doesn't find one, it generates a clean one. +# +# If you create a DMG template, you should make one containing all +# the files listed in $(SOURCE_FILES) below, and arrange everything to suit +# your style. The contents of the files themselves does not matter, so +# they can be empty (they will be overwritten later). +# +# Remko Tronçon +# https://el-tramo.be +# Licensed under the MIT License. See COPYING for details. + + +################################################################################ +# Customizable variables +################################################################################ + +NAME ?= qutebrowser + +SOURCE_DIR ?= . +SOURCE_FILES ?= dist/qutebrowser.app COPYING + +TEMPLATE_DMG ?= template.dmg +TEMPLATE_SIZE ?= 120m + +################################################################################ +# DMG building. No editing should be needed beyond this point. +################################################################################ + +MASTER_DMG=$(NAME).dmg +WC_DMG=wc.dmg +WC_DIR=wc + +.PHONY: all +all: $(MASTER_DMG) + +$(TEMPLATE_DMG): $(TEMPLATE_DMG).bz2 + bunzip2 -k $< + +$(TEMPLATE_DMG).bz2: + @echo + @echo --------------------- Generating empty template -------------------- + mkdir template + hdiutil create -fs HFSX -layout SPUD -size $(TEMPLATE_SIZE) "$(TEMPLATE_DMG)" -srcfolder template -format UDRW -volname "$(NAME)" -quiet + rmdir template + bzip2 "$(TEMPLATE_DMG)" + @echo + +$(WC_DMG): $(TEMPLATE_DMG) + cp $< $@ + +$(MASTER_DMG): $(WC_DMG) $(addprefix $(SOURCE_DIR)/,$(SOURCE_FILES)) + @echo + @echo --------------------- Creating Disk Image -------------------- + mkdir -p $(WC_DIR) + hdiutil attach "$(WC_DMG)" -noautoopen -quiet -mountpoint "$(WC_DIR)" + for i in $(SOURCE_FILES); do \ + rm -rf "$(WC_DIR)/$$i"; \ + ditto -rsrc "$(SOURCE_DIR)/$$i" "$(WC_DIR)/$${i##*/}"; \ + done + ln -s /Applications $(WC_DIR) + #rm -f "$@" + #hdiutil create -srcfolder "$(WC_DIR)" -format UDZO -imagekey zlib-level=9 "$@" -volname "$(NAME) $(VERSION)" -scrub -quiet + WC_DEV=`hdiutil info | grep "$(WC_DIR)" | grep "Apple_HFS" | awk '{print $$1}'` && \ + hdiutil detach $$WC_DEV -quiet -force + rm -f "$(MASTER_DMG)" + hdiutil convert "$(WC_DMG)" -quiet -format UDZO -imagekey zlib-level=9 -o "$@" + rm -rf $(WC_DIR) + @echo + +.PHONY: clean +clean: + -rm -rf $(TEMPLATE_DMG) $(MASTER_DMG) $(WC_DMG) diff --git a/scripts/dev/build_release.py b/scripts/dev/build_release.py index 2e487efad..31906b0c2 100755 --- a/scripts/dev/build_release.py +++ b/scripts/dev/build_release.py @@ -50,17 +50,18 @@ def call_script(name, *args, python=sys.executable): subprocess.check_call([python, path] + list(args)) -def call_freeze(*args, python=sys.executable): - """Call freeze.py via tox. +def call_tox(toxenv, *args, python=sys.executable): + """Call tox. Args: + toxenv: Which tox environment to use *args: The arguments to pass. python: The python interpreter to use. """ env = os.environ.copy() env['PYTHON'] = python subprocess.check_call( - [sys.executable, '-m', 'tox', '-e', 'cxfreeze-windows'] + list(args), + [sys.executable, '-m', 'tox', '-e', toxenv] + list(args), env=env) @@ -88,6 +89,21 @@ def smoke_test(executable): '--temp-basedir', 'about:blank', ':later 500 quit']) +def build_osx(): + """Build OS X .dmg/.app.""" + utils.print_title("Updating 3rdparty content") + update_3rdparty.update_pdfjs() + utils.print_title("Building .app via pyinstaller") + call_tox('pyinstaller') + utils.print_title("Building .dmg") + subprocess.check_call(['make', '-f', 'scripts/dev/Makefile-dmg']) + utils.print_title("Cleaning up...") + for f in ['wc.dmg', 'template.dmg']: + os.remove(f) + for d in ['dist', 'build']: + shutil.rmtree(d) + + def build_windows(): """Build windows executables/setups.""" utils.print_title("Updating 3rdparty content") @@ -101,13 +117,13 @@ def build_windows(): python_x64 = r'C:\Python{}'.format(ver) utils.print_title("Running 32bit freeze.py build_exe") - call_freeze('build_exe', python=python_x86) + call_tox('cxfreeze-windows', 'build_exe', python=python_x86) utils.print_title("Running 32bit freeze.py bdist_msi") - call_freeze('bdist_msi', python=python_x86) + call_tox('cxfreeze-windows', 'bdist_msi', python=python_x86) utils.print_title("Running 64bit freeze.py build_exe") - call_freeze('build_exe', python=python_x64) + call_tox('cxfreeze-windows', 'build_exe', python=python_x64) utils.print_title("Running 64bit freeze.py bdist_msi") - call_freeze('bdist_msi', python=python_x64) + call_tox('cxfreeze-windows', 'bdist_msi', python=python_x64) utils.print_title("Running 32bit smoke test") smoke_test('build/exe.win32-{}/qutebrowser.exe'.format(dotver)) @@ -201,6 +217,9 @@ def main(): sys.exit(1) run_asciidoc2html(args) build_windows() + elif sys.platform == 'darwin': + run_asciidoc2html(args) + build_osx() else: build_sdist() diff --git a/scripts/dev/update_3rdparty.py b/scripts/dev/update_3rdparty.py index 3d79328b5..65c5914a4 100755 --- a/scripts/dev/update_3rdparty.py +++ b/scripts/dev/update_3rdparty.py @@ -84,6 +84,18 @@ def update_pdfjs(target_version=None): urllib.request.urlcleanup() +def update_dmg_makefile(): + """Update fancy-dmg Makefile + + See https://el-tramo.be/blog/fancy-dmg/ + """ + print("Updating fancy-dmg Makefile...") + url = 'https://raw.githubusercontent.com/remko/fancy-dmg/master/Makefile' + target_path = os.path.join('scripts', 'dev', 'Makefile-dmg') + urllib.request.urlretrieve(url, target_path) + urllib.request.urlcleanup() + + def main(): parser = argparse.ArgumentParser() parser.add_argument( @@ -91,9 +103,14 @@ def main(): help='Specify pdfjs version. If not given, ' 'the latest version is used.', required=False, metavar='VERSION') + parser.add_argument('--fancy-dmg', help="Update fancy-dmg Makefile", + action='store_true') args = parser.parse_args() update_pdfjs(args.pdfjs) + if args.fancy_dmg: + update_dmg_makefile() + if __name__ == '__main__': main() diff --git a/tox.ini b/tox.ini index 17addccd5..63b2feae5 100644 --- a/tox.ini +++ b/tox.ini @@ -220,6 +220,16 @@ commands = {envpython} scripts/link_pyqt.py --tox {envdir} {envpython} scripts/dev/freeze.py {posargs} +[testenv:pyinstaller] +basepython = python3 +-skip_install = true +deps = + -r{toxinidir}/requirements.txt + PyInstaller==3.2 +commands = + {envpython} scripts/link_pyqt.py --tox {envdir} + {envbindir}/pyinstaller --noconfirm misc/qutebrowser.spec + [testenv:eslint] skip_install = True deps =