Skip to content

Commit

Permalink
Build script is now Python instead of shell
Browse files Browse the repository at this point in the history
Letting a shell file become that long has always been a liability. With
recent bugs regarding portability across various shells and systems,
it's time to replace the shell file with something more maintainable.
Valloric committed Jan 18, 2015
1 parent bfa20ad commit 439a809
Showing 4 changed files with 177 additions and 183 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -19,3 +19,6 @@
[submodule "third_party/requests"]
path = third_party/requests
url = https://github.com/kennethreitz/requests
[submodule "third_party/sh"]
path = third_party/sh
url = https://github.com/amoffat/sh
173 changes: 173 additions & 0 deletions build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
#!/usr/bin/env python

import os
import os.path as p
import sys

major, minor = sys.version_info[ 0 : 2 ]
if major != 2 or minor < 6:
sys.exit( 'The build script requires Python version > 2.6 and < 3.0.' )

DIR_OF_THIS_SCRIPT = p.dirname( p.abspath( __file__ ) )

for folder in os.listdir( p.join( DIR_OF_THIS_SCRIPT, 'third_party' ) ):
if p.isdir( folder ) and not os.listdir( folder ):
sys.exit( 'Some folders in ./third_party are empty; '
'you probably forgot to run:'
'\n\tgit submodule update --init --recursive\n\n' )

sys.path.insert( 0, p.abspath( 'third_party/sh' ) )
sys.path.insert( 0, p.abspath( 'third_party/argparse' ) )

import sh
import platform
import argparse
import multiprocessing
from distutils.spawn import find_executable


def OnMac():
return platform.system() == 'Darwin'


def PathToFirstExistingExecutable( executable_name_list ):
for executable_name in executable_name_list:
path = find_executable( executable_name )
if path:
return path
return None


def NumCores():
ycm_cores = os.environ.get( 'YCM_CORES' )
if ycm_cores:
return int( ycm_cores )
try:
return multiprocessing.cpu_count()
except NotImplementedError:
return 1


def CheckDeps():
if not PathToFirstExistingExecutable( [ 'cmake' ] ):
sys.exit( 'Please install CMake and retry.')


def CustomPythonCmakeArgs():
# The CMake 'FindPythonLibs' Module does not work properly.
# So we are forced to do its job for it.

python_prefix = sh.python_config( '--prefix' ).strip()
if p.isfile( p.join( python_prefix, '/Python' ) ):
python_library = p.join( python_prefix, '/Python' )
python_include = p.join( python_prefix, '/Headers' )
else:
which_python = sh.python(
'-c',
'import sys;i=sys.version_info;print "python%d.%d" % (i.major, i.minor)'
).strip()
lib_python = '{0}/lib/lib{1}'.format( python_prefix, which_python ).strip()

if p.isfile( '{0}.a'.format( lib_python ) ):
python_library = '{0}.a'.format( lib_python )
# This check is for for CYGWIN
elif p.isfile( '{0}.dll.a'.format( lib_python ) ):
python_library = '{0}.dll.a'.format( lib_python )
else:
python_library = '{0}.dylib'.format( lib_python )
python_include = '{0}/include/{1}'.format( python_prefix, which_python )

return [
'-DPYTHON_LIBRARY={0}'.format( python_library ),
'-DPYTHON_INCLUDE_DIR={0}'.format( python_include )
]


def ParseArguments():
parser = argparse.ArgumentParser()
parser.add_argument( '--clang-completer', action = 'store_true',
help = 'Build C-family semantic completion engine.')
parser.add_argument( '--system-libclang', action = 'store_true',
help = 'Use system libclang instead of downloading one '
'from llvm.org. NOT RECOMMENDED OR SUPPORTED!' )
parser.add_argument( '--omnisharp-completer', action = 'store_true',
help = 'Build C# semantic completion engine.' )
parser.add_argument( '--system-boost', action = 'store_true',
help = 'Use the system boost instead of bundled one. '
'NOT RECOMMENDED OR SUPPORTED!')
args = parser.parse_args()

if args.system_libclang and not args.clang_completer:
sys.exit( "You can't pass --system-libclang without also passing "
"--clang-completer as well." )
return args


def GetCmakeArgs( parsed_args ):
cmake_args = []
if parsed_args.clang_completer:
cmake_args.append( '-DUSE_CLANG_COMPLETER=ON' )

if parsed_args.system_libclang:
cmake_args.append( '-DUSE_SYSTEM_LIBCLANG=ON' )

if parsed_args.system_boost:
cmake_args.append( '-DUSE_SYSTEM_BOOST=ON' )

extra_cmake_args = os.environ.get( 'EXTRA_CMAKE_ARGS', '' )
cmake_args.extend( extra_cmake_args.split() )
return cmake_args


def RunYcmdTests( build_dir ):
tests_dir = p.join( build_dir, 'ycm/tests' )
sh.cd( tests_dir )
new_env = os.environ.copy()
new_env[ 'LD_LIBRARY_PATH' ] = DIR_OF_THIS_SCRIPT
sh.Command( p.join( tests_dir, 'ycm_core_tests' ) )(
_env = new_env, _out = sys.stdout )


def BuildYcmdLibs( cmake_args ):
build_dir = unicode( sh.mktemp( '-d', '-t', 'ycm_build.XXXXXX' ) ).strip()

try:
full_cmake_args = [ '-G', 'Unix Makefiles' ]
if OnMac():
full_cmake_args.extend( CustomPythonCmakeArgs() )
full_cmake_args.extend( cmake_args )
full_cmake_args.append( p.join( DIR_OF_THIS_SCRIPT, 'cpp' ) )

sh.cd( build_dir )
sh.cmake( *full_cmake_args, _out = sys.stdout )

build_target = ( 'ycm_support_libs' if 'YCM_TESTRUN' not in os.environ else
'ycm_core_tests' )
sh.make( '-j', NumCores(), build_target, _out = sys.stdout )

if 'YCM_TESTRUN' in os.environ:
RunYcmdTests( build_dir )
finally:
sh.cd( DIR_OF_THIS_SCRIPT )
sh.rm( '-rf', build_dir )


def BuildOmniSharp():
build_comamnd = PathToFirstExistingExecutable(
[ 'msbuild', 'msbuild.exe', 'xbuild' ] )
if not build_comamnd:
sys.exit( 'msbuild or xbuild is required to build Omnisharp' )

sh.cd( p.join( DIR_OF_THIS_SCRIPT, 'third_party/OmniSharpServer' ) )
sh.Command( build_comamnd )( _out = sys.stdout )


def Main():
CheckDeps()
args = ParseArguments()
BuildYcmdLibs( GetCmakeArgs( args ) )
if args.omnisharp_completer:
BuildOmniSharp()

if __name__ == "__main__":
Main()
183 changes: 0 additions & 183 deletions build.sh

This file was deleted.

1 change: 1 addition & 0 deletions third_party/sh
Submodule sh added at c361f9

0 comments on commit 439a809

Please sign in to comment.