From d6a2af7ac6eef3c53410609fcdf5e43abd3df0e3 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 30 Jun 2026 11:41:30 +0530 Subject: [PATCH] Get freezing of slang on linux working --- bypy/init_env.py | 3 ++- bypy/linux/__main__.py | 16 ++++++++++------ kitty/constants.py | 4 +++- kitty/shaders/slang.py | 27 ++++++++++++++++++++++++++- kitty_tests/check_build.py | 4 ++++ setup.py | 15 ++++++++++----- 6 files changed, 55 insertions(+), 14 deletions(-) diff --git a/bypy/init_env.py b/bypy/init_env.py index c676fbc82..572d8763c 100644 --- a/bypy/init_env.py +++ b/bypy/init_env.py @@ -90,7 +90,7 @@ def build_frozen_tools(kitty_exe): def sanitize_source_folder(path: str) -> None: for q in walk(path): - if os.path.splitext(q)[1] not in ('.py', '.glsl', '.ttf', '.otf', '.json'): + if os.path.splitext(q)[1] not in ('.py', '.glsl', '.slang', '.ttf', '.otf', '.json'): os.unlink(q) @@ -100,6 +100,7 @@ def build_c_extensions(ext_dir, args): shutil.copytree( KITTY_DIR, writeable_src_dir, symlinks=True, ignore=shutil.ignore_patterns('b', 'build', 'dist', '*_commands.json', '*.o', '*.so', '*.dylib', '*.pyd')) + shutil.rmtree(os.path.join(writeable_src_dir, 'shaders')) with suppress(FileNotFoundError): os.unlink(os.path.join(writeable_src_dir, 'kitty', 'launcher', 'kitty')) diff --git a/bypy/linux/__main__.py b/bypy/linux/__main__.py index 777d7dd5b..b1027799c 100644 --- a/bypy/linux/__main__.py +++ b/bypy/linux/__main__.py @@ -10,7 +10,7 @@ import subprocess import tarfile import time -from bypy.constants import OUTPUT_DIR, PREFIX, python_major_minor_version +from bypy.constants import LIBDIR, BIN, OUTPUT_DIR, PREFIX, python_major_minor_version from bypy.freeze import extract_extension_modules, freeze_python, path_to_freeze_dir from bypy.utils import get_dll_path, mkdtemp, py_compile, walk @@ -90,6 +90,14 @@ def copy_libs(env): shutil.copy2(x, dest) dest = os.path.join(dest, os.path.basename(x)) subprocess.check_call(['chrpath', '-d', dest]) + # Copy slangc + for x in ('compiler', 'rt'): + x = f'libslang-{x}.so' + shutil.copy2(os.path.join(LIBDIR, f'{x}.0.0.0.0'), env.lib_dir) + os.symlink(f'{x}.0.0.0.0', os.path.join(env.lib_dir, x)) + shutil.copy2(os.path.join(LIBDIR, 'libslang-glsl-module-0.0.0.so'), env.lib_dir) + shutil.copy2(os.path.join(LIBDIR, 'libslang-glslang-0.0.0.so'), env.lib_dir) + shutil.copy2(os.path.join(BIN, 'slangc'), env.bin_dir) def add_ca_certs(env): @@ -138,10 +146,6 @@ def copy_python(env): shutil.rmtree(env.py_dir) -def build_launcher(env): - iv['build_frozen_launcher']([path_to_freeze_dir(), env.obj_dir]) - - def is_elf(path): with open(path, 'rb') as f: return f.read(4) == b'\x7fELF' @@ -228,7 +232,7 @@ def main(): env = Env(os.path.join(ext_dir, kitty_constants['appname'])) copy_libs(env) copy_python(env) - build_launcher(env) + iv['build_frozen_launcher']([path_to_freeze_dir(), env.obj_dir]) files = find_binaries(env) fix_permissions(files) add_ca_certs(env) diff --git a/kitty/constants.py b/kitty/constants.py index 891168b1d..77efd6eef 100644 --- a/kitty/constants.py +++ b/kitty/constants.py @@ -63,7 +63,9 @@ if getattr(sys, 'frozen', False): ans = os.path.join(ans, 'kitty') return ans kitty_base_dir = get_frozen_base() - del get_frozen_base + if rpath := kitty_run_data.get('bundle_exe_dir'): + slangc = [os.path.join(rpath, 'slangc')] + del get_frozen_base, rpath else: kitty_base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) extensions_dir = os.path.join(kitty_base_dir, 'kitty') diff --git a/kitty/shaders/slang.py b/kitty/shaders/slang.py index 7b58f0310..2603122f4 100644 --- a/kitty/shaders/slang.py +++ b/kitty/shaders/slang.py @@ -16,7 +16,7 @@ from pathlib import Path from types import MappingProxyType from typing import Callable, Iterable, Iterator, NamedTuple -from kitty.constants import slangc +from kitty.constants import read_kitty_resource, shaders_dir, slangc from kitty.fast_data_types import ( BLINK, COLOR_IS_INDEX, @@ -35,6 +35,11 @@ from kitty.fast_data_types import ( from kitty.options.types import defaults +@lru_cache(maxsize=64) +def get_shader_src(name: str) -> str: + return read_kitty_resource(f'{name}.slang', 'kitty.shaders').decode() + + class Stage(Enum): vertex = 'vertex' fragment = 'fragment' @@ -442,5 +447,25 @@ def main() -> None: compile_builtin_shaders(sys.argv[-2], sys.argv[-1], prun) +def test_slang_build() -> None: + import subprocess + if shutil.which(slangc[0]) is None: + raise AssertionError(f'The shader slang compiler ({slangc[0]}) not in PATH: {os.environ.get("PATH")}') + q = os.path.join(shaders_dir, 'graphics.spv') + if not os.path.isfile(q): + raise AssertionError(f'The compiled graphics shader {q} does not exist') + if not get_shader_src('graphics'): + raise AssertionError('Could not load graphics.slang shader source') + src = b''' +#language slang 2026 +[shader("vertex")] +float4 main(uint vertex_id : SV_VertexID) : SV_Position { return float4(vertex_id, 1, 0, 1); } +''' + cp = subprocess.run(slangc + '-lang slang -entry main -stage vertex -target glsl -o /dev/stdout -- -'.split(), + input=src, capture_output=True) + if cp.returncode != 0: + raise AssertionError(f'Test compile of shader to GLSL failed with returncode: {cp.returncode} and stderr: {cp.stderr.decode()}') + + if __name__ == '__main__': main() diff --git a/kitty_tests/check_build.py b/kitty_tests/check_build.py index 8e734f25e..53d7189b8 100644 --- a/kitty_tests/check_build.py +++ b/kitty_tests/check_build.py @@ -35,6 +35,10 @@ class TestBuild(BaseTest): from kittens.transfer import rsync del fdt, rsync + def test_slang_build(self) -> None: + from kitty.shaders.slang import test_slang_build + test_slang_build() + def test_loading_shaders(self) -> None: from kitty.shaders.legacy import Program for name in 'cell border bgimage tint graphics'.split(): diff --git a/setup.py b/setup.py index 0ea2d6051..9d8cbd0ca 100755 --- a/setup.py +++ b/setup.py @@ -1421,7 +1421,7 @@ def wrapped_kittens() -> str: raise Exception('Failed to read wrapped kittens from kitty wrapper script') -def build_shaders(args: Options, kitty_exe: str) -> None: +def build_shaders(args: Options, kitty_exe: str, for_freeze: bool) -> None: if args.skip_code_generation: print('Skipping building of shaders due to command line option', flush=True) return @@ -1434,6 +1434,9 @@ def build_shaders(args: Options, kitty_exe: str) -> None: if os.environ.get('CI') == 'true' and cp.returncode < 0 and shutil.which('coredumpctl'): subprocess.run(['sh', '-c', 'echo bt | coredumpctl debug']) raise SystemExit(f'Generating shaders failed with exit code: {cp.returncode}') + if for_freeze: + libdir = os.path.join(os.path.dirname(kitty_exe), '..', 'lib', 'kitty') + shutil.copytree('shaders', os.path.join(libdir, 'shaders'), dirs_exist_ok=True) def build(args: Options, native_optimizations: bool = True, call_init: bool = True) -> None: @@ -1512,7 +1515,8 @@ def build_static_kittens( raise SystemExit(f'The version of go on this system ({current_go_version}) is too old. go >= {required_go_version} is needed') if not for_platform: update_go_generated_files(args, os.path.join(launcher_dir, appname)) - build_shaders(args, os.path.join(launcher_dir, appname)) + build_shaders(args, os.path.join(launcher_dir, appname), for_freeze) + if args.skip_building_kitten: print('Skipping building of the kitten binary because of a command line option. Build is incomplete', file=sys.stderr) return '' @@ -2157,9 +2161,10 @@ def package(args: Options, bundle_type: str, do_build_all: bool = True) -> None: for f_ in files: path = os.path.join(root, f_) 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 for_freeze: + if 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)