diff options
author | toofar <toofar@spalge.com> | 2023-08-14 15:42:38 +1200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-14 15:42:38 +1200 |
commit | 65750e4b5459a933d3013bbb7e19cbeb292ff200 (patch) | |
tree | 7ed2b659ef09fc65084f4eee6dab26814be76a09 | |
parent | 1dba77ddb31fc0b861cffacc64beb04666e273ac (diff) | |
parent | 7599dbc2090466ecac8c75ac5c36ec9ffbd882b2 (diff) | |
download | qutebrowser-65750e4b5459a933d3013bbb7e19cbeb292ff200.tar.gz qutebrowser-65750e4b5459a933d3013bbb7e19cbeb292ff200.zip |
Merge pull request #7803 from qutebrowser/feat/mac_sandbox_pre_release_pyinstaller
Feat/mac sandbox pre release pyinstaller
-rw-r--r-- | doc/changelog.asciidoc | 2 | ||||
-rwxr-xr-x | misc/nsis/install.nsh | 33 | ||||
-rwxr-xr-x | misc/nsis/qutebrowser.nsi | 7 | ||||
-rw-r--r-- | misc/qutebrowser.spec | 19 | ||||
-rw-r--r-- | misc/requirements/requirements-pyinstaller.txt | 2 | ||||
-rw-r--r-- | misc/requirements/requirements-pyinstaller.txt-raw | 2 | ||||
-rw-r--r-- | qutebrowser/app.py | 9 | ||||
-rw-r--r-- | qutebrowser/browser/qutescheme.py | 3 | ||||
-rw-r--r-- | qutebrowser/extensions/loader.py | 28 | ||||
-rw-r--r-- | qutebrowser/html/warning-sandboxing.html | 16 | ||||
-rw-r--r-- | qutebrowser/utils/resources.py | 5 | ||||
-rwxr-xr-x | scripts/dev/build_release.py | 67 | ||||
-rw-r--r-- | tests/unit/extensions/test_loader.py | 8 | ||||
-rw-r--r-- | tox.ini | 2 |
14 files changed, 88 insertions, 115 deletions
diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc index 5deb381f7..96a4b42e7 100644 --- a/doc/changelog.asciidoc +++ b/doc/changelog.asciidoc @@ -88,7 +88,7 @@ Removed * Qt 6.4 was the latest version to support macOS 10.14 and 10.15. * It should be possible to build a custom .dmg with Qt 6.4, but this is unsupported and not recommended. -- Support for Windows 8 and for Windows 10 before 1809 is now dropped. +- Support for Windows 8 and for Windows 10 before 1607 is now dropped. * Support for older Windows 10 versions might still be present in Qt 6.0/6.1/6.2 * Support for Windows 8.1 is still present in Qt 5.15 * It should be possible to build a custom .exe with those versions, but this diff --git a/misc/nsis/install.nsh b/misc/nsis/install.nsh index e7d8b4956..282a254eb 100755 --- a/misc/nsis/install.nsh +++ b/misc/nsis/install.nsh @@ -430,8 +430,37 @@ SectionEnd ; Callbacks Function .onInit StrCpy $KeepReg 1 - !insertmacro CheckPlatform ${PLATFORM} - !insertmacro CheckMinWinVer ${MIN_WIN_VER} + +; OS version check + ${If} ${RunningX64} + ; https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoa#remarks + GetWinVer $R0 Major + !if "${QT5}" == "True" + IntCmpU $R0 6 0 _os_check_fail _os_check_pass + GetWinVer $R1 Minor + IntCmpU $R1 2 _os_check_pass _os_check_fail _os_check_pass + !else + IntCmpU $R0 10 0 _os_check_fail _os_check_pass + GetWinVer $R1 Build + ${If} $R1 >= 22000 ; Windows 11 21H2 + Goto _os_check_pass + ${ElseIf} $R1 >= 14393 ; Windows 10 1607 + ${AndIf} ${IsNativeAMD64} ; Windows 10 has no x86_64 emulation on arm64 + Goto _os_check_pass + ${EndIf} + !endif + ${EndIf} + _os_check_fail: + !if "${QT5}" == "True" + MessageBox MB_OK|MB_ICONSTOP "This version of ${PRODUCT_NAME} requires a 64-bit$\r$\n\ + version of Windows 8 or later." + !else + MessageBox MB_OK|MB_ICONSTOP "This version of ${PRODUCT_NAME} requires a 64-bit$\r$\n\ + version of Windows 10 1607 or later." + !endif + Abort + _os_check_pass: + ${ifnot} ${UAC_IsInnerInstance} !insertmacro CheckSingleInstance "Setup" "Global" "${SETUP_MUTEX}" !insertmacro CheckSingleInstance "Application" "Local" "${APP_MUTEX}" diff --git a/misc/nsis/qutebrowser.nsi b/misc/nsis/qutebrowser.nsi index 60b174bdd..bd5156e83 100755 --- a/misc/nsis/qutebrowser.nsi +++ b/misc/nsis/qutebrowser.nsi @@ -124,13 +124,16 @@ ShowUninstDetails hide ; If not defined, get VERSION from PROGEXE. Set DIST_DIR accordingly.
!ifndef VERSION
- !define /ifndef DIST_DIR ".\..\..\dist\${PRODUCT_NAME}-${ARCH}"
+ !define /ifndef DIST_DIR ".\..\..\dist\${PRODUCT_NAME}"
!getdllversion "${DIST_DIR}\${PROGEXE}" expv_
!define VERSION "${expv_1}.${expv_2}.${expv_3}"
!else
- !define /ifndef DIST_DIR ".\..\..\dist\${PRODUCT_NAME}-${VERSION}-${ARCH}"
+ !define /ifndef DIST_DIR ".\..\..\dist\${PRODUCT_NAME}-${VERSION}"
!endif
+; If not defined, assume Qt6 (requires a more recent windows version)
+!define /ifndef QT5 "False"
+
; Pack the exe header with upx if UPX is defined.
!ifdef UPX
!packhdr "$%TEMP%\exehead.tmp" '"upx" "--ultra-brute" "$%TEMP%\exehead.tmp"'
diff --git a/misc/qutebrowser.spec b/misc/qutebrowser.spec index 1eee9161d..ecb9da68e 100644 --- a/misc/qutebrowser.spec +++ b/misc/qutebrowser.spec @@ -64,17 +64,17 @@ INFO_PLIST_UPDATES = { def get_data_files(): data_files = [ - ('../qutebrowser/html', 'html'), - ('../qutebrowser/img', 'img'), - ('../qutebrowser/icons', 'icons'), - ('../qutebrowser/javascript', 'javascript'), - ('../qutebrowser/html/doc', 'html/doc'), - ('../qutebrowser/git-commit-id', '.'), - ('../qutebrowser/config/configdata.yml', 'config'), + ('../qutebrowser/html', 'qutebrowser/html'), + ('../qutebrowser/img', 'qutebrowser/img'), + ('../qutebrowser/icons', 'qutebrowser/icons'), + ('../qutebrowser/javascript', 'qutebrowser/javascript'), + ('../qutebrowser/html/doc', 'qutebrowser/html/doc'), + ('../qutebrowser/git-commit-id', 'qutebrowser/git-commit-id'), + ('../qutebrowser/config/configdata.yml', 'qutebrowser/config'), ] if os.path.exists(os.path.join('qutebrowser', '3rdparty', 'pdfjs')): - data_files.append(('../qutebrowser/3rdparty/pdfjs', '3rdparty/pdfjs')) + data_files.append(('../qutebrowser/3rdparty/pdfjs', 'qutebrowser/3rdparty/pdfjs')) else: print("Warning: excluding pdfjs as it's not present!") @@ -137,5 +137,4 @@ app = BUNDLE(coll, name='qutebrowser.app', icon=icon, info_plist=INFO_PLIST_UPDATES, - # https://github.com/pyinstaller/pyinstaller/blob/b78bfe530cdc2904f65ce098bdf2de08c9037abb/PyInstaller/hooks/hook-PyQt5.QtWebEngineWidgets.py#L24 - bundle_identifier='org.qt-project.Qt.QtWebEngineCore') + bundle_identifier='org.qutebrowser.qutebrowser') diff --git a/misc/requirements/requirements-pyinstaller.txt b/misc/requirements/requirements-pyinstaller.txt index 912b38cd3..b112963b0 100644 --- a/misc/requirements/requirements-pyinstaller.txt +++ b/misc/requirements/requirements-pyinstaller.txt @@ -1,5 +1,5 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py altgraph==0.17.3 -pyinstaller==5.13.0 +pyinstaller @ git+https://github.com/pyinstaller/pyinstaller.git@79f62ef29822169ae00cd4271390d0e3175476ad pyinstaller-hooks-contrib==2023.6 diff --git a/misc/requirements/requirements-pyinstaller.txt-raw b/misc/requirements/requirements-pyinstaller.txt-raw index c313980b0..7b4c8c84c 100644 --- a/misc/requirements/requirements-pyinstaller.txt-raw +++ b/misc/requirements/requirements-pyinstaller.txt-raw @@ -1 +1 @@ -PyInstaller +pyinstaller @ git+https://github.com/pyinstaller/pyinstaller.git@79f62ef29822169ae00cd4271390d0e3175476ad diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 94cc53c72..60eedeb1b 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -346,15 +346,6 @@ def _open_special_pages(args): True, 'qute://warning/sessions'), - ('sandboxing-warning-shown', - ( - hasattr(sys, "frozen") and - utils.is_mac and - machinery.IS_QT6 and - os.environ.get("QTWEBENGINE_DISABLE_SANDBOX") == "1" - ), - 'qute://warning/sandboxing'), - ('qt5-warning-shown', ( machinery.IS_QT5 and diff --git a/qutebrowser/browser/qutescheme.py b/qutebrowser/browser/qutescheme.py index dae862b8b..f325ff9e3 100644 --- a/qutebrowser/browser/qutescheme.py +++ b/qutebrowser/browser/qutescheme.py @@ -568,9 +568,6 @@ def qute_warning(url: QUrl) -> _HandlerRet: title='Qt 5.15 sessions warning', datadir=standarddir.data(), sep=os.sep) - elif path == '/sandboxing': - src = jinja.render('warning-sandboxing.html', - title='Qt 6 macOS sandboxing warning') elif path == '/qt5': is_venv = hasattr(sys, 'real_prefix') or sys.base_prefix != sys.prefix src = jinja.render('warning-qt5.html', diff --git a/qutebrowser/extensions/loader.py b/qutebrowser/extensions/loader.py index b5b232c5a..7ccdabc88 100644 --- a/qutebrowser/extensions/loader.py +++ b/qutebrowser/extensions/loader.py @@ -6,11 +6,12 @@ import pkgutil import types +import sys import pathlib import importlib import argparse import dataclasses -from typing import Callable, Iterator, List, Optional, Tuple +from typing import Callable, Iterator, List, Optional, Set, Tuple from qutebrowser.qt.core import pyqtSlot @@ -79,6 +80,14 @@ def load_components(*, skip_hooks: bool = False) -> None: def walk_components() -> Iterator[ExtensionInfo]: """Yield ExtensionInfo objects for all modules.""" + if hasattr(sys, 'frozen'): + yield from _walk_pyinstaller() + else: + yield from _walk_normal() + + +def _walk_normal() -> Iterator[ExtensionInfo]: + """Walk extensions when not using PyInstaller.""" for _finder, name, ispkg in pkgutil.walk_packages( path=components.__path__, prefix=components.__name__ + '.', @@ -93,6 +102,23 @@ def walk_components() -> Iterator[ExtensionInfo]: yield ExtensionInfo(name=name) +def _walk_pyinstaller() -> Iterator[ExtensionInfo]: + """Walk extensions when using PyInstaller. + + See https://github.com/pyinstaller/pyinstaller/issues/1905 + + Inspired by: + https://github.com/webcomics/dosage/blob/master/dosagelib/loader.py + """ + toc: Set[str] = set() + for importer in pkgutil.iter_importers('qutebrowser'): + if hasattr(importer, 'toc'): + toc |= importer.toc + for name in toc: + if name.startswith(components.__name__ + '.'): + yield ExtensionInfo(name=name) + + def _get_init_context() -> InitContext: """Get an InitContext object.""" return InitContext(data_dir=pathlib.Path(standarddir.data()), diff --git a/qutebrowser/html/warning-sandboxing.html b/qutebrowser/html/warning-sandboxing.html deleted file mode 100644 index 186d938e7..000000000 --- a/qutebrowser/html/warning-sandboxing.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends "styled.html" %} - -{% block content %} -<h1>{{ title }}</h1> -<span class="note">Note this warning will only appear once. Use <span class="mono">:open -qute://warning/sandboxing</span> to show it again at a later time.</span> - -<p> - Due to a <a href="https://github.com/pyinstaller/pyinstaller/pull/6903">PyInstaller issue</a>, - Chromium's <a href="https://chromium.googlesource.com/chromium/src/+/HEAD/docs/design/sandbox_faq.md">sandboxing</a> - is currently disabled for macOS builds with Qt 6. This means that there will be no additional layer of protection - in case of Chromium security bugs. Thus, it's advised to - <b>not use this build in production</b>. Hopefully, this situation will be - resolved before the final 3.0.0 release. -</p> -{% endblock %} diff --git a/qutebrowser/utils/resources.py b/qutebrowser/utils/resources.py index 494f01bff..60d90fd31 100644 --- a/qutebrowser/utils/resources.py +++ b/qutebrowser/utils/resources.py @@ -36,11 +36,6 @@ def _path(filename: str) -> _ResourceType: assert not posixpath.isabs(filename), filename assert os.path.pardir not in filename.split(posixpath.sep), filename - if hasattr(sys, 'frozen'): - # For PyInstaller, where we can't store resource files in a qutebrowser/ folder - # because the executable is already named "qutebrowser" (at least on macOS). - return pathlib.Path(sys.executable).parent / filename - return importlib_resources.files(qutebrowser) / filename @contextlib.contextmanager diff --git a/scripts/dev/build_release.py b/scripts/dev/build_release.py index 73689ea88..a638508de 100755 --- a/scripts/dev/build_release.py +++ b/scripts/dev/build_release.py @@ -171,9 +171,6 @@ def smoke_test(executable: pathlib.Path, debug: bool, qt5: bool) -> None: r'[0-9:]* WARNING: Qt WebEngine resources not found at .*', (r'[0-9:]* WARNING: Installed Qt WebEngine locales directory not found at ' r'location /qtwebengine_locales\. Trying application directory\.\.\.'), - - # https://github.com/pyinstaller/pyinstaller/pull/6903 - r"[0-9:]* INFO: Sandboxing disabled by user\.", ]) elif IS_WINDOWS: stderr_whitelist.extend([ @@ -245,66 +242,11 @@ def verify_windows_exe(exe_path: pathlib.Path) -> None: assert pe.verify_checksum() -def patch_mac_app(qt5: bool) -> None: - """Patch .app to save some space and make it signable.""" - dist_path = pathlib.Path('dist') - ver = '5' if qt5 else '6' - app_path = dist_path / 'qutebrowser.app' - - contents_path = app_path / 'Contents' - macos_path = contents_path / 'MacOS' - resources_path = contents_path / 'Resources' - pyqt_path = macos_path / f'PyQt{ver}' - - # Replace some duplicate files by symlinks - framework_path = pyqt_path / f'Qt{ver}' / 'lib' / 'QtWebEngineCore.framework' - - framework_resource_path = framework_path / 'Resources' - for file_path in framework_resource_path.iterdir(): - target = pathlib.Path(*[os.pardir] * 5, file_path.name) - if file_path.is_dir(): - shutil.rmtree(file_path) - else: - file_path.unlink() - file_path.symlink_to(target) - - if not qt5: - # Symlinking QtWebEngineCore.framework does not seem to work with Qt 6. - # Also, the symlinking/moving before signing doesn't seem to be required. - return - - core_lib = framework_path / 'Versions' / '5' / 'QtWebEngineCore' - core_lib.unlink() - core_target = pathlib.Path(*[os.pardir] * 7, 'MacOS', 'QtWebEngineCore') - core_lib.symlink_to(core_target) - - # Move stuff around to make things signable on macOS - # See https://github.com/pyinstaller/pyinstaller/issues/6612 - pyqt_path_dest = resources_path / pyqt_path.name - shutil.move(pyqt_path, pyqt_path_dest) - pyqt_path_target = pathlib.Path("..") / pyqt_path_dest.relative_to(contents_path) - pyqt_path.symlink_to(pyqt_path_target) - - for path in macos_path.glob("Qt*"): - link_path = resources_path / path.name - target_path = pathlib.Path("..") / path.relative_to(contents_path) - link_path.symlink_to(target_path) - - -def sign_mac_app() -> None: +def verify_mac_app() -> None: """Re-sign and verify the Mac .app.""" app_path = pathlib.Path('dist') / 'qutebrowser.app' subprocess.run([ 'codesign', - '-s', '-', - '--force', - '--timestamp', - '--deep', - '--verbose', - app_path, - ], check=True) - subprocess.run([ - 'codesign', '--verify', '--strict', '--deep', @@ -341,10 +283,8 @@ def build_mac( utils.print_title("Building .app via pyinstaller") call_tox(f'pyinstaller{"-qt5" if qt5 else ""}', '-r', debug=debug) - utils.print_title("Patching .app") - patch_mac_app(qt5=qt5) - utils.print_title("Re-signing .app") - sign_mac_app() + utils.print_title("Verifying .app") + verify_mac_app() dist_path = pathlib.Path("dist") @@ -483,6 +423,7 @@ def _package_windows_single( utils.print_subtitle("Building installer...") subprocess.run(['makensis.exe', f'/DVERSION={qutebrowser.__version__}', + f'/DQT5={qt5}', 'misc/nsis/qutebrowser.nsi'], check=True) name_parts = [ diff --git a/tests/unit/extensions/test_loader.py b/tests/unit/extensions/test_loader.py index a2a99f305..fd15130ba 100644 --- a/tests/unit/extensions/test_loader.py +++ b/tests/unit/extensions/test_loader.py @@ -20,10 +20,16 @@ def test_on_walk_error(): def test_walk_normal(): - names = [info.name for info in loader.walk_components()] + names = [info.name for info in loader._walk_normal()] assert 'qutebrowser.components.scrollcommands' in names +def test_walk_pyinstaller(): + # We can't test whether we get something back without being frozen by + # PyInstaller, but at least we can test that we don't crash. + list(loader._walk_pyinstaller()) + + def test_load_component(monkeypatch): monkeypatch.setattr(objects, 'commands', {}) @@ -187,6 +187,7 @@ passenv = APPDATA HOME PYINSTALLER_DEBUG + PYINSTALLER_COMPILE_BOOTLOADER setenv = qt5: PYINSTALLER_QT5=true deps = @@ -268,6 +269,7 @@ passenv = * # Override default PyQt6 from [testenv] setenv = qt5: QUTE_QT_WRAPPER=PyQt5 + PYINSTALLER_COMPILE_BOOTLOADER=true usedevelop = true deps = -r{toxinidir}/requirements.txt |