diff options
-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 | 66 | ||||
-rw-r--r-- | tests/unit/extensions/test_loader.py | 8 | ||||
-rw-r--r-- | tox.ini | 2 |
11 files changed, 50 insertions, 110 deletions
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..4c443136f 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") 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 |