-
Notifications
You must be signed in to change notification settings - Fork 291
/
_build_backend.py
128 lines (105 loc) · 3.74 KB
/
_build_backend.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
"""Build backend that also compiles translations and frontend."""
from __future__ import annotations
import shutil
import subprocess
from itertools import chain
from os import walk
from pathlib import Path
from typing import TYPE_CHECKING
from babel.messages.mofile import write_mo
from babel.messages.pofile import read_po
from setuptools import build_meta
from setuptools.build_meta import get_requires_for_build_editable
from setuptools.build_meta import get_requires_for_build_sdist
from setuptools.build_meta import get_requires_for_build_wheel
from setuptools.build_meta import prepare_metadata_for_build_editable
from setuptools.build_meta import prepare_metadata_for_build_wheel
if TYPE_CHECKING: # pragma: no cover
from collections.abc import Iterable
__all__ = [
"build_editable",
"build_sdist",
"build_wheel",
"get_requires_for_build_editable",
"get_requires_for_build_sdist",
"get_requires_for_build_wheel",
"prepare_metadata_for_build_editable",
"prepare_metadata_for_build_wheel",
]
def _frontend_sources() -> Iterable[Path]:
"""List all frontend sources that should trigger a rebuild if changed.
Yields:
The files relevant for the frontend build.
"""
yield Path("frontend/package-lock.json")
yield Path("frontend/build.ts")
for directory, _dirnames, files in chain(
walk(Path("frontend/css")),
walk(Path("frontend/src")),
):
dirpath = Path(directory)
for file in files:
yield dirpath / file
def _compile_frontend() -> None:
"""Compile the frontend (if changed or missing)."""
source_mtime = max(p.stat().st_mtime_ns for p in _frontend_sources())
app_js = Path("src/fava/static/app.js")
if app_js.exists() and source_mtime < app_js.stat().st_mtime_ns:
return
npm = shutil.which("npm")
if npm is None:
msg = "npm is missing"
raise RuntimeError(msg)
# Clean outpute directory before building
for p in Path("src/fava/static").iterdir():
if p.name != "favicon.ico":
p.unlink()
subprocess.run((npm, "install"), cwd="frontend", check=True)
Path("frontend/node_modules").touch()
subprocess.run((npm, "run", "build"), cwd="frontend", check=True)
def _compile_translations() -> None:
"""Compile the translations from .po to .mo (if changed or missing)."""
for source in Path().glob("src/fava/translations/**/messages.po"):
target = source.parent / "messages.mo"
if (
not target.exists()
or target.stat().st_mtime_ns < source.stat().st_mtime_ns
):
locale = source.parts[-3]
catalog = read_po(source.open("rb"), locale)
write_mo(target.open("wb"), catalog)
def _build_fava() -> None:
"""Run the build steps for Fava."""
_compile_frontend()
_compile_translations()
def build_wheel(
wheel_directory: str,
config_settings: dict[str, str | list[str] | None] | None = None,
metadata_directory: str | None = None,
) -> str:
_build_fava()
return build_meta.build_wheel(
wheel_directory,
config_settings=config_settings,
metadata_directory=metadata_directory,
)
def build_editable(
wheel_directory: str,
config_settings: dict[str, str | list[str] | None] | None = None,
metadata_directory: str | None = None,
) -> str:
_build_fava()
return build_meta.build_editable(
wheel_directory,
config_settings=config_settings,
metadata_directory=metadata_directory,
)
def build_sdist(
sdist_directory: str,
config_settings: dict[str, str | list[str] | None] | None = None,
) -> str:
_build_fava()
return build_meta.build_sdist(
sdist_directory,
config_settings=config_settings,
)