2016-11-21 07:55:53 +03:00
|
|
|
#!/usr/bin/env python3
|
2016-10-31 12:15:49 +03:00
|
|
|
# vim:fileencoding=utf-8
|
|
|
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
|
|
|
|
2017-11-14 07:01:23 +03:00
|
|
|
import argparse
|
2018-03-12 05:20:54 +03:00
|
|
|
import glob
|
2017-11-19 20:54:36 +03:00
|
|
|
import importlib
|
2017-11-14 07:01:23 +03:00
|
|
|
import json
|
2016-10-31 12:15:49 +03:00
|
|
|
import os
|
|
|
|
import re
|
2019-03-03 04:54:05 +03:00
|
|
|
import runpy
|
2016-10-31 12:15:49 +03:00
|
|
|
import shlex
|
2017-01-08 09:47:44 +03:00
|
|
|
import shutil
|
2016-10-31 12:15:49 +03:00
|
|
|
import subprocess
|
2017-11-14 07:01:23 +03:00
|
|
|
import sys
|
|
|
|
import sysconfig
|
2018-01-10 04:28:57 +03:00
|
|
|
import time
|
2016-10-31 12:15:49 +03:00
|
|
|
|
|
|
|
base = os.path.dirname(os.path.abspath(__file__))
|
2017-11-19 20:54:36 +03:00
|
|
|
sys.path.insert(0, os.path.join(base, 'glfw'))
|
|
|
|
glfw = importlib.import_module('glfw')
|
2017-11-20 12:56:27 +03:00
|
|
|
verbose = False
|
2017-11-19 20:54:36 +03:00
|
|
|
del sys.path[0]
|
2016-10-31 12:15:49 +03:00
|
|
|
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)
|
2017-02-09 21:34:05 +03:00
|
|
|
version = tuple(
|
|
|
|
map(
|
|
|
|
int,
|
|
|
|
re.search(
|
|
|
|
r"^version = \((\d+), (\d+), (\d+)\)", constants, re.MULTILINE
|
|
|
|
).group(1, 2, 3)
|
|
|
|
)
|
|
|
|
)
|
2017-01-07 10:31:32 +03:00
|
|
|
_plat = sys.platform.lower()
|
2017-11-21 03:49:46 +03:00
|
|
|
is_macos = 'darwin' in _plat
|
2017-11-19 10:24:02 +03:00
|
|
|
env = None
|
2016-10-31 12:15:49 +03:00
|
|
|
|
2017-01-10 07:15:33 +03:00
|
|
|
PKGCONFIG = os.environ.get('PKGCONFIG_EXE', 'pkg-config')
|
2016-10-31 12:15:49 +03:00
|
|
|
|
|
|
|
|
2017-11-21 05:02:48 +03:00
|
|
|
def emphasis(text):
|
|
|
|
if sys.stdout.isatty():
|
|
|
|
text = '\033[32m' + text + '\033[39m'
|
|
|
|
return text
|
|
|
|
|
|
|
|
|
|
|
|
def error(text):
|
|
|
|
if sys.stdout.isatty():
|
|
|
|
text = '\033[91m' + text + '\033[39m'
|
|
|
|
return text
|
|
|
|
|
|
|
|
|
2016-11-02 18:57:20 +03:00
|
|
|
def pkg_config(pkg, *args):
|
2017-11-21 02:28:35 +03:00
|
|
|
try:
|
|
|
|
return list(
|
|
|
|
filter(
|
|
|
|
None,
|
|
|
|
shlex.split(
|
|
|
|
subprocess.check_output([PKGCONFIG, pkg] + list(args))
|
|
|
|
.decode('utf-8')
|
|
|
|
)
|
2017-02-09 21:34:05 +03:00
|
|
|
)
|
|
|
|
)
|
2017-11-21 02:28:35 +03:00
|
|
|
except subprocess.CalledProcessError:
|
2017-11-21 05:02:48 +03:00
|
|
|
raise SystemExit('The package {} was not found on your system'.format(error(pkg)))
|
2016-11-02 18:57:20 +03:00
|
|
|
|
|
|
|
|
2017-11-05 06:52:15 +03:00
|
|
|
def at_least_version(package, major, minor=0):
|
|
|
|
q = '{}.{}'.format(major, minor)
|
|
|
|
if subprocess.run([PKGCONFIG, package, '--atleast-version=' + q]
|
|
|
|
).returncode != 0:
|
|
|
|
try:
|
|
|
|
ver = subprocess.check_output([PKGCONFIG, package, '--modversion']
|
|
|
|
).decode('utf-8').strip()
|
|
|
|
qmajor, qminor = map(int, re.match(r'(\d+).(\d+)', ver).groups())
|
|
|
|
except Exception:
|
|
|
|
ver = 'not found'
|
|
|
|
qmajor = qminor = 0
|
|
|
|
if qmajor < major or (qmajor == major and qminor < minor):
|
|
|
|
raise SystemExit(
|
|
|
|
'{} >= {}.{} is required, found version: {}'.format(
|
2017-11-21 05:02:48 +03:00
|
|
|
error(package), major, minor, ver
|
2017-11-05 06:52:15 +03:00
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2016-11-12 06:23:27 +03:00
|
|
|
def cc_version():
|
2019-01-30 06:35:42 +03:00
|
|
|
if 'CC' in os.environ:
|
|
|
|
cc = os.environ['CC']
|
|
|
|
else:
|
|
|
|
if is_macos:
|
|
|
|
cc = 'clang'
|
|
|
|
else:
|
|
|
|
if shutil.which('gcc'):
|
|
|
|
cc = 'gcc'
|
|
|
|
elif shutil.which('clang'):
|
|
|
|
cc = 'clang'
|
|
|
|
else:
|
|
|
|
cc = 'cc'
|
2016-11-12 06:23:27 +03:00
|
|
|
raw = subprocess.check_output([cc, '-dumpversion']).decode('utf-8')
|
|
|
|
ver = raw.split('.')[:2]
|
|
|
|
try:
|
|
|
|
ver = tuple(map(int, ver))
|
|
|
|
except Exception:
|
|
|
|
ver = (0, 0)
|
2017-05-15 07:48:59 +03:00
|
|
|
return cc, ver
|
2016-11-12 06:23:27 +03:00
|
|
|
|
|
|
|
|
2017-01-10 07:15:33 +03:00
|
|
|
def get_python_flags(cflags):
|
2017-02-09 21:34:05 +03:00
|
|
|
cflags.extend(
|
|
|
|
'-I' + sysconfig.get_path(x) for x in 'include platinclude'.split()
|
|
|
|
)
|
2017-01-10 07:15:33 +03:00
|
|
|
libs = []
|
|
|
|
libs += sysconfig.get_config_var('LIBS').split()
|
|
|
|
libs += sysconfig.get_config_var('SYSLIBS').split()
|
2017-01-10 07:25:44 +03:00
|
|
|
fw = sysconfig.get_config_var('PYTHONFRAMEWORK')
|
|
|
|
if fw:
|
2017-01-10 10:36:48 +03:00
|
|
|
for var in 'data include stdlib'.split():
|
2017-01-10 07:15:33 +03:00
|
|
|
val = sysconfig.get_path(var)
|
2017-01-10 07:25:44 +03:00
|
|
|
if val and '/{}.framework'.format(fw) in val:
|
2017-01-10 10:15:33 +03:00
|
|
|
fdir = val[:val.index('/{}.framework'.format(fw))]
|
2017-02-09 21:34:05 +03:00
|
|
|
if os.path.isdir(
|
|
|
|
os.path.join(fdir, '{}.framework'.format(fw))
|
|
|
|
):
|
2017-01-20 20:08:58 +03:00
|
|
|
framework_dir = fdir
|
2017-01-10 10:23:23 +03:00
|
|
|
break
|
2017-01-20 20:08:58 +03:00
|
|
|
else:
|
|
|
|
raise SystemExit('Failed to find Python framework')
|
2017-02-09 21:34:05 +03:00
|
|
|
libs.append(
|
|
|
|
os.path.join(framework_dir, sysconfig.get_config_var('LDLIBRARY'))
|
|
|
|
)
|
2017-01-10 07:15:33 +03:00
|
|
|
else:
|
2017-01-10 07:19:53 +03:00
|
|
|
libs += ['-L' + sysconfig.get_config_var('LIBDIR')]
|
2017-02-09 21:34:05 +03:00
|
|
|
libs += [
|
|
|
|
'-lpython' + sysconfig.get_config_var('VERSION') + sys.abiflags
|
|
|
|
]
|
2017-01-10 07:15:33 +03:00
|
|
|
libs += sysconfig.get_config_var('LINKFORSHARED').split()
|
|
|
|
return libs
|
|
|
|
|
|
|
|
|
2017-05-15 07:48:59 +03:00
|
|
|
def get_sanitize_args(cc, ccver):
|
2017-11-01 12:05:57 +03:00
|
|
|
sanitize_args = ['-fsanitize=address']
|
2017-11-08 15:20:32 +03:00
|
|
|
if ccver >= (5, 0):
|
2017-11-01 12:05:57 +03:00
|
|
|
sanitize_args.append('-fsanitize=undefined')
|
2017-05-15 07:48:59 +03:00
|
|
|
# if cc == 'gcc' or (cc == 'clang' and ccver >= (4, 2)):
|
2017-11-01 12:05:57 +03:00
|
|
|
# sanitize_args.append('-fno-sanitize-recover=all')
|
|
|
|
sanitize_args.append('-fno-omit-frame-pointer')
|
2017-05-15 07:48:59 +03:00
|
|
|
return sanitize_args
|
|
|
|
|
|
|
|
|
2018-04-20 18:32:05 +03:00
|
|
|
def test_compile(cc, *cflags, src=None):
|
2018-10-03 08:26:47 +03:00
|
|
|
src = src or 'int main(void) { return 0; }'
|
|
|
|
p = subprocess.Popen([cc] + list(cflags) + ['-x', 'c', '-o', os.devnull, '-'], stdin=subprocess.PIPE)
|
2018-10-03 10:16:08 +03:00
|
|
|
try:
|
|
|
|
p.stdin.write(src.encode('utf-8')), p.stdin.close()
|
|
|
|
except BrokenPipeError:
|
|
|
|
return False
|
2018-10-03 08:26:47 +03:00
|
|
|
return p.wait() == 0
|
2018-04-20 18:02:16 +03:00
|
|
|
|
|
|
|
|
2018-04-20 18:32:05 +03:00
|
|
|
def first_successful_compile(cc, *cflags, src=None):
|
2018-04-20 18:26:29 +03:00
|
|
|
for x in cflags:
|
2018-04-20 18:32:05 +03:00
|
|
|
if test_compile(cc, *shlex.split(x), src=src):
|
2018-04-20 18:26:29 +03:00
|
|
|
return x
|
|
|
|
return ''
|
|
|
|
|
|
|
|
|
2017-11-19 10:24:02 +03:00
|
|
|
class Env:
|
|
|
|
|
2018-04-19 06:07:01 +03:00
|
|
|
def __init__(self, cc, cppflags, cflags, ldflags, ldpaths=[]):
|
|
|
|
self.cc, self.cppflags, self.cflags, self.ldflags, self.ldpaths = cc, cppflags, cflags, ldflags, ldpaths
|
2017-11-19 10:24:02 +03:00
|
|
|
|
|
|
|
def copy(self):
|
2018-06-01 13:03:08 +03:00
|
|
|
return Env(self.cc, list(self.cppflags), list(self.cflags), list(self.ldflags), list(self.ldpaths))
|
2017-11-19 10:24:02 +03:00
|
|
|
|
|
|
|
|
2017-11-05 06:52:15 +03:00
|
|
|
def init_env(
|
2019-04-24 13:46:40 +03:00
|
|
|
debug=False, sanitize=False, native_optimizations=True, profile=False,
|
|
|
|
extra_logging=()
|
2017-11-05 06:52:15 +03:00
|
|
|
):
|
2017-05-15 07:48:59 +03:00
|
|
|
native_optimizations = native_optimizations and not sanitize and not debug
|
|
|
|
cc, ccver = cc_version()
|
|
|
|
print('CC:', cc, ccver)
|
2018-04-20 18:26:29 +03:00
|
|
|
stack_protector = first_successful_compile(cc, '-fstack-protector-strong', '-fstack-protector')
|
2016-11-12 06:34:17 +03:00
|
|
|
missing_braces = ''
|
2017-05-15 07:48:59 +03:00
|
|
|
if ccver < (5, 2) and cc == 'gcc':
|
2016-11-12 06:34:17 +03:00
|
|
|
missing_braces = '-Wno-missing-braces'
|
2017-11-08 13:43:21 +03:00
|
|
|
df = '-g3'
|
|
|
|
if ccver >= (5, 0):
|
|
|
|
df += ' -Og'
|
|
|
|
optimize = df if debug or sanitize else '-O3'
|
2017-05-15 07:48:59 +03:00
|
|
|
sanitize_args = get_sanitize_args(cc, ccver) if sanitize else set()
|
2018-04-19 06:07:01 +03:00
|
|
|
cppflags = os.environ.get(
|
|
|
|
'OVERRIDE_CPPFLAGS', (
|
2018-07-28 20:26:18 +03:00
|
|
|
'-D{}DEBUG'
|
2018-04-19 06:07:01 +03:00
|
|
|
).format(
|
|
|
|
('' if debug else 'N'),
|
|
|
|
)
|
|
|
|
)
|
2018-04-19 08:03:10 +03:00
|
|
|
cppflags = shlex.split(cppflags)
|
2019-04-24 13:46:40 +03:00
|
|
|
for el in extra_logging:
|
|
|
|
cppflags.append('-DDEBUG_{}'.format(el.upper().replace('-', '_')))
|
2017-02-09 21:34:05 +03:00
|
|
|
cflags = os.environ.get(
|
|
|
|
'OVERRIDE_CFLAGS', (
|
2019-05-13 08:29:01 +03:00
|
|
|
'-Wextra -Wno-missing-field-initializers -Wall -Wstrict-prototypes -std=c11'
|
2018-04-19 06:07:01 +03:00
|
|
|
' -pedantic-errors -Werror {} {} -fwrapv {} {} -pipe {} -fvisibility=hidden'
|
2017-02-21 14:05:25 +03:00
|
|
|
).format(
|
2017-11-05 06:52:15 +03:00
|
|
|
optimize,
|
|
|
|
' '.join(sanitize_args),
|
|
|
|
stack_protector,
|
|
|
|
missing_braces,
|
2017-08-26 07:54:12 +03:00
|
|
|
'-march=native' if native_optimizations else '',
|
2017-02-21 14:05:25 +03:00
|
|
|
)
|
2017-02-09 21:34:05 +03:00
|
|
|
)
|
2017-11-05 06:52:15 +03:00
|
|
|
cflags = shlex.split(cflags) + shlex.split(
|
|
|
|
sysconfig.get_config_var('CCSHARED')
|
|
|
|
)
|
2017-02-09 21:34:05 +03:00
|
|
|
ldflags = os.environ.get(
|
2017-11-05 06:52:15 +03:00
|
|
|
'OVERRIDE_LDFLAGS',
|
|
|
|
'-Wall ' + ' '.join(sanitize_args) + ('' if debug else ' -O3')
|
2017-02-09 21:34:05 +03:00
|
|
|
)
|
2016-10-31 12:15:49 +03:00
|
|
|
ldflags = shlex.split(ldflags)
|
2017-11-19 20:54:36 +03:00
|
|
|
ldflags.append('-shared')
|
2018-04-19 06:07:01 +03:00
|
|
|
cppflags += shlex.split(os.environ.get('CPPFLAGS', ''))
|
2016-10-31 12:15:49 +03:00
|
|
|
cflags += shlex.split(os.environ.get('CFLAGS', ''))
|
|
|
|
ldflags += shlex.split(os.environ.get('LDFLAGS', ''))
|
2017-11-01 12:05:57 +03:00
|
|
|
if not debug and not sanitize:
|
|
|
|
# See https://github.com/google/sanitizers/issues/647
|
2017-10-05 18:48:21 +03:00
|
|
|
cflags.append('-flto'), ldflags.append('-flto')
|
2016-10-31 12:15:49 +03:00
|
|
|
|
2017-08-26 07:54:12 +03:00
|
|
|
if profile:
|
2018-04-19 06:07:01 +03:00
|
|
|
cppflags.append('-DWITH_PROFILER')
|
2017-11-01 12:05:57 +03:00
|
|
|
cflags.append('-g3')
|
2017-08-26 07:54:12 +03:00
|
|
|
ldflags.append('-lprofiler')
|
2018-06-01 13:03:08 +03:00
|
|
|
ldpaths = []
|
|
|
|
return Env(cc, cppflags, cflags, ldflags, ldpaths=ldpaths)
|
2017-11-19 10:24:02 +03:00
|
|
|
|
|
|
|
|
|
|
|
def kitty_env():
|
|
|
|
ans = env.copy()
|
2017-11-19 20:54:36 +03:00
|
|
|
cflags = ans.cflags
|
2016-10-31 12:15:49 +03:00
|
|
|
cflags.append('-pthread')
|
2017-05-19 11:53:11 +03:00
|
|
|
# We add 4000 to the primary version because vim turns on SGR mouse mode
|
|
|
|
# automatically if this version is high enough
|
2018-04-19 06:07:01 +03:00
|
|
|
cppflags = ans.cppflags
|
|
|
|
cppflags.append('-DPRIMARY_VERSION={}'.format(version[0] + 4000))
|
|
|
|
cppflags.append('-DSECONDARY_VERSION={}'.format(version[1]))
|
2017-11-05 07:08:58 +03:00
|
|
|
at_least_version('harfbuzz', 1, 5)
|
2017-12-06 18:40:35 +03:00
|
|
|
cflags.extend(pkg_config('libpng', '--cflags-only-I'))
|
2017-11-21 03:49:46 +03:00
|
|
|
if is_macos:
|
2017-01-10 17:20:56 +03:00
|
|
|
font_libs = ['-framework', 'CoreText', '-framework', 'CoreGraphics']
|
2018-07-29 17:05:43 +03:00
|
|
|
# Apple deprecated OpenGL in Mojave (10.14) silence the endless
|
|
|
|
# warnings about it
|
|
|
|
cppflags.append('-DGL_SILENCE_DEPRECATION')
|
2017-01-10 17:20:56 +03:00
|
|
|
else:
|
2017-02-08 19:22:10 +03:00
|
|
|
cflags.extend(pkg_config('fontconfig', '--cflags-only-I'))
|
|
|
|
font_libs = pkg_config('fontconfig', '--libs')
|
2017-10-30 19:54:01 +03:00
|
|
|
cflags.extend(pkg_config('harfbuzz', '--cflags-only-I'))
|
|
|
|
font_libs.extend(pkg_config('harfbuzz', '--libs'))
|
2017-01-10 07:15:33 +03:00
|
|
|
pylib = get_python_flags(cflags)
|
2017-11-21 03:49:46 +03:00
|
|
|
gl_libs = ['-framework', 'OpenGL'] if is_macos else pkg_config('gl', '--libs')
|
2017-09-27 15:01:11 +03:00
|
|
|
libpng = pkg_config('libpng', '--libs')
|
2018-01-17 21:39:40 +03:00
|
|
|
ans.ldpaths += pylib + font_libs + gl_libs + libpng
|
2017-11-21 03:49:46 +03:00
|
|
|
if is_macos:
|
2017-11-20 11:39:13 +03:00
|
|
|
ans.ldpaths.extend('-framework Cocoa'.split())
|
|
|
|
else:
|
2017-11-19 10:24:02 +03:00
|
|
|
ans.ldpaths += ['-lrt']
|
|
|
|
if '-ldl' not in ans.ldpaths:
|
|
|
|
ans.ldpaths.append('-ldl')
|
|
|
|
if '-lz' not in ans.ldpaths:
|
|
|
|
ans.ldpaths.append('-lz')
|
2016-10-31 12:15:49 +03:00
|
|
|
|
|
|
|
try:
|
|
|
|
os.mkdir(build_dir)
|
|
|
|
except FileExistsError:
|
|
|
|
pass
|
2017-11-19 10:24:02 +03:00
|
|
|
return ans
|
2016-10-31 12:15:49 +03:00
|
|
|
|
|
|
|
|
2016-11-12 09:11:40 +03:00
|
|
|
def define(x):
|
|
|
|
return '-D' + x
|
|
|
|
|
|
|
|
|
2017-11-19 09:08:35 +03:00
|
|
|
def run_tool(cmd, desc=None):
|
2016-10-31 12:15:49 +03:00
|
|
|
if isinstance(cmd, str):
|
|
|
|
cmd = shlex.split(cmd[0])
|
2017-11-20 12:56:27 +03:00
|
|
|
if verbose:
|
|
|
|
desc = None
|
2017-11-19 09:08:35 +03:00
|
|
|
print(desc or ' '.join(cmd))
|
2016-10-31 12:15:49 +03:00
|
|
|
p = subprocess.Popen(cmd)
|
|
|
|
ret = p.wait()
|
|
|
|
if ret != 0:
|
2017-11-19 20:54:36 +03:00
|
|
|
if desc:
|
|
|
|
print(' '.join(cmd))
|
2016-10-31 12:15:49 +03:00
|
|
|
raise SystemExit(ret)
|
|
|
|
|
2016-11-28 17:36:42 +03:00
|
|
|
|
2016-11-12 09:11:40 +03:00
|
|
|
SPECIAL_SOURCES = {
|
|
|
|
'kitty/parser_dump.c': ('kitty/parser.c', ['DUMP_COMMANDS']),
|
|
|
|
}
|
|
|
|
|
2016-10-31 12:15:49 +03:00
|
|
|
|
2017-01-11 02:15:27 +03:00
|
|
|
def newer(dest, *sources):
|
|
|
|
try:
|
|
|
|
dtime = os.path.getmtime(dest)
|
|
|
|
except EnvironmentError:
|
|
|
|
return True
|
|
|
|
for s in sources:
|
|
|
|
if os.path.getmtime(s) >= dtime:
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
2017-09-30 09:48:08 +03:00
|
|
|
def dependecies_for(src, obj, all_headers):
|
|
|
|
dep_file = obj.rpartition('.')[0] + '.d'
|
|
|
|
try:
|
2017-09-30 09:55:03 +03:00
|
|
|
deps = open(dep_file).read()
|
2017-09-30 09:48:08 +03:00
|
|
|
except FileNotFoundError:
|
|
|
|
yield src
|
|
|
|
yield from iter(all_headers)
|
|
|
|
else:
|
2017-11-05 06:52:15 +03:00
|
|
|
RE_INC = re.compile(
|
|
|
|
r'^(?P<target>.+?):\s+(?P<deps>.+?)$', re.MULTILINE
|
|
|
|
)
|
2017-09-30 09:55:03 +03:00
|
|
|
SPACE_TOK = '\x1B'
|
|
|
|
|
|
|
|
text = deps.replace('\\\n', ' ').replace('\\ ', SPACE_TOK)
|
|
|
|
for match in RE_INC.finditer(text):
|
2017-11-05 06:52:15 +03:00
|
|
|
files = (
|
|
|
|
f.replace(SPACE_TOK, ' ') for f in match.group('deps').split()
|
|
|
|
)
|
2017-09-30 09:55:03 +03:00
|
|
|
for path in files:
|
|
|
|
path = os.path.abspath(path)
|
|
|
|
if path.startswith(base):
|
|
|
|
yield path
|
2017-09-30 09:48:08 +03:00
|
|
|
|
|
|
|
|
2017-11-19 09:08:35 +03:00
|
|
|
def parallel_run(todo, desc='Compiling {} ...'):
|
2017-11-19 09:19:42 +03:00
|
|
|
try:
|
2019-04-22 06:19:22 +03:00
|
|
|
num_workers = max(2, os.cpu_count())
|
2017-11-19 09:19:42 +03:00
|
|
|
except Exception:
|
|
|
|
num_workers = 2
|
2017-12-11 17:58:36 +03:00
|
|
|
items = list(reversed(tuple(todo.items())))
|
2017-11-19 09:08:35 +03:00
|
|
|
workers = {}
|
|
|
|
failed = None
|
|
|
|
|
|
|
|
def wait():
|
|
|
|
nonlocal failed
|
|
|
|
if not workers:
|
|
|
|
return
|
|
|
|
pid, s = os.wait()
|
|
|
|
name, cmd, w = workers.pop(pid, (None, None, None))
|
|
|
|
if name is not None and ((s & 0xff) != 0 or ((s >> 8) & 0xff) != 0) and failed is None:
|
|
|
|
failed = name, cmd
|
|
|
|
|
|
|
|
while items and failed is None:
|
|
|
|
while len(workers) < num_workers and items:
|
|
|
|
name, cmd = items.pop()
|
2017-11-20 12:56:27 +03:00
|
|
|
if verbose:
|
|
|
|
print(' '.join(cmd))
|
|
|
|
else:
|
|
|
|
print(desc.format(emphasis(name)))
|
2017-11-19 09:08:35 +03:00
|
|
|
w = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
|
|
|
|
workers[w.pid] = name, cmd, w
|
|
|
|
wait()
|
|
|
|
while len(workers):
|
|
|
|
wait()
|
|
|
|
if failed:
|
|
|
|
run_tool(failed[1])
|
|
|
|
|
|
|
|
|
2017-11-21 02:50:10 +03:00
|
|
|
def compile_c_extension(kenv, module, incremental, compilation_database, all_keys, sources, headers):
|
2016-10-31 12:15:49 +03:00
|
|
|
prefix = os.path.basename(module)
|
2017-02-09 21:34:05 +03:00
|
|
|
objects = [
|
|
|
|
os.path.join(build_dir, prefix + '-' + os.path.basename(src) + '.o')
|
|
|
|
for src in sources
|
|
|
|
]
|
2017-01-11 02:15:27 +03:00
|
|
|
|
2017-11-19 09:08:35 +03:00
|
|
|
todo = {}
|
|
|
|
|
2017-11-14 07:01:23 +03:00
|
|
|
for original_src, dest in zip(sources, objects):
|
|
|
|
src = original_src
|
2018-04-19 08:03:10 +03:00
|
|
|
cppflags = kenv.cppflags[:]
|
2017-11-14 07:01:23 +03:00
|
|
|
is_special = src in SPECIAL_SOURCES
|
|
|
|
if is_special:
|
2016-11-12 09:11:40 +03:00
|
|
|
src, defines = SPECIAL_SOURCES[src]
|
2018-04-19 08:03:10 +03:00
|
|
|
cppflags.extend(map(define, defines))
|
2016-11-12 09:11:40 +03:00
|
|
|
|
2018-04-29 16:17:14 +03:00
|
|
|
if src == 'kitty/data-types.c':
|
|
|
|
if os.path.exists('.git'):
|
|
|
|
rev = subprocess.check_output(['git', 'rev-parse', 'HEAD']).decode('utf-8').strip()
|
|
|
|
cppflags.append(define('KITTY_VCS_REV="{}"'.format(rev)))
|
2018-04-19 08:03:10 +03:00
|
|
|
cmd = [kenv.cc, '-MMD'] + cppflags + kenv.cflags
|
2017-11-21 02:42:26 +03:00
|
|
|
key = original_src, os.path.basename(dest)
|
2017-11-21 02:50:10 +03:00
|
|
|
all_keys.add(key)
|
2017-11-21 02:42:26 +03:00
|
|
|
cmd_changed = compilation_database.get(key, [])[:-4] != cmd
|
2017-11-14 07:01:23 +03:00
|
|
|
must_compile = not incremental or cmd_changed
|
2016-10-31 12:15:49 +03:00
|
|
|
src = os.path.join(base, src)
|
2017-11-14 07:01:23 +03:00
|
|
|
if must_compile or newer(
|
2017-11-05 06:52:15 +03:00
|
|
|
dest, *dependecies_for(src, dest, headers)
|
|
|
|
):
|
2017-11-14 07:01:23 +03:00
|
|
|
cmd += ['-c', src] + ['-o', dest]
|
2017-11-21 02:42:26 +03:00
|
|
|
compilation_database[key] = cmd
|
2017-11-19 09:08:35 +03:00
|
|
|
todo[original_src] = cmd
|
|
|
|
if todo:
|
|
|
|
parallel_run(todo)
|
2018-12-30 12:52:05 +03:00
|
|
|
dest = os.path.join(base, module + '.temp.so')
|
2019-01-06 07:55:37 +03:00
|
|
|
real_dest = dest[:-len('.temp.so')] + '.so'
|
|
|
|
if not incremental or newer(real_dest, *objects):
|
2018-08-29 04:18:25 +03:00
|
|
|
# Old versions of clang don't like -pthread being passed to the linker
|
|
|
|
# Don't treat linker warnings as errors (linker generates spurious
|
2018-04-03 07:43:18 +03:00
|
|
|
# warnings on some old systems)
|
2018-04-03 07:49:21 +03:00
|
|
|
unsafe = {'-pthread', '-Werror', '-pedantic-errors'}
|
|
|
|
linker_cflags = list(filter(lambda x: x not in unsafe, kenv.cflags))
|
2018-12-30 12:52:05 +03:00
|
|
|
try:
|
|
|
|
run_tool([kenv.cc] + linker_cflags + kenv.ldflags + objects + kenv.ldpaths + ['-o', dest], desc='Linking {} ...'.format(emphasis(module)))
|
|
|
|
except Exception:
|
|
|
|
try:
|
|
|
|
os.remove(dest)
|
|
|
|
except EnvironmentError:
|
|
|
|
pass
|
|
|
|
else:
|
2019-01-06 07:55:37 +03:00
|
|
|
os.rename(dest, real_dest)
|
2016-10-31 12:15:49 +03:00
|
|
|
|
2016-11-07 10:53:03 +03:00
|
|
|
|
2016-11-11 19:41:40 +03:00
|
|
|
def find_c_files():
|
2017-01-11 02:15:27 +03:00
|
|
|
ans, headers = [], []
|
2016-11-11 19:41:40 +03:00
|
|
|
d = os.path.join(base, 'kitty')
|
2018-07-24 08:37:53 +03:00
|
|
|
exclude = {'fontconfig.c', 'freetype.c', 'desktop.c'} if is_macos else {'core_text.m', 'cocoa_window.m', 'macos_process_info.c'}
|
2016-11-11 19:41:40 +03:00
|
|
|
for x in os.listdir(d):
|
2017-01-11 02:15:27 +03:00
|
|
|
ext = os.path.splitext(x)[1]
|
|
|
|
if ext in ('.c', '.m') and os.path.basename(x) not in exclude:
|
2016-11-13 07:57:24 +03:00
|
|
|
ans.append(os.path.join('kitty', x))
|
2017-01-11 02:15:27 +03:00
|
|
|
elif ext == '.h':
|
|
|
|
headers.append(os.path.join('kitty', x))
|
2017-02-09 21:34:05 +03:00
|
|
|
ans.sort(
|
|
|
|
key=lambda x: os.path.getmtime(os.path.join(base, x)), reverse=True
|
|
|
|
)
|
2016-11-13 07:57:24 +03:00
|
|
|
ans.append('kitty/parser_dump.c')
|
2017-01-11 02:15:27 +03:00
|
|
|
return tuple(ans), tuple(headers)
|
2016-11-11 19:41:40 +03:00
|
|
|
|
|
|
|
|
2017-11-21 02:50:10 +03:00
|
|
|
def compile_glfw(incremental, compilation_database, all_keys):
|
2017-11-21 03:49:46 +03:00
|
|
|
modules = 'cocoa' if is_macos else 'x11 wayland'
|
2017-11-19 20:54:36 +03:00
|
|
|
for module in modules.split():
|
2017-11-21 03:37:41 +03:00
|
|
|
try:
|
2018-10-03 08:32:51 +03:00
|
|
|
genv = glfw.init_env(env, pkg_config, at_least_version, test_compile, module)
|
2017-11-21 03:37:41 +03:00
|
|
|
except SystemExit as err:
|
|
|
|
if module != 'wayland':
|
|
|
|
raise
|
2017-11-21 04:37:53 +03:00
|
|
|
print(err, file=sys.stderr)
|
2017-11-21 05:02:48 +03:00
|
|
|
print(error('Disabling building of wayland backend'), file=sys.stderr)
|
2017-11-21 03:37:41 +03:00
|
|
|
continue
|
2017-11-19 20:54:36 +03:00
|
|
|
sources = [os.path.join('glfw', x) for x in genv.sources]
|
|
|
|
all_headers = [os.path.join('glfw', x) for x in genv.all_headers]
|
2017-11-21 03:37:41 +03:00
|
|
|
if module == 'wayland':
|
2017-12-27 03:52:36 +03:00
|
|
|
try:
|
|
|
|
glfw.build_wayland_protocols(genv, run_tool, emphasis, newer, os.path.join(base, 'glfw'))
|
|
|
|
except SystemExit as err:
|
|
|
|
print(err, file=sys.stderr)
|
|
|
|
print(error('Disabling building of wayland backend'), file=sys.stderr)
|
|
|
|
continue
|
2017-11-21 02:50:10 +03:00
|
|
|
compile_c_extension(genv, 'kitty/glfw-' + module, incremental, compilation_database, all_keys, sources, all_headers)
|
2017-11-19 20:54:36 +03:00
|
|
|
|
|
|
|
|
2018-02-09 17:26:25 +03:00
|
|
|
def kittens_env():
|
|
|
|
kenv = env.copy()
|
|
|
|
cflags = kenv.cflags
|
|
|
|
cflags.append('-pthread')
|
|
|
|
cflags.append('-Ikitty')
|
|
|
|
pylib = get_python_flags(cflags)
|
|
|
|
kenv.ldpaths += pylib
|
|
|
|
return kenv
|
|
|
|
|
|
|
|
|
|
|
|
def compile_kittens(incremental, compilation_database, all_keys):
|
2018-05-03 17:45:05 +03:00
|
|
|
kenv = kittens_env()
|
2019-01-23 17:08:08 +03:00
|
|
|
|
|
|
|
def list_files(q):
|
|
|
|
return [os.path.relpath(x, base) for x in glob.glob(q)]
|
|
|
|
|
|
|
|
def files(kitten, output, extra_headers=(), extra_sources=(), filter_sources=None):
|
|
|
|
sources = list(filter(filter_sources, list(extra_sources) + list_files(os.path.join('kittens', kitten, '*.c'))))
|
|
|
|
headers = list_files(os.path.join('kittens', kitten, '*.h')) + list(extra_headers)
|
|
|
|
return (sources, headers, 'kittens/{}/{}'.format(kitten, output))
|
|
|
|
|
|
|
|
for sources, all_headers, dest in (
|
|
|
|
files('unicode_input', 'unicode_names'),
|
|
|
|
files('diff', 'diff_speedup'),
|
|
|
|
files(
|
|
|
|
'choose', 'subseq_matcher',
|
|
|
|
extra_headers=('kitty/charsets.h',),
|
|
|
|
extra_sources=('kitty/charsets.c',),
|
|
|
|
filter_sources=lambda x: 'windows_compat.c' not in x),
|
|
|
|
):
|
|
|
|
compile_c_extension(
|
|
|
|
kenv, dest, incremental, compilation_database, all_keys, sources, all_headers + ['kitty/data-types.h'])
|
2018-02-09 17:26:25 +03:00
|
|
|
|
|
|
|
|
2017-02-21 14:05:25 +03:00
|
|
|
def build(args, native_optimizations=True):
|
2017-11-19 10:24:02 +03:00
|
|
|
global env
|
2017-11-14 07:01:23 +03:00
|
|
|
try:
|
|
|
|
with open('compile_commands.json') as f:
|
|
|
|
compilation_database = json.load(f)
|
|
|
|
except FileNotFoundError:
|
|
|
|
compilation_database = []
|
2017-11-21 02:50:10 +03:00
|
|
|
all_keys = set()
|
2017-11-14 07:01:23 +03:00
|
|
|
compilation_database = {
|
2017-11-21 02:42:26 +03:00
|
|
|
(k['file'], k.get('output')): k['arguments'] for k in compilation_database
|
2017-11-14 07:01:23 +03:00
|
|
|
}
|
2019-04-24 13:46:40 +03:00
|
|
|
env = init_env(args.debug, args.sanitize, native_optimizations, args.profile, args.extra_logging)
|
2017-11-21 02:50:10 +03:00
|
|
|
try:
|
|
|
|
compile_c_extension(
|
|
|
|
kitty_env(), 'kitty/fast_data_types', args.incremental, compilation_database, all_keys, *find_c_files()
|
|
|
|
)
|
|
|
|
compile_glfw(args.incremental, compilation_database, all_keys)
|
2018-02-09 17:26:25 +03:00
|
|
|
compile_kittens(args.incremental, compilation_database, all_keys)
|
2017-11-21 02:50:10 +03:00
|
|
|
for key in set(compilation_database) - all_keys:
|
|
|
|
del compilation_database[key]
|
|
|
|
finally:
|
|
|
|
compilation_database = [
|
|
|
|
{'file': k[0], 'arguments': v, 'directory': base, 'output': k[1]} for k, v in compilation_database.items()
|
|
|
|
]
|
|
|
|
with open('compile_commands.json', 'w') as f:
|
|
|
|
json.dump(compilation_database, f, indent=2, sort_keys=True)
|
2017-01-08 09:47:44 +03:00
|
|
|
|
|
|
|
|
2017-01-09 08:07:10 +03:00
|
|
|
def safe_makedirs(path):
|
2017-06-08 21:25:34 +03:00
|
|
|
os.makedirs(path, exist_ok=True)
|
2017-01-09 08:07:10 +03:00
|
|
|
|
|
|
|
|
2017-11-01 09:33:59 +03:00
|
|
|
def build_asan_launcher(args):
|
|
|
|
dest = 'asan-launcher'
|
|
|
|
src = 'asan-launcher.c'
|
|
|
|
if args.incremental and not newer(dest, src):
|
|
|
|
return
|
2017-05-15 07:48:59 +03:00
|
|
|
cc, ccver = cc_version()
|
2017-11-01 12:05:57 +03:00
|
|
|
cflags = '-g3 -Wall -Werror -fpie -std=c99'.split()
|
2017-05-15 07:48:59 +03:00
|
|
|
pylib = get_python_flags(cflags)
|
2017-11-21 03:49:46 +03:00
|
|
|
sanitize_lib = ['-lasan'] if cc == 'gcc' and not is_macos else []
|
2017-11-01 09:33:59 +03:00
|
|
|
cflags.extend(get_sanitize_args(cc, ccver))
|
|
|
|
cmd = [cc] + cflags + [src, '-o', dest] + sanitize_lib + pylib
|
2017-11-19 20:54:36 +03:00
|
|
|
run_tool(cmd, desc='Creating {} ...'.format(emphasis('asan-launcher')))
|
2017-05-15 07:48:59 +03:00
|
|
|
|
|
|
|
|
2018-05-31 20:32:38 +03:00
|
|
|
def build_linux_launcher(args, launcher_dir='.', for_bundle=False, sh_launcher=False, for_freeze=False):
|
2017-08-26 07:54:12 +03:00
|
|
|
cflags = '-Wall -Werror -fpie'.split()
|
2018-04-19 06:07:01 +03:00
|
|
|
cppflags = []
|
2017-08-26 07:54:12 +03:00
|
|
|
libs = []
|
|
|
|
if args.profile:
|
2018-04-19 06:07:01 +03:00
|
|
|
cppflags.append('-DWITH_PROFILER'), cflags.append('-g')
|
2017-08-26 07:54:12 +03:00
|
|
|
libs.append('-lprofiler')
|
|
|
|
else:
|
|
|
|
cflags.append('-O3')
|
2018-05-31 20:32:38 +03:00
|
|
|
if for_bundle or for_freeze:
|
2018-04-19 06:07:01 +03:00
|
|
|
cppflags.append('-DFOR_BUNDLE')
|
|
|
|
cppflags.append('-DPYVER="{}"'.format(sysconfig.get_python_version()))
|
2018-01-11 06:10:35 +03:00
|
|
|
elif sh_launcher:
|
2018-04-19 06:07:01 +03:00
|
|
|
cppflags.append('-DFOR_LAUNCHER')
|
|
|
|
cppflags.append('-DLIB_DIR_NAME="{}"'.format(args.libdir_name.strip('/')))
|
2017-08-26 07:54:12 +03:00
|
|
|
pylib = get_python_flags(cflags)
|
|
|
|
exe = 'kitty-profile' if args.profile else 'kitty'
|
2018-04-19 06:07:01 +03:00
|
|
|
cppflags += shlex.split(os.environ.get('CPPFLAGS', ''))
|
2018-03-11 05:42:17 +03:00
|
|
|
cflags += shlex.split(os.environ.get('CFLAGS', ''))
|
|
|
|
ldflags = shlex.split(os.environ.get('LDFLAGS', ''))
|
2018-05-31 20:32:38 +03:00
|
|
|
if for_freeze:
|
|
|
|
ldflags += ['-Wl,-rpath,$ORIGIN/../lib']
|
2018-04-19 06:07:01 +03:00
|
|
|
cmd = [env.cc] + cppflags + cflags + [
|
2017-11-05 06:52:15 +03:00
|
|
|
'linux-launcher.c', '-o',
|
|
|
|
os.path.join(launcher_dir, exe)
|
2018-03-11 05:42:17 +03:00
|
|
|
] + ldflags + libs + pylib
|
2017-08-26 07:54:12 +03:00
|
|
|
run_tool(cmd)
|
|
|
|
|
|
|
|
|
2018-06-08 08:36:41 +03:00
|
|
|
# Packaging {{{
|
|
|
|
|
|
|
|
|
|
|
|
def copy_man_pages(ddir):
|
|
|
|
mandir = os.path.join(ddir, 'share', 'man')
|
|
|
|
safe_makedirs(mandir)
|
|
|
|
try:
|
|
|
|
shutil.rmtree(os.path.join(mandir, 'man1'))
|
|
|
|
except FileNotFoundError:
|
|
|
|
pass
|
2018-06-12 06:54:36 +03:00
|
|
|
src = os.path.join(base, 'docs/_build/man')
|
2018-06-08 08:36:41 +03:00
|
|
|
if not os.path.exists(src):
|
|
|
|
raise SystemExit('''\
|
2018-06-12 06:44:16 +03:00
|
|
|
The kitty man page is missing. If you are building from git then run:
|
|
|
|
make && make docs
|
2018-06-08 08:36:41 +03:00
|
|
|
(needs the sphinx documentation system to be installed)
|
|
|
|
''')
|
|
|
|
shutil.copytree(src, os.path.join(mandir, 'man1'))
|
|
|
|
|
|
|
|
|
|
|
|
def copy_html_docs(ddir):
|
|
|
|
htmldir = os.path.join(ddir, 'share', 'doc', appname, 'html')
|
|
|
|
safe_makedirs(os.path.dirname(htmldir))
|
|
|
|
try:
|
|
|
|
shutil.rmtree(htmldir)
|
|
|
|
except FileNotFoundError:
|
|
|
|
pass
|
2018-06-12 06:54:36 +03:00
|
|
|
src = os.path.join(base, 'docs/_build/html')
|
2018-06-08 08:36:41 +03:00
|
|
|
if not os.path.exists(src):
|
|
|
|
raise SystemExit('''\
|
2018-06-12 06:44:16 +03:00
|
|
|
The kitty html docs are missing. If you are building from git then run:
|
|
|
|
make && make docs
|
2018-06-08 08:36:41 +03:00
|
|
|
(needs the sphinx documentation system to be installed)
|
|
|
|
''')
|
|
|
|
shutil.copytree(src, htmldir)
|
|
|
|
|
|
|
|
|
2018-07-29 10:46:35 +03:00
|
|
|
def compile_python(base_path):
|
|
|
|
import compileall
|
2018-11-20 06:12:08 +03:00
|
|
|
import py_compile
|
2018-07-29 10:46:35 +03:00
|
|
|
try:
|
2019-04-22 06:19:22 +03:00
|
|
|
num_workers = max(1, os.cpu_count())
|
2018-07-29 10:46:35 +03:00
|
|
|
except Exception:
|
|
|
|
num_workers = 1
|
|
|
|
for root, dirs, files in os.walk(base_path):
|
|
|
|
for f in files:
|
|
|
|
if f.rpartition('.')[-1] in ('pyc', 'pyo'):
|
|
|
|
os.remove(os.path.join(root, f))
|
2019-04-22 06:16:33 +03:00
|
|
|
for optimize in (0, 1, 2):
|
|
|
|
kwargs = dict(ddir='', force=True, optimize=optimize, quiet=1, workers=num_workers)
|
|
|
|
if hasattr(py_compile, 'PycInvalidationMode'):
|
|
|
|
kwargs['invalidation_mode'] = py_compile.PycInvalidationMode.UNCHECKED_HASH
|
|
|
|
compileall.compile_dir(base_path, **kwargs)
|
2018-07-29 10:46:35 +03:00
|
|
|
|
|
|
|
|
2018-06-08 08:36:41 +03:00
|
|
|
def package(args, for_bundle=False, sh_launcher=False):
|
2017-01-09 08:07:10 +03:00
|
|
|
ddir = args.prefix
|
2018-03-12 05:40:53 +03:00
|
|
|
if for_bundle or sh_launcher:
|
|
|
|
args.libdir_name = 'lib'
|
|
|
|
libdir = os.path.join(ddir, args.libdir_name.strip('/'), 'kitty')
|
2017-01-09 08:07:10 +03:00
|
|
|
if os.path.exists(libdir):
|
|
|
|
shutil.rmtree(libdir)
|
2017-01-18 18:22:06 +03:00
|
|
|
os.makedirs(os.path.join(libdir, 'logo'))
|
2019-03-03 04:54:05 +03:00
|
|
|
build_terminfo = runpy.run_path('build-terminfo', run_name='import_build')
|
2017-01-23 06:59:23 +03:00
|
|
|
for x in (libdir, os.path.join(ddir, 'share')):
|
|
|
|
odir = os.path.join(x, 'terminfo')
|
|
|
|
safe_makedirs(odir)
|
2019-03-03 04:54:05 +03:00
|
|
|
build_terminfo['compile_terminfo'](odir)
|
2017-01-09 08:07:10 +03:00
|
|
|
shutil.copy2('__main__.py', libdir)
|
2017-01-18 18:22:06 +03:00
|
|
|
shutil.copy2('logo/kitty.rgba', os.path.join(libdir, 'logo'))
|
2019-01-29 15:41:48 +03:00
|
|
|
shutil.copy2('logo/kitty.png', os.path.join(libdir, 'logo'))
|
2018-07-07 08:09:30 +03:00
|
|
|
shutil.copy2('logo/beam-cursor.png', os.path.join(libdir, 'logo'))
|
|
|
|
shutil.copy2('logo/beam-cursor@2x.png', os.path.join(libdir, 'logo'))
|
2017-01-08 09:47:44 +03:00
|
|
|
|
|
|
|
def src_ignore(parent, entries):
|
2017-02-09 21:34:05 +03:00
|
|
|
return [
|
|
|
|
x for x in entries
|
2017-11-05 06:52:15 +03:00
|
|
|
if '.' in x and x.rpartition('.')[2] not in
|
2018-06-05 06:47:31 +03:00
|
|
|
('py', 'so', 'glsl')
|
2017-02-09 21:34:05 +03:00
|
|
|
]
|
2017-01-08 09:47:44 +03:00
|
|
|
|
2017-01-09 08:07:10 +03:00
|
|
|
shutil.copytree('kitty', os.path.join(libdir, 'kitty'), ignore=src_ignore)
|
2018-02-09 14:29:15 +03:00
|
|
|
shutil.copytree('kittens', os.path.join(libdir, 'kittens'), ignore=src_ignore)
|
2018-07-29 10:46:35 +03:00
|
|
|
compile_python(libdir)
|
2018-02-16 00:46:51 +03:00
|
|
|
for root, dirs, files in os.walk(libdir):
|
2017-01-09 08:07:10 +03:00
|
|
|
for f in files:
|
|
|
|
path = os.path.join(root, f)
|
|
|
|
os.chmod(path, 0o755 if f.endswith('.so') else 0o644)
|
2018-02-27 21:55:57 +03:00
|
|
|
shutil.copy2('kitty/launcher/kitty', os.path.join(libdir, 'kitty', 'launcher'))
|
2017-01-09 08:07:10 +03:00
|
|
|
launcher_dir = os.path.join(ddir, 'bin')
|
|
|
|
safe_makedirs(launcher_dir)
|
2018-05-31 20:32:38 +03:00
|
|
|
build_linux_launcher(args, launcher_dir, for_bundle, sh_launcher, args.for_freeze)
|
2017-11-21 03:49:46 +03:00
|
|
|
if not is_macos: # {{{ linux desktop gunk
|
2018-06-08 08:36:41 +03:00
|
|
|
copy_man_pages(ddir)
|
|
|
|
copy_html_docs(ddir)
|
2017-11-08 15:08:34 +03:00
|
|
|
icdir = os.path.join(ddir, 'share', 'icons', 'hicolor', '256x256', 'apps')
|
2017-01-18 17:56:39 +03:00
|
|
|
safe_makedirs(icdir)
|
|
|
|
shutil.copy2('logo/kitty.png', icdir)
|
|
|
|
deskdir = os.path.join(ddir, 'share', 'applications')
|
|
|
|
safe_makedirs(deskdir)
|
|
|
|
with open(os.path.join(deskdir, 'kitty.desktop'), 'w') as f:
|
2017-02-09 21:34:05 +03:00
|
|
|
f.write(
|
|
|
|
'''\
|
2017-01-18 17:56:39 +03:00
|
|
|
[Desktop Entry]
|
|
|
|
Version=1.0
|
|
|
|
Type=Application
|
|
|
|
Name=kitty
|
|
|
|
GenericName=Terminal emulator
|
2018-06-08 08:36:41 +03:00
|
|
|
Comment=A fast, feature full, GPU based terminal emulator
|
2017-01-18 17:56:39 +03:00
|
|
|
TryExec=kitty
|
|
|
|
Exec=kitty
|
|
|
|
Icon=kitty
|
2018-03-21 13:28:57 +03:00
|
|
|
Categories=System;TerminalEmulator;
|
2017-02-09 21:34:05 +03:00
|
|
|
'''
|
|
|
|
)
|
2017-02-02 09:34:52 +03:00
|
|
|
# }}}
|
|
|
|
|
2019-01-29 01:57:28 +03:00
|
|
|
if for_bundle or sh_launcher: # macOS bundle gunk {{{
|
2018-01-10 04:28:57 +03:00
|
|
|
import plistlib
|
|
|
|
logo_dir = os.path.abspath(os.path.join('logo', appname + '.iconset'))
|
2017-02-02 09:34:52 +03:00
|
|
|
os.chdir(ddir)
|
|
|
|
os.mkdir('Contents')
|
|
|
|
os.chdir('Contents')
|
2018-01-10 04:28:57 +03:00
|
|
|
VERSION = '.'.join(map(str, version))
|
|
|
|
pl = dict(
|
|
|
|
CFBundleDevelopmentRegion='English',
|
|
|
|
CFBundleDisplayName=appname,
|
|
|
|
CFBundleName=appname,
|
|
|
|
CFBundleIdentifier='net.kovidgoyal.' + appname,
|
|
|
|
CFBundleVersion=VERSION,
|
|
|
|
CFBundleShortVersionString=VERSION,
|
|
|
|
CFBundlePackageType='APPL',
|
|
|
|
CFBundleSignature='????',
|
|
|
|
CFBundleExecutable=appname,
|
|
|
|
LSMinimumSystemVersion='10.12.0',
|
|
|
|
LSRequiresNativeExecution=True,
|
|
|
|
NSAppleScriptEnabled=False,
|
2018-08-01 01:23:17 +03:00
|
|
|
# Needed for dark mode in Mojave when linking against older SDKs
|
|
|
|
NSRequiresAquaSystemAppearance='NO',
|
2018-01-10 04:28:57 +03:00
|
|
|
NSHumanReadableCopyright=time.strftime(
|
|
|
|
'Copyright %Y, Kovid Goyal'),
|
2018-07-29 16:33:41 +03:00
|
|
|
CFBundleGetInfoString='kitty, an OpenGL based terminal emulator https://sw.kovidgoyal.net/kitty',
|
2018-01-10 04:28:57 +03:00
|
|
|
CFBundleIconFile=appname + '.icns',
|
|
|
|
NSHighResolutionCapable=True,
|
|
|
|
NSSupportsAutomaticGraphicsSwitching=True,
|
|
|
|
LSApplicationCategoryType='public.app-category.utilities',
|
2018-07-15 09:49:48 +03:00
|
|
|
LSEnvironment={'KITTY_LAUNCHED_BY_LAUNCH_SERVICES': '1'},
|
2019-01-30 14:00:33 +03:00
|
|
|
NSServices=[
|
|
|
|
{
|
|
|
|
'NSMenuItem': {'default': 'New ' + appname + ' Tab Here'},
|
|
|
|
'NSMessage': 'openTab',
|
|
|
|
'NSRequiredContext': {'NSTextContent': 'FilePath'},
|
|
|
|
'NSSendTypes': ['NSFilenamesPboardType', 'public.plain-text'],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
'NSMenuItem': {'default': 'New ' + appname + ' Window Here'},
|
|
|
|
'NSMessage': 'openOSWindow',
|
|
|
|
'NSRequiredContext': {'NSTextContent': 'FilePath'},
|
|
|
|
'NSSendTypes': ['NSFilenamesPboardType', 'public.plain-text'],
|
|
|
|
},
|
|
|
|
],
|
2018-01-10 04:28:57 +03:00
|
|
|
)
|
2018-08-28 00:54:45 +03:00
|
|
|
with open('Info.plist', 'wb') as fp:
|
|
|
|
plistlib.dump(pl, fp)
|
2017-02-02 09:34:52 +03:00
|
|
|
os.rename('../share', 'Resources')
|
|
|
|
os.rename('../bin', 'MacOS')
|
|
|
|
os.rename('../lib', 'Frameworks')
|
2018-01-10 04:28:57 +03:00
|
|
|
if not os.path.exists(logo_dir):
|
|
|
|
raise SystemExit('The kitty logo has not been generated, you need to run logo/make.py')
|
2019-05-11 07:55:06 +03:00
|
|
|
cmd = [env.cc] + ['-Wall', '-Werror'] + [
|
2019-05-11 09:34:07 +03:00
|
|
|
os.path.join(base, 'symlink-deref.c'), '-o', os.path.join('MacOS', 'kitty-deref-symlink')]
|
2019-05-11 07:55:06 +03:00
|
|
|
run_tool(cmd)
|
|
|
|
|
2018-01-10 04:28:57 +03:00
|
|
|
subprocess.check_call([
|
|
|
|
'iconutil', '-c', 'icns', logo_dir, '-o',
|
|
|
|
os.path.join('Resources', os.path.basename(logo_dir).partition('.')[0] + '.icns')
|
|
|
|
])
|
2017-02-09 21:34:05 +03:00
|
|
|
# }}}
|
2018-01-11 06:10:35 +03:00
|
|
|
# }}}
|
2017-01-08 09:47:44 +03:00
|
|
|
|
|
|
|
|
2017-10-17 11:05:54 +03:00
|
|
|
def clean():
|
2018-03-12 05:20:54 +03:00
|
|
|
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
if os.path.exists('.git'):
|
|
|
|
for f in subprocess.check_output(
|
|
|
|
'git ls-files --others --ignored --exclude-from=.gitignore'.split()
|
|
|
|
).decode('utf-8').splitlines():
|
|
|
|
if f.startswith('logo/kitty.iconset') or f.startswith('dev/'):
|
|
|
|
continue
|
|
|
|
os.unlink(f)
|
|
|
|
if os.sep in f and not os.listdir(os.path.dirname(f)):
|
|
|
|
os.rmdir(os.path.dirname(f))
|
|
|
|
return
|
|
|
|
# Not a git checkout, clean manually
|
|
|
|
|
|
|
|
def safe_remove(*entries):
|
|
|
|
for x in entries:
|
|
|
|
if os.path.exists(x):
|
|
|
|
if os.path.isdir(x):
|
|
|
|
shutil.rmtree(x)
|
|
|
|
else:
|
|
|
|
os.unlink(x)
|
|
|
|
|
2018-03-14 09:40:15 +03:00
|
|
|
safe_remove('build', 'compile_commands.json', 'linux-package', 'kitty.app')
|
2018-03-12 05:20:54 +03:00
|
|
|
for root, dirs, files in os.walk('.'):
|
|
|
|
remove_dirs = {d for d in dirs if d == '__pycache__'}
|
|
|
|
[(shutil.rmtree(os.path.join(root, d)), dirs.remove(d)) for d in remove_dirs]
|
|
|
|
for f in files:
|
|
|
|
ext = f.rpartition('.')[-1]
|
|
|
|
if ext in ('so', 'dylib', 'pyc', 'pyo'):
|
|
|
|
os.unlink(os.path.join(root, f))
|
2018-03-12 05:22:08 +03:00
|
|
|
for x in glob.glob('glfw/wayland-*-protocol.[ch]'):
|
2018-03-12 05:20:54 +03:00
|
|
|
os.unlink(x)
|
2017-10-17 11:05:54 +03:00
|
|
|
|
|
|
|
|
2018-05-31 20:32:38 +03:00
|
|
|
def option_parser(): # {{{
|
2017-11-21 02:50:10 +03:00
|
|
|
p = argparse.ArgumentParser()
|
|
|
|
p.add_argument(
|
|
|
|
'action',
|
|
|
|
nargs='?',
|
|
|
|
default='build',
|
2019-01-28 06:14:37 +03:00
|
|
|
choices='build test linux-package kitty.app macos-bundle osx-bundle clean'.split(),
|
2017-11-21 02:50:10 +03:00
|
|
|
help='Action to perform (default is build)'
|
|
|
|
)
|
|
|
|
p.add_argument(
|
|
|
|
'--debug',
|
|
|
|
default=False,
|
|
|
|
action='store_true',
|
|
|
|
help='Build extension modules with debugging symbols'
|
|
|
|
)
|
|
|
|
p.add_argument(
|
|
|
|
'-v', '--verbose',
|
|
|
|
default=0,
|
|
|
|
action='count',
|
|
|
|
help='Be verbose'
|
|
|
|
)
|
|
|
|
p.add_argument(
|
|
|
|
'--sanitize',
|
|
|
|
default=False,
|
|
|
|
action='store_true',
|
|
|
|
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',
|
|
|
|
default='./linux-package',
|
|
|
|
help='Where to create the linux package'
|
|
|
|
)
|
|
|
|
p.add_argument(
|
|
|
|
'--full',
|
|
|
|
dest='incremental',
|
|
|
|
default=True,
|
|
|
|
action='store_false',
|
|
|
|
help='Do a full build, even for unchanged files'
|
|
|
|
)
|
|
|
|
p.add_argument(
|
|
|
|
'--profile',
|
|
|
|
default=False,
|
|
|
|
action='store_true',
|
|
|
|
help='Use the -pg compile flag to add profiling information'
|
|
|
|
)
|
2018-05-31 20:32:38 +03:00
|
|
|
p.add_argument(
|
|
|
|
'--for-freeze',
|
|
|
|
default=False,
|
|
|
|
action='store_true',
|
|
|
|
help='Internal use'
|
|
|
|
)
|
2018-03-12 05:40:53 +03:00
|
|
|
p.add_argument(
|
|
|
|
'--libdir-name',
|
|
|
|
default='lib',
|
2018-03-12 05:41:43 +03:00
|
|
|
help='The name of the directory inside --prefix in which to store compiled files. Defaults to "lib"'
|
2018-03-12 05:40:53 +03:00
|
|
|
)
|
2019-04-24 13:46:40 +03:00
|
|
|
p.add_argument(
|
|
|
|
'--extra-logging',
|
|
|
|
action='append',
|
2019-04-24 13:49:30 +03:00
|
|
|
default=[],
|
2019-04-24 13:46:40 +03:00
|
|
|
choices=('event-loop',),
|
|
|
|
help='Turn on extra logging for debugging in this build. Can be specified multiple times, to turn'
|
|
|
|
'on different types of logging.'
|
|
|
|
)
|
2017-11-21 02:50:10 +03:00
|
|
|
return p
|
2018-05-31 20:32:38 +03:00
|
|
|
# }}}
|
2017-11-21 02:50:10 +03:00
|
|
|
|
|
|
|
|
2016-11-07 10:53:03 +03:00
|
|
|
def main():
|
2017-11-20 12:56:27 +03:00
|
|
|
global verbose
|
2016-10-31 12:15:49 +03:00
|
|
|
if sys.version_info < (3, 5):
|
|
|
|
raise SystemExit('python >= 3.5 required')
|
2016-11-07 10:53:03 +03:00
|
|
|
args = option_parser().parse_args()
|
2017-11-20 12:56:27 +03:00
|
|
|
verbose = args.verbose > 0
|
2017-01-09 08:07:10 +03:00
|
|
|
args.prefix = os.path.abspath(args.prefix)
|
|
|
|
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
2016-11-07 10:53:03 +03:00
|
|
|
if args.action == 'build':
|
2017-01-08 09:47:44 +03:00
|
|
|
build(args)
|
2018-02-16 06:26:30 +03:00
|
|
|
if args.sanitize:
|
|
|
|
build_asan_launcher(args)
|
2017-08-26 07:54:12 +03:00
|
|
|
if args.profile:
|
|
|
|
build_linux_launcher(args)
|
|
|
|
print('kitty profile executable is', 'kitty-profile')
|
2016-11-07 10:53:03 +03:00
|
|
|
elif args.action == 'test':
|
2017-02-09 21:34:05 +03:00
|
|
|
os.execlp(
|
|
|
|
sys.executable, sys.executable, os.path.join(base, 'test.py')
|
|
|
|
)
|
2017-01-08 09:47:44 +03:00
|
|
|
elif args.action == 'linux-package':
|
2017-02-21 14:05:25 +03:00
|
|
|
build(args, native_optimizations=False)
|
2018-06-12 06:54:36 +03:00
|
|
|
if not os.path.exists(os.path.join(base, 'docs/_build/html')):
|
2018-06-12 06:50:18 +03:00
|
|
|
run_tool(['make', 'docs'])
|
2017-01-08 09:47:44 +03:00
|
|
|
package(args)
|
2019-01-28 06:14:37 +03:00
|
|
|
elif args.action in ('macos-bundle', 'osx-bundle'):
|
2017-02-21 14:05:25 +03:00
|
|
|
build(args, native_optimizations=False)
|
2017-02-02 09:34:52 +03:00
|
|
|
package(args, for_bundle=True)
|
2018-01-10 05:14:16 +03:00
|
|
|
elif args.action == 'kitty.app':
|
|
|
|
args.prefix = 'kitty.app'
|
2018-01-11 06:10:35 +03:00
|
|
|
if os.path.exists(args.prefix):
|
|
|
|
shutil.rmtree(args.prefix)
|
2018-01-10 05:14:16 +03:00
|
|
|
build(args)
|
2018-01-11 06:10:35 +03:00
|
|
|
package(args, for_bundle=False, sh_launcher=True)
|
2018-01-10 05:14:16 +03:00
|
|
|
print('kitty.app successfully built!')
|
2017-10-17 11:05:54 +03:00
|
|
|
elif args.action == 'clean':
|
|
|
|
clean()
|
2016-11-07 10:53:03 +03:00
|
|
|
|
2016-11-28 17:36:42 +03:00
|
|
|
|
2016-11-07 10:53:03 +03:00
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|