Repository navigation
Expand file tree
/
Copy pathsetup.py
More file actions
205 lines (175 loc) · 6.37 KB
/
Copy pathsetup.py
File metadata and controls
205 lines (175 loc) · 6.37 KB
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# SPDX-FileCopyrightText: 2026 Brad Barnett
#
# SPDX-License-Identifier: MIT
"""Build the lvgl CPython extension (generated/lvgl_python.c + LVGL sources).
It sits at the repository root, not in ports/cpython/, because the wheel
build (cibuildwheel, for Linux, Windows, Android and Pyodide) runs from the
root, and every source it compiles -- the generated binding, lv_conf.h and
the lvgl submodule -- lives here. The CPython runtime's own C files, the
vendored jpegio decoder and the extension's tests are in ports/cpython/.
"""
from __future__ import annotations
import os
import sys
from pathlib import Path
from setuptools import Extension, setup
from setuptools.command.build_ext import build_ext
ROOT = Path(__file__).resolve().parent
PORT = "ports/cpython"
LVGL_DIR = ROOT / "lvgl"
GENERATED = ROOT / "generated" / "lvgl_python.c"
GENERATED_PYI = ROOT / "generated" / "lvgl.pyi"
JPEGIO_PIN = ROOT / PORT / "JPEGIO_COMMIT"
JPEGIO_DIR = ROOT / PORT / "src" / "jpegio"
if not GENERATED.is_file():
raise SystemExit(
f"{GENERATED} not found. Run: {ROOT / 'regenerate_all.sh'} --target cpython"
)
if not JPEGIO_PIN.is_file() or len(JPEGIO_PIN.read_text().strip()) != 40:
raise SystemExit(f"{JPEGIO_PIN} must contain the exact source commit")
if not (JPEGIO_DIR / "lvgl_decoder.c").is_file():
raise SystemExit(
f"{JPEGIO_DIR} not found. Run: {ROOT / PORT / 'scripts/sync_from_jpegio.sh'}"
)
if not (LVGL_DIR / "lvgl.h").is_file():
raise SystemExit(
"lvgl submodule missing. Run: git submodule update --init lvgl"
)
# LVGL's own TJPGD is off (lv_conf.h: LV_USE_TJPGD 0), so its tjpgd.c compiles
# to nothing. The JPEG decoder is jpegio's, from src/jpegio/ (JPEGIO_COMMIT),
# registered by lv.init() through the LVPY_AFTER_LV_INIT hook.
# Paths are relative to the root, where the build runs.
lvgl_sources = [
os.path.relpath(p, ROOT)
for p in (LVGL_DIR / "src").rglob("*.c")
]
runtime_sources = [
f"{PORT}/src/lvpy_runtime.c",
f"{PORT}/src/lvpy_jpegio.c",
f"{PORT}/src/jpegio/lvgl_decoder.c",
f"{PORT}/src/jpegio/tjpgd/tjpgd.c",
"generated/lvgl_python.c",
]
include_dirs = [
str(ROOT),
str(ROOT / PORT / "src"),
str(JPEGIO_DIR / "tjpgd"),
str(LVGL_DIR),
]
define_macros = [
("LV_CPYTHON_BUILD", "1"),
("LVPY_AFTER_LV_INIT", "lvpy_register_jpegio"),
]
if sys.platform == "win32":
extra_compile_args = ["/wd4101", "/wd4244", "/wd4267", "/wd4996"]
else:
extra_compile_args = [
"-Wno-unused-function",
"-Wno-sign-compare",
"-Wno-incompatible-pointer-types",
"-Wno-unused-variable",
"-Wno-deprecated-declarations",
]
ext = Extension(
"lvgl",
sources=runtime_sources + lvgl_sources,
include_dirs=include_dirs,
define_macros=define_macros,
extra_compile_args=extra_compile_args,
)
class Win32LinkRspBuildExt(build_ext):
"""MSVC link.exe has an ~8k command-line limit; LVGL needs a response file."""
def run(self):
super().run()
self._install_pyi_stubs()
def _install_pyi_stubs(self) -> None:
pyi_src = GENERATED_PYI
if not pyi_src.is_file():
return
for ext in self.extensions:
if ext.name != "lvgl":
continue
ext_path = Path(self.get_ext_fullpath(self.get_ext_fullname(ext.name)))
# Type checkers look for lvgl.pyi, not the extension's full
# platform-tagged name.
dest = ext_path.parent / "lvgl.pyi"
dest.write_bytes(pyi_src.read_bytes())
def build_extensions(self):
if sys.platform == "win32":
self._patch_msvc_link()
super().build_extensions()
def _patch_msvc_link(self) -> None:
try:
from setuptools._distutils import _msvccompiler as msvc
except ImportError:
return
compiler_cls = getattr(msvc, "MSVCCompiler", None)
if compiler_cls is None or getattr(compiler_cls.link, "_lvcp_rsp", False):
return
orig_link = compiler_cls.link
def link_with_rsp(
compiler,
target_desc,
objects,
output_filename,
output_dir,
libraries,
library_dirs,
runtime_library_dirs,
export_symbols,
debug,
extra_preargs,
extra_postargs,
build_temp,
target_lang,
):
obj_count = len(objects)
real_build_temp = build_temp
if not real_build_temp and objects and not str(objects[0]).startswith("@"):
real_build_temp = os.path.dirname(objects[0])
if obj_count > 50:
if not real_build_temp:
real_build_temp = str(ROOT / "build" / "msvc")
os.makedirs(real_build_temp, exist_ok=True)
rsp_path = os.path.join(real_build_temp, "lvgl_link_objects.rsp")
with open(rsp_path, "w", encoding="utf-8") as rsp:
for obj in objects:
rsp.write(f'"{obj}"\n')
objects = [f"@{rsp_path}"]
orig_dirname = os.path.dirname
def dirname_fixup(path: str) -> str:
if isinstance(path, str) and path.startswith("@"):
return real_build_temp or orig_dirname(path[1:])
return orig_dirname(path)
os.path.dirname = dirname_fixup
try:
return orig_link(
compiler,
target_desc,
objects,
output_filename,
output_dir,
libraries,
library_dirs,
runtime_library_dirs,
export_symbols,
debug,
extra_preargs,
extra_postargs,
real_build_temp,
target_lang,
)
finally:
os.path.dirname = orig_dirname
link_with_rsp._lvcp_rsp = True
compiler_cls.link = link_with_rsp
setup(
name="pydevices-lvgl",
description="LVGL bindings for CPython (generated)",
ext_modules=[ext],
# fs_driver.py is kept once, in python/, for all three ports.
package_dir={"": "python"},
py_modules=["fs_driver"],
python_requires=">=3.11",
cmdclass={"build_ext": Win32LinkRspBuildExt},
)