From eccf0ca54e38b11108c2170ae451a75083cbe64b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 31 Oct 2016 14:45:49 +0530 Subject: [PATCH] Infrastructure for building C extensions --- .gitignore | 2 ++ kitty/data-types.c | 32 +++++++++++++++++++++ setup.py | 70 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 kitty/data-types.c create mode 100644 setup.py diff --git a/.gitignore b/.gitignore index 4a39caf7b..d5211a006 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ *.js *.pyj-cached +*.so .build-cache tags +build diff --git a/kitty/data-types.c b/kitty/data-types.c new file mode 100644 index 000000000..ce097cb9d --- /dev/null +++ b/kitty/data-types.c @@ -0,0 +1,32 @@ +/* + * data-types.c + * Copyright (C) 2016 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ + +#include + +static PyMethodDef module_methods[] = { + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +static struct PyModuleDef module = { + PyModuleDef_HEAD_INIT, + "fast_data_types", /* name of module */ + NULL, /* module documentation, may be NULL */ + -1, /* size of per-interpreter state of the module, + or -1 if the module keeps state in global variables. */ + module_methods +}; + +PyMODINIT_FUNC +PyInit_fast_data_types(void) { + PyObject *m; + + m = PyModule_Create(&module); + if (m == NULL) return NULL; + + return m; +} + diff --git a/setup.py b/setup.py new file mode 100644 index 000000000..709e0b4d0 --- /dev/null +++ b/setup.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +# License: GPL v3 Copyright: 2016, Kovid Goyal + +import os +import re +import sys +import sysconfig +import shlex +import subprocess + +base = os.path.dirname(os.path.abspath(__file__)) +build_dir = os.path.join(base, 'build') +constants = os.path.join(base, 'kitty', 'constants.py') +with open(constants, 'rb') as f: + constants = f.read().decode('utf-8') +appname = re.search(r"^appname = '([^']+)'", constants, re.MULTILINE).group(1) +version = tuple(map(int, re.search(r"^version = \((\d+), (\d+), (\d+)\)", constants, re.MULTILINE).group(1, 2, 3))) + + +cflags = ldflags = cc = ldpaths = None + + +def init_env(): + global cflags, ldflags, cc, ldpaths + cc = os.environ.get('CC', 'gcc') + cflags = os.environ.get('OVERRIDE_CFLAGS', + '-Wall -Werror -O3 -DNDEBUG -fwrapv -fstack-protector-strong -pipe') + cflags = shlex.split(cflags) + shlex.split(sysconfig.get_config_var('CCSHARED')) + ldflags = os.environ.get('OVERRIDE_LDFLAGS', '-Wall -O3') + ldflags = shlex.split(ldflags) + cflags += shlex.split(os.environ.get('CFLAGS', '')) + ldflags += shlex.split(os.environ.get('LDFLAGS', '')) + + cflags.append('-pthread') + ldflags.append('-pthread') + ldflags.append('-shared') + cflags.append('-I' + sysconfig.get_config_var('CONFINCLUDEPY')) + lib = sysconfig.get_config_var('LDLIBRARY')[3:-3] + ldpaths = ['-L' + sysconfig.get_config_var('LIBDIR'), '-l' + lib] + + try: + os.mkdir(build_dir) + except FileExistsError: + pass + + +def run_tool(cmd): + if isinstance(cmd, str): + cmd = shlex.split(cmd[0]) + print(' '.join(cmd)) + p = subprocess.Popen(cmd) + ret = p.wait() + if ret != 0: + raise SystemExit(ret) + + +def compile_c_extension(module, *sources): + prefix = os.path.basename(module) + objects = [os.path.join(build_dir, prefix + '-' + os.path.basename(src) + '.o') for src in sources] + for src, dest in zip(sources, objects): + src = os.path.join(base, src) + run_tool([cc] + cflags + ['-c', src] + ['-o', dest]) + run_tool([cc] + ldflags + objects + ldpaths + ['-o', os.path.join(base, module + '.so')]) + +if __name__ == '__main__': + if sys.version_info < (3, 5): + raise SystemExit('python >= 3.5 required') + init_env() + compile_c_extension('kitty/fast_data_types', 'kitty/data-types.c')