mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 14:02:21 -04:00
Split libankihelper into a separate module
It's rarely updated, and the old approach resulted in a 'proper' aqt build only being done on a Mac.
This commit is contained in:
parent
344cac1ef4
commit
cd411927cc
9 changed files with 125 additions and 71 deletions
|
@ -27,7 +27,6 @@ pub fn build_and_check_aqt(build: &mut Build) -> Result<()> {
|
||||||
build_forms(build)?;
|
build_forms(build)?;
|
||||||
build_generated_sources(build)?;
|
build_generated_sources(build)?;
|
||||||
build_data_folder(build)?;
|
build_data_folder(build)?;
|
||||||
build_macos_helper(build)?;
|
|
||||||
build_wheel(build)?;
|
build_wheel(build)?;
|
||||||
check_python(build)?;
|
check_python(build)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -337,27 +336,6 @@ impl BuildAction for BuildThemedIcon<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_macos_helper(build: &mut Build) -> Result<()> {
|
|
||||||
if cfg!(target_os = "macos") {
|
|
||||||
build.add_action(
|
|
||||||
"qt:aqt:data:lib:libankihelper",
|
|
||||||
RunCommand {
|
|
||||||
command: ":pyenv:bin",
|
|
||||||
args: "$script $out $in",
|
|
||||||
inputs: hashmap! {
|
|
||||||
"script" => inputs!["qt/mac/helper_build.py"],
|
|
||||||
"in" => inputs![glob!["qt/mac/*.swift"]],
|
|
||||||
"" => inputs!["out/env"],
|
|
||||||
},
|
|
||||||
outputs: hashmap! {
|
|
||||||
"out" => vec!["qt/_aqt/data/lib/libankihelper.dylib"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_wheel(build: &mut Build) -> Result<()> {
|
fn build_wheel(build: &mut Build) -> Result<()> {
|
||||||
build.add_action(
|
build.add_action(
|
||||||
"wheels:aqt",
|
"wheels:aqt",
|
||||||
|
|
|
@ -3,50 +3,11 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
from collections.abc import Callable
|
|
||||||
from ctypes import CDLL, CFUNCTYPE, c_bool, c_char_p
|
|
||||||
|
|
||||||
import aqt
|
|
||||||
import aqt.utils
|
|
||||||
|
|
||||||
|
|
||||||
class _MacOSHelper:
|
|
||||||
def __init__(self) -> None:
|
|
||||||
path = os.path.join(aqt.utils.aqt_data_folder(), "lib", "libankihelper.dylib")
|
|
||||||
|
|
||||||
self._dll = CDLL(path)
|
|
||||||
self._dll.system_is_dark.restype = c_bool
|
|
||||||
|
|
||||||
def system_is_dark(self) -> bool:
|
|
||||||
return self._dll.system_is_dark()
|
|
||||||
|
|
||||||
def set_darkmode_enabled(self, enabled: bool) -> bool:
|
|
||||||
return self._dll.set_darkmode_enabled(enabled)
|
|
||||||
|
|
||||||
def start_wav_record(self, path: str, on_error: Callable[[str], None]) -> None:
|
|
||||||
global _on_audio_error
|
|
||||||
_on_audio_error = on_error
|
|
||||||
self._dll.start_wav_record(path.encode("utf8"), _audio_error_callback)
|
|
||||||
|
|
||||||
def end_wav_record(self) -> None:
|
|
||||||
"On completion, file should be saved if no error has arrived."
|
|
||||||
self._dll.end_wav_record()
|
|
||||||
|
|
||||||
|
|
||||||
# this must not be overwritten or deallocated
|
|
||||||
@CFUNCTYPE(None, c_char_p) # type: ignore
|
|
||||||
def _audio_error_callback(msg: str) -> None:
|
|
||||||
if handler := _on_audio_error:
|
|
||||||
handler(msg)
|
|
||||||
|
|
||||||
|
|
||||||
_on_audio_error: Callable[[str], None] | None = None
|
|
||||||
|
|
||||||
macos_helper: _MacOSHelper | None = None
|
|
||||||
if sys.platform == "darwin":
|
if sys.platform == "darwin":
|
||||||
try:
|
from anki_mac_helper import ( # pylint:disable=unused-import,import-error
|
||||||
macos_helper = _MacOSHelper()
|
macos_helper,
|
||||||
except Exception as e:
|
)
|
||||||
print("macos_helper:", e)
|
else:
|
||||||
|
macos_helper = None
|
||||||
|
|
51
qt/mac/anki_mac_helper/__init__.py
Normal file
51
qt/mac/anki_mac_helper/__init__.py
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
# Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from collections.abc import Callable
|
||||||
|
from ctypes import CDLL, CFUNCTYPE, c_bool, c_char_p
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
class _MacOSHelper:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
# Look for the dylib in the same directory as this module
|
||||||
|
module_dir = Path(__file__).parent
|
||||||
|
path = module_dir / "libankihelper.dylib"
|
||||||
|
|
||||||
|
self._dll = CDLL(str(path))
|
||||||
|
self._dll.system_is_dark.restype = c_bool
|
||||||
|
|
||||||
|
def system_is_dark(self) -> bool:
|
||||||
|
return self._dll.system_is_dark()
|
||||||
|
|
||||||
|
def set_darkmode_enabled(self, enabled: bool) -> bool:
|
||||||
|
return self._dll.set_darkmode_enabled(enabled)
|
||||||
|
|
||||||
|
def start_wav_record(self, path: str, on_error: Callable[[str], None]) -> None:
|
||||||
|
global _on_audio_error
|
||||||
|
_on_audio_error = on_error
|
||||||
|
self._dll.start_wav_record(path.encode("utf8"), _audio_error_callback)
|
||||||
|
|
||||||
|
def end_wav_record(self) -> None:
|
||||||
|
"On completion, file should be saved if no error has arrived."
|
||||||
|
self._dll.end_wav_record()
|
||||||
|
|
||||||
|
|
||||||
|
# this must not be overwritten or deallocated
|
||||||
|
@CFUNCTYPE(None, c_char_p) # type: ignore
|
||||||
|
def _audio_error_callback(msg: str) -> None:
|
||||||
|
if handler := _on_audio_error:
|
||||||
|
handler(msg)
|
||||||
|
|
||||||
|
|
||||||
|
_on_audio_error: Callable[[str], None] | None = None
|
||||||
|
|
||||||
|
macos_helper: _MacOSHelper | None = None
|
||||||
|
if sys.platform == "darwin":
|
||||||
|
try:
|
||||||
|
macos_helper = _MacOSHelper()
|
||||||
|
except Exception as e:
|
||||||
|
print("macos_helper:", e)
|
0
qt/mac/anki_mac_helper/py.typed
Normal file
0
qt/mac/anki_mac_helper/py.typed
Normal file
20
qt/mac/build.sh
Executable file
20
qt/mac/build.sh
Executable file
|
@ -0,0 +1,20 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Get the project root directory
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJ_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||||
|
|
||||||
|
# Build the dylib first
|
||||||
|
echo "Building macOS helper dylib..."
|
||||||
|
"$PROJ_ROOT/out/pyenv/bin/python" "$SCRIPT_DIR/helper_build.py"
|
||||||
|
|
||||||
|
# Create the wheel using uv
|
||||||
|
echo "Creating wheel..."
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
"$PROJ_ROOT/out/extracted/uv/uv" build --wheel
|
||||||
|
|
||||||
|
echo "Build complete!"
|
|
@ -7,8 +7,16 @@ import subprocess
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
# If no arguments provided, build for the anki_mac_helper package
|
||||||
|
if len(sys.argv) == 1:
|
||||||
|
script_dir = Path(__file__).parent
|
||||||
|
out_dylib = script_dir / "anki_mac_helper" / "libankihelper.dylib"
|
||||||
|
src_files = list(script_dir.glob("*.swift"))
|
||||||
|
else:
|
||||||
out_dylib, *src_files = sys.argv[1:]
|
out_dylib, *src_files = sys.argv[1:]
|
||||||
out_dir = Path(out_dylib).parent.resolve()
|
|
||||||
|
out_dylib = Path(out_dylib)
|
||||||
|
out_dir = out_dylib.parent.resolve()
|
||||||
src_dir = Path(src_files[0]).parent.resolve()
|
src_dir = Path(src_files[0]).parent.resolve()
|
||||||
|
|
||||||
# Build for both architectures
|
# Build for both architectures
|
||||||
|
@ -29,12 +37,20 @@ for arch in architectures:
|
||||||
"ankihelper",
|
"ankihelper",
|
||||||
"-O",
|
"-O",
|
||||||
]
|
]
|
||||||
|
if isinstance(src_files[0], Path):
|
||||||
|
args.extend(src_files)
|
||||||
|
else:
|
||||||
args.extend(src_dir / Path(file).name for file in src_files)
|
args.extend(src_dir / Path(file).name for file in src_files)
|
||||||
args.extend(["-o", str(temp_out)])
|
args.extend(["-o", str(temp_out)])
|
||||||
subprocess.run(args, check=True, cwd=out_dir)
|
subprocess.run(args, check=True, cwd=out_dir)
|
||||||
|
|
||||||
|
# Ensure output directory exists
|
||||||
|
out_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
# Create universal binary
|
# Create universal binary
|
||||||
lipo_args = ["lipo", "-create", "-output", out_dylib] + [str(f) for f in temp_files]
|
lipo_args = ["lipo", "-create", "-output", str(out_dylib)] + [
|
||||||
|
str(f) for f in temp_files
|
||||||
|
]
|
||||||
subprocess.run(lipo_args, check=True)
|
subprocess.run(lipo_args, check=True)
|
||||||
|
|
||||||
# Clean up temporary files
|
# Clean up temporary files
|
||||||
|
|
17
qt/mac/pyproject.toml
Normal file
17
qt/mac/pyproject.toml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[build-system]
|
||||||
|
requires = ["hatchling"]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "anki-mac-helper"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Small support library for Anki on Macs"
|
||||||
|
requires-python = ">=3.9"
|
||||||
|
license = { text = "AGPL-3.0-or-later" }
|
||||||
|
authors = [
|
||||||
|
{ name = "Anki Team" },
|
||||||
|
]
|
||||||
|
urls = { Homepage = "https://github.com/ankitects/anki" }
|
||||||
|
|
||||||
|
[tool.hatch.build.targets.wheel]
|
||||||
|
packages = ["anki_mac_helper"]
|
|
@ -14,6 +14,7 @@ dependencies = [
|
||||||
"waitress>=2.0.0",
|
"waitress>=2.0.0",
|
||||||
"psutil; sys.platform == 'win32'",
|
"psutil; sys.platform == 'win32'",
|
||||||
"pywin32; sys.platform == 'win32'",
|
"pywin32; sys.platform == 'win32'",
|
||||||
|
"anki-mac-helper; sys.platform == 'darwin'",
|
||||||
"pip-system-certs!=5.1",
|
"pip-system-certs!=5.1",
|
||||||
"mock",
|
"mock",
|
||||||
"types-decorator",
|
"types-decorator",
|
||||||
|
|
12
uv.lock
12
uv.lock
|
@ -141,11 +141,20 @@ dev = [
|
||||||
{ name = "wheel" },
|
{ name = "wheel" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anki-mac-helper"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b3/9f/c4d3e635ddbd2c6c24ff5454e96900fd2061b9abbb0198b9283446780d08/anki_mac_helper-0.1.0-py3-none-any.whl", hash = "sha256:ed449aba27ea3bc7999054afa10dacf08ef856ed7af46526d9c8599d8179a618", size = 40637, upload-time = "2025-06-19T14:38:07.672Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aqt"
|
name = "aqt"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
source = { editable = "qt" }
|
source = { editable = "qt" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
{ name = "anki-mac-helper", marker = "sys_platform == 'darwin' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" },
|
||||||
{ name = "beautifulsoup4" },
|
{ name = "beautifulsoup4" },
|
||||||
{ name = "flask" },
|
{ name = "flask" },
|
||||||
{ name = "flask-cors" },
|
{ name = "flask-cors" },
|
||||||
|
@ -201,6 +210,7 @@ qt67 = [
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "anki-audio", marker = "(sys_platform == 'darwin' and extra == 'audio') or (sys_platform == 'win32' and extra == 'audio')", specifier = "==0.1.0" },
|
{ name = "anki-audio", marker = "(sys_platform == 'darwin' and extra == 'audio') or (sys_platform == 'win32' and extra == 'audio')", specifier = "==0.1.0" },
|
||||||
|
{ name = "anki-mac-helper", marker = "sys_platform == 'darwin'" },
|
||||||
{ name = "beautifulsoup4" },
|
{ name = "beautifulsoup4" },
|
||||||
{ name = "flask" },
|
{ name = "flask" },
|
||||||
{ name = "flask-cors" },
|
{ name = "flask-cors" },
|
||||||
|
@ -572,7 +582,7 @@ name = "importlib-metadata"
|
||||||
version = "8.7.0"
|
version = "8.7.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "zipp" },
|
{ name = "zipp", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt67') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt67')" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
|
|
Loading…
Reference in a new issue