Skip to content

Instantly share code, notes, and snippets.

@ichizok
Last active November 3, 2023 15:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ichizok/1ba3d767dc161d29d29d21edd6f75cca to your computer and use it in GitHub Desktop.
Save ichizok/1ba3d767dc161d29d29d21edd6f75cca to your computer and use it in GitHub Desktop.
For building Vim on macOS
#!/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