1#!/usr/bin/env python 2""" 3Build script for the shared library providing the C ABI bridge to LLVM. 4""" 5 6from __future__ import print_function 7 8from ctypes.util import find_library 9import re 10import os 11import subprocess 12import shutil 13import sys 14import tempfile 15 16 17here_dir = os.path.abspath(os.path.dirname(__file__)) 18build_dir = os.path.join(here_dir, 'build') 19target_dir = os.path.join(os.path.dirname(here_dir), 'llvmlite', 'binding') 20 21is_64bit = sys.maxsize >= 2**32 22 23 24def try_cmake(cmake_dir, build_dir, generator): 25 old_dir = os.getcwd() 26 try: 27 os.chdir(build_dir) 28 subprocess.check_call(['cmake', '-G', generator, cmake_dir]) 29 finally: 30 os.chdir(old_dir) 31 32 33def run_llvm_config(llvm_config, args): 34 cmd = [llvm_config] + args 35 p = subprocess.Popen(cmd, 36 stdout=subprocess.PIPE, 37 stderr=subprocess.PIPE) 38 out, err = p.communicate() 39 out = out.decode() 40 err = err.decode() 41 rc = p.wait() 42 if rc != 0: 43 raise RuntimeError("Command %s returned with code %d; stderr follows:\n%s\n" 44 % (cmd, rc, err)) 45 return out 46 47 48def find_win32_generator(): 49 """ 50 Find a suitable cmake "generator" under Windows. 51 """ 52 # XXX this assumes we will find a generator that's the same, or 53 # compatible with, the one which was used to compile LLVM... cmake 54 # seems a bit lacking here. 55 cmake_dir = os.path.join(here_dir, 'dummy') 56 # LLVM 4.0+ needs VS 2015 minimum. 57 generators = [] 58 if os.environ.get("CMAKE_GENERATOR"): 59 generators.append(os.environ.get("CMAKE_GENERATOR")) 60 61 # Drop generators that are too old 62 vspat = re.compile(r'Visual Studio (\d+)') 63 def drop_old_vs(g): 64 m = vspat.match(g) 65 if m is None: 66 return True # keep those we don't recognize 67 ver = int(m.group(1)) 68 return ver >= 14 69 generators = list(filter(drop_old_vs, generators)) 70 71 generators.append('Visual Studio 15 2017' + (' Win64' if is_64bit else '')) 72 for generator in generators: 73 build_dir = tempfile.mkdtemp() 74 print("Trying generator %r" % (generator,)) 75 try: 76 try_cmake(cmake_dir, build_dir, generator) 77 except subprocess.CalledProcessError: 78 continue 79 else: 80 # Success 81 return generator 82 finally: 83 shutil.rmtree(build_dir) 84 raise RuntimeError("No compatible cmake generator installed on this machine") 85 86 87def main_win32(): 88 generator = find_win32_generator() 89 config = 'Release' 90 if not os.path.exists(build_dir): 91 os.mkdir(build_dir) 92 # Run configuration step 93 try_cmake(here_dir, build_dir, generator) 94 subprocess.check_call(['cmake', '--build', build_dir, '--config', config]) 95 shutil.copy(os.path.join(build_dir, config, 'llvmlite.dll'), target_dir) 96 97 98def main_posix(kind, library_ext): 99 os.chdir(here_dir) 100 # Check availability of llvm-config 101 llvm_config = os.environ.get('LLVM_CONFIG', 'llvm-config') 102 print("LLVM version... ", end='') 103 sys.stdout.flush() 104 try: 105 out = subprocess.check_output([llvm_config, '--version']) 106 except (OSError, subprocess.CalledProcessError): 107 raise RuntimeError("%s failed executing, please point LLVM_CONFIG " 108 "to the path for llvm-config" % (llvm_config,)) 109 110 out = out.decode('latin1') 111 print(out) 112 113 # See if the user is overriding the version check, this is unsupported 114 try: 115 _ver_check_skip = os.environ.get("LLVMLITE_SKIP_LLVM_VERSION_CHECK", 0) 116 skipcheck = int(_ver_check_skip) 117 except ValueError as e: 118 msg = ('If set, the environment variable ' 119 'LLVMLITE_SKIP_LLVM_VERSION_CHECK should be an integer, got ' 120 '"{}".') 121 raise ValueError(msg.format(_ver_check_skip)) from e 122 123 if skipcheck: 124 # user wants to use an unsupported version, warn about doing this... 125 msg = ("The LLVM version check for supported versions has been " 126 "overridden.\nThis is unsupported behaviour, llvmlite may not " 127 "work as intended.\nRequested LLVM version: {}".format( 128 out.strip())) 129 warn = ' * '.join(("WARNING",) * 8) 130 blk = '=' * 80 131 warning = '{}\n{}\n{}'.format(blk, warn, blk) 132 print(warning) 133 print(msg) 134 print(warning + '\n') 135 else: 136 137 if not (out.startswith('10.0.') or out.startswith('9.0')): 138 msg = ("Building llvmlite requires LLVM 10.0.x or 9.0.x, got " 139 "{!r}. Be sure to set LLVM_CONFIG to the right executable " 140 "path.\nRead the documentation at " 141 "http://llvmlite.pydata.org/ for more information about " 142 "building llvmlite.\n".format(out.strip())) 143 raise RuntimeError(msg) 144 145 # Get LLVM information for building 146 libs = run_llvm_config(llvm_config, "--system-libs --libs all".split()) 147 # Normalize whitespace (trim newlines) 148 os.environ['LLVM_LIBS'] = ' '.join(libs.split()) 149 150 cxxflags = run_llvm_config(llvm_config, ["--cxxflags"]) 151 # on OSX cxxflags has null bytes at the end of the string, remove them 152 cxxflags = cxxflags.replace('\0', '') 153 cxxflags = cxxflags.split() + ['-fno-rtti', '-g'] 154 155 # look for SVML 156 include_dir = run_llvm_config(llvm_config, ['--includedir']).strip() 157 svml_indicator = os.path.join(include_dir, 'llvm', 'IR', 'SVML.inc') 158 if os.path.isfile(svml_indicator): 159 cxxflags = cxxflags + ['-DHAVE_SVML'] 160 print('SVML detected') 161 else: 162 print('SVML not detected') 163 164 os.environ['LLVM_CXXFLAGS'] = ' '.join(cxxflags) 165 166 ldflags = run_llvm_config(llvm_config, ["--ldflags"]) 167 os.environ['LLVM_LDFLAGS'] = ldflags.strip() 168 # static link libstdc++ for portability 169 if int(os.environ.get('LLVMLITE_CXX_STATIC_LINK', 0)): 170 os.environ['CXX_STATIC_LINK'] = "-static-libstdc++" 171 172 makefile = "Makefile.%s" % (kind,) 173 subprocess.check_call(['make', '-f', makefile]) 174 shutil.copy('libllvmlite' + library_ext, target_dir) 175 176 177def main(): 178 if sys.platform == 'win32': 179 main_win32() 180 elif sys.platform.startswith('linux'): 181 main_posix('linux', '.so') 182 elif sys.platform.startswith(('freebsd','openbsd', 'dragonfly')): 183 main_posix('freebsd', '.so') 184 elif sys.platform == 'darwin': 185 main_posix('osx', '.dylib') 186 else: 187 raise RuntimeError("unsupported platform: %r" % (sys.platform,)) 188 189 190if __name__ == "__main__": 191 main() 192