Get freezing of slang on linux working

This commit is contained in:
Kovid Goyal 2026-06-30 11:41:30 +05:30
parent 51c2c82d02
commit d6a2af7ac6
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
6 changed files with 55 additions and 14 deletions

View file

@ -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'))

View file

@ -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)

View file

@ -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')

View file

@ -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()

View file

@ -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():

View file

@ -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)