Last active
November 3, 2023 15:02
-
-
Save ichizok/1ba3d767dc161d29d29d21edd6f75cca to your computer and use it in GitHub Desktop.
For building Vim on macOS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import argparse | |
import fileinput | |
import multiprocessing | |
import os | |
import re | |
import shutil | |
import subprocess | |
import sys | |
parser = argparse.ArgumentParser(description='Build vim') | |
parser.add_argument('-v', '--version', default='', | |
help='''Specify vim version''') | |
parser.add_argument('-c', '--clean', action='store_true', | |
help='''Cleanup before build''') | |
parser.add_argument('-d', '--debug', action='store_true', | |
help='''Enable debugging features''') | |
parser.add_argument('-f', '--feature', action='store_true', | |
help='''Enable extra features''') | |
parser.add_argument('-g', '--gui', action='store_true', | |
help='''Build MacVim''') | |
parser.add_argument('-s', '--dont-update', action='store_true', | |
help='''Do not update repository''') | |
parser.add_argument('-u', '--update-only', action='store_true', | |
help='''Do only update repository (and generate tags if available)''') | |
parser.add_argument('-x', '--dont-build', action='store_true', | |
help='''Do not run `make` (stop by `configure`)''') | |
parser.add_argument('--asan', action='store_true', | |
help='''Enable Address Sanitizer (need `--debug`)''') | |
parser.add_argument('--coverage', action='store_true', | |
help='''Enable coverage analysis''') | |
parser.add_argument('--shadow', action='store_true', | |
help='''Build on shadow directory''') | |
parser.add_argument('vimdir', nargs='?', default='.', help='''Vim directory''') | |
# PREFIX = '/usr/local' | |
PREFIX = '/opt/homebrew' | |
def _(path, prefix=PREFIX): | |
return path.format(prefix) | |
def cleanup_repo(): | |
subprocess.call(['rm', '-f', 'src/auto/config.cache']) | |
subprocess.call(['make', 'distclean']) | |
def update_git_repo(version): | |
try: | |
subprocess.check_call(['git', 'reset', '--hard']) | |
subprocess.check_call(['git', 'checkout', 'master']) | |
subprocess.check_call(['git', 'pull']) | |
if len(version) > 0: | |
tag = version if version.startswith('v') else 'v' + version | |
else: | |
tag = subprocess.check_output( | |
['git', 'tag', '--sort=-creatordate']).splitlines()[0].decode() | |
subprocess.check_call(['git', 'checkout', tag]) | |
return tag[1:] | |
except subprocess.CalledProcessError: | |
print("Failed git command.", file=sys.stderr) | |
return None | |
def update_hg_repo(version): | |
try: | |
subprocess.check_call(['hg', 'rev', '--all', '--no-backup']) | |
try: | |
qtop = subprocess.check_output(['hg', 'qtop']).rstrip() | |
except subprocess.CalledProcessError: | |
qtop = None | |
if qtop is not None: | |
subprocess.call(['hg', 'qpop', '-a']) | |
subprocess.check_call(['hg', 'pull']) | |
if len(version) > 0: | |
tag = version if version.startswith('v') else 'v' + version | |
else: | |
tag = subprocess.check_output( | |
['hg', 'tags']).splitlines()[1].split()[0].decode() | |
subprocess.check_call(['hg', 'up', tag]) | |
if qtop is not None: | |
subprocess.check_call(['hg', 'qgoto', qtop]) | |
return tag[1:] | |
except subprocess.CalledProcessError: | |
print("Failed hg command.", file=sys.stderr) | |
return None | |
def update_repo(version): | |
if os.path.exists('.git'): | |
return update_git_repo(version) | |
elif os.path.isdir('.hg'): | |
return update_hg_repo(version) | |
else: | |
return None | |
def generate_tags(): | |
subprocess.call(['bash', '-c', ''' | |
if [[ -e .git/hooks/post-merge ]]; then | |
.git/hooks/post-merge | |
elif type ctags &>/dev/null; then | |
ctags -R --languages=C,C++,ObjectiveC src | |
fi | |
''']) | |
if __name__ == '__main__': | |
args = parser.parse_args() | |
vimdir = os.path.abspath(os.path.expanduser(args.vimdir)) | |
os.chdir(vimdir) | |
if args.clean: | |
cleanup_repo() | |
if not args.dont_update: | |
if not os.path.isfile('vimtutor.com'): | |
print('Maybe here is not Vim source code repository.', file=sys.stderr) | |
sys.exit(1) | |
version = update_repo(args.version) | |
if version is None: | |
sys.exit(1) | |
generate_tags() | |
if args.update_only: | |
sys.exit(0) | |
CPPFLAGS = [ | |
# '-D_GNU_SOURCE', | |
# '-D_LARGEFILE_SOURCE', | |
# '-D_FILE_OFFSET_BITS=64', | |
# _('-F{0}/Frameworks'), | |
] | |
CFLAGS = [ | |
'-Wall', | |
'-Wextra', | |
'-Wshadow', | |
'-Wno-unknown-pragmas', | |
'-g', | |
] | |
LDFLAGS = [ | |
# _('-L{0}/lib'), | |
# _('-F{0}/Frameworks'), | |
] | |
if args.debug: | |
CPPFLAGS.append('-DWE_ARE_PROFILING') | |
CFLAGS.extend(['-glldb', '-O0']) | |
LDFLAGS.append('-Wl,-no_pie') | |
if args.asan: | |
CPPFLAGS.extend([ | |
'-DABORT_ON_INTERNAL_ERROR', | |
'-DEXITFREE', | |
]) | |
else: | |
CFLAGS.extend([ | |
'-O2', | |
# '-mtune=native', | |
]) | |
if args.coverage: | |
CPPFLAGS.append('-DUSE_GCOV_FLUSH') | |
CFLAGS.append('--coverage') | |
LDFLAGS.append('--coverage') | |
command = [ | |
'/bin/sh', | |
'configure', | |
_('--prefix={0}'), | |
'--enable-gui={}'.format('macvim' if args.gui else 'no'), | |
# '--without-x', | |
'--disable-nls', | |
'--enable-multibyte', | |
'--enable-terminal', | |
'--with-tlib=ncurses', | |
'--with-features=huge', | |
# _('--with-global-runtime={0}', os.path.join(os.getcwd(), "runtime")), | |
'--enable-fail-if-missing', | |
] | |
if args.feature: | |
command.extend([ | |
# '--enable-cscope', | |
'--enable-luainterp=dynamic', | |
_('--with-lua-prefix={0}/opt/luajit')), | |
'--with-luajit', | |
'--enable-perlinterp=dynamic', | |
# '--enable-pythoninterp=dynamic', | |
'--enable-python3interp=dynamic', | |
# _('--with-python-command={0}/opt/python@2/bin/python'), | |
# _('--with-python3-command={0}/opt/python/bin/python3'), | |
'--enable-rubyinterp=dynamic', | |
_('--with-ruby-command={0}/opt/ruby/bin/ruby'), | |
]) | |
if args.gui: | |
command.append( | |
'--with-xcodecfg={}'.format('Debug' if args.debug else 'Release')) | |
os.environ['CC']='cc' | |
for name in ['CPPFLAGS', 'CFLAGS', 'LDFLAGS']: | |
os.environ[name]=' '.join(globals()[name]) | |
if args.shadow: | |
srcdir='src/shadow' | |
if os.path.exists(srcdir) and args.clean: | |
shutil.rmtree(srcdir) | |
subprocess.check_call(['make', '-C', 'src', 'shadow']) | |
else: | |
srcdir='src' | |
os.chdir(srcdir) | |
subprocess.check_call(command) | |
if args.feature or args.asan: | |
with fileinput.input('auto/config.mk', inplace = True) as f: | |
pat=re.compile(_(r'(?<={0})/Cellar/([^/]+)/[^/]+/')) | |
for line in f: | |
line=pat.sub(r'/opt/\1/', line) | |
if args.feature: | |
line=line.replace( | |
r'-DDYNAMIC_LUA_DLL=\"', _(r'-DDYNAMIC_LUA_DLL=\"{0}/opt/luajit/lib/')) | |
line=line.replace(r'-DDYNAMIC_PYTHON_DLL=\"', | |
_(r'-DDYNAMIC_PYTHON_DLL=\"{0}/Frameworks/')) | |
line=line.replace(r'-DDYNAMIC_PYTHON3_DLL=\"', | |
_(r'-DDYNAMIC_PYTHON3_DLL=\"{0}/Frameworks/')) | |
line=line.replace(r'-DDYNAMIC_RUBY_DLL=\"', | |
_(r'-DDYNAMIC_RUBY_DLL=\"{0}/opt/ruby/lib/')) | |
if args.asan: | |
if line.startswith('CFLAGS'): | |
line=line.rstrip() + " -fsanitize=address -fno-omit-frame-pointer\n" | |
elif line.startswith('LDFLAGS'): | |
line=line.rstrip() + " -fsanitize=address\n" | |
sys.stdout.write(line) | |
if not args.dont_build: | |
subprocess.call(['make', '-j{}'.format(multiprocessing.cpu_count())]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment