diff --git a/.gitignore b/.gitignore index 5f8c39bc5..20cb7aac5 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ build README.html linux-package logo/*.iconset +test-launcher diff --git a/.travis.yml b/.travis.yml index 04c63069c..3f09b8f87 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ matrix: group: beta sudo: false env: - - CC=gcc ASANLIB=libasan.so.0 ASAN_ARG=--asan + - CC=gcc SANITIZE_ARG=--sanitize language: python python: "3.5" addons: @@ -24,7 +24,25 @@ matrix: group: beta sudo: false env: - - CC=clang RUN_FLAKE=1 BUILD_PKG=1 + - CC=clang SANITIZE_ARG=--sanitize + language: python + python: "3.5" + addons: + apt: + packages: + - libfontconfig1-dev + - libglew-dev + - libxi-dev + - libxrandr-dev + - libxinerama-dev + - libxcursor-dev + + - os: linux + dist: trusty + group: beta + sudo: false + env: + - RUN_FLAKE=1 BUILD_PKG=1 language: python python: "3.5" addons: @@ -45,6 +63,13 @@ matrix: language: generic env: USE_BREW=1 BUILD_PKG=1 +env: + global: + - PYTHON=python3 + - PKG_CONFIG_PATH=$HOME/glfw/lib/pkgconfig:$PKG_CONFIG_PATH + - LD_LIBRARY_PATH=$HOME/glfw/lib:$LD_LIBRARY_PATH + - ASAN_OPTIONS=leak_check_at_exit=0 + install: | set -e if [[ "$RUN_FLAKE" == "1" ]]; then pip install flake8; fi @@ -59,7 +84,7 @@ install: | fi else wget -O glfw-3.2.1.zip https://github.com/glfw/glfw/archive/3.2.1.zip - unzip glfw-3.2.1.zip + unzip -q glfw-3.2.1.zip cd glfw-3.2.1 cmake -DBUILD_SHARED_LIBS=ON -DGLFW_BUILD_EXAMPLES=OFF -DGLFW_BUILD_TESTS=OFF -DGLFW_BUILD_DOCS=OFF -DCMAKE_INSTALL_PREFIX=$HOME/glfw make @@ -67,17 +92,18 @@ install: | cd .. fi pkg-config --cflags glfw3 + if [[ "$TRAVIS_OS_NAME" != 'osx' ]]; then + PLIB=$(ldd `which python` | grep libpython | cut -d ' ' -f 3) + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`dirname $PLIB` + fi set +e -env: - global: - - PKG_CONFIG_PATH=$HOME/glfw/lib/pkgconfig - - LD_LIBRARY_PATH=$HOME/glfw/lib - - ASAN_OPTIONS=leak_check_at_exit=0 - - PYTHON=python3 before_script: - - $PYTHON setup.py build --debug $ASAN_ARG; + - echo $LD_LIBRARY_PATH + - $PYTHON setup.py build --debug $SANITIZE_ARG; + - if [[ "$TRAVIS_OS_NAME" != 'osx' ]]; then ldd ./test-launcher `which $PYTHON`; fi + script: - - LD_PRELOAD=$ASANLIB $PYTHON setup.py test + - ./test-launcher - if [[ "$RUN_FLAKE" == "1" ]]; then flake8 --count .; fi - if [[ "$BUILD_PKG" == "1" ]]; then $PYTHON setup.py linux-package; fi diff --git a/kitty/freetype.c b/kitty/freetype.c index 617050daf..7c6ed9a19 100644 --- a/kitty/freetype.c +++ b/kitty/freetype.c @@ -140,7 +140,7 @@ static PyStructSequence_Field gm_fields[] = { }; static PyStructSequence_Desc gm_desc = {"GlpyhMetrics", NULL, gm_fields, 8}; -static PyTypeObject GlpyhMetricsType = {0}; +static PyTypeObject GlpyhMetricsType = {{{0}}}; static PyObject* glyph_metrics(Face *self) { @@ -167,7 +167,7 @@ static PyStructSequence_Field bm_fields[] = { {NULL} }; static PyStructSequence_Desc bm_desc = {"Bitmap", NULL, bm_fields, 7}; -static PyTypeObject BitmapType = {0}; +static PyTypeObject BitmapType = {{{0}}}; static PyObject* bitmap(Face *self) { diff --git a/kitty/history.c b/kitty/history.c index ae98c2193..a086553e9 100644 --- a/kitty/history.c +++ b/kitty/history.c @@ -95,7 +95,7 @@ index_type historybuf_push(HistoryBuf *self) { bool historybuf_resize(HistoryBuf *self, index_type lines) { - HistoryBuf t = {0}; + HistoryBuf t = {{0}}; t.xnum=self->xnum; t.ynum=lines; if (t.ynum > 0 && t.ynum != self->ynum) { diff --git a/kitty/line-buf.c b/kitty/line-buf.c index 2cc953c17..6472e6b99 100644 --- a/kitty/line-buf.c +++ b/kitty/line-buf.c @@ -423,7 +423,7 @@ copy_old(LineBuf *self, PyObject *y) { if (!PyObject_TypeCheck(y, &LineBuf_Type)) { PyErr_SetString(PyExc_TypeError, "Not a LineBuf object"); return NULL; } LineBuf *other = (LineBuf*)y; if (other->xnum != self->xnum) { PyErr_SetString(PyExc_ValueError, "LineBuf has a different number of columns"); return NULL; } - Line sl = {0}, ol = {0}; + Line sl = {{0}}, ol = {{0}}; sl.xnum = self->xnum; ol.xnum = other->xnum; for (index_type i = 0; i < MIN(self->ynum, other->ynum); i++) { diff --git a/setup.py b/setup.py index 2fd28bf22..1a28f11df 100755 --- a/setup.py +++ b/setup.py @@ -53,7 +53,7 @@ def cc_version(): ver = tuple(map(int, ver)) except Exception: ver = (0, 0) - return ver + return cc, ver def get_python_flags(cflags): @@ -88,36 +88,43 @@ def get_python_flags(cflags): return libs -def init_env(debug=False, asan=False, native_optimizations=True): +def get_sanitize_args(cc, ccver): + sanitize_args = set() + sanitize_args.add('-fno-omit-frame-pointer') + sanitize_args.add('-fsanitize=address') + if (cc == 'gcc' and ccver >= (5, 0)) or cc == 'clang': + sanitize_args.add('-fsanitize=undefined') + # if cc == 'gcc' or (cc == 'clang' and ccver >= (4, 2)): + # sanitize_args.add('-fno-sanitize-recover=all') + return sanitize_args + + +def init_env(debug=False, sanitize=False, native_optimizations=True): global cflags, ldflags, cc, ldpaths - native_optimizations = native_optimizations and not asan and not debug - ccver = cc_version() + native_optimizations = native_optimizations and not sanitize and not debug + cc, ccver = cc_version() + print('CC:', cc, ccver) stack_protector = '-fstack-protector' - if ccver >= (4, 9): + if ccver >= (4, 9) and cc == 'gcc': stack_protector += '-strong' missing_braces = '' - if ccver < (5, 2): + if ccver < (5, 2) and cc == 'gcc': missing_braces = '-Wno-missing-braces' - cc = os.environ.get('CC', 'gcc') - optimize = '-O3' - if debug or asan: - optimize = '-ggdb' - if asan: - optimize += ' -fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all -fno-omit-frame-pointer' + optimize = '-ggdb' if debug or sanitize else '-O3' + sanitize_args = get_sanitize_args(cc, ccver) if sanitize else set() cflags = os.environ.get( 'OVERRIDE_CFLAGS', ( '-Wextra -Wno-missing-field-initializers -Wall -std=c99 -D_XOPEN_SOURCE=700' - ' -pedantic-errors -Werror {} -DNDEBUG -fwrapv {} {} -pipe {}' + ' -pedantic-errors -Werror {} {} -DNDEBUG -fwrapv {} {} -pipe {}' ).format( - optimize, stack_protector, missing_braces, '-march=native' + optimize, ' '.join(sanitize_args), stack_protector, missing_braces, '-march=native' if native_optimizations else '' ) ) cflags = shlex.split(cflags ) + shlex.split(sysconfig.get_config_var('CCSHARED')) ldflags = os.environ.get( - 'OVERRIDE_LDFLAGS', '-Wall ' + - ('-fsanitize=address -fsanitize=undefined' if asan else ('' if debug else '-O3')) + 'OVERRIDE_LDFLAGS', '-Wall ' + ' '.join(sanitize_args) + ('' if debug else ' -O3') ) ldflags = shlex.split(ldflags) cflags += shlex.split(os.environ.get('CFLAGS', '')) @@ -230,11 +237,13 @@ def option_parser(): help='Build extension modules with debugging symbols' ) p.add_argument( - '--asan', + '--sanitize', default=False, action='store_true', - help='Turn on address sanitization to detect memory access errors. Note that if you do turn it on,' - ' you have to run kitty with the environment variable LD_PRELOAD=/usr/lib/libasan.so' + help='Turn on sanitization to detect memory access errors and undefined behavior. Note that if you do turn it on,' + ' a special executable will be built for running the test suite. If you want to run normal kitty' + ' with sanitization, use LD_PRELOAD=libasan.so (for gcc) and' + ' LD_PRELOAD=/usr/lib/clang/4.0.0/lib/linux/libclang_rt.asan-x86_64.so (for clang, changing path as appropriate).' ) p.add_argument( '--prefix', @@ -268,7 +277,7 @@ def find_c_files(): def build(args, native_optimizations=True): - init_env(args.debug, args.asan, native_optimizations) + init_env(args.debug, args.sanitize, native_optimizations) compile_c_extension( 'kitty/fast_data_types', args.incremental, *find_c_files() ) @@ -281,6 +290,18 @@ def safe_makedirs(path): pass +def build_test_launcher(args): + cc, ccver = cc_version() + cflags = '-g -Wall -Werror -fpie'.split() + pylib = get_python_flags(cflags) + sanitize_lib = (['-lasan'] if cc == 'gcc' else []) if args.sanitize else [] + cflags.extend(get_sanitize_args(cc, ccver) if args.sanitize else []) + cmd = [cc] + cflags + [ + 'test-launcher.c', '-o', 'test-launcher', + ] + sanitize_lib + pylib + run_tool(cmd) + + def package(args, for_bundle=False): # {{{ ddir = args.prefix libdir = os.path.join(ddir, 'lib', 'kitty') @@ -360,6 +381,7 @@ def main(): os.chdir(os.path.dirname(os.path.abspath(__file__))) if args.action == 'build': build(args) + build_test_launcher(args) elif args.action == 'test': os.execlp( sys.executable, sys.executable, os.path.join(base, 'test.py') diff --git a/test-launcher.c b/test-launcher.c new file mode 100644 index 000000000..916faaa02 --- /dev/null +++ b/test-launcher.c @@ -0,0 +1,13 @@ +/* + * linux-launcher.c + * Copyright (C) 2017 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ + +#include + +int main(int argc, char *argv[]) { + wchar_t *wargv[2] = {L"kitty-test", L"test.py"}; + return Py_Main(2, wargv); +}