|
2 | 2 | # vim:fileencoding=utf-8 |
3 | 3 | # License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net> |
4 | 4 |
|
5 | | -import json |
6 | 5 | import os |
7 | 6 | import re |
8 | | -import subprocess |
9 | 7 | import sys |
10 | | -from enum import Enum |
11 | | -from typing import Any, Callable, Dict, List, NamedTuple, Optional, Sequence, Tuple |
12 | 8 |
|
13 | 9 | _plat = sys.platform.lower() |
14 | 10 | is_linux = 'linux' in _plat |
15 | 11 | is_openbsd = 'openbsd' in _plat |
16 | | -base = os.path.dirname(os.path.abspath(__file__)) |
17 | | - |
18 | | - |
19 | | -def null_func() -> None: |
20 | | - return None |
21 | | - |
22 | | - |
23 | | -class CompileKey(NamedTuple): |
24 | | - src: str |
25 | | - dest: str |
26 | | - |
27 | | - |
28 | | -class Command(NamedTuple): |
29 | | - desc: str |
30 | | - cmd: Sequence[str] |
31 | | - is_newer_func: Callable[[], bool] |
32 | | - on_success: Callable[[], None] = null_func |
33 | | - key: Optional[CompileKey] = None |
34 | | - keyfile: Optional[str] = None |
35 | | - |
36 | | - |
37 | | -class ISA(Enum): |
38 | | - X86 = 0x03 |
39 | | - AMD64 = 0x3e |
40 | | - ARM64 = 0xb7 |
41 | | - Other = 0x0 |
42 | | - |
43 | | - |
44 | | -class BinaryArch(NamedTuple): |
45 | | - bits: int = 64 |
46 | | - isa: ISA = ISA.AMD64 |
47 | | - |
48 | | - |
49 | | -class CompilerType(Enum): |
50 | | - gcc = 'gcc' |
51 | | - clang = 'clang' |
52 | | - unknown = 'unknown' |
53 | | - |
54 | | - |
55 | | -class Env: |
56 | | - |
57 | | - cc: List[str] = [] |
58 | | - cppflags: List[str] = [] |
59 | | - cflags: List[str] = [] |
60 | | - ldflags: List[str] = [] |
61 | | - library_paths: Dict[str, List[str]] = {} |
62 | | - ldpaths: List[str] = [] |
63 | | - ccver: Tuple[int, int] |
64 | | - vcs_rev: str = '' |
65 | | - binary_arch: BinaryArch = BinaryArch() |
66 | | - native_optimizations: bool = False |
67 | | - primary_version: int = 0 |
68 | | - secondary_version: int = 0 |
69 | | - xt_version: str = '' |
70 | | - has_copy_file_range: bool = False |
71 | | - |
72 | | - # glfw stuff |
73 | | - all_headers: List[str] = [] |
74 | | - sources: List[str] = [] |
75 | | - wayland_packagedir: str = '' |
76 | | - wayland_scanner: str = '' |
77 | | - wayland_scanner_code: str = '' |
78 | | - wayland_protocols: Tuple[str, ...] = () |
79 | | - |
80 | | - def __init__( |
81 | | - self, cc: List[str] = [], cppflags: List[str] = [], cflags: List[str] = [], ldflags: List[str] = [], |
82 | | - library_paths: Dict[str, List[str]] = {}, ldpaths: Optional[List[str]] = None, ccver: Tuple[int, int] = (0, 0), |
83 | | - vcs_rev: str = '', binary_arch: BinaryArch = BinaryArch(), |
84 | | - native_optimizations: bool = False, |
85 | | - ): |
86 | | - self.cc, self.cppflags, self.cflags, self.ldflags, self.library_paths = cc, cppflags, cflags, ldflags, library_paths |
87 | | - self.ldpaths = ldpaths or [] |
88 | | - self.ccver = ccver |
89 | | - self.vcs_rev = vcs_rev |
90 | | - self.binary_arch = binary_arch |
91 | | - self.native_optimizations = native_optimizations |
92 | | - self._cc_version_string = '' |
93 | | - self._compiler_type: Optional[CompilerType] = None |
94 | | - |
95 | | - @property |
96 | | - def cc_version_string(self) -> str: |
97 | | - if not self._cc_version_string: |
98 | | - self._cc_version_string = subprocess.check_output(self.cc + ['--version']).decode() |
99 | | - return self._cc_version_string |
100 | | - |
101 | | - @property |
102 | | - def compiler_type(self) -> CompilerType: |
103 | | - if self._compiler_type is None: |
104 | | - raw = self.cc_version_string |
105 | | - if 'Free Software Foundation' in raw: |
106 | | - self._compiler_type = CompilerType.gcc |
107 | | - elif 'clang' in raw.lower().split(): |
108 | | - self._compiler_type = CompilerType.clang |
109 | | - else: |
110 | | - self._compiler_type = CompilerType.unknown |
111 | | - return self._compiler_type |
112 | | - |
113 | | - def copy(self) -> 'Env': |
114 | | - ans = Env(self.cc, list(self.cppflags), list(self.cflags), list(self.ldflags), dict(self.library_paths), list(self.ldpaths), self.ccver) |
115 | | - ans.all_headers = list(self.all_headers) |
116 | | - ans._cc_version_string = self._cc_version_string |
117 | | - ans.sources = list(self.sources) |
118 | | - ans.wayland_packagedir = self.wayland_packagedir |
119 | | - ans.wayland_scanner = self.wayland_scanner |
120 | | - ans.wayland_scanner_code = self.wayland_scanner_code |
121 | | - ans.wayland_protocols = self.wayland_protocols |
122 | | - ans.vcs_rev = self.vcs_rev |
123 | | - ans.binary_arch = self.binary_arch |
124 | | - ans.native_optimizations = self.native_optimizations |
125 | | - ans.primary_version = self.primary_version |
126 | | - ans.secondary_version = self.secondary_version |
127 | | - ans.xt_version = self.xt_version |
128 | | - ans.has_copy_file_range = self.has_copy_file_range |
129 | | - return ans |
130 | | - |
131 | | - |
132 | | -def wayland_protocol_file_name(base: str, ext: str = 'c') -> str: |
133 | | - base = os.path.basename(base).rpartition('.')[0] |
134 | | - return f'wayland-{base}-client-protocol.{ext}' |
135 | | - |
136 | | - |
137 | | -def init_env( |
138 | | - env: Env, |
139 | | - pkg_config: Callable[..., List[str]], |
140 | | - pkg_version: Callable[[str], Tuple[int, int]], |
141 | | - at_least_version: Callable[..., None], |
142 | | - test_compile: Callable[..., Any], |
143 | | - module: str = 'x11' |
144 | | -) -> Env: |
145 | | - ans = env.copy() |
146 | | - ans.cflags.append('-fPIC') |
147 | | - ans.cppflags.append(f'-D_GLFW_{module.upper()}') |
148 | | - ans.cppflags.append('-D_GLFW_BUILD_DLL') |
149 | | - |
150 | | - with open(os.path.join(base, 'source-info.json')) as f: |
151 | | - sinfo = json.load(f) |
152 | | - module_sources = list(sinfo[module]['sources']) |
153 | | - if module in ('x11', 'wayland'): |
154 | | - remove = 'null_joystick.c' if is_linux else 'linux_joystick.c' |
155 | | - module_sources.remove(remove) |
156 | | - |
157 | | - ans.sources = sinfo['common']['sources'] + module_sources |
158 | | - ans.all_headers = [x for x in os.listdir(base) if x.endswith('.h')] |
159 | | - |
160 | | - if module in ('x11', 'wayland'): |
161 | | - ans.cflags.append('-pthread') |
162 | | - ans.ldpaths.extend('-pthread -lm'.split()) |
163 | | - if not is_openbsd: |
164 | | - ans.ldpaths.extend('-lrt -ldl'.split()) |
165 | | - major, minor = pkg_version('xkbcommon') |
166 | | - if (major, minor) < (0, 5): |
167 | | - raise SystemExit('libxkbcommon >= 0.5 required') |
168 | | - if major < 1: |
169 | | - ans.cflags.append('-DXKB_HAS_NO_UTF32') |
170 | | - |
171 | | - if module == 'x11': |
172 | | - for dep in 'x11 xrandr xinerama xcursor xkbcommon xkbcommon-x11 x11-xcb dbus-1'.split(): |
173 | | - ans.cflags.extend(pkg_config(dep, '--cflags-only-I')) |
174 | | - ans.ldpaths.extend(pkg_config(dep, '--libs')) |
175 | | - |
176 | | - elif module == 'cocoa': |
177 | | - ans.cppflags.append('-DGL_SILENCE_DEPRECATION') |
178 | | - for f_ in 'Cocoa IOKit CoreFoundation CoreVideo QuartzCore UniformTypeIdentifiers'.split(): |
179 | | - ans.ldpaths.extend(('-framework', f_)) |
180 | | - |
181 | | - elif module == 'wayland': |
182 | | - at_least_version('wayland-protocols', *sinfo['wayland_protocols']) |
183 | | - ans.wayland_packagedir = os.path.abspath(pkg_config('wayland-protocols', '--variable=pkgdatadir')[0]) |
184 | | - ans.wayland_scanner = os.path.abspath(pkg_config('wayland-scanner', '--variable=wayland_scanner')[0]) |
185 | | - scanner_version = tuple(map(int, pkg_config('wayland-scanner', '--modversion')[0].strip().split('.'))) |
186 | | - ans.wayland_scanner_code = 'private-code' if scanner_version >= (1, 14, 91) else 'code' |
187 | | - ans.wayland_protocols = tuple(sinfo[module]['protocols']) |
188 | | - for p in ans.wayland_protocols: |
189 | | - ans.sources.append(wayland_protocol_file_name(p)) |
190 | | - ans.all_headers.append(wayland_protocol_file_name(p, 'h')) |
191 | | - for dep in 'wayland-client wayland-cursor xkbcommon dbus-1'.split(): |
192 | | - ans.cflags.extend(pkg_config(dep, '--cflags-only-I')) |
193 | | - ans.ldpaths.extend(pkg_config(dep, '--libs')) |
194 | | - has_memfd_create = test_compile(env.cc, '-Werror', src='''#define _GNU_SOURCE |
195 | | - #include <unistd.h> |
196 | | - #include <sys/syscall.h> |
197 | | - int main(void) { |
198 | | - return syscall(__NR_memfd_create, "test", 0); |
199 | | - }''') |
200 | | - if has_memfd_create: |
201 | | - ans.cppflags.append('-DHAS_MEMFD_CREATE') |
202 | | - |
203 | | - return ans |
204 | | - |
205 | | - |
206 | | -def build_wayland_protocols( |
207 | | - env: Env, |
208 | | - parallel_run: Callable[[List[Command]], None], |
209 | | - emphasis: Callable[[str], str], |
210 | | - newer: Callable[..., bool], |
211 | | - dest_dir: str |
212 | | -) -> None: |
213 | | - items = [] |
214 | | - for protocol in env.wayland_protocols: |
215 | | - if '/' in protocol: |
216 | | - src = os.path.join(env.wayland_packagedir, protocol) |
217 | | - if not os.path.exists(src): |
218 | | - raise SystemExit(f'The wayland-protocols package on your system is missing the {protocol} protocol definition file') |
219 | | - else: |
220 | | - src = os.path.join(os.path.dirname(os.path.abspath(__file__)), protocol) |
221 | | - if not os.path.exists(src): |
222 | | - raise SystemExit(f'The local Wayland protocol {protocol} is missing from kitty sources') |
223 | | - for ext in 'hc': |
224 | | - dest = wayland_protocol_file_name(src, ext) |
225 | | - dest = os.path.join(dest_dir, dest) |
226 | | - if newer(dest, src): |
227 | | - q = 'client-header' if ext == 'h' else env.wayland_scanner_code |
228 | | - items.append(Command( |
229 | | - f'Generating {emphasis(os.path.basename(dest))} ...', |
230 | | - [env.wayland_scanner, q, src, dest], lambda: True)) |
231 | | - if items: |
232 | | - parallel_run(items) |
233 | 12 |
|
234 | 13 |
|
235 | 14 | class Arg: |
|
0 commit comments