mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 06:22:22 -04:00
Add a hidden env var to preload our libs and audio helpers on macOS
This commit is contained in:
parent
3d69083f67
commit
726318f016
6 changed files with 67 additions and 137 deletions
|
@ -3,12 +3,18 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import atexit
|
||||
import logging
|
||||
import sys
|
||||
from collections.abc import Callable
|
||||
from typing import TYPE_CHECKING, Any, Union, cast
|
||||
|
||||
if "ANKI_FIRST_RUN" in os.environ:
|
||||
from .package import first_run_setup
|
||||
|
||||
first_run_setup()
|
||||
|
||||
try:
|
||||
import pip_system_certs.wrapt_requests
|
||||
except ModuleNotFoundError:
|
||||
|
@ -32,18 +38,9 @@ if "--syncserver" in sys.argv:
|
|||
from anki.syncserver import run_sync_server
|
||||
from anki.utils import is_mac
|
||||
|
||||
from .package import _fix_protobuf_path
|
||||
|
||||
if is_mac and getattr(sys, "frozen", False):
|
||||
_fix_protobuf_path()
|
||||
|
||||
# does not return
|
||||
run_sync_server()
|
||||
|
||||
from .package import packaged_build_setup
|
||||
|
||||
packaged_build_setup()
|
||||
|
||||
import argparse
|
||||
import builtins
|
||||
import cProfile
|
||||
|
@ -270,12 +267,6 @@ def setupLangAndBackend(
|
|||
# load qt translations
|
||||
_qtrans = QTranslator()
|
||||
|
||||
if is_mac and getattr(sys, "frozen", False):
|
||||
qt_dir = os.path.join(sys.prefix, "../Resources/qt_translations")
|
||||
else:
|
||||
if qtmajor == 5:
|
||||
qt_dir = QLibraryInfo.location(QLibraryInfo.TranslationsPath) # type: ignore
|
||||
else:
|
||||
qt_dir = QLibraryInfo.path(QLibraryInfo.LibraryPath.TranslationsPath)
|
||||
qt_lang = lang.replace("-", "_")
|
||||
if _qtrans.load(f"qtbase_{qt_lang}", qt_dir):
|
||||
|
@ -607,14 +598,13 @@ def _run(argv: list[str] | None = None, exec: bool = True) -> AnkiApp | None:
|
|||
profiler = cProfile.Profile()
|
||||
profiler.enable()
|
||||
|
||||
packaged = getattr(sys, "frozen", False)
|
||||
x11_available = os.getenv("DISPLAY")
|
||||
wayland_configured = qtmajor > 5 and (
|
||||
os.getenv("QT_QPA_PLATFORM") == "wayland" or os.getenv("WAYLAND_DISPLAY")
|
||||
)
|
||||
wayland_forced = os.getenv("ANKI_WAYLAND")
|
||||
|
||||
if (packaged or is_gnome) and wayland_configured:
|
||||
if is_gnome and wayland_configured:
|
||||
if wayland_forced or not x11_available:
|
||||
# Work around broken fractional scaling in Wayland
|
||||
# https://bugreports.qt.io/browse/QTBUG-113574
|
||||
|
|
|
@ -14,9 +14,6 @@ import aqt.utils
|
|||
|
||||
class _MacOSHelper:
|
||||
def __init__(self) -> None:
|
||||
if getattr(sys, "frozen", False):
|
||||
path = os.path.join(sys.prefix, "libankihelper.dylib")
|
||||
else:
|
||||
path = os.path.join(
|
||||
aqt.utils.aqt_data_folder(), "lib", "libankihelper.dylib"
|
||||
)
|
||||
|
|
|
@ -252,12 +252,6 @@ def _handle_local_file_request(request: LocalFileRequest) -> Response:
|
|||
def _builtin_data(path: str) -> bytes:
|
||||
"""Return data from file in aqt/data folder.
|
||||
Path must use forward slash separators."""
|
||||
# packaged build?
|
||||
if getattr(sys, "frozen", False):
|
||||
reader = aqt.__loader__.get_resource_reader("_aqt") # type: ignore
|
||||
with reader.open_resource(path) as f:
|
||||
return f.read()
|
||||
else:
|
||||
full_path = aqt_data_path() / ".." / path
|
||||
return full_path.read_bytes()
|
||||
|
||||
|
|
|
@ -4,94 +4,51 @@
|
|||
"""Helpers for the packaged version of Anki."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
from anki.utils import is_mac
|
||||
|
||||
def first_run_setup() -> None:
|
||||
"""Code run the first time after install/upgrade.
|
||||
|
||||
def _fix_pywin32() -> None:
|
||||
# extend sys.path with .pth files
|
||||
import site
|
||||
Currently, we just import our main libraries and invoke
|
||||
mpv/lame on macOS, which is slow on the first run, and doing
|
||||
it this way shows progress being made.
|
||||
"""
|
||||
|
||||
site.addsitedir(sys.path[0])
|
||||
|
||||
# use updated sys.path to locate dll folder and add it to path
|
||||
path = sys.path[-1]
|
||||
path = path.replace("Pythonwin", "pywin32_system32")
|
||||
os.environ["PATH"] += ";" + path
|
||||
|
||||
# import Python modules from .dll files
|
||||
import importlib.machinery
|
||||
|
||||
for name in "pythoncom", "pywintypes":
|
||||
filename = os.path.join(path, name + "39.dll")
|
||||
loader = importlib.machinery.ExtensionFileLoader(name, filename)
|
||||
spec = importlib.machinery.ModuleSpec(name=name, loader=loader, origin=filename)
|
||||
_mod = importlib._bootstrap._load(spec) # type: ignore
|
||||
|
||||
|
||||
def _patch_pkgutil() -> None:
|
||||
"""Teach pkgutil.get_data() how to read files from in-memory resources.
|
||||
|
||||
This is required for jsonschema."""
|
||||
import importlib
|
||||
import pkgutil
|
||||
|
||||
def get_data_custom(package: str, resource: str) -> bytes | None:
|
||||
try:
|
||||
module = importlib.import_module(package)
|
||||
reader = module.__loader__.get_resource_reader(package) # type: ignore
|
||||
with reader.open_resource(resource) as f:
|
||||
return f.read()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
pkgutil.get_data = get_data_custom
|
||||
|
||||
|
||||
def _patch_certifi() -> None:
|
||||
"""Tell certifi (and thus requests) to use a file in our package folder.
|
||||
|
||||
By default it creates a copy of the data in a temporary folder, which then gets
|
||||
cleaned up by macOS's temp file cleaner."""
|
||||
import certifi
|
||||
|
||||
def where() -> str:
|
||||
prefix = Path(sys.prefix)
|
||||
if sys.platform == "darwin":
|
||||
path = prefix / "../Resources/certifi/cacert.pem"
|
||||
else:
|
||||
path = prefix / "lib" / "certifi" / "cacert.pem"
|
||||
return str(path)
|
||||
|
||||
certifi.where = where
|
||||
|
||||
|
||||
def _fix_protobuf_path() -> None:
|
||||
sys.path.append(str(Path(sys.prefix) / "../Resources"))
|
||||
|
||||
|
||||
def packaged_build_setup() -> None:
|
||||
if not getattr(sys, "frozen", False):
|
||||
if not is_mac:
|
||||
return
|
||||
|
||||
print("Initial setup...")
|
||||
def _dot():
|
||||
print(".", flush=True, end="")
|
||||
|
||||
if sys.platform == "win32":
|
||||
_fix_pywin32()
|
||||
elif sys.platform == "darwin":
|
||||
_fix_protobuf_path()
|
||||
_dot()
|
||||
import anki.collection
|
||||
_dot()
|
||||
import PyQt6.sip
|
||||
_dot()
|
||||
import PyQt6.QtCore
|
||||
_dot()
|
||||
import PyQt6.QtGui
|
||||
_dot()
|
||||
import PyQt6.QtNetwork
|
||||
_dot()
|
||||
import PyQt6.QtQuick
|
||||
_dot()
|
||||
import PyQt6.QtWebChannel
|
||||
_dot()
|
||||
import PyQt6.QtWebEngineCore
|
||||
_dot()
|
||||
import PyQt6.QtWebEngineWidgets
|
||||
_dot()
|
||||
import PyQt6.QtWidgets
|
||||
|
||||
_patch_pkgutil()
|
||||
_patch_certifi()
|
||||
import anki_audio
|
||||
audio_pkg_path = Path(anki_audio.__file__).parent
|
||||
|
||||
# escape hatch for debugging issues with packaged build startup
|
||||
if os.getenv("ANKI_STARTUP_REPL"):
|
||||
# mypy incorrectly thinks this does not exist on Windows
|
||||
is_tty = os.isatty(sys.stdin.fileno()) # type: ignore
|
||||
if is_tty:
|
||||
import code
|
||||
|
||||
code.InteractiveConsole().interact()
|
||||
sys.exit(0)
|
||||
# Invoke mpv and lame
|
||||
cmd = [Path(""), "--version"]
|
||||
for cmd_name in ["mpv", "lame"]:
|
||||
_dot()
|
||||
cmd[0] = audio_pkg_path / cmd_name
|
||||
subprocess.run(cmd, check=True, capture_output=True)
|
||||
|
|
|
@ -87,15 +87,6 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
def aqt_data_path() -> Path:
|
||||
# packaged?
|
||||
if getattr(sys, "frozen", False):
|
||||
prefix = Path(sys.prefix)
|
||||
path = prefix / "lib/_aqt/data"
|
||||
if path.exists():
|
||||
return path
|
||||
else:
|
||||
return prefix / "../Resources/_aqt/data"
|
||||
else:
|
||||
import _aqt.colors
|
||||
|
||||
data_folder = Path(inspect.getfile(_aqt.colors)).with_name("data")
|
||||
|
@ -1207,12 +1198,11 @@ def supportText() -> str:
|
|||
platname = platform.platform()
|
||||
|
||||
return """\
|
||||
Anki {} {} {}
|
||||
Anki {} {}
|
||||
Python {} Qt {} PyQt {}
|
||||
Platform: {}
|
||||
""".format(
|
||||
version_with_build(),
|
||||
"(src)" if not getattr(sys, "frozen", False) else "",
|
||||
"(ao)" if mw.addonManager.dirty else "",
|
||||
platform.python_version(),
|
||||
qVersion(),
|
||||
|
|
|
@ -87,7 +87,10 @@ fn main() {
|
|||
// Pre-validate by running --version to trigger any Gatekeeper checks
|
||||
let anki_bin = uv_install_root.join(".venv/bin/anki");
|
||||
println!("\n\x1B[1mThis may take a few minutes. Please wait...\x1B[0m");
|
||||
let _ = Command::new(&anki_bin).arg("--version").output();
|
||||
let _ = Command::new(&anki_bin)
|
||||
.env("ANKI_FIRST_RUN", "1")
|
||||
.arg("--version")
|
||||
.status();
|
||||
|
||||
// Then launch the binary as detached subprocess so the terminal can close
|
||||
let child = Command::new(&anki_bin)
|
||||
|
@ -98,7 +101,6 @@ fn main() {
|
|||
.spawn()
|
||||
.unwrap();
|
||||
std::mem::forget(child);
|
||||
println!("Anki launched successfully");
|
||||
} else {
|
||||
// If venv already existed, exec as normal
|
||||
println!(
|
||||
|
|
Loading…
Reference in a new issue