forked from beancount/fava
-
Notifications
You must be signed in to change notification settings - Fork 0
/
_build_backend.py
121 lines (100 loc) · 3.53 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
"""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 Iterable
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
__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."""
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] | 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] | 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] | None = None,
) -> str:
_build_fava()
return build_meta.build_sdist(
sdist_directory,
config_settings=config_settings,
)