z3/scripts/mk_util.py

3482 lines
137 KiB
Python

############################################
# Copyright (c) 2012 Microsoft Corporation
#
# Auxiliary scripts for generating Makefiles
# and Visual Studio project files.
#
# Author: Leonardo de Moura (leonardo)
############################################
import io
import sys
import os
import re
import getopt
import shutil
from mk_exception import *
import mk_genfile_common
from fnmatch import fnmatch
import distutils.sysconfig
import compileall
import subprocess
def getenv(name, default):
try:
return os.environ[name].strip(' "\'')
except:
return default
CXX=getenv("CXX", None)
CC=getenv("CC", None)
CPPFLAGS=getenv("CPPFLAGS", "")
CXXFLAGS=getenv("CXXFLAGS", "")
AR=getenv("AR", "ar")
EXAMP_DEBUG_FLAG=''
LDFLAGS=getenv("LDFLAGS", "")
JNI_HOME=getenv("JNI_HOME", None)
OCAMLC=getenv("OCAMLC", "ocamlc")
OCAMLOPT=getenv("OCAMLOPT", "ocamlopt")
OCAML_LIB=getenv("OCAML_LIB", None)
OCAMLFIND=getenv("OCAMLFIND", "ocamlfind")
CSC=getenv("CSC", None)
DOTNET="dotnet"
GACUTIL=getenv("GACUTIL", 'gacutil')
# Standard install directories relative to PREFIX
INSTALL_BIN_DIR=getenv("Z3_INSTALL_BIN_DIR", "bin")
INSTALL_LIB_DIR=getenv("Z3_INSTALL_LIB_DIR", "lib")
INSTALL_INCLUDE_DIR=getenv("Z3_INSTALL_INCLUDE_DIR", "include")
INSTALL_PKGCONFIG_DIR=getenv("Z3_INSTALL_PKGCONFIG_DIR", os.path.join(INSTALL_LIB_DIR, 'pkgconfig'))
CXX_COMPILERS=['g++', 'clang++']
C_COMPILERS=['gcc', 'clang']
CSC_COMPILERS=['csc', 'mcs']
JAVAC=None
JAR=None
PYTHON_PACKAGE_DIR=distutils.sysconfig.get_python_lib(prefix=getenv("PREFIX", None))
BUILD_DIR='build'
REV_BUILD_DIR='..'
SRC_DIR='src'
EXAMPLE_DIR='examples'
# Required Components
Z3_DLL_COMPONENT='api_dll'
PATTERN_COMPONENT='pattern'
UTIL_COMPONENT='util'
API_COMPONENT='api'
DOTNET_COMPONENT='dotnet'
DOTNET_CORE_COMPONENT='dotnet'
JAVA_COMPONENT='java'
ML_COMPONENT='ml'
CPP_COMPONENT='cpp'
PYTHON_COMPONENT='python'
#####################
IS_WINDOWS=False
IS_LINUX=False
IS_HURD=False
IS_OSX=False
IS_FREEBSD=False
IS_NETBSD=False
IS_OPENBSD=False
IS_CYGWIN=False
IS_CYGWIN_MINGW=False
IS_MSYS2=False
VERBOSE=True
DEBUG_MODE=False
SHOW_CPPS = True
VS_X64 = False
VS_ARM = False
LINUX_X64 = True
ONLY_MAKEFILES = False
Z3PY_SRC_DIR=None
Z3JS_SRC_DIR=None
VS_PROJ = False
TRACE = False
PYTHON_ENABLED=False
DOTNET_CORE_ENABLED=False
ESRP_SIGN=False
DOTNET_KEY_FILE=getenv("Z3_DOTNET_KEY_FILE", None)
JAVA_ENABLED=False
ML_ENABLED=False
JS_ENABLED=False
PYTHON_INSTALL_ENABLED=False
STATIC_LIB=False
STATIC_BIN=False
VER_MAJOR=None
VER_MINOR=None
VER_BUILD=None
VER_TWEAK=None
PREFIX=sys.prefix
GMP=False
VS_PAR=False
VS_PAR_NUM=8
GPROF=False
GIT_HASH=False
GIT_DESCRIBE=False
SLOW_OPTIMIZE=False
LOG_SYNC=False
SINGLE_THREADED=False
GUARD_CF=False
ALWAYS_DYNAMIC_BASE=False
FPMATH="Default"
FPMATH_FLAGS="-mfpmath=sse -msse -msse2"
def check_output(cmd):
out = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0]
if out != None:
enc = sys.stdout.encoding
if enc != None: return out.decode(enc).rstrip('\r\n')
else: return out.rstrip('\r\n')
else:
return ""
def git_hash():
try:
branch = check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD'])
r = check_output(['git', 'show-ref', '--abbrev=12', 'refs/heads/%s' % branch])
except:
raise MKException("Failed to retrieve git hash")
ls = r.split(' ')
if len(ls) != 2:
raise MKException("Unexpected git output " + r)
return ls[0]
def is_windows():
return IS_WINDOWS
def is_linux():
return IS_LINUX
def is_hurd():
return IS_HURD
def is_freebsd():
return IS_FREEBSD
def is_netbsd():
return IS_NETBSD
def is_openbsd():
return IS_OPENBSD
def is_osx():
return IS_OSX
def is_cygwin():
return IS_CYGWIN
def is_cygwin_mingw():
return IS_CYGWIN_MINGW
def is_msys2():
return IS_MSYS2
def norm_path(p):
return os.path.expanduser(os.path.normpath(p))
def which(program):
import os
def is_exe(fpath):
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
fpath, fname = os.path.split(program)
if fpath:
if is_exe(program):
return program
else:
for path in getenv("PATH", "").split(os.pathsep):
exe_file = os.path.join(path, program)
if is_exe(exe_file):
return exe_file
return None
class TempFile:
def __init__(self, name):
try:
self.name = name
self.fname = open(name, 'w')
except:
raise MKException("Failed to create temporary file '%s'" % self.name)
def add(self, s):
self.fname.write(s)
def commit(self):
self.fname.close()
def __del__(self):
self.fname.close()
try:
os.remove(self.name)
except:
pass
def exec_cmd(cmd):
if isinstance(cmd, str):
cmd = cmd.split(' ')
new_cmd = []
first = True
for e in cmd:
if first:
first = False
new_cmd.append(e)
else:
if e != "":
se = e.split(' ')
if len(se) > 1:
for e2 in se:
if e2 != "":
new_cmd.append(e2)
else:
new_cmd.append(e)
cmd = new_cmd
null = open(os.devnull, 'wb')
try:
return subprocess.call(cmd, stdout=null, stderr=null)
except:
# Failed to create process
return 1
finally:
null.close()
# rm -f fname
def rmf(fname):
if os.path.exists(fname):
os.remove(fname)
def exec_compiler_cmd(cmd):
r = exec_cmd(cmd)
if is_windows() or is_cygwin_mingw() or is_cygwin() or is_msys2():
rmf('a.exe')
else:
rmf('a.out')
return r
def test_cxx_compiler(cc):
if is_verbose():
print("Testing %s..." % cc)
t = TempFile('tst.cpp')
t.add('#include<iostream>\nint main() { return 0; }\n')
t.commit()
return exec_compiler_cmd([cc, CPPFLAGS, CXXFLAGS, 'tst.cpp', LDFLAGS]) == 0
def test_c_compiler(cc):
if is_verbose():
print("Testing %s..." % cc)
t = TempFile('tst.c')
t.add('#include<stdio.h>\nint main() { return 0; }\n')
t.commit()
return exec_compiler_cmd([cc, CPPFLAGS, 'tst.c', LDFLAGS]) == 0
def test_gmp(cc):
if is_verbose():
print("Testing GMP...")
t = TempFile('tstgmp.cpp')
t.add('#include<gmp.h>\nint main() { mpz_t t; mpz_init(t); mpz_clear(t); return 0; }\n')
t.commit()
return exec_compiler_cmd([cc, CPPFLAGS, 'tstgmp.cpp', LDFLAGS, '-lgmp']) == 0
def test_fpmath(cc):
global FPMATH_FLAGS
if is_verbose():
print("Testing floating point support...")
t = TempFile('tstsse.cpp')
t.add('int main() { return 42; }\n')
t.commit()
# -Werror is needed because some versions of clang warn about unrecognized
# -m flags.
if exec_compiler_cmd([cc, CPPFLAGS, '-Werror', 'tstsse.cpp', LDFLAGS, '-mfpmath=sse -msse -msse2']) == 0:
FPMATH_FLAGS="-mfpmath=sse -msse -msse2"
return "SSE2-GCC"
elif exec_compiler_cmd([cc, CPPFLAGS, '-Werror', 'tstsse.cpp', LDFLAGS, '-msse -msse2']) == 0:
FPMATH_FLAGS="-msse -msse2"
return "SSE2-CLANG"
elif exec_compiler_cmd([cc, CPPFLAGS, '-Werror', 'tstsse.cpp', LDFLAGS, '-mfpu=vfp -mfloat-abi=hard']) == 0:
FPMATH_FLAGS="-mfpu=vfp -mfloat-abi=hard"
return "ARM-VFP"
else:
FPMATH_FLAGS=""
return "UNKNOWN"
def find_jni_h(path):
for root, dirs, files in os.walk(path):
for f in files:
if f == 'jni.h':
return root
return False
def check_java():
global JNI_HOME
global JAVAC
global JAR
JDK_HOME = getenv('JDK_HOME', None) # we only need to check this locally.
if is_verbose():
print("Finding javac ...")
if JDK_HOME is not None:
if IS_WINDOWS:
JAVAC = os.path.join(JDK_HOME, 'bin', 'javac.exe')
else:
JAVAC = os.path.join(JDK_HOME, 'bin', 'javac')
if not os.path.exists(JAVAC):
raise MKException("Failed to detect javac at '%s/bin'; the environment variable JDK_HOME is probably set to the wrong path." % os.path.join(JDK_HOME))
else:
# Search for javac in the path.
ind = 'javac'
if IS_WINDOWS:
ind = ind + '.exe'
paths = os.getenv('PATH', None)
if paths:
spaths = paths.split(os.pathsep)
for i in range(0, len(spaths)):
cmb = os.path.join(spaths[i], ind)
if os.path.exists(cmb):
JAVAC = cmb
break
if JAVAC is None:
raise MKException('No java compiler in the path, please adjust your PATH or set JDK_HOME to the location of the JDK.')
if is_verbose():
print("Finding jar ...")
if IS_WINDOWS:
JAR = os.path.join(os.path.dirname(JAVAC), 'jar.exe')
else:
JAR = os.path.join(os.path.dirname(JAVAC), 'jar')
if not os.path.exists(JAR):
raise MKException("Failed to detect jar at '%s'; the environment variable JDK_HOME is probably set to the wrong path." % os.path.join(JDK_HOME))
if is_verbose():
print("Testing %s..." % JAVAC)
t = TempFile('Hello.java')
t.add('public class Hello { public static void main(String[] args) { System.out.println("Hello, World"); }}\n')
t.commit()
oo = TempFile('output')
eo = TempFile('errout')
try:
subprocess.call([JAVAC, 'Hello.java', '-verbose'], stdout=oo.fname, stderr=eo.fname)
oo.commit()
eo.commit()
except:
raise MKException('Found, but failed to run Java compiler at %s' % (JAVAC))
os.remove('Hello.class')
if is_verbose():
print("Finding jni.h...")
if JNI_HOME is not None:
if not os.path.exists(os.path.join(JNI_HOME, 'jni.h')):
raise MKException("Failed to detect jni.h '%s'; the environment variable JNI_HOME is probably set to the wrong path." % os.path.join(JNI_HOME))
else:
# Search for jni.h in the library directories...
t = open('errout', 'r')
open_pat = re.compile("\[search path for class files: (.*)\]")
cdirs = []
for line in t:
m = open_pat.match(line)
if m:
libdirs = m.group(1).split(',')
for libdir in libdirs:
q = os.path.dirname(libdir)
if cdirs.count(q) == 0 and len(q) > 0:
cdirs.append(q)
t.close()
# ... plus some heuristic ones.
extra_dirs = []
# For the libraries, even the JDK usually uses a JRE that comes with it. To find the
# headers we have to go a little bit higher up.
for dir in cdirs:
extra_dirs.append(os.path.abspath(os.path.join(dir, '..')))
if IS_OSX: # Apparently Apple knows best where to put stuff...
extra_dirs.append('/System/Library/Frameworks/JavaVM.framework/Headers/')
cdirs[len(cdirs):] = extra_dirs
for dir in cdirs:
q = find_jni_h(dir)
if q is not False:
JNI_HOME = q
if JNI_HOME is None:
raise MKException("Failed to detect jni.h. Possible solution: set JNI_HOME with the path to JDK.")
def test_csc_compiler(c):
t = TempFile('hello.cs')
t.add('public class hello { public static void Main() {} }')
t.commit()
if is_verbose():
print ('Testing %s...' % c)
r = exec_cmd([c, 'hello.cs'])
try:
rmf('hello.cs')
rmf('hello.exe')
except:
pass
return r == 0
def check_dotnet_core():
if not IS_WINDOWS:
return
r = exec_cmd([DOTNET, '--help'])
if r != 0:
raise MKException('Failed testing dotnet. Make sure to install and configure dotnet core utilities')
def check_ml():
t = TempFile('hello.ml')
t.add('print_string "Hello world!\n";;')
t.commit()
if is_verbose():
print ('Testing %s...' % OCAMLC)
r = exec_cmd([OCAMLC, '-o', 'a.out', 'hello.ml'])
if r != 0:
raise MKException('Failed testing ocamlc compiler. Set environment variable OCAMLC with the path to the Ocaml compiler')
if is_verbose():
print ('Testing %s...' % OCAMLOPT)
r = exec_cmd([OCAMLOPT, '-o', 'a.out', 'hello.ml'])
if r != 0:
raise MKException('Failed testing ocamlopt compiler. Set environment variable OCAMLOPT with the path to the Ocaml native compiler. Note that ocamlopt may require flexlink to be in your path.')
try:
rmf('hello.cmi')
rmf('hello.cmo')
rmf('hello.cmx')
rmf('a.out')
rmf('hello.o')
except:
pass
find_ml_lib()
find_ocaml_find()
def find_ocaml_find():
global OCAMLFIND
if is_verbose():
print ("Testing %s..." % OCAMLFIND)
r = exec_cmd([OCAMLFIND, 'printconf'])
if r != 0:
OCAMLFIND = ''
def find_ml_lib():
global OCAML_LIB
if is_verbose():
print ('Finding OCAML_LIB...')
t = TempFile('output')
null = open(os.devnull, 'wb')
try:
subprocess.call([OCAMLC, '-where'], stdout=t.fname, stderr=null)
t.commit()
except:
raise MKException('Failed to find Ocaml library; please set OCAML_LIB')
finally:
null.close()
t = open('output', 'r')
for line in t:
OCAML_LIB = line[:-1]
if is_verbose():
print ('OCAML_LIB=%s' % OCAML_LIB)
t.close()
rmf('output')
return
def is64():
global LINUX_X64
return LINUX_X64 and sys.maxsize >= 2**32
def check_ar():
if is_verbose():
print("Testing ar...")
if which(AR) is None:
raise MKException('%s (archive tool) was not found' % AR)
def find_cxx_compiler():
global CXX, CXX_COMPILERS
if CXX is not None:
if test_cxx_compiler(CXX):
return CXX
for cxx in CXX_COMPILERS:
if test_cxx_compiler(cxx):
CXX = cxx
return CXX
raise MKException('C++ compiler was not found. Try to set the environment variable CXX with the C++ compiler available in your system.')
def find_c_compiler():
global CC, C_COMPILERS
if CC is not None:
if test_c_compiler(CC):
return CC
for c in C_COMPILERS:
if test_c_compiler(c):
CC = c
return CC
raise MKException('C compiler was not found. Try to set the environment variable CC with the C compiler available in your system.')
def set_version(major, minor, build, revision):
global VER_MAJOR, VER_MINOR, VER_BUILD, VER_TWEAK, GIT_DESCRIBE
VER_MAJOR = major
VER_MINOR = minor
VER_BUILD = build
VER_TWEAK = revision
if GIT_DESCRIBE:
branch = check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD'])
VER_TWEAK = int(check_output(['git', 'rev-list', '--count', 'HEAD']))
def get_version():
return (VER_MAJOR, VER_MINOR, VER_BUILD, VER_TWEAK)
def get_version_string(n):
if n == 3:
return "{}.{}.{}".format(VER_MAJOR,VER_MINOR,VER_BUILD)
return "{}.{}.{}.{}".format(VER_MAJOR,VER_MINOR,VER_BUILD,VER_TWEAK)
def build_static_lib():
return STATIC_LIB
def build_static_bin():
return STATIC_BIN
def is_cr_lf(fname):
# Check whether text files use cr/lf
f = open(fname, 'r')
line = f.readline()
f.close()
sz = len(line)
return sz >= 2 and line[sz-2] == '\r' and line[sz-1] == '\n'
# dos2unix in python
# cr/lf --> lf
def dos2unix(fname):
if is_cr_lf(fname):
fin = open(fname, 'r')
fname_new = '%s.new' % fname
fout = open(fname_new, 'w')
for line in fin:
line = line.rstrip('\r\n')
fout.write(line)
fout.write('\n')
fin.close()
fout.close()
shutil.move(fname_new, fname)
if is_verbose():
print("dos2unix '%s'" % fname)
def dos2unix_tree():
for root, dirs, files in os.walk('src'):
for f in files:
dos2unix(os.path.join(root, f))
def check_eol():
if not IS_WINDOWS:
# Linux/OSX/BSD check if the end-of-line is cr/lf
if is_cr_lf('LICENSE.txt'):
if is_verbose():
print("Fixing end of line...")
dos2unix_tree()
if os.name == 'nt':
IS_WINDOWS=True
# Visual Studio already displays the files being compiled
SHOW_CPPS=False
elif os.name == 'posix':
if os.uname()[0] == 'Darwin':
IS_OSX=True
PREFIX="/usr/local"
elif os.uname()[0] == 'Linux':
IS_LINUX=True
elif os.uname()[0] == 'GNU':
IS_HURD=True
elif os.uname()[0] == 'FreeBSD':
IS_FREEBSD=True
elif os.uname()[0] == 'NetBSD':
IS_NETBSD=True
elif os.uname()[0] == 'OpenBSD':
IS_OPENBSD=True
elif os.uname()[0][:6] == 'CYGWIN':
IS_CYGWIN=True
if (CC != None and "mingw" in CC):
IS_CYGWIN_MINGW=True
elif os.uname()[0].startswith('MSYS_NT') or os.uname()[0].startswith('MINGW'):
IS_MSYS2=True
if os.uname()[4] == 'x86_64':
LINUX_X64=True
else:
LINUX_X64=False
def display_help(exit_code):
print("mk_make.py: Z3 Makefile generator\n")
print("This script generates the Makefile for the Z3 theorem prover.")
print("It must be executed from the Z3 root directory.")
print("\nOptions:")
print(" -h, --help display this message.")
print(" -s, --silent do not print verbose messages.")
if not IS_WINDOWS:
print(" -p <dir>, --prefix=<dir> installation prefix (default: %s)." % PREFIX)
else:
print(" --parallel=num use cl option /MP with 'num' parallel processes")
print(" --pypkgdir=<dir> Force a particular Python package directory (default %s)" % PYTHON_PACKAGE_DIR)
print(" -b <subdir>, --build=<subdir> subdirectory where Z3 will be built (default: %s)." % BUILD_DIR)
print(" --githash=hash include the given hash in the binaries.")
print(" --git-describe include the output of 'git describe' in the version information.")
print(" -d, --debug compile Z3 in debug mode.")
print(" -t, --trace enable tracing in release mode.")
if IS_WINDOWS:
print(" --guardcf enable Control Flow Guard runtime checks.")
print(" -x, --x64 create 64 binary when using Visual Studio.")
else:
print(" --x86 force 32-bit x86 build on x64 systems.")
print(" -m, --makefiles generate only makefiles.")
if IS_WINDOWS:
print(" -v, --vsproj generate Visual Studio Project Files.")
print(" --optimize generate optimized code during linking.")
print(" --dotnet generate .NET platform bindings.")
print(" --dotnet-key=<file> sign the .NET assembly using the private key in <file>.")
print(" --java generate Java bindings.")
print(" --ml generate OCaml bindings.")
print(" --js generate JScript bindings.")
print(" --python generate Python bindings.")
print(" --staticlib build Z3 static library.")
print(" --staticbin build a statically linked Z3 binary.")
if not IS_WINDOWS:
print(" -g, --gmp use GMP.")
print(" --gprof enable gprof")
print(" --log-sync synchronize access to API log files to enable multi-thread API logging.")
print(" --single-threaded non-thread-safe build")
print("")
print("Some influential environment variables:")
if not IS_WINDOWS:
print(" CXX C++ compiler")
print(" CC C compiler")
print(" LDFLAGS Linker flags, e.g., -L<lib dir> if you have libraries in a non-standard directory")
print(" CPPFLAGS Preprocessor flags, e.g., -I<include dir> if you have header files in a non-standard directory")
print(" CXXFLAGS C++ compiler flags")
print(" JDK_HOME JDK installation directory (only relevant if -j or --java option is provided)")
print(" JNI_HOME JNI bindings directory (only relevant if -j or --java option is provided)")
print(" OCAMLC Ocaml byte-code compiler (only relevant with --ml)")
print(" OCAMLFIND Ocaml find tool (only relevant with --ml)")
print(" OCAMLOPT Ocaml native compiler (only relevant with --ml)")
print(" OCAML_LIB Ocaml library directory (only relevant with --ml)")
print(" CSC C# Compiler (only relevant if .NET bindings are enabled)")
print(" GACUTIL GAC Utility (only relevant if .NET bindings are enabled)")
print(" Z3_INSTALL_BIN_DIR Install directory for binaries relative to install prefix")
print(" Z3_INSTALL_LIB_DIR Install directory for libraries relative to install prefix")
print(" Z3_INSTALL_INCLUDE_DIR Install directory for header files relative to install prefix")
print(" Z3_INSTALL_PKGCONFIG_DIR Install directory for pkgconfig files relative to install prefix")
exit(exit_code)
# Parse configuration option for mk_make script
def parse_options():
global VERBOSE, DEBUG_MODE, IS_WINDOWS, VS_X64, ONLY_MAKEFILES, SHOW_CPPS, VS_PROJ, TRACE, VS_PAR, VS_PAR_NUM
global DOTNET_CORE_ENABLED, DOTNET_KEY_FILE, JAVA_ENABLED, ML_ENABLED, JS_ENABLED, STATIC_LIB, STATIC_BIN, PREFIX, GMP, PYTHON_PACKAGE_DIR, GPROF, GIT_HASH, GIT_DESCRIBE, PYTHON_INSTALL_ENABLED, PYTHON_ENABLED, ESRP_SIGN
global LINUX_X64, SLOW_OPTIMIZE, LOG_SYNC, SINGLE_THREADED
global GUARD_CF, ALWAYS_DYNAMIC_BASE
try:
options, remainder = getopt.gnu_getopt(sys.argv[1:],
'b:df:sxhmcvtnp:gj',
['build=', 'debug', 'silent', 'x64', 'help', 'makefiles', 'showcpp', 'vsproj', 'guardcf',
'trace', 'dotnet', 'dotnet-key=', 'esrp', 'staticlib', 'prefix=', 'gmp', 'java', 'parallel=', 'gprof', 'js',
'githash=', 'git-describe', 'x86', 'ml', 'optimize', 'pypkgdir=', 'python', 'staticbin', 'log-sync', 'single-threaded'])
except:
print("ERROR: Invalid command line option")
display_help(1)
for opt, arg in options:
print('opt = %s, arg = %s' % (opt, arg))
if opt in ('-b', '--build'):
if arg == 'src':
raise MKException('The src directory should not be used to host the Makefile')
set_build_dir(arg)
elif opt in ('-s', '--silent'):
VERBOSE = False
elif opt in ('-d', '--debug'):
DEBUG_MODE = True
elif opt in ('-x', '--x64'):
if not IS_WINDOWS:
raise MKException('x64 compilation mode can only be specified when using Visual Studio')
VS_X64 = True
elif opt in ('--x86'):
LINUX_X64=False
elif opt in ('-h', '--help'):
display_help(0)
elif opt in ('-m', '--makefiles'):
ONLY_MAKEFILES = True
elif opt in ('-c', '--showcpp'):
SHOW_CPPS = True
elif opt in ('-v', '--vsproj'):
VS_PROJ = True
elif opt in ('-t', '--trace'):
TRACE = True
elif opt in ('--dotnet',):
DOTNET_CORE_ENABLED = True
elif opt in ('--dotnet-key'):
DOTNET_KEY_FILE = arg
elif opt in ('--esrp'):
ESRP_SIGN = True
elif opt in ('--staticlib'):
STATIC_LIB = True
elif opt in ('--staticbin'):
STATIC_BIN = True
elif opt in ('--optimize'):
SLOW_OPTIMIZE = True
elif not IS_WINDOWS and opt in ('-p', '--prefix'):
PREFIX = arg
elif opt in ('--pypkgdir'):
PYTHON_PACKAGE_DIR = arg
elif IS_WINDOWS and opt == '--parallel':
VS_PAR = True
VS_PAR_NUM = int(arg)
elif opt in ('-g', '--gmp'):
GMP = True
elif opt in ('-j', '--java'):
JAVA_ENABLED = True
elif opt == '--gprof':
GPROF = True
elif opt == '--githash':
GIT_HASH=arg
elif opt == '--git-describe':
GIT_DESCRIBE = True
elif opt in ('', '--ml'):
ML_ENABLED = True
elif opt == "--js":
JS_ENABLED = True
elif opt in ('', '--log-sync'):
LOG_SYNC = True
elif opt == '--single-threaded':
SINGLE_THREADED = True
elif opt in ('--python'):
PYTHON_ENABLED = True
PYTHON_INSTALL_ENABLED = True
elif opt == '--guardcf':
GUARD_CF = True
ALWAYS_DYNAMIC_BASE = True # /GUARD:CF requires /DYNAMICBASE
else:
print("ERROR: Invalid command line option '%s'" % opt)
display_help(1)
# Return a list containing a file names included using '#include' in
# the given C/C++ file named fname.
def extract_c_includes(fname):
result = []
# We look for well behaved #include directives
std_inc_pat = re.compile("[ \t]*#include[ \t]*\"(.*)\"[ \t]*")
system_inc_pat = re.compile("[ \t]*#include[ \t]*\<.*\>[ \t]*")
# We should generate and error for any occurrence of #include that does not match the previous pattern.
non_std_inc_pat = re.compile(".*#include.*")
f = io.open(fname, encoding='utf-8', mode='r')
linenum = 1
for line in f:
m1 = std_inc_pat.match(line)
if m1:
root_file_name = m1.group(1)
slash_pos = root_file_name.rfind('/')
if slash_pos >= 0 and root_file_name.find("..") < 0 : #it is a hack for lp include files that behave as continued from "src"
# print(root_file_name)
root_file_name = root_file_name[slash_pos+1:]
result.append(root_file_name)
elif not system_inc_pat.match(line) and non_std_inc_pat.match(line):
raise MKException("Invalid #include directive at '%s':%s" % (fname, line))
linenum = linenum + 1
f.close()
return result
# Given a path dir1/subdir2/subdir3 returns ../../..
def reverse_path(p):
# Filter out empty components (e.g. will have one if path ends in a slash)
l = list(filter(lambda x: len(x) > 0, p.split(os.sep)))
n = len(l)
r = '..'
for i in range(1, n):
r = os.path.join(r, '..')
return r
def mk_dir(d):
if not os.path.exists(d):
os.makedirs(d)
def set_build_dir(d):
global BUILD_DIR, REV_BUILD_DIR
BUILD_DIR = norm_path(d)
REV_BUILD_DIR = reverse_path(d)
def set_z3js_dir(p):
global SRC_DIR, Z3JS_SRC_DIR
p = norm_path(p)
full = os.path.join(SRC_DIR, p)
if not os.path.exists(full):
raise MKException("Python bindings directory '%s' does not exist" % full)
Z3JS_SRC_DIR = full
if VERBOSE:
print("Js bindings directory was detected.")
def set_z3py_dir(p):
global SRC_DIR, Z3PY_SRC_DIR
p = norm_path(p)
full = os.path.join(SRC_DIR, p)
if not os.path.exists(full):
raise MKException("Python bindings directory '%s' does not exist" % full)
Z3PY_SRC_DIR = full
if VERBOSE:
print("Python bindings directory was detected.")
_UNIQ_ID = 0
def mk_fresh_name(prefix):
global _UNIQ_ID
r = '%s_%s' % (prefix, _UNIQ_ID)
_UNIQ_ID = _UNIQ_ID + 1
return r
_Id = 0
_Components = []
_ComponentNames = set()
_Name2Component = {}
_Processed_Headers = set()
# Return the Component object named name
def get_component(name):
return _Name2Component[name]
def get_components():
return _Components
# Return the directory where the python bindings are located.
def get_z3py_dir():
return Z3PY_SRC_DIR
# Return directory where the js bindings are located
def get_z3js_dir():
return Z3JS_SRC_DIR
# Return true if in verbose mode
def is_verbose():
return VERBOSE
def is_java_enabled():
return JAVA_ENABLED
def is_ml_enabled():
return ML_ENABLED
def is_js_enabled():
return JS_ENABLED
def is_dotnet_core_enabled():
return DOTNET_CORE_ENABLED
def is_python_enabled():
return PYTHON_ENABLED
def is_python_install_enabled():
return PYTHON_INSTALL_ENABLED
def is_compiler(given, expected):
"""
Return True if the 'given' compiler is the expected one.
>>> is_compiler('g++', 'g++')
True
>>> is_compiler('/home/g++', 'g++')
True
>>> is_compiler(os.path.join('home', 'g++'), 'g++')
True
>>> is_compiler('clang++', 'g++')
False
>>> is_compiler(os.path.join('home', 'clang++'), 'clang++')
True
"""
if given == expected:
return True
if len(expected) < len(given):
return given[len(given) - len(expected) - 1] == os.sep and given[len(given) - len(expected):] == expected
return False
def is_CXX_gpp():
return is_compiler(CXX, 'g++')
def is_clang_in_gpp_form(cc):
str = check_output([cc, '--version'])
try:
version_string = str.encode('utf-8')
except:
version_string = str
clang = 'clang'.encode('utf-8')
return version_string.find(clang) != -1
def is_CXX_clangpp():
if is_compiler(CXX, 'g++'):
return is_clang_in_gpp_form(CXX)
return is_compiler(CXX, 'clang++')
def get_cpp_files(path):
return filter(lambda f: f.endswith('.cpp'), os.listdir(path))
def get_c_files(path):
return filter(lambda f: f.endswith('.c'), os.listdir(path))
def get_cs_files(path):
return filter(lambda f: f.endswith('.cs'), os.listdir(path))
def get_java_files(path):
return filter(lambda f: f.endswith('.java'), os.listdir(path))
def get_ml_files(path):
return filter(lambda f: f.endswith('.ml'), os.listdir(path))
def find_all_deps(name, deps):
new_deps = []
for dep in deps:
if dep in _ComponentNames:
if not (dep in new_deps):
new_deps.append(dep)
for dep_dep in get_component(dep).deps:
if not (dep_dep in new_deps):
new_deps.append(dep_dep)
else:
raise MKException("Unknown component '%s' at '%s'." % (dep, name))
return new_deps
class Component:
def __init__(self, name, path, deps):
global BUILD_DIR, SRC_DIR, REV_BUILD_DIR
if name in _ComponentNames:
raise MKException("Component '%s' was already defined." % name)
if path is None:
path = name
self.name = name
path = norm_path(path)
self.path = path
self.deps = find_all_deps(name, deps)
self.build_dir = path
self.src_dir = os.path.join(SRC_DIR, path)
self.to_src_dir = os.path.join(REV_BUILD_DIR, self.src_dir)
def get_link_name(self):
return os.path.join(self.build_dir, self.name) + '$(LIB_EXT)'
# Find fname in the include paths for the given component.
# ownerfile is only used for creating error messages.
# That is, we were looking for fname when processing ownerfile
def find_file(self, fname, ownerfile):
full_fname = os.path.join(self.src_dir, fname)
if os.path.exists(full_fname):
return self
for dep in self.deps:
c_dep = get_component(dep)
full_fname = os.path.join(c_dep.src_dir, fname)
if os.path.exists(full_fname):
return c_dep
raise MKException("Failed to find include file '%s' for '%s' when processing '%s'." % (fname, ownerfile, self.name))
# Display all dependencies of file basename located in the given component directory.
# The result is displayed at out
def add_cpp_h_deps(self, out, basename):
includes = extract_c_includes(os.path.join(self.src_dir, basename))
out.write(os.path.join(self.to_src_dir, basename))
for include in includes:
owner = self.find_file(include, basename)
out.write(' %s.node' % os.path.join(owner.build_dir, include))
# Add a rule for each #include directive in the file basename located at the current component.
def add_rule_for_each_include(self, out, basename):
fullname = os.path.join(self.src_dir, basename)
includes = extract_c_includes(fullname)
for include in includes:
owner = self.find_file(include, fullname)
owner.add_h_rule(out, include)
# Display a Makefile rule for an include file located in the given component directory.
# 'include' is something of the form: ast.h, polynomial.h
# The rule displayed at out is of the form
# ast/ast_pp.h.node : ../src/util/ast_pp.h util/util.h.node ast/ast.h.node
# @echo "done" > ast/ast_pp.h.node
def add_h_rule(self, out, include):
include_src_path = os.path.join(self.to_src_dir, include)
if include_src_path in _Processed_Headers:
return
_Processed_Headers.add(include_src_path)
self.add_rule_for_each_include(out, include)
include_node = '%s.node' % os.path.join(self.build_dir, include)
out.write('%s: ' % include_node)
self.add_cpp_h_deps(out, include)
out.write('\n')
out.write('\t@echo done > %s\n' % include_node)
def add_cpp_rules(self, out, include_defs, cppfile):
self.add_rule_for_each_include(out, cppfile)
objfile = '%s$(OBJ_EXT)' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0])
srcfile = os.path.join(self.to_src_dir, cppfile)
out.write('%s: ' % objfile)
self.add_cpp_h_deps(out, cppfile)
out.write('\n')
if SHOW_CPPS:
out.write('\t@echo %s\n' % os.path.join(self.src_dir, cppfile))
out.write('\t@$(CXX) $(CXXFLAGS) $(%s) $(CXX_OUT_FLAG)%s %s\n' % (include_defs, objfile, srcfile))
def mk_makefile(self, out):
include_defs = mk_fresh_name('includes')
out.write('%s =' % include_defs)
for dep in self.deps:
out.write(' -I%s' % get_component(dep).to_src_dir)
out.write(' -I%s' % os.path.join(REV_BUILD_DIR,"src"))
out.write('\n')
mk_dir(os.path.join(BUILD_DIR, self.build_dir))
if VS_PAR and IS_WINDOWS:
cppfiles = list(get_cpp_files(self.src_dir))
dependencies = set()
for cppfile in cppfiles:
dependencies.add(os.path.join(self.to_src_dir, cppfile))
self.add_rule_for_each_include(out, cppfile)
includes = extract_c_includes(os.path.join(self.src_dir, cppfile))
for include in includes:
owner = self.find_file(include, cppfile)
dependencies.add('%s.node' % os.path.join(owner.build_dir, include))
for cppfile in cppfiles:
out.write('%s$(OBJ_EXT) ' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0]))
out.write(': ')
for dep in dependencies:
out.write(dep)
out.write(' ')
out.write('\n')
out.write('\t@$(CXX) $(CXXFLAGS) /MP%s $(%s)' % (VS_PAR_NUM, include_defs))
for cppfile in cppfiles:
out.write(' ')
out.write(os.path.join(self.to_src_dir, cppfile))
out.write('\n')
out.write('\tmove *.obj %s\n' % self.build_dir)
else:
for cppfile in get_cpp_files(self.src_dir):
self.add_cpp_rules(out, include_defs, cppfile)
# Return true if the component should be included in the all: rule
def main_component(self):
return False
# Return true if the component contains an AssemblyInfo.cs file that needs to be updated.
def has_assembly_info(self):
return False
# Return true if the component needs builder to generate an install_tactics.cpp file
def require_install_tactics(self):
return False
# Return true if the component needs a def file
def require_def_file(self):
return False
# Return true if the component needs builder to generate a mem_initializer.cpp file with mem_initialize() and mem_finalize() functions.
def require_mem_initializer(self):
return False
def mk_install_deps(self, out):
return
def mk_install(self, out):
return
def mk_uninstall(self, out):
return
def is_example(self):
return False
# Invoked when creating a (windows) distribution package using components at build_path, and
# storing them at dist_path
def mk_win_dist(self, build_path, dist_path):
return
def mk_unix_dist(self, build_path, dist_path):
return
# Used to print warnings or errors after mk_make.py is done, so that they
# are not quite as easy to miss.
def final_info(self):
pass
class LibComponent(Component):
def __init__(self, name, path, deps, includes2install):
Component.__init__(self, name, path, deps)
self.includes2install = includes2install
def mk_makefile(self, out):
Component.mk_makefile(self, out)
# generate rule for lib
objs = []
for cppfile in get_cpp_files(self.src_dir):
objfile = '%s$(OBJ_EXT)' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0])
objs.append(objfile)
libfile = '%s$(LIB_EXT)' % os.path.join(self.build_dir, self.name)
out.write('%s:' % libfile)
for obj in objs:
out.write(' ')
out.write(obj)
out.write('\n')
out.write('\t@$(AR) $(AR_FLAGS) $(AR_OUTFLAG)%s' % libfile)
for obj in objs:
out.write(' ')
out.write(obj)
out.write('\n')
out.write('%s: %s\n\n' % (self.name, libfile))
def mk_install_deps(self, out):
return
def mk_install(self, out):
for include in self.includes2install:
MakeRuleCmd.install_files(
out,
os.path.join(self.to_src_dir, include),
os.path.join(INSTALL_INCLUDE_DIR, include)
)
def mk_uninstall(self, out):
for include in self.includes2install:
MakeRuleCmd.remove_installed_files(out, os.path.join(INSTALL_INCLUDE_DIR, include))
def mk_win_dist(self, build_path, dist_path):
mk_dir(os.path.join(dist_path, INSTALL_INCLUDE_DIR))
for include in self.includes2install:
shutil.copy(os.path.join(self.src_dir, include),
os.path.join(dist_path, INSTALL_INCLUDE_DIR, include))
def mk_unix_dist(self, build_path, dist_path):
self.mk_win_dist(build_path, dist_path)
# "Library" containing only .h files. This is just a placeholder for includes files to be installed.
class HLibComponent(LibComponent):
def __init__(self, name, path, includes2install):
LibComponent.__init__(self, name, path, [], includes2install)
def mk_makefile(self, out):
return
# Auxiliary function for sort_components
def comp_components(c1, c2):
id1 = get_component(c1).id
id2 = get_component(c2).id
return id2 - id1
# Sort components based on (reverse) definition time
def sort_components(cnames):
return sorted(cnames, key=lambda c: get_component(c).id, reverse=True)
class ExeComponent(Component):
def __init__(self, name, exe_name, path, deps, install):
Component.__init__(self, name, path, deps)
if exe_name is None:
exe_name = name
self.exe_name = exe_name
self.install = install
def mk_makefile(self, out):
Component.mk_makefile(self, out)
# generate rule for exe
exefile = '%s$(EXE_EXT)' % self.exe_name
out.write('%s:' % exefile)
deps = sort_components(self.deps)
objs = []
for cppfile in get_cpp_files(self.src_dir):
objfile = '%s$(OBJ_EXT)' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0])
objs.append(objfile)
for obj in objs:
out.write(' ')
out.write(obj)
for dep in deps:
c_dep = get_component(dep)
out.write(' ' + c_dep.get_link_name())
out.write('\n')
extra_opt = '-static' if not IS_WINDOWS and STATIC_BIN else ''
out.write('\t$(LINK) %s $(LINK_OUT_FLAG)%s $(LINK_FLAGS)' % (extra_opt, exefile))
for obj in objs:
out.write(' ')
out.write(obj)
for dep in deps:
c_dep = get_component(dep)
out.write(' ' + c_dep.get_link_name())
out.write(' $(LINK_EXTRA_FLAGS)\n')
out.write('%s: %s\n\n' % (self.name, exefile))
def require_install_tactics(self):
return ('tactic' in self.deps) and ('cmd_context' in self.deps)
def require_mem_initializer(self):
return True
# All executables (to be installed) are included in the all: rule
def main_component(self):
return self.install
def mk_install_deps(self, out):
if self.install:
exefile = '%s$(EXE_EXT)' % self.exe_name
out.write('%s' % exefile)
def mk_install(self, out):
if self.install:
exefile = '%s$(EXE_EXT)' % self.exe_name
MakeRuleCmd.install_files(out, exefile, os.path.join(INSTALL_BIN_DIR, exefile))
def mk_uninstall(self, out):
if self.install:
exefile = '%s$(EXE_EXT)' % self.exe_name
MakeRuleCmd.remove_installed_files(out, os.path.join(INSTALL_BIN_DIR, exefile))
def mk_win_dist(self, build_path, dist_path):
if self.install:
mk_dir(os.path.join(dist_path, INSTALL_BIN_DIR))
shutil.copy('%s.exe' % os.path.join(build_path, self.exe_name),
'%s.exe' % os.path.join(dist_path, INSTALL_BIN_DIR, self.exe_name))
def mk_unix_dist(self, build_path, dist_path):
if self.install:
mk_dir(os.path.join(dist_path, INSTALL_BIN_DIR))
shutil.copy(os.path.join(build_path, self.exe_name),
os.path.join(dist_path, INSTALL_BIN_DIR, self.exe_name))
class ExtraExeComponent(ExeComponent):
def __init__(self, name, exe_name, path, deps, install):
ExeComponent.__init__(self, name, exe_name, path, deps, install)
def main_component(self):
return False
def require_mem_initializer(self):
return False
def get_so_ext():
sysname = os.uname()[0]
if sysname == 'Darwin':
return 'dylib'
elif sysname == 'Linux' or sysname == 'GNU' or sysname == 'FreeBSD' or sysname == 'NetBSD' or sysname == 'OpenBSD':
return 'so'
elif sysname == 'CYGWIN' or sysname.startswith('MSYS_NT') or sysname.startswith('MINGW'):
return 'dll'
else:
assert(False)
return 'dll'
class DLLComponent(Component):
def __init__(self, name, dll_name, path, deps, export_files, reexports, install, static, staging_link=None):
Component.__init__(self, name, path, deps)
if dll_name is None:
dll_name = name
self.dll_name = dll_name
self.export_files = export_files
self.reexports = reexports
self.install = install
self.static = static
self.staging_link = staging_link # link a copy of the shared object into this directory on build
def get_link_name(self):
if self.static:
return os.path.join(self.build_dir, self.name) + '$(LIB_EXT)'
else:
return self.name + '$(SO_EXT)'
def dll_file(self):
"""
Return file name of component suitable for use in a Makefile
"""
return '%s$(SO_EXT)' % self.dll_name
def install_path(self):
"""
Return install location of component (relative to prefix)
suitable for use in a Makefile
"""
return os.path.join(INSTALL_LIB_DIR, self.dll_file())
def mk_makefile(self, out):
Component.mk_makefile(self, out)
# generate rule for (SO_EXT)
out.write('%s:' % self.dll_file())
deps = sort_components(self.deps)
objs = []
for cppfile in get_cpp_files(self.src_dir):
objfile = '%s$(OBJ_EXT)' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0])
objs.append(objfile)
# Explicitly include obj files of reexport. This fixes problems with exported symbols on Linux and OSX.
for reexport in self.reexports:
reexport = get_component(reexport)
for cppfile in get_cpp_files(reexport.src_dir):
objfile = '%s$(OBJ_EXT)' % os.path.join(reexport.build_dir, os.path.splitext(cppfile)[0])
objs.append(objfile)
for obj in objs:
out.write(' ')
out.write(obj)
for dep in deps:
if dep not in self.reexports:
c_dep = get_component(dep)
out.write(' ' + c_dep.get_link_name())
out.write('\n')
out.write('\t$(LINK) $(SLINK_OUT_FLAG)%s $(SLINK_FLAGS)' % self.dll_file())
for obj in objs:
out.write(' ')
out.write(obj)
for dep in deps:
if dep not in self.reexports:
c_dep = get_component(dep)
out.write(' ' + c_dep.get_link_name())
out.write(' $(SLINK_EXTRA_FLAGS)')
if IS_WINDOWS:
out.write(' /DEF:%s.def' % os.path.join(self.to_src_dir, self.name))
if self.staging_link:
if IS_WINDOWS:
out.write('\n\tcopy %s %s' % (self.dll_file(), self.staging_link))
elif IS_OSX:
out.write('\n\tcp %s %s' % (self.dll_file(), self.staging_link))
else:
out.write('\n\tln -f -s %s %s' % (os.path.join(reverse_path(self.staging_link), self.dll_file()), self.staging_link))
out.write('\n')
if self.static:
if IS_WINDOWS:
libfile = '%s-static$(LIB_EXT)' % self.dll_name
else:
libfile = '%s$(LIB_EXT)' % self.dll_name
self.mk_static(out, libfile)
out.write('%s: %s %s\n\n' % (self.name, self.dll_file(), libfile))
else:
out.write('%s: %s\n\n' % (self.name, self.dll_file()))
def mk_static(self, out, libfile):
# generate rule for lib
objs = []
for cppfile in get_cpp_files(self.src_dir):
objfile = '%s$(OBJ_EXT)' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0])
objs.append(objfile)
# we have to "reexport" all object files
for dep in self.deps:
dep = get_component(dep)
for cppfile in get_cpp_files(dep.src_dir):
objfile = '%s$(OBJ_EXT)' % os.path.join(dep.build_dir, os.path.splitext(cppfile)[0])
objs.append(objfile)
out.write('%s:' % libfile)
for obj in objs:
out.write(' ')
out.write(obj)
out.write('\n')
out.write('\t@$(AR) $(AR_FLAGS) $(AR_OUTFLAG)%s' % libfile)
for obj in objs:
out.write(' ')
out.write(obj)
out.write('\n')
def main_component(self):
return self.install
def require_install_tactics(self):
return ('tactic' in self.deps) and ('cmd_context' in self.deps)
def require_mem_initializer(self):
return True
def require_def_file(self):
return IS_WINDOWS and self.export_files
def mk_install_deps(self, out):
out.write('%s$(SO_EXT)' % self.dll_name)
if self.static:
out.write(' %s$(LIB_EXT)' % self.dll_name)
def mk_install(self, out):
if self.install:
MakeRuleCmd.install_files(out, self.dll_file(), self.install_path())
if self.static:
libfile = '%s$(LIB_EXT)' % self.dll_name
MakeRuleCmd.install_files(out, libfile, os.path.join(INSTALL_LIB_DIR, libfile))
def mk_uninstall(self, out):
MakeRuleCmd.remove_installed_files(out, self.install_path())
libfile = '%s$(LIB_EXT)' % self.dll_name
MakeRuleCmd.remove_installed_files(out, os.path.join(INSTALL_LIB_DIR, libfile))
def mk_win_dist(self, build_path, dist_path):
if self.install:
mk_dir(os.path.join(dist_path, INSTALL_BIN_DIR))
shutil.copy('%s.dll' % os.path.join(build_path, self.dll_name),
'%s.dll' % os.path.join(dist_path, INSTALL_BIN_DIR, self.dll_name))
shutil.copy('%s.pdb' % os.path.join(build_path, self.dll_name),
'%s.pdb' % os.path.join(dist_path, INSTALL_BIN_DIR, self.dll_name))
shutil.copy('%s.lib' % os.path.join(build_path, self.dll_name),
'%s.lib' % os.path.join(dist_path, INSTALL_BIN_DIR, self.dll_name))
def mk_unix_dist(self, build_path, dist_path):
if self.install:
mk_dir(os.path.join(dist_path, INSTALL_BIN_DIR))
so = get_so_ext()
shutil.copy('%s.%s' % (os.path.join(build_path, self.dll_name), so),
'%s.%s' % (os.path.join(dist_path, INSTALL_BIN_DIR, self.dll_name), so))
shutil.copy('%s.a' % os.path.join(build_path, self.dll_name),
'%s.a' % os.path.join(dist_path, INSTALL_BIN_DIR, self.dll_name))
class JsComponent(Component):
def __init__(self):
Component.__init__(self, "js", None, [])
def main_component(self):
return False
def mk_win_dist(self, build_path, dist_path):
return
def mk_unix_dist(self, build_path, dist_path):
return
def mk_makefile(self, out):
return
class PythonComponent(Component):
def __init__(self, name, libz3Component):
assert isinstance(libz3Component, DLLComponent)
global PYTHON_ENABLED
Component.__init__(self, name, None, [])
self.libz3Component = libz3Component
def main_component(self):
return False
def mk_win_dist(self, build_path, dist_path):
if not is_python_enabled():
return
src = os.path.join(build_path, 'python', 'z3')
dst = os.path.join(dist_path, INSTALL_BIN_DIR, 'python', 'z3')
if os.path.exists(dst):
shutil.rmtree(dst)
shutil.copytree(src, dst)
def mk_unix_dist(self, build_path, dist_path):
self.mk_win_dist(build_path, dist_path)
def mk_makefile(self, out):
return
class PythonInstallComponent(Component):
def __init__(self, name, libz3Component):
assert isinstance(libz3Component, DLLComponent)
global PYTHON_INSTALL_ENABLED
Component.__init__(self, name, None, [])
self.pythonPkgDir = None
self.in_prefix_install = True
self.libz3Component = libz3Component
if not PYTHON_INSTALL_ENABLED:
return
if IS_WINDOWS:
# Installing under Windows doesn't make sense as the install prefix is used
# but that doesn't make sense under Windows
# CMW: It makes perfectly good sense; the prefix is Python's sys.prefix,
# i.e., something along the lines of C:\Python\... At the moment we are not
# sure whether we would want to install libz3.dll into that directory though.
PYTHON_INSTALL_ENABLED = False
return
else:
PYTHON_INSTALL_ENABLED = True
if IS_WINDOWS or IS_OSX:
# Use full path that is possibly outside of install prefix
self.in_prefix_install = PYTHON_PACKAGE_DIR.startswith(PREFIX)
self.pythonPkgDir = strip_path_prefix(PYTHON_PACKAGE_DIR, PREFIX)
else:
# Use path inside the prefix (should be the normal case on Linux)
# CMW: Also normal on *BSD?
if not PYTHON_PACKAGE_DIR.startswith(PREFIX):
raise MKException(('The python package directory ({}) must live ' +
'under the install prefix ({}) to install the python bindings.' +
'Use --pypkgdir and --prefix to set the python package directory ' +
'and install prefix respectively. Note that the python package ' +
'directory does not need to exist and will be created if ' +
'necessary during install.').format(
PYTHON_PACKAGE_DIR,
PREFIX))
self.pythonPkgDir = strip_path_prefix(PYTHON_PACKAGE_DIR, PREFIX)
self.in_prefix_install = True
if self.in_prefix_install:
assert not os.path.isabs(self.pythonPkgDir)
def final_info(self):
if not PYTHON_PACKAGE_DIR.startswith(PREFIX) and PYTHON_INSTALL_ENABLED:
print("Warning: The detected Python package directory (%s) is not "
"in the installation prefix (%s). This can lead to a broken "
"Python API installation. Use --pypkgdir= to change the "
"Python package directory." % (PYTHON_PACKAGE_DIR, PREFIX))
def main_component(self):
return False
def mk_install(self, out):
if not is_python_install_enabled():
return
MakeRuleCmd.make_install_directory(out,
os.path.join(self.pythonPkgDir, 'z3'),
in_prefix=self.in_prefix_install)
MakeRuleCmd.make_install_directory(out,
os.path.join(self.pythonPkgDir, 'z3', 'lib'),
in_prefix=self.in_prefix_install)
# Sym-link or copy libz3 into python package directory
if IS_WINDOWS or IS_OSX:
MakeRuleCmd.install_files(out,
self.libz3Component.dll_file(),
os.path.join(self.pythonPkgDir, 'z3', 'lib',
self.libz3Component.dll_file()),
in_prefix=self.in_prefix_install
)
else:
# Create symbolic link to save space.
# It's important that this symbolic link be relative (rather
# than absolute) so that the install is relocatable (needed for
# staged installs that use DESTDIR).
MakeRuleCmd.create_relative_symbolic_link(out,
self.libz3Component.install_path(),
os.path.join(self.pythonPkgDir, 'z3', 'lib',
self.libz3Component.dll_file()
),
)
MakeRuleCmd.install_files(out, os.path.join('python', 'z3', '*.py'),
os.path.join(self.pythonPkgDir, 'z3'),
in_prefix=self.in_prefix_install)
if sys.version >= "3":
pythonPycacheDir = os.path.join(self.pythonPkgDir, 'z3', '__pycache__')
MakeRuleCmd.make_install_directory(out,
pythonPycacheDir,
in_prefix=self.in_prefix_install)
MakeRuleCmd.install_files(out,
os.path.join('python', 'z3', '__pycache__', '*.pyc'),
pythonPycacheDir,
in_prefix=self.in_prefix_install)
else:
MakeRuleCmd.install_files(out,
os.path.join('python', 'z3', '*.pyc'),
os.path.join(self.pythonPkgDir,'z3'),
in_prefix=self.in_prefix_install)
if PYTHON_PACKAGE_DIR != distutils.sysconfig.get_python_lib():
out.write('\t@echo Z3Py was installed at \'%s\', make sure this directory is in your PYTHONPATH environment variable.' % PYTHON_PACKAGE_DIR)
def mk_uninstall(self, out):
if not is_python_install_enabled():
return
MakeRuleCmd.remove_installed_files(out,
os.path.join(self.pythonPkgDir,
self.libz3Component.dll_file()),
in_prefix=self.in_prefix_install
)
MakeRuleCmd.remove_installed_files(out,
os.path.join(self.pythonPkgDir, 'z3', '*.py'),
in_prefix=self.in_prefix_install)
MakeRuleCmd.remove_installed_files(out,
os.path.join(self.pythonPkgDir, 'z3', '*.pyc'),
in_prefix=self.in_prefix_install)
MakeRuleCmd.remove_installed_files(out,
os.path.join(self.pythonPkgDir, 'z3', '__pycache__', '*.pyc'),
in_prefix=self.in_prefix_install
)
MakeRuleCmd.remove_installed_files(out,
os.path.join(self.pythonPkgDir, 'z3', 'lib',
self.libz3Component.dll_file()))
def mk_makefile(self, out):
return
def set_key_file(self):
global DOTNET_KEY_FILE
# We need to give the assembly a strong name so that it
# can be installed into the GAC with ``make install``
if not DOTNET_KEY_FILE is None:
self.key_file = DOTNET_KEY_FILE
if not self.key_file is None:
if os.path.isfile(self.key_file):
self.key_file = os.path.abspath(self.key_file)
elif os.path.isfile(os.path.join(self.src_dir, self.key_file)):
self.key_file = os.path.abspath(os.path.join(self.src_dir, self.key_file))
else:
print("Keyfile '%s' could not be found; %s.dll will be unsigned." % (self.key_file, self.dll_name))
self.key_file = None
# build for dotnet core
class DotNetDLLComponent(Component):
def __init__(self, name, dll_name, path, deps, assembly_info_dir, default_key_file):
Component.__init__(self, name, path, deps)
if dll_name is None:
dll_name = name
if assembly_info_dir is None:
assembly_info_dir = "."
self.dll_name = dll_name
self.assembly_info_dir = assembly_info_dir
self.key_file = default_key_file
def mk_makefile(self, out):
if not is_dotnet_core_enabled():
return
cs_fp_files = []
for cs_file in get_cs_files(self.src_dir):
cs_fp_files.append(os.path.join(self.to_src_dir, cs_file))
if self.assembly_info_dir != '.':
for cs_file in get_cs_files(os.path.join(self.src_dir, self.assembly_info_dir)):
cs_fp_files.append(os.path.join(self.to_src_dir, self.assembly_info_dir, cs_file))
dllfile = '%s.dll' % self.dll_name
out.write('%s: %s$(SO_EXT)' % (dllfile, get_component(Z3_DLL_COMPONENT).dll_name))
for cs_file in cs_fp_files:
out.write(' ')
out.write(cs_file)
out.write('\n')
set_key_file(self)
key = ""
if not self.key_file is None:
key = "<AssemblyOriginatorKeyFile>%s</AssemblyOriginatorKeyFile>" % self.key_file
key += "\n<SignAssembly>true</SignAssembly>"
version = get_version_string(3)
core_csproj_str = """<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.4</TargetFramework>
<DefineConstants>$(DefineConstants);DOTNET_CORE</DefineConstants>
<DebugType>portable</DebugType>
<AssemblyName>Microsoft.Z3</AssemblyName>
<OutputType>Library</OutputType>
<PackageId>Microsoft.Z3</PackageId>
<RuntimeFrameworkVersion>1.0.4</RuntimeFrameworkVersion>
<Version>%s</Version>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Authors>Microsoft</Authors>
<Company>Microsoft</Company>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<Description>Z3 is a satisfiability modulo theories solver from Microsoft Research.</Description>
<Copyright>Copyright Microsoft Corporation. All rights reserved.</Copyright>
<PackageTags>smt constraint solver theorem prover</PackageTags>
%s
</PropertyGroup>
<ItemGroup>
<Compile Include="..\%s\*.cs;*.cs" Exclude="bin\**;obj\**;**\*.xproj;packages\**" />
</ItemGroup>
</Project>""" % (version, key, self.to_src_dir)
mk_dir(os.path.join(BUILD_DIR, 'dotnet'))
csproj = os.path.join('dotnet', 'z3.csproj')
with open(os.path.join(BUILD_DIR, csproj), 'w') as ous:
ous.write(core_csproj_str)
dotnetCmdLine = [DOTNET, "build", csproj]
dotnetCmdLine.extend(['-c'])
if DEBUG_MODE:
dotnetCmdLine.extend(['Debug'])
else:
dotnetCmdLine.extend(['Release'])
path = os.path.join(os.path.abspath(BUILD_DIR), ".")
dotnetCmdLine.extend(['-o', path])
MakeRuleCmd.write_cmd(out, ' '.join(dotnetCmdLine))
self.sign_esrp(out)
out.write('\n')
out.write('%s: %s\n\n' % (self.name, dllfile))
def sign_esrp(self, out):
global ESRP_SIGNx
print("esrp-sign", ESRP_SIGN)
if not ESRP_SIGN:
return
import uuid
guid = str(uuid.uuid4())
path = os.path.abspath(BUILD_DIR).replace("\\","\\\\")
assemblySignStr = """
{
"Version": "1.0.0",
"SignBatches"
:
[
{
"SourceLocationType": "UNC",
"SourceRootDirectory": "%s",
"DestinationLocationType": "UNC",
"DestinationRootDirectory": "c:\\\\ESRP\\\\output",
"SignRequestFiles": [
{
"CustomerCorrelationId": "%s",
"SourceLocation": "libz3.dll",
"DestinationLocation": "libz3.dll"
},
{
"CustomerCorrelationId": "%s",
"SourceLocation": "Microsoft.Z3.dll",
"DestinationLocation": "Microsoft.Z3.dll"
}
],
"SigningInfo": {
"Operations": [
{
"KeyCode" : "CP-230012",
"OperationCode" : "SigntoolSign",
"Parameters" : {
"OpusName": "Microsoft",
"OpusInfo": "http://www.microsoft.com",
"FileDigest": "/fd \\"SHA256\\"",
"PageHash": "/NPH",
"TimeStamp": "/tr \\"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\\" /td sha256"
},
"ToolName" : "sign",
"ToolVersion" : "1.0"
},
{
"KeyCode" : "CP-230012",
"OperationCode" : "SigntoolVerify",
"Parameters" : {},
"ToolName" : "sign",
"ToolVersion" : "1.0"
}
]
}
}
]
} """ % (path, guid, guid)
assemblySign = os.path.join(os.path.abspath(BUILD_DIR), 'dotnet', 'assembly-sign-input.json')
with open(assemblySign, 'w') as ous:
ous.write(assemblySignStr)
outputFile = os.path.join(os.path.abspath(BUILD_DIR), 'dotnet', "esrp-out.json")
esrpCmdLine = ["esrpclient.exe", "sign", "-a", "C:\\esrp\\config\\authorization.json", "-p", "C:\\esrp\\config\\policy.json", "-i", assemblySign, "-o", outputFile]
MakeRuleCmd.write_cmd(out, ' '.join(esrpCmdLine))
MakeRuleCmd.write_cmd(out, "move /Y C:\\esrp\\output\\libz3.dll .")
MakeRuleCmd.write_cmd(out, "move /Y C:\\esrp\\output\\Microsoft.Z3.dll .")
def main_component(self):
return is_dotnet_core_enabled()
def has_assembly_info(self):
# TBD: is this required for dotnet core given that version numbers are in z3.csproj file?
return False
def mk_win_dist(self, build_path, dist_path):
if is_dotnet_core_enabled():
mk_dir(os.path.join(dist_path, INSTALL_BIN_DIR))
shutil.copy('%s.dll' % os.path.join(build_path, self.dll_name),
'%s.dll' % os.path.join(dist_path, INSTALL_BIN_DIR, self.dll_name))
shutil.copy('%s.pdb' % os.path.join(build_path, self.dll_name),
'%s.pdb' % os.path.join(dist_path, INSTALL_BIN_DIR, self.dll_name))
shutil.copy('%s.deps.json' % os.path.join(build_path, self.dll_name),
'%s.deps.json' % os.path.join(dist_path, INSTALL_BIN_DIR, self.dll_name))
if DEBUG_MODE:
shutil.copy('%s.pdb' % os.path.join(build_path, self.dll_name),
'%s.pdb' % os.path.join(dist_path, INSTALL_BIN_DIR, self.dll_name))
def mk_unix_dist(self, build_path, dist_path):
if is_dotnet_core_enabled():
mk_dir(os.path.join(dist_path, INSTALL_BIN_DIR))
shutil.copy('%s.dll' % os.path.join(build_path, self.dll_name),
'%s.dll' % os.path.join(dist_path, INSTALL_BIN_DIR, self.dll_name))
shutil.copy('%s.deps.json' % os.path.join(build_path, self.dll_name),
'%s.deps.json' % os.path.join(dist_path, INSTALL_BIN_DIR, self.dll_name))
def mk_install_deps(self, out):
pass
def mk_install(self, out):
pass
def mk_uninstall(self, out):
pass
class JavaDLLComponent(Component):
def __init__(self, name, dll_name, package_name, manifest_file, path, deps):
Component.__init__(self, name, path, deps)
if dll_name is None:
dll_name = name
self.dll_name = dll_name
self.package_name = package_name
self.manifest_file = manifest_file
self.install = not is_windows()
def mk_makefile(self, out):
global JAVAC
global JAR
if is_java_enabled():
mk_dir(os.path.join(BUILD_DIR, 'api', 'java', 'classes'))
dllfile = '%s$(SO_EXT)' % self.dll_name
out.write('libz3java$(SO_EXT): libz3$(SO_EXT) %s\n' % os.path.join(self.to_src_dir, 'Native.cpp'))
t = '\t$(CXX) $(CXXFLAGS) $(CXX_OUT_FLAG)api/java/Native$(OBJ_EXT) -I"%s" -I"%s/PLATFORM" -I%s %s/Native.cpp\n' % (JNI_HOME, JNI_HOME, get_component('api').to_src_dir, self.to_src_dir)
if IS_OSX:
t = t.replace('PLATFORM', 'darwin')
elif is_linux():
t = t.replace('PLATFORM', 'linux')
elif is_hurd():
t = t.replace('PLATFORM', 'hurd')
elif IS_FREEBSD:
t = t.replace('PLATFORM', 'freebsd')
elif IS_NETBSD:
t = t.replace('PLATFORM', 'netbsd')
elif IS_OPENBSD:
t = t.replace('PLATFORM', 'openbsd')
elif IS_CYGWIN:
t = t.replace('PLATFORM', 'cygwin')
elif IS_MSYS2:
t = t.replace('PLATFORM', 'win32')
else:
t = t.replace('PLATFORM', 'win32')
out.write(t)
if IS_WINDOWS: # On Windows, CL creates a .lib file to link against.
out.write('\t$(SLINK) $(SLINK_OUT_FLAG)libz3java$(SO_EXT) $(SLINK_FLAGS) %s$(OBJ_EXT) libz3$(LIB_EXT)\n' %
os.path.join('api', 'java', 'Native'))
else:
out.write('\t$(SLINK) $(SLINK_OUT_FLAG)libz3java$(SO_EXT) $(SLINK_FLAGS) %s$(OBJ_EXT) libz3$(SO_EXT)\n' %
os.path.join('api', 'java', 'Native'))
out.write('%s.jar: libz3java$(SO_EXT) ' % self.package_name)
deps = ''
for jfile in get_java_files(self.src_dir):
deps += ('%s ' % os.path.join(self.to_src_dir, jfile))
for jfile in get_java_files(os.path.join(self.src_dir, "enumerations")):
deps += '%s ' % os.path.join(self.to_src_dir, 'enumerations', jfile)
out.write(deps)
out.write('\n')
#if IS_WINDOWS:
JAVAC = '"%s"' % JAVAC
JAR = '"%s"' % JAR
t = ('\t%s %s.java -d %s\n' % (JAVAC, os.path.join(self.to_src_dir, 'enumerations', '*'), os.path.join('api', 'java', 'classes')))
out.write(t)
t = ('\t%s -cp %s %s.java -d %s\n' % (JAVAC,
os.path.join('api', 'java', 'classes'),
os.path.join(self.to_src_dir, '*'),
os.path.join('api', 'java', 'classes')))
out.write(t)
out.write('\t%s cfm %s.jar %s -C %s .\n' % (JAR, self.package_name,
os.path.join(self.to_src_dir, 'manifest'),
os.path.join('api', 'java', 'classes')))
out.write('java: %s.jar\n\n' % self.package_name)
def main_component(self):
return is_java_enabled()
def mk_win_dist(self, build_path, dist_path):
if JAVA_ENABLED:
mk_dir(os.path.join(dist_path, INSTALL_BIN_DIR))
shutil.copy('%s.jar' % os.path.join(build_path, self.package_name),
'%s.jar' % os.path.join(dist_path, INSTALL_BIN_DIR, self.package_name))
shutil.copy(os.path.join(build_path, 'libz3java.dll'),
os.path.join(dist_path, INSTALL_BIN_DIR, 'libz3java.dll'))
shutil.copy(os.path.join(build_path, 'libz3java.lib'),
os.path.join(dist_path, INSTALL_BIN_DIR, 'libz3java.lib'))
def mk_unix_dist(self, build_path, dist_path):
if JAVA_ENABLED:
mk_dir(os.path.join(dist_path, INSTALL_BIN_DIR))
shutil.copy('%s.jar' % os.path.join(build_path, self.package_name),
'%s.jar' % os.path.join(dist_path, INSTALL_BIN_DIR, self.package_name))
so = get_so_ext()
shutil.copy(os.path.join(build_path, 'libz3java.%s' % so),
os.path.join(dist_path, INSTALL_BIN_DIR, 'libz3java.%s' % so))
def mk_install(self, out):
if is_java_enabled() and self.install:
dllfile = '%s$(SO_EXT)' % self.dll_name
MakeRuleCmd.install_files(out, dllfile, os.path.join(INSTALL_LIB_DIR, dllfile))
jarfile = '{}.jar'.format(self.package_name)
MakeRuleCmd.install_files(out, jarfile, os.path.join(INSTALL_LIB_DIR, jarfile))
def mk_uninstall(self, out):
if is_java_enabled() and self.install:
dllfile = '%s$(SO_EXT)' % self.dll_name
MakeRuleCmd.remove_installed_files(out, os.path.join(INSTALL_LIB_DIR, dllfile))
jarfile = '{}.jar'.format(self.package_name)
MakeRuleCmd.remove_installed_files(out, os.path.join(INSTALL_LIB_DIR, jarfile))
class MLComponent(Component):
def __init__(self, name, lib_name, path, deps):
Component.__init__(self, name, path, deps)
if lib_name is None:
lib_name = name
self.lib_name = lib_name
self.modules = ["z3enums", "z3native", "z3"] # dependencies in this order!
self.stubs = "z3native_stubs"
self.sub_dir = os.path.join('api', 'ml')
self.destdir = ""
self.ldconf = ""
# Calling _init_ocamlfind_paths() is postponed to later because
# OCAMLFIND hasn't been checked yet.
def _install_bindings(self):
# FIXME: Depending on global state is gross. We can't pre-compute this
# in the constructor because we haven't tested for ocamlfind yet
return OCAMLFIND != ''
def _init_ocamlfind_paths(self):
"""
Initialises self.destdir and self.ldconf
Do not call this from the MLComponent constructor because OCAMLFIND
has not been checked at that point
"""
if self.destdir != "" and self.ldconf != "":
# Initialisation already done
return
# Use Ocamlfind to get the default destdir and ldconf path
self.destdir = check_output([OCAMLFIND, 'printconf', 'destdir'])
if self.destdir == "":
raise MKException('Failed to get OCaml destdir')
if not os.path.isdir(self.destdir):
raise MKException('The destdir reported by {ocamlfind} ({destdir}) does not exist'.format(ocamlfind=OCAMLFIND, destdir=self.destdir))
self.ldconf = check_output([OCAMLFIND, 'printconf', 'ldconf'])
if self.ldconf == "":
raise MKException('Failed to get OCaml ldconf path')
def final_info(self):
if not self._install_bindings():
print("WARNING: Could not find ocamlfind utility. OCaml bindings will not be installed")
def mk_makefile(self, out):
if is_ml_enabled():
CP_CMD = 'cp'
if IS_WINDOWS:
CP_CMD='copy'
OCAML_FLAGS = ''
if DEBUG_MODE:
OCAML_FLAGS += '-g'
if OCAMLFIND:
OCAMLCF = OCAMLFIND + ' ' + 'ocamlc -package zarith' + ' ' + OCAML_FLAGS
OCAMLOPTF = OCAMLFIND + ' ' + 'ocamlopt -package zarith' + ' ' + OCAML_FLAGS
else:
OCAMLCF = OCAMLC + ' ' + OCAML_FLAGS
OCAMLOPTF = OCAMLOPT + ' ' + OCAML_FLAGS
src_dir = self.to_src_dir
mk_dir(os.path.join(BUILD_DIR, self.sub_dir))
api_src = get_component(API_COMPONENT).to_src_dir
# remove /GL and -std=c++11; the ocaml tools don't like them.
if IS_WINDOWS:
out.write('CXXFLAGS_OCAML=$(CXXFLAGS:/GL=)\n')
else:
out.write('CXXFLAGS_OCAML=$(subst -std=c++11,,$(CXXFLAGS))\n')
substitutions = { 'VERSION': "{}.{}.{}.{}".format(VER_MAJOR, VER_MINOR, VER_BUILD, VER_TWEAK) }
configure_file(os.path.join(self.src_dir, 'META.in'),
os.path.join(BUILD_DIR, self.sub_dir, 'META'),
substitutions)
stubsc = os.path.join(src_dir, self.stubs + '.c')
stubso = os.path.join(self.sub_dir, self.stubs) + '$(OBJ_EXT)'
base_dll_name = get_component(Z3_DLL_COMPONENT).dll_name
if STATIC_LIB:
z3link = 'z3-static'
z3linkdep = base_dll_name + '-static$(LIB_EXT)'
out.write('%s: %s\n' % (z3linkdep, base_dll_name + '$(LIB_EXT)'))
out.write('\tcp $< $@\n')
else:
z3link = 'z3'
z3linkdep = base_dll_name + '$(SO_EXT)'
out.write('%s: %s %s\n' % (stubso, stubsc, z3linkdep))
out.write('\t%s -ccopt "$(CXXFLAGS_OCAML) -I %s -I %s -I %s $(CXX_OUT_FLAG)%s" -c %s\n' %
(OCAMLCF, OCAML_LIB, api_src, src_dir, stubso, stubsc))
cmos = ''
for m in self.modules:
ml = os.path.join(src_dir, m + '.ml')
cmo = os.path.join(self.sub_dir, m + '.cmo')
existing_mli = os.path.join(src_dir, m + '.mli')
mli = os.path.join(self.sub_dir, m + '.mli')
cmi = os.path.join(self.sub_dir, m + '.cmi')
out.write('%s: %s %s\n' % (cmo, ml, cmos))
if (os.path.exists(existing_mli[3:])):
out.write('\t%s %s %s\n' % (CP_CMD, existing_mli, mli))
else:
out.write('\t%s -i -I %s -c %s > %s\n' % (OCAMLCF, self.sub_dir, ml, mli))
out.write('\t%s -I %s -o %s -c %s\n' % (OCAMLCF, self.sub_dir, cmi, mli))
out.write('\t%s -I %s -o %s -c %s\n' % (OCAMLCF, self.sub_dir, cmo, ml))
cmos = cmos + cmo + ' '
cmxs = ''
for m in self.modules:
ff = os.path.join(src_dir, m + '.ml')
ft = os.path.join(self.sub_dir, m + '.cmx')
out.write('%s: %s %s %s\n' % (ft, ff, cmos, cmxs))
out.write('\t%s -I %s -o %s -c %s\n' % (OCAMLOPTF, self.sub_dir, ft, ff))
cmxs = cmxs + ' ' + ft
OCAMLMKLIB = 'ocamlmklib'
LIBZ3 = '-cclib -l' + z3link
if is_cygwin() and not(is_cygwin_mingw()):
LIBZ3 = z3linkdep
LIBZ3 = LIBZ3 + ' ' + ' '.join(map(lambda x: '-cclib ' + x, LDFLAGS.split()))
if DEBUG_MODE and not(is_cygwin()):
# Some ocamlmklib's don't like -g; observed on cygwin, but may be others as well.
OCAMLMKLIB += ' -g'
z3mls = os.path.join(self.sub_dir, 'z3ml')
out.write('%s.cma: %s %s %s\n' % (z3mls, cmos, stubso, z3linkdep))
out.write('\t%s -o %s -I %s %s %s %s\n' % (OCAMLMKLIB, z3mls, self.sub_dir, stubso, cmos, LIBZ3))
out.write('%s.cmxa: %s %s %s %s.cma\n' % (z3mls, cmxs, stubso, z3linkdep, z3mls))
out.write('\t%s -o %s -I %s %s %s %s\n' % (OCAMLMKLIB, z3mls, self.sub_dir, stubso, cmxs, LIBZ3))
out.write('%s.cmxs: %s.cmxa\n' % (z3mls, z3mls))
out.write('\t%s -linkall -shared -o %s.cmxs -I . -I %s %s.cmxa\n' % (OCAMLOPTF, z3mls, self.sub_dir, z3mls))
out.write('\n')
out.write('ml: %s.cma %s.cmxa %s.cmxs\n' % (z3mls, z3mls, z3mls))
out.write('\n')
if IS_WINDOWS:
out.write('ocamlfind_install: ')
self.mk_install_deps(out)
out.write('\n')
self.mk_install(out)
out.write('\n')
out.write('ocamlfind_uninstall:\n')
self.mk_uninstall(out)
out.write('\n')
def mk_install_deps(self, out):
if is_ml_enabled() and self._install_bindings():
out.write(get_component(Z3_DLL_COMPONENT).dll_name + '$(SO_EXT) ')
out.write(os.path.join(self.sub_dir, 'META '))
out.write(os.path.join(self.sub_dir, 'z3ml.cma '))
out.write(os.path.join(self.sub_dir, 'z3ml.cmxa '))
out.write(os.path.join(self.sub_dir, 'z3ml.cmxs '))
def mk_install(self, out):
if is_ml_enabled() and self._install_bindings():
self._init_ocamlfind_paths()
in_prefix = self.destdir.startswith(PREFIX)
maybe_stripped_destdir = strip_path_prefix(self.destdir, PREFIX)
# Note that when doing a staged install with DESTDIR that modifying
# OCaml's ``ld.conf`` may fail. Therefore packagers will need to
# make their packages modify it manually at package install time
# as opposed to ``make install`` time.
MakeRuleCmd.make_install_directory(out,
maybe_stripped_destdir,
in_prefix=in_prefix)
out.write('\t@{ocamlfind} install -ldconf $(DESTDIR){ldconf} -destdir $(DESTDIR){ocaml_destdir} Z3 {metafile}'.format(
ldconf=self.ldconf,
ocamlfind=OCAMLFIND,
ocaml_destdir=self.destdir,
metafile=os.path.join(self.sub_dir, 'META')))
for m in self.modules:
mli = os.path.join(self.src_dir, m) + '.mli'
if os.path.exists(mli):
out.write(' ' + os.path.join(self.to_src_dir, m) + '.mli')
else:
out.write(' ' + os.path.join(self.sub_dir, m) + '.mli')
out.write(' ' + os.path.join(self.sub_dir, m) + '.cmi')
out.write(' ' + os.path.join(self.sub_dir, m) + '.cmx')
out.write(' %s' % ((os.path.join(self.sub_dir, 'libz3ml$(LIB_EXT)'))))
out.write(' %s' % ((os.path.join(self.sub_dir, 'z3ml$(LIB_EXT)'))))
out.write(' %s' % ((os.path.join(self.sub_dir, 'z3ml.cma'))))
out.write(' %s' % ((os.path.join(self.sub_dir, 'z3ml.cmxa'))))
out.write(' %s' % ((os.path.join(self.sub_dir, 'z3ml.cmxs'))))
out.write(' %s' % ((os.path.join(self.sub_dir, 'dllz3ml'))))
if is_windows() or is_cygwin_mingw():
out.write('.dll')
else:
out.write('.so') # .so also on OSX!
out.write('\n')
def mk_uninstall(self, out):
if is_ml_enabled() and self._install_bindings():
self._init_ocamlfind_paths()
out.write('\t@{ocamlfind} remove -ldconf $(DESTDIR){ldconf} -destdir $(DESTDIR){ocaml_destdir} Z3\n'.format(
ldconf=self.ldconf,
ocamlfind=OCAMLFIND,
ocaml_destdir=self.destdir))
def main_component(self):
return is_ml_enabled()
class ExampleComponent(Component):
def __init__(self, name, path):
Component.__init__(self, name, path, [])
self.ex_dir = os.path.join(EXAMPLE_DIR, self.path)
self.to_ex_dir = os.path.join(REV_BUILD_DIR, self.ex_dir)
def is_example(self):
return True
class CppExampleComponent(ExampleComponent):
def __init__(self, name, path):
ExampleComponent.__init__(self, name, path)
def compiler(self):
return "$(CXX)"
def src_files(self):
return get_cpp_files(self.ex_dir)
def mk_makefile(self, out):
dll_name = get_component(Z3_DLL_COMPONENT).dll_name
dll = '%s$(SO_EXT)' % dll_name
objfiles = ''
for cppfile in self.src_files():
objfile = '%s$(OBJ_EXT)' % (cppfile[:cppfile.rfind('.')])
objfiles = objfiles + ('%s ' % objfile)
out.write('%s: %s\n' % (objfile, os.path.join(self.to_ex_dir, cppfile)));
out.write('\t%s $(CXXFLAGS) $(OS_DEFINES) $(EXAMP_DEBUG_FLAG) $(CXX_OUT_FLAG)%s $(LINK_FLAGS)' % (self.compiler(), objfile))
# Add include dir components
out.write(' -I%s' % get_component(API_COMPONENT).to_src_dir)
out.write(' -I%s' % get_component(CPP_COMPONENT).to_src_dir)
out.write(' %s' % os.path.join(self.to_ex_dir, cppfile))
out.write('\n')
exefile = '%s$(EXE_EXT)' % self.name
out.write('%s: %s %s\n' % (exefile, dll, objfiles))
out.write('\t$(LINK) $(LINK_OUT_FLAG)%s $(LINK_FLAGS) %s ' % (exefile, objfiles))
if IS_WINDOWS:
out.write('%s.lib' % dll_name)
else:
out.write(dll)
out.write(' $(LINK_EXTRA_FLAGS)\n')
out.write('_ex_%s: %s\n\n' % (self.name, exefile))
class CExampleComponent(CppExampleComponent):
def __init__(self, name, path):
CppExampleComponent.__init__(self, name, path)
def compiler(self):
return "$(CC)"
def src_files(self):
return get_c_files(self.ex_dir)
def mk_makefile(self, out):
dll_name = get_component(Z3_DLL_COMPONENT).dll_name
dll = '%s$(SO_EXT)' % dll_name
objfiles = ''
for cfile in self.src_files():
objfile = '%s$(OBJ_EXT)' % (cfile[:cfile.rfind('.')])
objfiles = objfiles + ('%s ' % objfile)
out.write('%s: %s\n' % (objfile, os.path.join(self.to_ex_dir, cfile)));
out.write('\t%s $(CFLAGS) $(OS_DEFINES) $(EXAMP_DEBUG_FLAG) $(C_OUT_FLAG)%s $(LINK_FLAGS)' % (self.compiler(), objfile))
out.write(' -I%s' % get_component(API_COMPONENT).to_src_dir)
out.write(' %s' % os.path.join(self.to_ex_dir, cfile))
out.write('\n')
exefile = '%s$(EXE_EXT)' % self.name
out.write('%s: %s %s\n' % (exefile, dll, objfiles))
out.write('\t$(LINK) $(LINK_OUT_FLAG)%s $(LINK_FLAGS) %s ' % (exefile, objfiles))
if IS_WINDOWS:
out.write('%s.lib' % dll_name)
else:
out.write(dll)
out.write(' $(LINK_EXTRA_FLAGS)\n')
out.write('_ex_%s: %s\n\n' % (self.name, exefile))
class DotNetExampleComponent(ExampleComponent):
def __init__(self, name, path):
ExampleComponent.__init__(self, name, path)
def is_example(self):
return is_dotnet_core_enabled()
def mk_makefile(self, out):
if is_dotnet_core_enabled():
proj_name = 'dotnet_example.csproj'
out.write('_ex_%s:' % self.name)
for csfile in get_cs_files(self.ex_dir):
out.write(' ')
out.write(os.path.join(self.to_ex_dir, csfile))
mk_dir(os.path.join(BUILD_DIR, 'dotnet_example'))
csproj = os.path.join('dotnet_example', proj_name)
if VS_X64:
platform = 'x64'
elif VS_ARM:
platform = 'ARM'
else:
platform = 'x86'
dotnet_proj_str = """<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<PlatformTarget>%s</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\%s/*.cs" />
<Reference Include="Microsoft.Z3">
<HintPath>..\Microsoft.Z3.dll</HintPath>
</Reference>
</ItemGroup>
</Project>""" % (platform, self.to_ex_dir)
with open(os.path.join(BUILD_DIR, csproj), 'w') as ous:
ous.write(dotnet_proj_str)
out.write('\n')
dotnetCmdLine = [DOTNET, "build", csproj]
dotnetCmdLine.extend(['-c'])
if DEBUG_MODE:
dotnetCmdLine.extend(['Debug'])
else:
dotnetCmdLine.extend(['Release'])
MakeRuleCmd.write_cmd(out, ' '.join(dotnetCmdLine))
out.write('\n')
class JavaExampleComponent(ExampleComponent):
def __init__(self, name, path):
ExampleComponent.__init__(self, name, path)
def is_example(self):
return JAVA_ENABLED
def mk_makefile(self, out):
if JAVA_ENABLED:
pkg = get_component(JAVA_COMPONENT).package_name + '.jar'
out.write('JavaExample.class: %s' % (pkg))
deps = ''
for jfile in get_java_files(self.ex_dir):
out.write(' %s' % os.path.join(self.to_ex_dir, jfile))
if IS_WINDOWS:
deps = deps.replace('/', '\\')
out.write('%s\n' % deps)
out.write('\t%s -cp %s ' % (JAVAC, pkg))
win_ex_dir = self.to_ex_dir
for javafile in get_java_files(self.ex_dir):
out.write(' ')
out.write(os.path.join(win_ex_dir, javafile))
out.write(' -d .\n')
out.write('_ex_%s: JavaExample.class\n\n' % (self.name))
class MLExampleComponent(ExampleComponent):
def __init__(self, name, path):
ExampleComponent.__init__(self, name, path)
def is_example(self):
return ML_ENABLED
def mk_makefile(self, out):
if ML_ENABLED:
out.write('ml_example.byte: api/ml/z3ml.cma')
for mlfile in get_ml_files(self.ex_dir):
out.write(' %s' % os.path.join(self.to_ex_dir, mlfile))
out.write('\n')
out.write('\t%s ' % OCAMLC)
if DEBUG_MODE:
out.write('-g ')
out.write('-custom -o ml_example.byte -I -zarith -I api/ml -cclib "-L. -lz3" zarith.cma z3ml.cma')
for mlfile in get_ml_files(self.ex_dir):
out.write(' %s/%s' % (self.to_ex_dir, mlfile))
out.write('\n')
out.write('ml_example$(EXE_EXT): api/ml/z3ml.cmxa')
for mlfile in get_ml_files(self.ex_dir):
out.write(' %s' % os.path.join(self.to_ex_dir, mlfile))
out.write('\n')
out.write('\t%s ' % OCAMLOPT)
if DEBUG_MODE:
out.write('-g ')
out.write('-o ml_example$(EXE_EXT) -I -zarith -I api/ml -cclib "-L. -lz3" zarith.cmxa z3ml.cmxa')
for mlfile in get_ml_files(self.ex_dir):
out.write(' %s/%s' % (self.to_ex_dir, mlfile))
out.write('\n')
out.write('_ex_%s: ml_example.byte ml_example$(EXE_EXT)\n\n' % self.name)
class PythonExampleComponent(ExampleComponent):
def __init__(self, name, path):
ExampleComponent.__init__(self, name, path)
# Python examples are just placeholders, we just copy the *.py files when mk_makefile is invoked.
# We don't need to include them in the :examples rule
def mk_makefile(self, out):
full = os.path.join(EXAMPLE_DIR, self.path)
for py in filter(lambda f: f.endswith('.py'), os.listdir(full)):
shutil.copyfile(os.path.join(full, py), os.path.join(BUILD_DIR, 'python', py))
if is_verbose():
print("Copied Z3Py example '%s' to '%s'" % (py, os.path.join(BUILD_DIR, 'python')))
out.write('_ex_%s: \n\n' % self.name)
def mk_win_dist(self, build_path, dist_path):
full = os.path.join(EXAMPLE_DIR, self.path)
py = 'example.py'
shutil.copyfile(os.path.join(full, py),
os.path.join(dist_path, INSTALL_BIN_DIR, 'python', py))
def mk_unix_dist(self, build_path, dist_path):
self.mk_win_dist(build_path, dist_path)
def reg_component(name, c):
global _Id, _Components, _ComponentNames, _Name2Component
c.id = _Id
_Id = _Id + 1
_Components.append(c)
_ComponentNames.add(name)
_Name2Component[name] = c
if VERBOSE:
print("New component: '%s'" % name)
def add_lib(name, deps=[], path=None, includes2install=[]):
c = LibComponent(name, path, deps, includes2install)
reg_component(name, c)
def add_hlib(name, path=None, includes2install=[]):
c = HLibComponent(name, path, includes2install)
reg_component(name, c)
def add_exe(name, deps=[], path=None, exe_name=None, install=True):
c = ExeComponent(name, exe_name, path, deps, install)
reg_component(name, c)
def add_extra_exe(name, deps=[], path=None, exe_name=None, install=True):
c = ExtraExeComponent(name, exe_name, path, deps, install)
reg_component(name, c)
def add_dll(name, deps=[], path=None, dll_name=None, export_files=[], reexports=[], install=True, static=False, staging_link=None):
c = DLLComponent(name, dll_name, path, deps, export_files, reexports, install, static, staging_link)
reg_component(name, c)
return c
def add_dot_net_core_dll(name, deps=[], path=None, dll_name=None, assembly_info_dir=None, default_key_file=None):
c = DotNetDLLComponent(name, dll_name, path, deps, assembly_info_dir, default_key_file)
reg_component(name, c)
def add_java_dll(name, deps=[], path=None, dll_name=None, package_name=None, manifest_file=None):
c = JavaDLLComponent(name, dll_name, package_name, manifest_file, path, deps)
reg_component(name, c)
def add_python(libz3Component):
name = 'python'
reg_component(name, PythonComponent(name, libz3Component))
def add_js():
reg_component('js', JsComponent())
def add_python_install(libz3Component):
name = 'python_install'
reg_component(name, PythonInstallComponent(name, libz3Component))
def add_ml_lib(name, deps=[], path=None, lib_name=None):
c = MLComponent(name, lib_name, path, deps)
reg_component(name, c)
def add_cpp_example(name, path=None):
c = CppExampleComponent(name, path)
reg_component(name, c)
def add_c_example(name, path=None):
c = CExampleComponent(name, path)
reg_component(name, c)
def add_dotnet_example(name, path=None):
c = DotNetExampleComponent(name, path)
reg_component(name, c)
def add_java_example(name, path=None):
c = JavaExampleComponent(name, path)
reg_component(name, c)
def add_ml_example(name, path=None):
c = MLExampleComponent(name, path)
reg_component(name, c)
def add_z3py_example(name, path=None):
c = PythonExampleComponent(name, path)
reg_component(name, c)
def mk_config():
if ONLY_MAKEFILES:
return
config = open(os.path.join(BUILD_DIR, 'config.mk'), 'w')
global CXX, CC, GMP, CPPFLAGS, CXXFLAGS, LDFLAGS, EXAMP_DEBUG_FLAG, FPMATH_FLAGS, LOG_SYNC, SINGLE_THREADED
if IS_WINDOWS:
config.write(
'CC=cl\n'
'CXX=cl\n'
'CXX_OUT_FLAG=/Fo\n'
'C_OUT_FLAG=/Fo\n'
'OBJ_EXT=.obj\n'
'LIB_EXT=.lib\n'
'AR=lib\n'
'AR_OUTFLAG=/OUT:\n'
'EXE_EXT=.exe\n'
'LINK=cl\n'
'LINK_OUT_FLAG=/Fe\n'
'SO_EXT=.dll\n'
'SLINK=cl\n'
'SLINK_OUT_FLAG=/Fe\n'
'OS_DEFINES=/D _WINDOWS\n')
extra_opt = ''
link_extra_opt = ''
if LOG_SYNC:
extra_opt = '%s /DZ3_LOG_SYNC' % extra_opt
if SINGLE_THREADED:
extra_opt = '%s /DSINGLE_THREAD' % extra_opt
if GIT_HASH:
extra_opt = ' %s /D Z3GITHASH=%s' % (extra_opt, GIT_HASH)
if GUARD_CF:
extra_opt = ' %s /guard:cf' % extra_opt
link_extra_opt = ' %s /GUARD:CF' % link_extra_opt
if STATIC_BIN:
static_opt = '/MT'
else:
static_opt = '/MD'
maybe_disable_dynamic_base = '/DYNAMICBASE' if ALWAYS_DYNAMIC_BASE else '/DYNAMICBASE:NO'
if DEBUG_MODE:
static_opt = static_opt + 'd'
config.write(
'AR_FLAGS=/nologo\n'
'LINK_FLAGS=/nologo %s\n'
'SLINK_FLAGS=/nologo /LDd\n' % static_opt)
if VS_X64:
config.write(
'CXXFLAGS=/c /Zi /nologo /W3 /WX- /Od /Oy- /D WIN32 /D _DEBUG /D Z3DEBUG /D _CONSOLE /D _TRACE /D _WINDOWS /Gm- /EHsc /RTC1 /GS /Gd %s %s\n' % (extra_opt, static_opt))
config.write(
'LINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X64 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT %s\n'
'SLINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X64 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 %s %s\n' % (link_extra_opt, maybe_disable_dynamic_base, link_extra_opt))
elif VS_ARM:
print("ARM on VS is unsupported")
exit(1)
else:
config.write(
'CXXFLAGS=/c /Zi /nologo /W3 /WX- /Od /Oy- /D WIN32 /D _DEBUG /D Z3DEBUG /D _CONSOLE /D _TRACE /D _WINDOWS /Gm- /EHsc /RTC1 /GS /Gd /arch:SSE2 %s %s\n' % (extra_opt, static_opt))
config.write(
'LINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X86 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT %s\n'
'SLINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X86 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 %s %s\n' % (link_extra_opt, maybe_disable_dynamic_base, link_extra_opt))
else:
# Windows Release mode
LTCG=' /LTCG' if SLOW_OPTIMIZE else ''
GL = ' /GL' if SLOW_OPTIMIZE else ''
config.write(
'AR_FLAGS=/nologo %s\n'
'LINK_FLAGS=/nologo %s\n'
'SLINK_FLAGS=/nologo /LD\n' % (LTCG, static_opt))
if TRACE:
extra_opt = '%s /D _TRACE ' % extra_opt
if VS_X64:
config.write(
'CXXFLAGS=/c%s /Zi /nologo /W3 /WX- /O2 /D _EXTERNAL_RELEASE /D WIN32 /D NDEBUG /D _LIB /D _WINDOWS /D _UNICODE /D UNICODE /Gm- /EHsc /GS /Gd /GF /Gy /TP %s %s\n' % (GL, extra_opt, static_opt))
config.write(
'LINK_EXTRA_FLAGS=/link%s /profile /MACHINE:X64 /SUBSYSTEM:CONSOLE /STACK:8388608 %s\n'
'SLINK_EXTRA_FLAGS=/link%s /profile /MACHINE:X64 /SUBSYSTEM:WINDOWS /STACK:8388608 %s\n' % (LTCG, link_extra_opt, LTCG, link_extra_opt))
elif VS_ARM:
print("ARM on VS is unsupported")
exit(1)
else:
config.write(
'CXXFLAGS=/nologo /c%s /Zi /W3 /WX- /O2 /Oy- /D _EXTERNAL_RELEASE /D WIN32 /D NDEBUG /D _CONSOLE /D _WINDOWS /D ASYNC_COMMANDS /Gm- /EHsc /GS /Gd /arch:SSE2 %s %s\n' % (GL, extra_opt, static_opt))
config.write(
'LINK_EXTRA_FLAGS=/link%s /DEBUG /MACHINE:X86 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT %s\n'
'SLINK_EXTRA_FLAGS=/link%s /DEBUG /MACHINE:X86 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 %s %s\n' % (LTCG, link_extra_opt, LTCG, maybe_disable_dynamic_base, link_extra_opt))
config.write('CFLAGS=$(CXXFLAGS)\n')
# End of Windows VS config.mk
if is_verbose():
print('64-bit: %s' % is64())
if is_java_enabled():
print('JNI Bindings: %s' % JNI_HOME)
print('Java Compiler: %s' % JAVAC)
if is_ml_enabled():
print('OCaml Compiler: %s' % OCAMLC)
print('OCaml Find tool: %s' % OCAMLFIND)
print('OCaml Native: %s' % OCAMLOPT)
print('OCaml Library: %s' % OCAML_LIB)
else:
OS_DEFINES = ""
ARITH = "internal"
check_ar()
CXX = find_cxx_compiler()
CC = find_c_compiler()
SLIBEXTRAFLAGS = ''
# SLIBEXTRAFLAGS = '%s -Wl,-soname,libz3.so.0' % LDFLAGS
EXE_EXT = ''
LIB_EXT = '.a'
if GPROF:
CXXFLAGS = '%s -pg' % CXXFLAGS
LDFLAGS = '%s -pg' % LDFLAGS
if GMP:
test_gmp(CXX)
ARITH = "gmp"
CPPFLAGS = '%s -D_MP_GMP' % CPPFLAGS
LDFLAGS = '%s -lgmp' % LDFLAGS
SLIBEXTRAFLAGS = '%s -lgmp' % SLIBEXTRAFLAGS
else:
CPPFLAGS = '%s -D_MP_INTERNAL' % CPPFLAGS
if GIT_HASH:
CPPFLAGS = '%s -DZ3GITHASH=%s' % (CPPFLAGS, GIT_HASH)
CXXFLAGS = '%s -std=c++11' % CXXFLAGS
CXXFLAGS = '%s -fvisibility=hidden -c' % CXXFLAGS
FPMATH = test_fpmath(CXX)
CXXFLAGS = '%s %s' % (CXXFLAGS, FPMATH_FLAGS)
if LOG_SYNC:
CXXFLAGS = '%s -DZ3_LOG_SYNC' % CXXFLAGS
if SINGLE_THREADED:
CXXFLAGS = '%s -DSINGLE_THREAD' % CXXFLAGS
if DEBUG_MODE:
CXXFLAGS = '%s -g -Wall' % CXXFLAGS
EXAMP_DEBUG_FLAG = '-g'
CPPFLAGS = '%s -DZ3DEBUG -D_DEBUG' % CPPFLAGS
else:
CXXFLAGS = '%s -O3' % CXXFLAGS
if GPROF:
CXXFLAGS += '-fomit-frame-pointer'
CPPFLAGS = '%s -DNDEBUG -D_EXTERNAL_RELEASE' % CPPFLAGS
if is_CXX_clangpp():
CXXFLAGS = '%s -Wno-unknown-pragmas -Wno-overloaded-virtual -Wno-unused-value' % CXXFLAGS
sysname, _, _, _, machine = os.uname()
if sysname == 'Darwin':
SO_EXT = '.dylib'
SLIBFLAGS = '-dynamiclib'
elif sysname == 'Linux':
CXXFLAGS = '%s -D_LINUX_' % CXXFLAGS
OS_DEFINES = '-D_LINUX_'
SO_EXT = '.so'
SLIBFLAGS = '-shared'
SLIBEXTRAFLAGS = '%s -Wl,-soname,libz3.so' % SLIBEXTRAFLAGS
elif sysname == 'GNU':
CXXFLAGS = '%s -D_HURD_' % CXXFLAGS
OS_DEFINES = '-D_HURD_'
SO_EXT = '.so'
SLIBFLAGS = '-shared'
elif sysname == 'FreeBSD':
CXXFLAGS = '%s -D_FREEBSD_' % CXXFLAGS
OS_DEFINES = '-D_FREEBSD_'
SO_EXT = '.so'
SLIBFLAGS = '-shared'
elif sysname == 'NetBSD':
CXXFLAGS = '%s -D_NETBSD_' % CXXFLAGS
OS_DEFINES = '-D_NETBSD_'
SO_EXT = '.so'
SLIBFLAGS = '-shared'
elif sysname == 'OpenBSD':
CXXFLAGS = '%s -D_OPENBSD_' % CXXFLAGS
OS_DEFINES = '-D_OPENBSD_'
SO_EXT = '.so'
SLIBFLAGS = '-shared'
elif sysname.startswith('CYGWIN'):
CXXFLAGS = '%s -D_CYGWIN' % CXXFLAGS
OS_DEFINES = '-D_CYGWIN'
SO_EXT = '.dll'
SLIBFLAGS = '-shared'
elif sysname.startswith('MSYS_NT') or sysname.startswith('MINGW'):
CXXFLAGS = '%s -D_MINGW' % CXXFLAGS
OS_DEFINES = '-D_MINGW'
SO_EXT = '.dll'
SLIBFLAGS = '-shared'
EXE_EXT = '.exe'
LIB_EXT = '.lib'
else:
raise MKException('Unsupported platform: %s' % sysname)
if is64():
if not sysname.startswith('CYGWIN') and not sysname.startswith('MSYS') and not sysname.startswith('MINGW'):
CXXFLAGS = '%s -fPIC' % CXXFLAGS
if sysname == 'Linux':
CPPFLAGS = '%s -D_USE_THREAD_LOCAL' % CPPFLAGS
elif not LINUX_X64:
CXXFLAGS = '%s -m32' % CXXFLAGS
LDFLAGS = '%s -m32' % LDFLAGS
SLIBFLAGS = '%s -m32' % SLIBFLAGS
if TRACE or DEBUG_MODE:
CPPFLAGS = '%s -D_TRACE' % CPPFLAGS
if is_cygwin_mingw():
# when cross-compiling with MinGW, we need to statically link its standard libraries
# and to make it create an import library.
SLIBEXTRAFLAGS = '%s -static-libgcc -static-libstdc++ -Wl,--out-implib,libz3.dll.a' % SLIBEXTRAFLAGS
LDFLAGS = '%s -static-libgcc -static-libstdc++' % LDFLAGS
if sysname == 'Linux' and machine.startswith('armv7') or machine.startswith('armv8'):
CXXFLAGS = '%s -fpic' % CXXFLAGS
config.write('PREFIX=%s\n' % PREFIX)
config.write('CC=%s\n' % CC)
config.write('CXX=%s\n' % CXX)
config.write('CXXFLAGS=%s %s\n' % (CPPFLAGS, CXXFLAGS))
config.write('CFLAGS=%s %s\n' % (CPPFLAGS, CXXFLAGS.replace('-std=c++11', '')))
config.write('EXAMP_DEBUG_FLAG=%s\n' % EXAMP_DEBUG_FLAG)
config.write('CXX_OUT_FLAG=-o \n')
config.write('C_OUT_FLAG=-o \n')
config.write('OBJ_EXT=.o\n')
config.write('LIB_EXT=%s\n' % LIB_EXT)
config.write('AR=%s\n' % AR)
config.write('AR_FLAGS=rcs\n')
config.write('AR_OUTFLAG=\n')
config.write('EXE_EXT=%s\n' % EXE_EXT)
config.write('LINK=%s\n' % CXX)
config.write('LINK_FLAGS=\n')
config.write('LINK_OUT_FLAG=-o \n')
if is_linux() and (build_static_lib() or build_static_bin()):
config.write('LINK_EXTRA_FLAGS=-Wl,--whole-archive -lpthread -Wl,--no-whole-archive %s\n' % LDFLAGS)
else:
config.write('LINK_EXTRA_FLAGS=-lpthread %s\n' % LDFLAGS)
config.write('SO_EXT=%s\n' % SO_EXT)
config.write('SLINK=%s\n' % CXX)
config.write('SLINK_FLAGS=%s\n' % SLIBFLAGS)
config.write('SLINK_EXTRA_FLAGS=-lpthread %s\n' % SLIBEXTRAFLAGS)
config.write('SLINK_OUT_FLAG=-o \n')
config.write('OS_DEFINES=%s\n' % OS_DEFINES)
if is_verbose():
print('Host platform: %s' % sysname)
print('C++ Compiler: %s' % CXX)
print('C Compiler : %s' % CC)
if is_cygwin_mingw():
print('MinGW32 cross: %s' % (is_cygwin_mingw()))
print('Archive Tool: %s' % AR)
print('Arithmetic: %s' % ARITH)
print('Prefix: %s' % PREFIX)
print('64-bit: %s' % is64())
print('FP math: %s' % FPMATH)
print("Python pkg dir: %s" % PYTHON_PACKAGE_DIR)
if GPROF:
print('gprof: enabled')
print('Python version: %s' % distutils.sysconfig.get_python_version())
if is_java_enabled():
print('JNI Bindings: %s' % JNI_HOME)
print('Java Compiler: %s' % JAVAC)
if is_ml_enabled():
print('OCaml Compiler: %s' % OCAMLC)
print('OCaml Find tool: %s' % OCAMLFIND)
print('OCaml Native: %s' % OCAMLOPT)
print('OCaml Library: %s' % OCAML_LIB)
if is_dotnet_core_enabled():
print('C# Compiler: %s' % DOTNET)
config.close()
def mk_install(out):
out.write('install: ')
for c in get_components():
c.mk_install_deps(out)
out.write(' ')
out.write('\n')
MakeRuleCmd.make_install_directory(out, INSTALL_BIN_DIR)
MakeRuleCmd.make_install_directory(out, INSTALL_INCLUDE_DIR)
MakeRuleCmd.make_install_directory(out, INSTALL_LIB_DIR)
for c in get_components():
c.mk_install(out)
out.write('\t@echo Z3 was successfully installed.\n')
out.write('\n')
def mk_uninstall(out):
out.write('uninstall:\n')
for c in get_components():
c.mk_uninstall(out)
out.write('\t@echo Z3 was successfully uninstalled.\n')
out.write('\n')
# Generate the Z3 makefile
def mk_makefile():
mk_dir(BUILD_DIR)
mk_config()
if VERBOSE:
print("Writing %s" % os.path.join(BUILD_DIR, 'Makefile'))
out = open(os.path.join(BUILD_DIR, 'Makefile'), 'w')
out.write('# Automatically generated file.\n')
out.write('include config.mk\n')
# Generate :all rule
out.write('all:')
for c in get_components():
if c.main_component():
out.write(' %s' % c.name)
out.write('\n\t@echo Z3 was successfully built.\n')
out.write("\t@echo \"Z3Py scripts can already be executed in the \'%s\' directory.\"\n" % os.path.join(BUILD_DIR, 'python'))
pathvar = "DYLD_LIBRARY_PATH" if IS_OSX else "PATH" if IS_WINDOWS else "LD_LIBRARY_PATH"
out.write("\t@echo \"Z3Py scripts stored in arbitrary directories can be executed if the \'%s\' directory is added to the PYTHONPATH environment variable and the \'%s\' directory is added to the %s environment variable.\"\n" % (os.path.join(BUILD_DIR, 'python'), BUILD_DIR, pathvar))
if not IS_WINDOWS:
out.write("\t@echo Use the following command to install Z3 at prefix $(PREFIX).\n")
out.write('\t@echo " sudo make install"\n\n')
# out.write("\t@echo If you are doing a staged install you can use DESTDIR.\n")
# out.write('\t@echo " make DESTDIR=/some/temp/directory install"\n')
# Generate :examples rule
out.write('examples:')
for c in get_components():
if c.is_example():
out.write(' _ex_%s' % c.name)
out.write('\n\t@echo Z3 examples were successfully built.\n')
# Generate components
for c in get_components():
c.mk_makefile(out)
# Generate install/uninstall rules if not WINDOWS
if not IS_WINDOWS:
mk_install(out)
mk_uninstall(out)
for c in get_components():
c.final_info()
out.close()
# Finalize
if VERBOSE:
print("Makefile was successfully generated.")
if DEBUG_MODE:
print(" compilation mode: Debug")
else:
print(" compilation mode: Release")
if IS_WINDOWS:
if VS_X64:
print(" platform: x64\n")
print("To build Z3, open a [Visual Studio x64 Command Prompt], then")
elif VS_ARM:
print(" platform: ARM\n")
print("To build Z3, open a [Visual Studio ARM Command Prompt], then")
else:
print(" platform: x86")
print("To build Z3, open a [Visual Studio Command Prompt], then")
print("type 'cd %s && nmake'\n" % os.path.join(os.getcwd(), BUILD_DIR))
print('Remark: to open a Visual Studio Command Prompt, go to: "Start > All Programs > Visual Studio > Visual Studio Tools"')
else:
print("Type 'cd %s; make' to build Z3" % BUILD_DIR)
# Generate automatically generated source code
def mk_auto_src():
if not ONLY_MAKEFILES:
exec_pyg_scripts()
mk_pat_db()
mk_all_install_tactic_cpps()
mk_all_mem_initializer_cpps()
mk_all_gparams_register_modules()
def _execfile(file, globals=globals(), locals=locals()):
if sys.version < "2.7":
execfile(file, globals, locals)
else:
with open(file, "r") as fh:
exec(fh.read()+"\n", globals, locals)
# Execute python auxiliary scripts that generate extra code for Z3.
def exec_pyg_scripts():
for root, dirs, files in os.walk('src'):
for f in files:
if f.endswith('.pyg'):
script = os.path.join(root, f)
generated_file = mk_genfile_common.mk_hpp_from_pyg(script, root)
if is_verbose():
print("Generated '{}'".format(generated_file))
# TODO: delete after src/ast/pattern/expr_pattern_match
# database.smt ==> database.h
def mk_pat_db():
c = get_component(PATTERN_COMPONENT)
fin = os.path.join(c.src_dir, 'database.smt2')
fout = os.path.join(c.src_dir, 'database.h')
mk_genfile_common.mk_pat_db_internal(fin, fout)
if VERBOSE:
print("Generated '{}'".format(fout))
# Update version numbers
def update_version():
major = VER_MAJOR
minor = VER_MINOR
build = VER_BUILD
revision = VER_TWEAK
if major is None or minor is None or build is None or revision is None:
raise MKException("set_version(major, minor, build, revision) must be used before invoking update_version()")
if not ONLY_MAKEFILES:
mk_version_dot_h(major, minor, build, revision)
mk_all_assembly_infos(major, minor, build, revision)
mk_def_files()
def get_full_version_string(major, minor, build, revision):
global GIT_HASH, GIT_DESCRIBE
res = "Z3 %s.%s.%s.%s" % (major, minor, build, revision)
if GIT_HASH:
res += " " + GIT_HASH
if GIT_DESCRIBE:
branch = check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD'])
res += " " + branch + " " + check_output(['git', 'describe'])
return '"' + res + '"'
# Update files with the version number
def mk_version_dot_h(major, minor, build, revision):
c = get_component(UTIL_COMPONENT)
version_template = os.path.join(c.src_dir, 'z3_version.h.in')
version_header_output = os.path.join(c.src_dir, 'z3_version.h')
# Note the substitution names are what is used by the CMake
# builds system. If you change these you should change them
# in the CMake build too
configure_file(version_template, version_header_output,
{ 'Z3_VERSION_MAJOR': str(major),
'Z3_VERSION_MINOR': str(minor),
'Z3_VERSION_PATCH': str(build),
'Z3_VERSION_TWEAK': str(revision),
'Z3_FULL_VERSION': get_full_version_string(major, minor, build, revision)
}
)
if VERBOSE:
print("Generated '%s'" % version_header_output)
# Generate AssemblyInfo.cs files with the right version numbers by using ``AssemblyInfo.cs.in`` files as a template
def mk_all_assembly_infos(major, minor, build, revision):
for c in get_components():
if c.has_assembly_info():
c.make_assembly_info(major, minor, build, revision)
def get_header_files_for_components(component_src_dirs):
assert isinstance(component_src_dirs, list)
h_files_full_path = []
for component_src_dir in sorted(component_src_dirs):
h_files = filter(lambda f: f.endswith('.h') or f.endswith('.hpp'), os.listdir(component_src_dir))
h_files = list(map(lambda p: os.path.join(component_src_dir, p), h_files))
h_files_full_path.extend(h_files)
return h_files_full_path
def mk_install_tactic_cpp(cnames, path):
component_src_dirs = []
for cname in cnames:
print("Component %s" % cname)
c = get_component(cname)
component_src_dirs.append(c.src_dir)
h_files_full_path = get_header_files_for_components(component_src_dirs)
generated_file = mk_genfile_common.mk_install_tactic_cpp_internal(h_files_full_path, path)
if VERBOSE:
print("Generated '{}'".format(generated_file))
def mk_all_install_tactic_cpps():
if not ONLY_MAKEFILES:
for c in get_components():
if c.require_install_tactics():
cnames = []
cnames.extend(c.deps)
cnames.append(c.name)
mk_install_tactic_cpp(cnames, c.src_dir)
def mk_mem_initializer_cpp(cnames, path):
component_src_dirs = []
for cname in cnames:
c = get_component(cname)
component_src_dirs.append(c.src_dir)
h_files_full_path = get_header_files_for_components(component_src_dirs)
generated_file = mk_genfile_common.mk_mem_initializer_cpp_internal(h_files_full_path, path)
if VERBOSE:
print("Generated '{}'".format(generated_file))
def mk_all_mem_initializer_cpps():
if not ONLY_MAKEFILES:
for c in get_components():
if c.require_mem_initializer():
cnames = []
cnames.extend(c.deps)
cnames.append(c.name)
mk_mem_initializer_cpp(cnames, c.src_dir)
def mk_gparams_register_modules(cnames, path):
component_src_dirs = []
for cname in cnames:
c = get_component(cname)
component_src_dirs.append(c.src_dir)
h_files_full_path = get_header_files_for_components(component_src_dirs)
generated_file = mk_genfile_common.mk_gparams_register_modules_internal(h_files_full_path, path)
if VERBOSE:
print("Generated '{}'".format(generated_file))
def mk_all_gparams_register_modules():
if not ONLY_MAKEFILES:
for c in get_components():
if c.require_mem_initializer():
cnames = []
cnames.extend(c.deps)
cnames.append(c.name)
mk_gparams_register_modules(cnames, c.src_dir)
# Generate a .def based on the files at c.export_files slot.
def mk_def_file(c):
defname = '%s.def' % os.path.join(c.src_dir, c.name)
dll_name = c.dll_name
export_header_files = []
for dot_h in c.export_files:
dot_h_c = c.find_file(dot_h, c.name)
api = os.path.join(dot_h_c.src_dir, dot_h)
export_header_files.append(api)
mk_genfile_common.mk_def_file_internal(defname, dll_name, export_header_files)
if VERBOSE:
print("Generated '%s'" % defname)
def mk_def_files():
if not ONLY_MAKEFILES:
for c in get_components():
if c.require_def_file():
mk_def_file(c)
def cp_z3py_to_build():
mk_dir(BUILD_DIR)
mk_dir(os.path.join(BUILD_DIR, 'python'))
z3py_dest = os.path.join(BUILD_DIR, 'python', 'z3')
z3py_src = os.path.join(Z3PY_SRC_DIR, 'z3')
# Erase existing .pyc files
for root, dirs, files in os.walk(Z3PY_SRC_DIR):
for f in files:
if f.endswith('.pyc'):
rmf(os.path.join(root, f))
# Compile Z3Py files
if compileall.compile_dir(z3py_src, force=1) != 1:
raise MKException("failed to compile Z3Py sources")
if is_verbose:
print("Generated python bytecode")
# Copy sources to build
mk_dir(z3py_dest)
for py in filter(lambda f: f.endswith('.py'), os.listdir(z3py_src)):
shutil.copyfile(os.path.join(z3py_src, py), os.path.join(z3py_dest, py))
if is_verbose():
print("Copied '%s'" % py)
# Python 2.x support
for pyc in filter(lambda f: f.endswith('.pyc'), os.listdir(z3py_src)):
shutil.copyfile(os.path.join(z3py_src, pyc), os.path.join(z3py_dest, pyc))
if is_verbose():
print("Copied '%s'" % pyc)
# Python 3.x support
src_pycache = os.path.join(z3py_src, '__pycache__')
target_pycache = os.path.join(z3py_dest, '__pycache__')
if os.path.exists(src_pycache):
for pyc in filter(lambda f: f.endswith('.pyc'), os.listdir(src_pycache)):
mk_dir(target_pycache)
shutil.copyfile(os.path.join(src_pycache, pyc), os.path.join(target_pycache, pyc))
if is_verbose():
print("Copied '%s'" % pyc)
# Copy z3test.py
shutil.copyfile(os.path.join(Z3PY_SRC_DIR, 'z3test.py'), os.path.join(BUILD_DIR, 'python', 'z3test.py'))
def mk_bindings(api_files):
if not ONLY_MAKEFILES:
mk_z3consts_py(api_files)
new_api_files = []
api = get_component(API_COMPONENT)
for api_file in api_files:
api_file_path = api.find_file(api_file, api.name)
new_api_files.append(os.path.join(api_file_path.src_dir, api_file))
g = globals()
g["API_FILES"] = new_api_files
if is_java_enabled():
check_java()
mk_z3consts_java(api_files)
# Generate some of the bindings and "api" module files
import update_api
dotnet_output_dir = None
if is_dotnet_core_enabled():
dotnet_output_dir = os.path.join(BUILD_DIR, 'dotnet')
mk_dir(dotnet_output_dir)
java_output_dir = None
java_package_name = None
if is_java_enabled():
java_output_dir = get_component('java').src_dir
java_package_name = get_component('java').package_name
ml_output_dir = None
if is_ml_enabled():
ml_output_dir = get_component('ml').src_dir
if is_js_enabled():
set_z3js_dir("api/js")
js_output_dir = get_component('js').src_dir
# Get the update_api module to do the work for us
update_api.generate_files(api_files=new_api_files,
api_output_dir=get_component('api').src_dir,
z3py_output_dir=get_z3py_dir(),
dotnet_output_dir=dotnet_output_dir,
java_output_dir=java_output_dir,
java_package_name=java_package_name,
js_output_dir=get_z3js_dir(),
ml_output_dir=ml_output_dir,
ml_src_dir=ml_output_dir
)
cp_z3py_to_build()
if is_ml_enabled():
check_ml()
mk_z3consts_ml(api_files)
if is_dotnet_core_enabled():
check_dotnet_core()
mk_z3consts_dotnet(api_files, dotnet_output_dir)
# Extract enumeration types from API files, and add python definitions.
def mk_z3consts_py(api_files):
if Z3PY_SRC_DIR is None:
raise MKException("You must invoke set_z3py_dir(path):")
full_path_api_files = []
api_dll = get_component(Z3_DLL_COMPONENT)
for api_file in api_files:
api_file_c = api_dll.find_file(api_file, api_dll.name)
api_file = os.path.join(api_file_c.src_dir, api_file)
full_path_api_files.append(api_file)
generated_file = mk_genfile_common.mk_z3consts_py_internal(full_path_api_files, Z3PY_SRC_DIR)
if VERBOSE:
print("Generated '{}".format(generated_file))
# Extract enumeration types from z3_api.h, and add .Net definitions
def mk_z3consts_dotnet(api_files, output_dir):
dotnet = get_component(DOTNET_COMPONENT)
if not dotnet:
dotnet = get_component(DOTNET_CORE_COMPONENT)
full_path_api_files = []
for api_file in api_files:
api_file_c = dotnet.find_file(api_file, dotnet.name)
api_file = os.path.join(api_file_c.src_dir, api_file)
full_path_api_files.append(api_file)
generated_file = mk_genfile_common.mk_z3consts_dotnet_internal(full_path_api_files, output_dir)
if VERBOSE:
print("Generated '{}".format(generated_file))
# Extract enumeration types from z3_api.h, and add Java definitions
def mk_z3consts_java(api_files):
java = get_component(JAVA_COMPONENT)
full_path_api_files = []
for api_file in api_files:
api_file_c = java.find_file(api_file, java.name)
api_file = os.path.join(api_file_c.src_dir, api_file)
full_path_api_files.append(api_file)
generated_files = mk_genfile_common.mk_z3consts_java_internal(
full_path_api_files,
java.package_name,
java.src_dir)
if VERBOSE:
for generated_file in generated_files:
print("Generated '{}'".format(generated_file))
# Extract enumeration types from z3_api.h, and add ML definitions
def mk_z3consts_ml(api_files):
ml = get_component(ML_COMPONENT)
full_path_api_files = []
for api_file in api_files:
api_file_c = ml.find_file(api_file, ml.name)
api_file = os.path.join(api_file_c.src_dir, api_file)
full_path_api_files.append(api_file)
generated_file = mk_genfile_common.mk_z3consts_ml_internal(
full_path_api_files,
ml.src_dir)
if VERBOSE:
print ('Generated "%s"' % generated_file)
def mk_gui_str(id):
return '4D2F40D8-E5F9-473B-B548-%012d' % id
def get_platform_toolset_str():
default = 'v110';
vstr = check_output(['msbuild', '/ver'])
lines = vstr.split('\n')
lline = lines[-1]
tokens = lline.split('.')
if len(tokens) < 2:
return default
else:
if tokens[0] == "15":
# Visual Studio 2017 reports 15.* but the PlatformToolsetVersion is 141
return "v141"
else:
return 'v' + tokens[0] + tokens[1]
def mk_vs_proj_property_groups(f, name, target_ext, type):
f.write(' <ItemGroup Label="ProjectConfigurations">\n')
f.write(' <ProjectConfiguration Include="Debug|Win32">\n')
f.write(' <Configuration>Debug</Configuration>\n')
f.write(' <Platform>Win32</Platform>\n')
f.write(' </ProjectConfiguration>\n')
f.write(' <ProjectConfiguration Include="Release|Win32">\n')
f.write(' <Configuration>Release</Configuration>\n')
f.write(' <Platform>Win32</Platform>\n')
f.write(' </ProjectConfiguration>\n')
f.write(' </ItemGroup>\n')
f.write(' <PropertyGroup Label="Globals">\n')
f.write(' <ProjectGuid>{%s}</ProjectGuid>\n' % mk_gui_str(0))
f.write(' <ProjectName>%s</ProjectName>\n' % name)
f.write(' <Keyword>Win32Proj</Keyword>\n')
f.write(' <PlatformToolset>%s</PlatformToolset>\n' % get_platform_toolset_str())
f.write(' </PropertyGroup>\n')
f.write(' <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />\n')
f.write(' <PropertyGroup Condition="\'$(Configuration)|$(Platform)\'==\'Debug|Win32\'" Label="Configuration">\n')
f.write(' <ConfigurationType>%s</ConfigurationType>\n' % type)
f.write(' <CharacterSet>Unicode</CharacterSet>\n')
f.write(' <UseOfMfc>false</UseOfMfc>\n')
f.write(' </PropertyGroup>\n')
f.write(' <PropertyGroup Condition="\'$(Configuration)|$(Platform)\'==\'Release|Win32\'" Label="Configuration">\n')
f.write(' <ConfigurationType>%s</ConfigurationType>\n' % type)
f.write(' <CharacterSet>Unicode</CharacterSet>\n')
f.write(' <UseOfMfc>false</UseOfMfc>\n')
f.write(' </PropertyGroup>\n')
f.write(' <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />\n')
f.write(' <ImportGroup Label="ExtensionSettings" />\n')
f.write(' <ImportGroup Label="PropertySheets">\n')
f.write(' <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists(\'$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props\')" Label="LocalAppDataPlatform" /> </ImportGroup>\n')
f.write(' <PropertyGroup Label="UserMacros" />\n')
f.write(' <PropertyGroup>\n')
f.write(' <OutDir Condition="\'$(Configuration)|$(Platform)\'==\'Debug|Win32\'">$(SolutionDir)\$(ProjectName)\$(Configuration)\</OutDir>\n')
f.write(' <TargetName Condition="\'$(Configuration)|$(Platform)\'==\'Debug|Win32\'">%s</TargetName>\n' % name)
f.write(' <TargetExt Condition="\'$(Configuration)|$(Platform)\'==\'Debug|Win32\'">.%s</TargetExt>\n' % target_ext)
f.write(' <OutDir Condition="\'$(Configuration)|$(Platform)\'==\'Release|Win32\'">$(SolutionDir)\$(ProjectName)\$(Configuration)\</OutDir>\n')
f.write(' <TargetName Condition="\'$(Configuration)|$(Platform)\'==\'Release|Win32\'">%s</TargetName>\n' % name)
f.write(' <TargetExt Condition="\'$(Configuration)|$(Platform)\'==\'Release|Win32\'">.%s</TargetExt>\n' % target_ext)
f.write(' </PropertyGroup>\n')
f.write(' <PropertyGroup Condition="\'$(Configuration)|$(Platform)\'==\'Debug|Win32\'">\n')
f.write(' <IntDir>$(ProjectName)\$(Configuration)\</IntDir>\n')
f.write(' </PropertyGroup>\n')
f.write(' <PropertyGroup Condition="\'$(Configuration)|$(Platform)\'==\'Release|Win32\'">\n')
f.write(' <IntDir>$(ProjectName)\$(Configuration)\</IntDir>\n')
f.write(' </PropertyGroup>\n')
def mk_vs_proj_cl_compile(f, name, components, debug):
f.write(' <ClCompile>\n')
f.write(' <Optimization>Disabled</Optimization>\n')
if debug:
f.write(' <PreprocessorDefinitions>WIN32;_DEBUG;Z3DEBUG;_TRACE;_MP_INTERNAL;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n')
else:
f.write(' <PreprocessorDefinitions>WIN32;NDEBUG;_MP_INTERNAL;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n')
if VS_PAR:
f.write(' <MinimalRebuild>false</MinimalRebuild>\n')
f.write(' <MultiProcessorCompilation>true</MultiProcessorCompilation>\n')
else:
f.write(' <MinimalRebuild>true</MinimalRebuild>\n')
f.write(' <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\n')
f.write(' <WarningLevel>Level3</WarningLevel>\n')
if debug:
f.write(' <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>\n')
else:
f.write(' <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>\n')
f.write(' <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\n')
f.write(' <AdditionalIncludeDirectories>')
deps = find_all_deps(name, components)
first = True
for dep in deps:
if first:
first = False
else:
f.write(';')
f.write(get_component(dep).to_src_dir)
f.write(';%s\n' % os.path.join(REV_BUILD_DIR, SRC_DIR))
f.write('</AdditionalIncludeDirectories>\n')
f.write(' </ClCompile>\n')
def mk_vs_proj_dep_groups(f, name, components):
f.write(' <ItemGroup>\n')
deps = find_all_deps(name, components)
for dep in deps:
dep = get_component(dep)
for cpp in filter(lambda f: f.endswith('.cpp'), os.listdir(dep.src_dir)):
f.write(' <ClCompile Include="%s" />\n' % os.path.join(dep.to_src_dir, cpp))
f.write(' </ItemGroup>\n')
def mk_vs_proj_link_exe(f, name, debug):
f.write(' <Link>\n')
f.write(' <OutputFile>$(OutDir)%s.exe</OutputFile>\n' % name)
f.write(' <GenerateDebugInformation>true</GenerateDebugInformation>\n')
f.write(' <SubSystem>Console</SubSystem>\n')
f.write(' <StackReserveSize>8388608</StackReserveSize>\n')
f.write(' <RandomizedBaseAddress>false</RandomizedBaseAddress>\n')
f.write(' <DataExecutionPrevention/>\n')
f.write(' <TargetMachine>MachineX86</TargetMachine>\n')
f.write(' <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\n')
f.write(' <AdditionalDependencies>psapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n')
f.write(' </Link>\n')
def mk_vs_proj(name, components):
if not VS_PROJ:
return
proj_name = '%s.vcxproj' % os.path.join(BUILD_DIR, name)
modes=['Debug', 'Release']
PLATFORMS=['Win32']
f = open(proj_name, 'w')
f.write('<?xml version="1.0" encoding="utf-8"?>\n')
f.write('<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\n')
mk_vs_proj_property_groups(f, name, 'exe', 'Application')
f.write(' <ItemDefinitionGroup Condition="\'$(Configuration)|$(Platform)\'==\'Debug|Win32\'">\n')
mk_vs_proj_cl_compile(f, name, components, debug=True)
mk_vs_proj_link_exe(f, name, debug=True)
f.write(' </ItemDefinitionGroup>\n')
f.write(' <ItemDefinitionGroup Condition="\'$(Configuration)|$(Platform)\'==\'Release|Win32\'">\n')
mk_vs_proj_cl_compile(f, name, components, debug=False)
mk_vs_proj_link_exe(f, name, debug=False)
f.write(' </ItemDefinitionGroup>\n')
mk_vs_proj_dep_groups(f, name, components)
f.write(' <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\n')
f.write(' <ImportGroup Label="ExtensionTargets">\n')
f.write(' </ImportGroup>\n')
f.write('</Project>\n')
f.close()
if is_verbose():
print("Generated '%s'" % proj_name)
def mk_vs_proj_link_dll(f, name, debug):
f.write(' <Link>\n')
f.write(' <OutputFile>$(OutDir)%s.dll</OutputFile>\n' % name)
f.write(' <GenerateDebugInformation>true</GenerateDebugInformation>\n')
f.write(' <SubSystem>Console</SubSystem>\n')
f.write(' <StackReserveSize>8388608</StackReserveSize>\n')
f.write(' <RandomizedBaseAddress>false</RandomizedBaseAddress>\n')
f.write(' <DataExecutionPrevention/>\n')
f.write(' <TargetMachine>MachineX86</TargetMachine>\n')
f.write(' <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\n')
f.write(' <AdditionalDependencies>psapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n')
f.write(' <ModuleDefinitionFile>%s</ModuleDefinitionFile>' % os.path.join(get_component('api_dll').to_src_dir, 'api_dll.def'))
f.write(' </Link>\n')
def mk_vs_proj_dll(name, components):
if not VS_PROJ:
return
proj_name = '%s.vcxproj' % os.path.join(BUILD_DIR, name)
modes=['Debug', 'Release']
PLATFORMS=['Win32']
f = open(proj_name, 'w')
f.write('<?xml version="1.0" encoding="utf-8"?>\n')
f.write('<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\n')
mk_vs_proj_property_groups(f, name, 'dll', 'DynamicLibrary')
f.write(' <ItemDefinitionGroup Condition="\'$(Configuration)|$(Platform)\'==\'Debug|Win32\'">\n')
mk_vs_proj_cl_compile(f, name, components, debug=True)
mk_vs_proj_link_dll(f, name, debug=True)
f.write(' </ItemDefinitionGroup>\n')
f.write(' <ItemDefinitionGroup Condition="\'$(Configuration)|$(Platform)\'==\'Release|Win32\'">\n')
mk_vs_proj_cl_compile(f, name, components, debug=False)
mk_vs_proj_link_dll(f, name, debug=False)
f.write(' </ItemDefinitionGroup>\n')
mk_vs_proj_dep_groups(f, name, components)
f.write(' <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\n')
f.write(' <ImportGroup Label="ExtensionTargets">\n')
f.write(' </ImportGroup>\n')
f.write('</Project>\n')
f.close()
if is_verbose():
print("Generated '%s'" % proj_name)
def mk_win_dist(build_path, dist_path):
for c in get_components():
c.mk_win_dist(build_path, dist_path)
def mk_unix_dist(build_path, dist_path):
for c in get_components():
c.mk_unix_dist(build_path, dist_path)
# Add Z3Py to bin directory
for pyc in filter(lambda f: f.endswith('.pyc') or f.endswith('.py'), os.listdir(build_path)):
shutil.copy(os.path.join(build_path, pyc),
os.path.join(dist_path, INSTALL_BIN_DIR, pyc))
class MakeRuleCmd(object):
"""
These class methods provide a convenient way to emit frequently
needed commands used in Makefile rules
Note that several of the method are meant for use during ``make
install`` and ``make uninstall``. These methods correctly use
``$(PREFIX)`` and ``$(DESTDIR)`` and therefore are preferable
to writing commands manually which can be error prone.
"""
@classmethod
def install_root(cls):
"""
Returns a string that will expand to the
install location when used in a makefile rule.
"""
# Note: DESTDIR is to support staged installs
return "$(DESTDIR)$(PREFIX)/"
@classmethod
def _is_str(cls, obj):
if sys.version_info.major > 2:
# Python 3 or newer. Strings are always unicode and of type str
return isinstance(obj, str)
else:
# Python 2. Has byte-string and unicode representation, allow both
return isinstance(obj, str) or isinstance(obj, unicode)
@classmethod
def _install_root(cls, path, in_prefix, out, is_install=True):
if not in_prefix:
# The Python bindings on OSX are sometimes not installed inside the prefix.
install_root = "$(DESTDIR)"
action_string = 'install' if is_install else 'uninstall'
cls.write_cmd(out, 'echo "WARNING: {}ing files/directories ({}) that are not in the install prefix ($(PREFIX))."'.format(
action_string, path))
#print("WARNING: Generating makefile rule that {}s {} '{}' which is outside the installation prefix '{}'.".format(
# action_string, 'to' if is_install else 'from', path, PREFIX))
else:
# assert not os.path.isabs(path)
install_root = cls.install_root()
return install_root
@classmethod
def install_files(cls, out, src_pattern, dest, in_prefix=True):
assert len(dest) > 0
assert cls._is_str(src_pattern)
assert not ' ' in src_pattern
assert cls._is_str(dest)
assert not ' ' in dest
assert not os.path.isabs(src_pattern)
install_root = cls._install_root(dest, in_prefix, out)
cls.write_cmd(out, "cp {src_pattern} {install_root}{dest}".format(
src_pattern=src_pattern,
install_root=install_root,
dest=dest))
@classmethod
def remove_installed_files(cls, out, pattern, in_prefix=True):
assert len(pattern) > 0
assert cls._is_str(pattern)
assert not ' ' in pattern
install_root = cls._install_root(pattern, in_prefix, out, is_install=False)
cls.write_cmd(out, "rm -f {install_root}{pattern}".format(
install_root=install_root,
pattern=pattern))
@classmethod
def make_install_directory(cls, out, dir, in_prefix=True):
assert len(dir) > 0
assert cls._is_str(dir)
assert not ' ' in dir
install_root = cls._install_root(dir, in_prefix, out)
if is_windows():
cls.write_cmd(out, "IF NOT EXIST {dir} (mkdir {dir})".format(
install_root=install_root,
dir=dir))
else:
cls.write_cmd(out, "mkdir -p {install_root}{dir}".format(
install_root=install_root,
dir=dir))
@classmethod
def _is_path_prefix_of(cls, temp_path, target_as_abs):
"""
Returns True iff ``temp_path`` is a path prefix
of ``target_as_abs``
"""
assert cls._is_str(temp_path)
assert cls._is_str(target_as_abs)
assert len(temp_path) > 0
assert len(target_as_abs) > 0
assert os.path.isabs(temp_path)
assert os.path.isabs(target_as_abs)
# Need to stick extra slash in front otherwise we might think that
# ``/lib`` is a prefix of ``/lib64``. Of course if ``temp_path ==
# '/'`` then we shouldn't else we would check if ``//`` (rather than
# ``/``) is a prefix of ``/lib64``, which would fail.
if len(temp_path) > 1:
temp_path += os.sep
return target_as_abs.startswith(temp_path)
@classmethod
def create_relative_symbolic_link(cls, out, target, link_name):
assert cls._is_str(target)
assert cls._is_str(link_name)
assert len(target) > 0
assert len(link_name) > 0
assert not os.path.isabs(target)
assert not os.path.isabs(link_name)
# We can't test to see if link_name is a file or directory
# because it may not exist yet. Instead follow the convention
# that if there is a leading slash target is a directory otherwise
# it's a file
if link_name[-1] != '/':
# link_name is a file
temp_path = os.path.dirname(link_name)
else:
# link_name is a directory
temp_path = link_name[:-1]
temp_path = '/' + temp_path
relative_path = ""
targetAsAbs = '/' + target
assert os.path.isabs(targetAsAbs)
assert os.path.isabs(temp_path)
# Keep walking up the directory tree until temp_path
# is a prefix of targetAsAbs
while not cls._is_path_prefix_of(temp_path, targetAsAbs):
assert temp_path != '/'
temp_path = os.path.dirname(temp_path)
relative_path += '../'
# Now get the path from the common prefix directory to the target
target_from_prefix = targetAsAbs[len(temp_path):]
relative_path += target_from_prefix
# Remove any double slashes
relative_path = relative_path.replace('//','/')
cls.create_symbolic_link(out, relative_path, link_name)
@classmethod
def create_symbolic_link(cls, out, target, link_name):
assert cls._is_str(target)
assert cls._is_str(link_name)
assert not os.path.isabs(target)
cls.write_cmd(out, 'ln -s {target} {install_root}{link_name}'.format(
target=target,
install_root=cls.install_root(),
link_name=link_name))
# TODO: Refactor all of the build system to emit commands using this
# helper to simplify code. This will also let us replace ``@`` with
# ``$(Verb)`` and have it set to ``@`` or empty at build time depending on
# a variable (e.g. ``VERBOSE``) passed to the ``make`` invocation. This
# would be very helpful for debugging.
@classmethod
def write_cmd(cls, out, line):
out.write("\t@{}\n".format(line))
def strip_path_prefix(path, prefix):
if path.startswith(prefix):
stripped_path = path[len(prefix):]
stripped_path.replace('//','/')
if stripped_path[0] == '/':
stripped_path = stripped_path[1:]
assert not os.path.isabs(stripped_path)
return stripped_path
else:
return path
def configure_file(template_file_path, output_file_path, substitutions):
"""
Read a template file ``template_file_path``, perform substitutions
found in the ``substitutions`` dictionary and write the result to
the output file ``output_file_path``.
The template file should contain zero or more template strings of the
form ``@NAME@``.
The substitutions dictionary maps old strings (without the ``@``
symbols) to their replacements.
"""
assert isinstance(template_file_path, str)
assert isinstance(output_file_path, str)
assert isinstance(substitutions, dict)
assert len(template_file_path) > 0
assert len(output_file_path) > 0
print("Generating {} from {}".format(output_file_path, template_file_path))
if not os.path.exists(template_file_path):
raise MKException('Could not find template file "{}"'.format(template_file_path))
# Read whole template file into string
template_string = None
with open(template_file_path, 'r') as f:
template_string = f.read()
# Do replacements
for (old_string, replacement) in substitutions.items():
template_string = template_string.replace('@{}@'.format(old_string), replacement)
# Write the string to the file
with open(output_file_path, 'w') as f:
f.write(template_string)
if __name__ == '__main__':
import doctest
doctest.testmod()