diff --git a/glfw/__init__.py b/glfw/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glfw/glfw.py b/glfw/glfw.py index 79894ef51..4f04ddf5e 100755 --- a/glfw/glfw.py +++ b/glfw/glfw.py @@ -2,234 +2,13 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2017, Kovid Goyal -import json import os import re -import subprocess import sys -from enum import Enum -from typing import Any, Callable, Dict, List, NamedTuple, Optional, Sequence, Tuple _plat = sys.platform.lower() is_linux = 'linux' in _plat is_openbsd = 'openbsd' in _plat -base = os.path.dirname(os.path.abspath(__file__)) - - -def null_func() -> None: - return None - - -class CompileKey(NamedTuple): - src: str - dest: str - - -class Command(NamedTuple): - desc: str - cmd: Sequence[str] - is_newer_func: Callable[[], bool] - on_success: Callable[[], None] = null_func - key: Optional[CompileKey] = None - keyfile: Optional[str] = None - - -class ISA(Enum): - X86 = 0x03 - AMD64 = 0x3e - ARM64 = 0xb7 - Other = 0x0 - - -class BinaryArch(NamedTuple): - bits: int = 64 - isa: ISA = ISA.AMD64 - - -class CompilerType(Enum): - gcc = 'gcc' - clang = 'clang' - unknown = 'unknown' - - -class Env: - - cc: List[str] = [] - cppflags: List[str] = [] - cflags: List[str] = [] - ldflags: List[str] = [] - library_paths: Dict[str, List[str]] = {} - ldpaths: List[str] = [] - ccver: Tuple[int, int] - vcs_rev: str = '' - binary_arch: BinaryArch = BinaryArch() - native_optimizations: bool = False - primary_version: int = 0 - secondary_version: int = 0 - xt_version: str = '' - has_copy_file_range: bool = False - - # glfw stuff - all_headers: List[str] = [] - sources: List[str] = [] - wayland_packagedir: str = '' - wayland_scanner: str = '' - wayland_scanner_code: str = '' - wayland_protocols: Tuple[str, ...] = () - - def __init__( - self, cc: List[str] = [], cppflags: List[str] = [], cflags: List[str] = [], ldflags: List[str] = [], - library_paths: Dict[str, List[str]] = {}, ldpaths: Optional[List[str]] = None, ccver: Tuple[int, int] = (0, 0), - vcs_rev: str = '', binary_arch: BinaryArch = BinaryArch(), - native_optimizations: bool = False, - ): - self.cc, self.cppflags, self.cflags, self.ldflags, self.library_paths = cc, cppflags, cflags, ldflags, library_paths - self.ldpaths = ldpaths or [] - self.ccver = ccver - self.vcs_rev = vcs_rev - self.binary_arch = binary_arch - self.native_optimizations = native_optimizations - self._cc_version_string = '' - self._compiler_type: Optional[CompilerType] = None - - @property - def cc_version_string(self) -> str: - if not self._cc_version_string: - self._cc_version_string = subprocess.check_output(self.cc + ['--version']).decode() - return self._cc_version_string - - @property - def compiler_type(self) -> CompilerType: - if self._compiler_type is None: - raw = self.cc_version_string - if 'Free Software Foundation' in raw: - self._compiler_type = CompilerType.gcc - elif 'clang' in raw.lower().split(): - self._compiler_type = CompilerType.clang - else: - self._compiler_type = CompilerType.unknown - return self._compiler_type - - def copy(self) -> 'Env': - ans = Env(self.cc, list(self.cppflags), list(self.cflags), list(self.ldflags), dict(self.library_paths), list(self.ldpaths), self.ccver) - ans.all_headers = list(self.all_headers) - ans._cc_version_string = self._cc_version_string - ans.sources = list(self.sources) - ans.wayland_packagedir = self.wayland_packagedir - ans.wayland_scanner = self.wayland_scanner - ans.wayland_scanner_code = self.wayland_scanner_code - ans.wayland_protocols = self.wayland_protocols - ans.vcs_rev = self.vcs_rev - ans.binary_arch = self.binary_arch - ans.native_optimizations = self.native_optimizations - ans.primary_version = self.primary_version - ans.secondary_version = self.secondary_version - ans.xt_version = self.xt_version - ans.has_copy_file_range = self.has_copy_file_range - return ans - - -def wayland_protocol_file_name(base: str, ext: str = 'c') -> str: - base = os.path.basename(base).rpartition('.')[0] - return f'wayland-{base}-client-protocol.{ext}' - - -def init_env( - env: Env, - pkg_config: Callable[..., List[str]], - pkg_version: Callable[[str], Tuple[int, int]], - at_least_version: Callable[..., None], - test_compile: Callable[..., Any], - module: str = 'x11' -) -> Env: - ans = env.copy() - ans.cflags.append('-fPIC') - ans.cppflags.append(f'-D_GLFW_{module.upper()}') - ans.cppflags.append('-D_GLFW_BUILD_DLL') - - with open(os.path.join(base, 'source-info.json')) as f: - sinfo = json.load(f) - module_sources = list(sinfo[module]['sources']) - if module in ('x11', 'wayland'): - remove = 'null_joystick.c' if is_linux else 'linux_joystick.c' - module_sources.remove(remove) - - ans.sources = sinfo['common']['sources'] + module_sources - ans.all_headers = [x for x in os.listdir(base) if x.endswith('.h')] - - if module in ('x11', 'wayland'): - ans.cflags.append('-pthread') - ans.ldpaths.extend('-pthread -lm'.split()) - if not is_openbsd: - ans.ldpaths.extend('-lrt -ldl'.split()) - major, minor = pkg_version('xkbcommon') - if (major, minor) < (0, 5): - raise SystemExit('libxkbcommon >= 0.5 required') - if major < 1: - ans.cflags.append('-DXKB_HAS_NO_UTF32') - - if module == 'x11': - for dep in 'x11 xrandr xinerama xcursor xkbcommon xkbcommon-x11 x11-xcb dbus-1'.split(): - ans.cflags.extend(pkg_config(dep, '--cflags-only-I')) - ans.ldpaths.extend(pkg_config(dep, '--libs')) - - elif module == 'cocoa': - ans.cppflags.append('-DGL_SILENCE_DEPRECATION') - for f_ in 'Cocoa IOKit CoreFoundation CoreVideo QuartzCore UniformTypeIdentifiers'.split(): - ans.ldpaths.extend(('-framework', f_)) - - elif module == 'wayland': - at_least_version('wayland-protocols', *sinfo['wayland_protocols']) - ans.wayland_packagedir = os.path.abspath(pkg_config('wayland-protocols', '--variable=pkgdatadir')[0]) - ans.wayland_scanner = os.path.abspath(pkg_config('wayland-scanner', '--variable=wayland_scanner')[0]) - scanner_version = tuple(map(int, pkg_config('wayland-scanner', '--modversion')[0].strip().split('.'))) - ans.wayland_scanner_code = 'private-code' if scanner_version >= (1, 14, 91) else 'code' - ans.wayland_protocols = tuple(sinfo[module]['protocols']) - for p in ans.wayland_protocols: - ans.sources.append(wayland_protocol_file_name(p)) - ans.all_headers.append(wayland_protocol_file_name(p, 'h')) - for dep in 'wayland-client wayland-cursor xkbcommon dbus-1'.split(): - ans.cflags.extend(pkg_config(dep, '--cflags-only-I')) - ans.ldpaths.extend(pkg_config(dep, '--libs')) - has_memfd_create = test_compile(env.cc, '-Werror', src='''#define _GNU_SOURCE - #include - #include - int main(void) { - return syscall(__NR_memfd_create, "test", 0); - }''') - if has_memfd_create: - ans.cppflags.append('-DHAS_MEMFD_CREATE') - - return ans - - -def build_wayland_protocols( - env: Env, - parallel_run: Callable[[List[Command]], None], - emphasis: Callable[[str], str], - newer: Callable[..., bool], - dest_dir: str -) -> None: - items = [] - for protocol in env.wayland_protocols: - if '/' in protocol: - src = os.path.join(env.wayland_packagedir, protocol) - if not os.path.exists(src): - raise SystemExit(f'The wayland-protocols package on your system is missing the {protocol} protocol definition file') - else: - src = os.path.join(os.path.dirname(os.path.abspath(__file__)), protocol) - if not os.path.exists(src): - raise SystemExit(f'The local Wayland protocol {protocol} is missing from kitty sources') - for ext in 'hc': - dest = wayland_protocol_file_name(src, ext) - dest = os.path.join(dest_dir, dest) - if newer(dest, src): - q = 'client-header' if ext == 'h' else env.wayland_scanner_code - items.append(Command( - f'Generating {emphasis(os.path.basename(dest))} ...', - [env.wayland_scanner, q, src, dest], lambda: True)) - if items: - parallel_run(items) class Arg: diff --git a/kitty/shaders/slang.py b/kitty/shaders/slang.py index c9bb7fce8..17619824b 100644 --- a/kitty/shaders/slang.py +++ b/kitty/shaders/slang.py @@ -404,6 +404,8 @@ def create_specialisations(sources: dict[str, SlangFile], build_dir: str, dest_d def compile_builtin_shaders(build_dir: str, dest_dir: str, parallel_run: ParallelRun) -> None: + os.makedirs(build_dir, exist_ok=True) + os.makedirs(dest_dir, exist_ok=True) src_dir = os.path.abspath('kitty/shaders') source_tree = get_ordered_sources_in_tree(src_dir) # First ensure all IR is generated diff --git a/setup.py b/setup.py index 9a0d4b88a..0ea2d6051 100755 --- a/setup.py +++ b/setup.py @@ -18,14 +18,13 @@ import tempfile import textwrap import time from contextlib import suppress +from enum import Enum from functools import lru_cache, partial from pathlib import Path -from typing import Callable, Dict, FrozenSet, Iterable, Iterator, List, Optional, Sequence, Set, Tuple, Union, cast - -from glfw import glfw -from glfw.glfw import ISA, BinaryArch, Command, CompileKey, CompilerType +from typing import Any, Callable, Dict, FrozenSet, Iterable, Iterator, List, NamedTuple, Optional, Sequence, Set, Tuple, Union, cast src_base = os.path.dirname(os.path.abspath(__file__)) +glfw_base = os.path.join(src_base, 'glfw') setattr(sys, 'running_from_setup', True) def check_version_info() -> None: @@ -64,18 +63,133 @@ is_macos = 'darwin' in _plat is_openbsd = 'openbsd' in _plat is_freebsd = 'freebsd' in _plat is_netbsd = 'netbsd' in _plat +is_linux = 'linux' in _plat is_dragonflybsd = 'dragonfly' in _plat is_bsd = is_freebsd or is_netbsd or is_dragonflybsd or is_openbsd is_windows = sys.platform == 'win32' is_arm = platform.processor() == 'arm' or platform.machine() in ('arm64', 'aarch64') c_std = '' if is_openbsd else '-std=c11' -Env = glfw.Env -env = Env() PKGCONFIG = os.environ.get('PKGCONFIG_EXE', 'pkg-config') link_targets: List[str] = [] macos_universal_arches = ('arm64', 'x86_64') if is_arm else ('x86_64', 'arm64') +def null_func() -> None: + return None + + +class CompileKey(NamedTuple): + src: str + dest: str + + +class Command(NamedTuple): + desc: str + cmd: Sequence[str] + is_newer_func: Callable[[], bool] + on_success: Callable[[], None] = null_func + key: Optional[CompileKey] = None + keyfile: Optional[str] = None + + +class ISA(Enum): + X86 = 0x03 + AMD64 = 0x3e + ARM64 = 0xb7 + Other = 0x0 + + +class BinaryArch(NamedTuple): + bits: int = 64 + isa: ISA = ISA.AMD64 + + +class CompilerType(Enum): + gcc = 'gcc' + clang = 'clang' + unknown = 'unknown' + + +class Env: + + cc: List[str] = [] + cppflags: List[str] = [] + cflags: List[str] = [] + ldflags: List[str] = [] + library_paths: Dict[str, List[str]] = {} + ldpaths: List[str] = [] + ccver: Tuple[int, int] + vcs_rev: str = '' + binary_arch: BinaryArch = BinaryArch() + native_optimizations: bool = False + primary_version: int = 0 + secondary_version: int = 0 + xt_version: str = '' + has_copy_file_range: bool = False + + # glfw stuff + all_headers: List[str] = [] + sources: List[str] = [] + wayland_packagedir: str = '' + wayland_scanner: str = '' + wayland_scanner_code: str = '' + wayland_protocols: Tuple[str, ...] = () + + def __init__( + self, cc: List[str] = [], cppflags: List[str] = [], cflags: List[str] = [], ldflags: List[str] = [], + library_paths: Dict[str, List[str]] = {}, ldpaths: Optional[List[str]] = None, ccver: Tuple[int, int] = (0, 0), + vcs_rev: str = '', binary_arch: BinaryArch = BinaryArch(), + native_optimizations: bool = False, + ): + self.cc, self.cppflags, self.cflags, self.ldflags, self.library_paths = cc, cppflags, cflags, ldflags, library_paths + self.ldpaths = ldpaths or [] + self.ccver = ccver + self.vcs_rev = vcs_rev + self.binary_arch = binary_arch + self.native_optimizations = native_optimizations + self._cc_version_string = '' + self._compiler_type: Optional[CompilerType] = None + + @property + def cc_version_string(self) -> str: + if not self._cc_version_string: + self._cc_version_string = subprocess.check_output(self.cc + ['--version']).decode() + return self._cc_version_string + + @property + def compiler_type(self) -> CompilerType: + if self._compiler_type is None: + raw = self.cc_version_string + if 'Free Software Foundation' in raw: + self._compiler_type = CompilerType.gcc + elif 'clang' in raw.lower().split(): + self._compiler_type = CompilerType.clang + else: + self._compiler_type = CompilerType.unknown + return self._compiler_type + + def copy(self) -> 'Env': + ans = Env(self.cc, list(self.cppflags), list(self.cflags), list(self.ldflags), dict(self.library_paths), list(self.ldpaths), self.ccver) + ans.all_headers = list(self.all_headers) + ans._cc_version_string = self._cc_version_string + ans.sources = list(self.sources) + ans.wayland_packagedir = self.wayland_packagedir + ans.wayland_scanner = self.wayland_scanner + ans.wayland_scanner_code = self.wayland_scanner_code + ans.wayland_protocols = self.wayland_protocols + ans.vcs_rev = self.vcs_rev + ans.binary_arch = self.binary_arch + ans.native_optimizations = self.native_optimizations + ans.primary_version = self.primary_version + ans.secondary_version = self.secondary_version + ans.xt_version = self.xt_version + ans.has_copy_file_range = self.has_copy_file_range + return ans + + +env = Env() + + def LinkKey(output: str) -> CompileKey: return CompileKey('', output) @@ -1037,11 +1151,114 @@ def find_c_files() -> Tuple[List[str], List[str]]: return ans, headers +def wayland_protocol_file_name(base: str, ext: str = 'c') -> str: + base = os.path.basename(base).rpartition('.')[0] + return f'wayland-{base}-client-protocol.{ext}' + + +def glfw_init_env( + env: Env, + pkg_config: Callable[..., List[str]], + pkg_version: Callable[[str], Tuple[int, int]], + at_least_version: Callable[..., None], + test_compile: Callable[..., Any], + module: str = 'x11' +) -> Env: + ans = env.copy() + ans.cflags.append('-fPIC') + ans.cppflags.append(f'-D_GLFW_{module.upper()}') + ans.cppflags.append('-D_GLFW_BUILD_DLL') + + with open(os.path.join(glfw_base, 'source-info.json')) as f: + sinfo = json.load(f) + module_sources = list(sinfo[module]['sources']) + if module in ('x11', 'wayland'): + remove = 'null_joystick.c' if is_linux else 'linux_joystick.c' + module_sources.remove(remove) + + ans.sources = sinfo['common']['sources'] + module_sources + ans.all_headers = [x for x in os.listdir(glfw_base) if x.endswith('.h')] + + if module in ('x11', 'wayland'): + ans.cflags.append('-pthread') + ans.ldpaths.extend('-pthread -lm'.split()) + if not is_openbsd: + ans.ldpaths.extend('-lrt -ldl'.split()) + major, minor = pkg_version('xkbcommon') + if (major, minor) < (0, 5): + raise SystemExit('libxkbcommon >= 0.5 required') + if major < 1: + ans.cflags.append('-DXKB_HAS_NO_UTF32') + + if module == 'x11': + for dep in 'x11 xrandr xinerama xcursor xkbcommon xkbcommon-x11 x11-xcb dbus-1'.split(): + ans.cflags.extend(pkg_config(dep, '--cflags-only-I')) + ans.ldpaths.extend(pkg_config(dep, '--libs')) + + elif module == 'cocoa': + ans.cppflags.append('-DGL_SILENCE_DEPRECATION') + for f_ in 'Cocoa IOKit CoreFoundation CoreVideo QuartzCore UniformTypeIdentifiers'.split(): + ans.ldpaths.extend(('-framework', f_)) + + elif module == 'wayland': + at_least_version('wayland-protocols', *sinfo['wayland_protocols']) + ans.wayland_packagedir = os.path.abspath(pkg_config('wayland-protocols', '--variable=pkgdatadir')[0]) + ans.wayland_scanner = os.path.abspath(pkg_config('wayland-scanner', '--variable=wayland_scanner')[0]) + scanner_version = tuple(map(int, pkg_config('wayland-scanner', '--modversion')[0].strip().split('.'))) + ans.wayland_scanner_code = 'private-code' if scanner_version >= (1, 14, 91) else 'code' + ans.wayland_protocols = tuple(sinfo[module]['protocols']) + for p in ans.wayland_protocols: + ans.sources.append(wayland_protocol_file_name(p)) + ans.all_headers.append(wayland_protocol_file_name(p, 'h')) + for dep in 'wayland-client wayland-cursor xkbcommon dbus-1'.split(): + ans.cflags.extend(pkg_config(dep, '--cflags-only-I')) + ans.ldpaths.extend(pkg_config(dep, '--libs')) + has_memfd_create = test_compile(env.cc, '-Werror', src='''#define _GNU_SOURCE + #include + #include + int main(void) { + return syscall(__NR_memfd_create, "test", 0); + }''') + if has_memfd_create: + ans.cppflags.append('-DHAS_MEMFD_CREATE') + + return ans + + +def build_wayland_protocols( + env: Env, + parallel_run: Callable[[List[Command]], None], + emphasis: Callable[[str], str], + newer: Callable[..., bool], + dest_dir: str +) -> None: + items = [] + for protocol in env.wayland_protocols: + if '/' in protocol: + src = os.path.join(env.wayland_packagedir, protocol) + if not os.path.exists(src): + raise SystemExit(f'The wayland-protocols package on your system is missing the {protocol} protocol definition file') + else: + src = os.path.join(glfw_base, protocol) + if not os.path.exists(src): + raise SystemExit(f'The local Wayland protocol {protocol} is missing from kitty sources') + for ext in 'hc': + dest = wayland_protocol_file_name(src, ext) + dest = os.path.join(dest_dir, dest) + if newer(dest, src): + q = 'client-header' if ext == 'h' else env.wayland_scanner_code + items.append(Command( + f'Generating {emphasis(os.path.basename(dest))} ...', + [env.wayland_scanner, q, src, dest], lambda: True)) + if items: + parallel_run(items) + + def compile_glfw(compilation_database: CompilationDatabase, build_dsym: bool = False) -> None: modules = 'cocoa' if is_macos else 'x11 wayland' for module in modules.split(): try: - genv = glfw.init_env(env, pkg_config, pkg_version, at_least_version, test_compile, module) + genv = glfw_init_env(env, pkg_config, pkg_version, at_least_version, test_compile, module) except SystemExit as err: if module != 'wayland': raise @@ -1052,7 +1269,7 @@ def compile_glfw(compilation_database: CompilationDatabase, build_dsym: bool = F all_headers = [os.path.join('glfw', x) for x in genv.all_headers] if module == 'wayland': try: - glfw.build_wayland_protocols(genv, parallel_run, emphasis, newer, 'glfw') + build_wayland_protocols(genv, parallel_run, emphasis, newer, 'glfw') except SystemExit as err: print(err, file=sys.stderr) print(error('Disabling building of wayland backend'), file=sys.stderr) @@ -1893,8 +2110,7 @@ def package(args: Options, bundle_type: str, do_build_all: bool = True) -> None: shutil.copy2('logo/beam-cursor@2x.png', os.path.join(libdir, 'logo')) shutil.copytree('shell-integration', os.path.join(libdir, 'shell-integration'), dirs_exist_ok=True) shutil.copytree('fonts', os.path.join(libdir, 'fonts'), dirs_exist_ok=True) - shutil.copytree('shaders', os.path.join(libdir, 'shaders'), dirs_exist_ok=True) - allowed_extensions = frozenset('py glsl so'.split()) + allowed_extensions = frozenset('py slang glsl so'.split()) def src_ignore(parent: str, entries: Iterable[str]) -> List[str]: return [ @@ -1943,6 +2159,7 @@ def package(args: Options, bundle_type: str, do_build_all: bool = True) -> None: os.chmod(path, 0o755 if should_be_executable(path) else 0o644) if not for_freeze and not bundle_type.startswith('macos-'): build_static_kittens(args, launcher_dir=launcher_dir) + shutil.copytree('shaders', os.path.join(libdir, 'shaders'), dirs_exist_ok=True) if not is_macos and not is_windows: create_linux_bundle_gunk(ddir, args)