mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-27 10:41:58 +02:00
Use the slangc binary instead trying to get the C++ extension working everywhere is too fragile
This commit is contained in:
18
.github/workflows/ci.py
vendored
18
.github/workflows/ci.py
vendored
@@ -25,6 +25,7 @@ is_codeql = os.environ.get('KITTY_CODEQL') == '1'
|
|||||||
is_macos = 'darwin' in sys.platform.lower()
|
is_macos = 'darwin' in sys.platform.lower()
|
||||||
running_under_sanitizer = os.environ.get('KITTY_SANITIZE') == '1'
|
running_under_sanitizer = os.environ.get('KITTY_SANITIZE') == '1'
|
||||||
SW = ''
|
SW = ''
|
||||||
|
SLANG_INSTALL_DIR = '/tmp/slang'
|
||||||
|
|
||||||
|
|
||||||
def do_print_crash_reports() -> None:
|
def do_print_crash_reports() -> None:
|
||||||
@@ -131,7 +132,7 @@ def install_slang_compiler() -> None:
|
|||||||
if url is None:
|
if url is None:
|
||||||
raise SystemExit(f'Could not find slang release asset: {asset_name}')
|
raise SystemExit(f'Could not find slang release asset: {asset_name}')
|
||||||
|
|
||||||
install_dir = '/tmp/slang'
|
install_dir = SLANG_INSTALL_DIR
|
||||||
os.makedirs(install_dir, exist_ok=True)
|
os.makedirs(install_dir, exist_ok=True)
|
||||||
data = download_with_retry(url)
|
data = download_with_retry(url)
|
||||||
with tarfile.open(fileobj=io.BytesIO(data), mode='r:gz') as tf:
|
with tarfile.open(fileobj=io.BytesIO(data), mode='r:gz') as tf:
|
||||||
@@ -187,12 +188,27 @@ def build_kitty() -> None:
|
|||||||
run(cmd)
|
run(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
def add_to_path(path: str, prepend: bool = False) -> None:
|
||||||
|
if existing := os.environ.get('PATH') or '':
|
||||||
|
parts = existing.split(os.pathsep)
|
||||||
|
parts.insert(0 if prepend else len(parts), path)
|
||||||
|
seen = set()
|
||||||
|
ans = []
|
||||||
|
for x in parts:
|
||||||
|
if x not in seen:
|
||||||
|
seen.add(x)
|
||||||
|
ans.append(x)
|
||||||
|
path = os.pathsep.join(ans)
|
||||||
|
os.environ['PATH'] = path
|
||||||
|
|
||||||
|
|
||||||
def test_kitty() -> None:
|
def test_kitty() -> None:
|
||||||
if is_macos:
|
if is_macos:
|
||||||
run('ulimit -c unlimited')
|
run('ulimit -c unlimited')
|
||||||
run('sudo chmod -R 777 /cores')
|
run('sudo chmod -R 777 /cores')
|
||||||
if running_under_sanitizer:
|
if running_under_sanitizer:
|
||||||
os.environ['MallocNanoZone'] = '0'
|
os.environ['MallocNanoZone'] = '0'
|
||||||
|
add_to_path(os.path.join(SW if is_bundle else SLANG_INSTALL_DIR, 'bin'))
|
||||||
run('./test.py', print_crash_reports=True)
|
run('./test.py', print_crash_reports=True)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ kitty_run_data: dict[str, Any] = getattr(sys, 'kitty_run_data', {})
|
|||||||
launched_by_launch_services = kitty_run_data.get('launched_by_launch_services', False)
|
launched_by_launch_services = kitty_run_data.get('launched_by_launch_services', False)
|
||||||
is_quick_access_terminal_app = kitty_run_data.get('is_quick_access_terminal_app', False)
|
is_quick_access_terminal_app = kitty_run_data.get('is_quick_access_terminal_app', False)
|
||||||
unserialize_launch_flag = 'kitty-unserialize-data='
|
unserialize_launch_flag = 'kitty-unserialize-data='
|
||||||
|
slangc = ['slang']
|
||||||
|
|
||||||
|
|
||||||
if getattr(sys, 'frozen', False):
|
if getattr(sys, 'frozen', False):
|
||||||
extensions_dir: str = kitty_run_data['extensions_dir']
|
extensions_dir: str = kitty_run_data['extensions_dir']
|
||||||
|
|||||||
@@ -1,194 +0,0 @@
|
|||||||
/*
|
|
||||||
* compiler.cpp
|
|
||||||
* Copyright (C) 2026 Kovid Goyal <kovid at kovidgoyal.net>
|
|
||||||
*
|
|
||||||
* Distributed under terms of the GPL3 license.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <Python.h>
|
|
||||||
#if __has_include("shader-slang/slang.h")
|
|
||||||
#include <shader-slang/slang.h>
|
|
||||||
#include <shader-slang/slang-com-ptr.h>
|
|
||||||
#include <shader-slang/slang-com-helper.h>
|
|
||||||
#else
|
|
||||||
#include <slang.h>
|
|
||||||
#include <slang-com-ptr.h>
|
|
||||||
#include <slang-com-helper.h>
|
|
||||||
#endif
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
using namespace slang;
|
|
||||||
|
|
||||||
class ScopedPyObject { // {{{
|
|
||||||
private:
|
|
||||||
PyObject* ptr;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Default constructor
|
|
||||||
ScopedPyObject() : ptr(nullptr) {}
|
|
||||||
|
|
||||||
// Constructor that takes ownership of a PyObject*
|
|
||||||
// Set 'is_strong_ref' to false if you are passing a borrowed reference
|
|
||||||
explicit ScopedPyObject(PyObject* p, bool is_strong_ref = true) : ptr(p) {
|
|
||||||
if (!is_strong_ref && ptr) {
|
|
||||||
Py_INCREF(ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destructor automatically decrements the reference count
|
|
||||||
~ScopedPyObject() {
|
|
||||||
Py_XDECREF(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete copy operations to prevent accidental double-decref
|
|
||||||
ScopedPyObject(const ScopedPyObject&) = delete;
|
|
||||||
ScopedPyObject& operator=(const ScopedPyObject&) = delete;
|
|
||||||
|
|
||||||
// Move constructor transfers ownership
|
|
||||||
ScopedPyObject(ScopedPyObject&& other) noexcept : ptr(other.ptr) {
|
|
||||||
other.ptr = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move assignment operator
|
|
||||||
ScopedPyObject& operator=(ScopedPyObject&& other) noexcept {
|
|
||||||
if (this != &other) {
|
|
||||||
Py_XDECREF(ptr);
|
|
||||||
ptr = other.ptr;
|
|
||||||
other.ptr = nullptr;
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow assignment directly from a raw PyObject* (assumes strong reference)
|
|
||||||
ScopedPyObject& operator=(PyObject* p) {
|
|
||||||
Py_XDECREF(ptr);
|
|
||||||
ptr = p;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Smart pointer operators
|
|
||||||
PyObject* get() const { return ptr; }
|
|
||||||
PyObject* operator->() const { return ptr; }
|
|
||||||
explicit operator bool() const { return ptr != nullptr; }
|
|
||||||
|
|
||||||
// Release ownership without changing the ref count (returns raw pointer)
|
|
||||||
PyObject* release() {
|
|
||||||
PyObject* temp = ptr;
|
|
||||||
ptr = nullptr;
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Safely reset to a new raw pointer
|
|
||||||
void reset(PyObject* p = nullptr, bool is_strong_ref = true) {
|
|
||||||
Py_XDECREF(ptr);
|
|
||||||
ptr = p;
|
|
||||||
if (!is_strong_ref && ptr) {
|
|
||||||
Py_INCREF(ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}; // }}}
|
|
||||||
|
|
||||||
typedef struct GlobalSession {
|
|
||||||
PyObject_HEAD
|
|
||||||
|
|
||||||
Slang::ComPtr<IGlobalSession> ptr;
|
|
||||||
} GlobalSession;
|
|
||||||
|
|
||||||
static std::string
|
|
||||||
get_slang_result_string(SlangResult result) {
|
|
||||||
switch (result) {
|
|
||||||
case SLANG_OK: return "SLANG_OK: Operation succeeded.";
|
|
||||||
case SLANG_FAIL: return "SLANG_FAIL: Generic operational failure.";
|
|
||||||
case SLANG_E_NOT_AVAILABLE: return "SLANG_E_NOT_AVAILABLE: The requested feature or interface is not available.";
|
|
||||||
case SLANG_E_NOT_IMPLEMENTED: return "SLANG_E_NOT_IMPLEMENTED: The feature has not been implemented.";
|
|
||||||
case SLANG_E_INVALID_ARG: return "SLANG_E_INVALID_ARG: One or more arguments are invalid.";
|
|
||||||
case SLANG_E_OUT_OF_MEMORY: return "SLANG_E_OUT_OF_MEMORY: The compiler ran out of memory.";
|
|
||||||
case SLANG_E_BUFFER_TOO_SMALL: return "SLANG_E_BUFFER_TOO_SMALL: The destination buffer is too small to hold the data.";
|
|
||||||
case SLANG_E_UNINITIALIZED: return "SLANG_E_UNINITIALIZED: A component or object was used without initialization.";
|
|
||||||
case SLANG_E_TIME_OUT: return "SLANG_E_TIME_OUT: The compilation or operation timed out.";
|
|
||||||
// Internal status codes often returned by compile steps
|
|
||||||
default:
|
|
||||||
if (result < 0) return "SLANG_ERROR_UNKNOWN: Code (0x" + std::to_string(result) + ")";
|
|
||||||
return "SLANG_STATUS_UNKNOWN: Code (0x" + std::to_string(result) + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject *Error = nullptr;
|
|
||||||
|
|
||||||
static void
|
|
||||||
set_python_error(SlangResult r, const char *msg, PyObject *exc_class = nullptr) {
|
|
||||||
if (exc_class == nullptr) exc_class = Error;
|
|
||||||
PyErr_Format(exc_class, "%s: %s", msg, get_slang_result_string(r).c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject*
|
|
||||||
new_gs(PyTypeObject *type, PyObject *args, PyObject *kwds) {
|
|
||||||
GlobalSession *self;
|
|
||||||
static const char* kw[] = {"enable_glsl_input", NULL};
|
|
||||||
int enable_glsl_input = 0;
|
|
||||||
if (args && !PyArg_ParseTupleAndKeywords(args, kwds, "|p", const_cast<char**>(kw), &enable_glsl_input)) return NULL;
|
|
||||||
self = reinterpret_cast<GlobalSession *>(type->tp_alloc(type, 0));
|
|
||||||
ScopedPyObject ans(reinterpret_cast<PyObject*>(self));
|
|
||||||
if (self != NULL) {
|
|
||||||
self->ptr = nullptr;
|
|
||||||
SlangGlobalSessionDesc desc = {.enableGLSL=static_cast<bool>(enable_glsl_input)};
|
|
||||||
SlangResult result = createGlobalSession(&desc, self->ptr.writeRef());
|
|
||||||
if (SLANG_FAILED(result)) {
|
|
||||||
set_python_error(result, "failed to create slang global session");
|
|
||||||
ans.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ans.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
dealloc_gs(GlobalSession* self) {
|
|
||||||
self->ptr = nullptr;
|
|
||||||
Py_TYPE(self)->tp_free((PyObject*)self);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyMethodDef gs_methods[] = {
|
|
||||||
{NULL} /* Sentinel */
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
PyTypeObject GlobalSession_Type = {
|
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
|
||||||
};
|
|
||||||
|
|
||||||
static char doc[] = "Compile shaders";
|
|
||||||
static PyMethodDef methods[] = {
|
|
||||||
{NULL} /* Sentinel */
|
|
||||||
};
|
|
||||||
|
|
||||||
static int
|
|
||||||
exec_module(PyObject *mod) {
|
|
||||||
Error = PyErr_NewException("slangc.Error", NULL, NULL);
|
|
||||||
if (Error == nullptr) return -1;
|
|
||||||
GlobalSession_Type.tp_name = "slangc.GlobalSession";
|
|
||||||
GlobalSession_Type.tp_basicsize = sizeof(GlobalSession);
|
|
||||||
GlobalSession_Type.tp_dealloc = (destructor)dealloc_gs;
|
|
||||||
GlobalSession_Type.tp_flags = Py_TPFLAGS_DEFAULT;
|
|
||||||
GlobalSession_Type.tp_doc = "GlobalSession";
|
|
||||||
GlobalSession_Type.tp_methods = gs_methods;
|
|
||||||
GlobalSession_Type.tp_new = new_gs;
|
|
||||||
if (PyType_Ready(&GlobalSession_Type) < 0) { return -1; }
|
|
||||||
if (PyModule_AddObject(mod, "GlobalSession", (PyObject *)&GlobalSession_Type) != 0) return -1;
|
|
||||||
Py_INCREF(&GlobalSession_Type);
|
|
||||||
if (PyModule_AddObject(mod, "Error", Error) < 0) { return -1; }
|
|
||||||
Py_INCREF(Error);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyModuleDef_Slot slots[] = { {Py_mod_exec, (void*)exec_module}, {0, NULL} };
|
|
||||||
|
|
||||||
static struct PyModuleDef module_def = {PyModuleDef_HEAD_INIT};
|
|
||||||
|
|
||||||
PyMODINIT_FUNC
|
|
||||||
PyInit_slangc(void) {
|
|
||||||
module_def.m_name = "slangc";
|
|
||||||
module_def.m_slots = slots;
|
|
||||||
module_def.m_doc = doc;
|
|
||||||
module_def.m_methods = methods;
|
|
||||||
return PyModuleDef_Init(&module_def);
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
class Error(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class GlobalSession:
|
|
||||||
def __init__(self, enable_glsl_input: bool = False): ...
|
|
||||||
@@ -19,7 +19,7 @@ from . import BaseTest
|
|||||||
class TestBuild(BaseTest):
|
class TestBuild(BaseTest):
|
||||||
|
|
||||||
def test_exe(self) -> None:
|
def test_exe(self) -> None:
|
||||||
from kitty.constants import kitten_exe, kitty_exe, str_version
|
from kitty.constants import kitten_exe, kitty_exe, slangc, str_version
|
||||||
exe = kitty_exe()
|
exe = kitty_exe()
|
||||||
self.assertTrue(os.access(exe, os.X_OK))
|
self.assertTrue(os.access(exe, os.X_OK))
|
||||||
self.assertTrue(os.path.isfile(exe))
|
self.assertTrue(os.path.isfile(exe))
|
||||||
@@ -28,12 +28,11 @@ class TestBuild(BaseTest):
|
|||||||
self.assertTrue(os.access(exe, os.X_OK))
|
self.assertTrue(os.access(exe, os.X_OK))
|
||||||
self.assertTrue(os.path.isfile(exe))
|
self.assertTrue(os.path.isfile(exe))
|
||||||
self.assertIn(str_version, subprocess.check_output([exe, '--version']).decode())
|
self.assertIn(str_version, subprocess.check_output([exe, '--version']).decode())
|
||||||
|
self.assertTrue(shutil.which(slangc[0]), f'slang compiler not found on PATH: {slangc[0]}')
|
||||||
|
|
||||||
def test_loading_extensions(self) -> None:
|
def test_loading_extensions(self) -> None:
|
||||||
import kitty.fast_data_types as fdt
|
import kitty.fast_data_types as fdt
|
||||||
from kittens.transfer import rsync
|
from kittens.transfer import rsync
|
||||||
from kitty.slangc import GlobalSession
|
|
||||||
GlobalSession()
|
|
||||||
del fdt, rsync
|
del fdt, rsync
|
||||||
|
|
||||||
def test_loading_shaders(self) -> None:
|
def test_loading_shaders(self) -> None:
|
||||||
|
|||||||
20
setup.py
20
setup.py
@@ -630,22 +630,6 @@ def init_env(
|
|||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
def slang_env(args: Options) -> Env:
|
|
||||||
ans = env.copy()
|
|
||||||
cflags = []
|
|
||||||
for x in ans.cflags:
|
|
||||||
if x == '-Wstrict-prototypes':
|
|
||||||
continue
|
|
||||||
if x.startswith('-std='):
|
|
||||||
x = '-std=c++20'
|
|
||||||
cflags.append(x)
|
|
||||||
cflags[:0] = pkg_config('slang-compiler', '--cflags-only-I')
|
|
||||||
pylib = get_python_flags(args, cflags)
|
|
||||||
ans.cflags = cflags
|
|
||||||
ans.ldflags = ['-lstdc++'] + pylib + ans.ldflags + pkg_config('slang-compiler', '--libs')
|
|
||||||
return ans
|
|
||||||
|
|
||||||
|
|
||||||
def kitty_env(args: Options) -> Env:
|
def kitty_env(args: Options) -> Env:
|
||||||
ans = env.copy()
|
ans = env.copy()
|
||||||
cflags = ans.cflags
|
cflags = ans.cflags
|
||||||
@@ -1232,10 +1216,6 @@ def build(args: Options, native_optimizations: bool = True, call_init: bool = Tr
|
|||||||
kitty_env(args), 'kitty/fast_data_types', args.compilation_database, sources, headers,
|
kitty_env(args), 'kitty/fast_data_types', args.compilation_database, sources, headers,
|
||||||
build_dsym=args.build_dsym,
|
build_dsym=args.build_dsym,
|
||||||
)
|
)
|
||||||
sources, headers = ['kitty/shaders/compiler.cpp'], []
|
|
||||||
compile_c_extension(
|
|
||||||
slang_env(args), 'kitty/slangc', args.compilation_database, sources, headers, build_dsym=args.build_dsym
|
|
||||||
)
|
|
||||||
compile_glfw(args.compilation_database, args.build_dsym)
|
compile_glfw(args.compilation_database, args.build_dsym)
|
||||||
compile_kittens(args)
|
compile_kittens(args)
|
||||||
add_builtin_fonts(args)
|
add_builtin_fonts(args)
|
||||||
|
|||||||
Reference in New Issue
Block a user