z3/scripts/mk_win_dist.py

343 lines
11 KiB
Python

############################################
# Copyright (c) 2012 Microsoft Corporation
#
# Scripts for automatically generating
# Window distribution zip files.
#
# Author: Leonardo de Moura (leonardo)
############################################
import os
import glob
import re
import getopt
import sys
import shutil
import subprocess
import zipfile
from mk_exception import *
from mk_project import *
import mk_util
BUILD_DIR='build-dist'
BUILD_X64_DIR=os.path.join('build-dist', 'x64')
BUILD_X86_DIR=os.path.join('build-dist', 'x86')
VERBOSE=True
DIST_DIR='dist'
FORCE_MK=False
DOTNET_CORE_ENABLED=True
ESRP_SIGN=False
DOTNET_KEY_FILE=None
JAVA_ENABLED=True
GIT_HASH=False
PYTHON_ENABLED=True
X86ONLY=False
X64ONLY=False
MAKEJOBS=getenv("MAKEJOBS", "24")
def set_verbose(flag):
global VERBOSE
VERBOSE = flag
def is_verbose():
return VERBOSE
def mk_dir(d):
if not os.path.exists(d):
os.makedirs(d)
def set_build_dir(path):
global BUILD_DIR, BUILD_X86_DIR, BUILD_X64_DIR
BUILD_DIR = mk_util.norm_path(path)
BUILD_X86_DIR = os.path.join(path, 'x86')
BUILD_X64_DIR = os.path.join(path, 'x64')
mk_dir(BUILD_X86_DIR)
mk_dir(BUILD_X64_DIR)
def display_help():
print("mk_win_dist.py: Z3 Windows distribution generator\n")
print("This script generates the zip files containing executables, dlls, header files for Windows.")
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.")
print(" -b <sudir>, --build=<subdir> subdirectory where x86 and x64 Z3 versions will be built (default: build-dist).")
print(" -f, --force force script to regenerate Makefiles.")
print(" --nodotnet do not include .NET bindings in the binary distribution files.")
print(" --dotnet-key=<file> sign the .NET assembly with the private key in <file>.")
print(" --esrp sign with esrp.")
print(" --nojava do not include Java bindings in the binary distribution files.")
print(" --nopython do not include Python bindings in the binary distribution files.")
print(" --githash include git hash in the Zip file.")
print(" --x86-only x86 dist only.")
print(" --x64-only x64 dist only.")
exit(0)
# Parse configuration option for mk_make script
def parse_options():
global FORCE_MK, JAVA_ENABLED, GIT_HASH, DOTNET_CORE_ENABLED, DOTNET_KEY_FILE, PYTHON_ENABLED, X86ONLY, X64ONLY, ESRP_SIGN
path = BUILD_DIR
options, remainder = getopt.gnu_getopt(sys.argv[1:], 'b:hsf', ['build=',
'help',
'silent',
'force',
'nojava',
'nodotnet',
'dotnet-key=',
'esrp',
'githash',
'nopython',
'x86-only',
'x64-only'
])
print(options)
for opt, arg in options:
if opt in ('-b', '--build'):
if arg == 'src':
raise MKException('The src directory should not be used to host the Makefile')
path = arg
elif opt in ('-s', '--silent'):
set_verbose(False)
elif opt in ('-h', '--help'):
display_help()
elif opt in ('-f', '--force'):
FORCE_MK = True
elif opt == '--nodotnet':
DOTNET_CORE_ENABLED = False
elif opt == '--nopython':
PYTHON_ENABLED = False
elif opt == '--dotnet-key':
DOTNET_KEY_FILE = arg
elif opt == '--esrp':
ESRP_SIGN = True
elif opt == '--nojava':
JAVA_ENABLED = False
elif opt == '--githash':
GIT_HASH = True
elif opt == '--x86-only' and not X64ONLY:
X86ONLY = True
elif opt == '--x64-only' and not X86ONLY:
X64ONLY = True
else:
raise MKException("Invalid command line option '%s'" % opt)
set_build_dir(path)
# Check whether build directory already exists or not
def check_build_dir(path):
return os.path.exists(path) and os.path.exists(os.path.join(path, 'Makefile'))
# Create a build directory using mk_make.py
def mk_build_dir(path, x64):
if not check_build_dir(path) or FORCE_MK:
parallel = '--parallel=' + MAKEJOBS
opts = ["python", os.path.join('scripts', 'mk_make.py'), parallel, "-b", path]
if DOTNET_CORE_ENABLED:
opts.append('--dotnet')
if not DOTNET_KEY_FILE is None:
opts.append('--dotnet-key=' + DOTNET_KEY_FILE)
if JAVA_ENABLED:
opts.append('--java')
if x64:
opts.append('-x')
if ESRP_SIGN:
opts.append('--esrp')
if GIT_HASH:
opts.append('--githash=%s' % mk_util.git_hash())
opts.append('--git-describe')
if PYTHON_ENABLED:
opts.append('--python')
if subprocess.call(opts) != 0:
raise MKException("Failed to generate build directory at '%s'" % path)
# Create build directories
def mk_build_dirs():
mk_build_dir(BUILD_X86_DIR, False)
mk_build_dir(BUILD_X64_DIR, True)
# Check if on Visual Studio command prompt
def check_vc_cmd_prompt():
try:
DEVNULL = open(os.devnull, 'wb')
subprocess.call(['cl'], stdout=DEVNULL, stderr=DEVNULL)
except:
raise MKException("You must execute the mk_win_dist.py script on a Visual Studio Command Prompt")
def exec_cmds(cmds):
cmd_file = 'z3_tmp.cmd'
f = open(cmd_file, 'w')
for cmd in cmds:
f.write(cmd)
f.write('\n')
f.close()
res = 0
try:
res = subprocess.call(cmd_file, shell=True)
except:
res = 1
try:
os.erase(cmd_file)
except:
pass
return res
# Compile Z3 (if x64 == True, then it builds it in x64 mode).
def mk_z3(x64):
cmds = []
if x64:
cmds.append('call "%VCINSTALLDIR%vcvarsall.bat" amd64')
cmds.append('cd %s' % BUILD_X64_DIR)
else:
cmds.append('call "%VCINSTALLDIR%vcvarsall.bat" x86')
cmds.append('cd %s' % BUILD_X86_DIR)
cmds.append('nmake')
if exec_cmds(cmds) != 0:
raise MKException("Failed to make z3, x64: %s" % x64)
def mk_z3s():
mk_z3(False)
mk_z3(True)
def get_z3_name(x64):
major, minor, build, revision = get_version()
if x64:
platform = "x64"
else:
platform = "x86"
if GIT_HASH:
return 'z3-%s.%s.%s.%s-%s-win' % (major, minor, build, mk_util.git_hash(), platform)
else:
return 'z3-%s.%s.%s-%s-win' % (major, minor, build, platform)
def mk_dist_dir(x64):
global ESRP_SIGN
if x64:
platform = "x64"
build_path = BUILD_X64_DIR
else:
platform = "x86"
build_path = BUILD_X86_DIR
dist_path = os.path.join(DIST_DIR, get_z3_name(x64))
mk_dir(dist_path)
mk_util.ESRP_SIGN = ESRP_SIGN
mk_util.DOTNET_CORE_ENABLED = True
mk_util.DOTNET_KEY_FILE = DOTNET_KEY_FILE
mk_util.JAVA_ENABLED = JAVA_ENABLED
mk_util.PYTHON_ENABLED = PYTHON_ENABLED
mk_win_dist(build_path, dist_path)
if is_verbose():
print("Generated %s distribution folder at '%s'" % (platform, dist_path))
def mk_dist_dirs():
mk_dist_dir(False)
mk_dist_dir(True)
def get_dist_path(x64):
return get_z3_name(x64)
def mk_zip(x64):
dist_path = get_dist_path(x64)
old = os.getcwd()
try:
os.chdir(DIST_DIR)
zfname = '%s.zip' % dist_path
zipout = zipfile.ZipFile(zfname, 'w', zipfile.ZIP_DEFLATED)
for root, dirs, files in os.walk(dist_path):
for f in files:
zipout.write(os.path.join(root, f))
if is_verbose():
print("Generated '%s'" % zfname)
except:
pass
os.chdir(old)
# Create a zip file for each platform
def mk_zips():
mk_zip(False)
mk_zip(True)
VS_RUNTIME_PATS = [re.compile('vcomp.*\.dll'),
re.compile('msvcp.*\.dll'),
re.compile('msvcr.*\.dll')]
# Copy Visual Studio Runtime libraries
def cp_vs_runtime(x64):
if x64:
platform = "x64"
else:
platform = "x86"
vcdir = os.environ['VCINSTALLDIR']
path = '%sredist' % vcdir
vs_runtime_files = []
print("Walking %s" % path)
# Everything changes with every release of VS
# Prior versions of VS had DLLs under "redist\x64"
# There are now several variants of redistributables
# The naming convention defies my understanding so
# we use a "check_root" filter to find some hopefully suitable
# redistributable.
def check_root(root):
return platform in root and ("CRT" in root or "MP" in root) and "onecore" not in root and "debug" not in root
for root, dirs, files in os.walk(path):
for filename in files:
if fnmatch(filename, '*.dll') and check_root(root):
print("Checking %s %s" % (root, filename))
for pat in VS_RUNTIME_PATS:
if pat.match(filename):
fname = os.path.join(root, filename)
if not os.path.isdir(fname):
vs_runtime_files.append(fname)
if not vs_runtime_files:
raise MKException("Did not find any runtime files to include")
bin_dist_path = os.path.join(DIST_DIR, get_dist_path(x64), 'bin')
for f in vs_runtime_files:
shutil.copy(f, bin_dist_path)
if is_verbose():
print("Copied '%s' to '%s'" % (f, bin_dist_path))
def cp_vs_runtimes():
cp_vs_runtime(True)
cp_vs_runtime(False)
def cp_license(x64):
shutil.copy("LICENSE.txt", os.path.join(DIST_DIR, get_dist_path(x64)))
def cp_licenses():
cp_license(True)
cp_license(False)
# Entry point
def main():
if os.name != 'nt':
raise MKException("This script is for Windows only")
parse_options()
check_vc_cmd_prompt()
if X86ONLY:
mk_build_dir(BUILD_X86_DIR, False)
mk_z3(False)
init_project_def()
mk_dist_dir(False)
cp_license(False)
cp_vs_runtime(False)
mk_zip(False)
elif X64ONLY:
mk_build_dir(BUILD_X64_DIR, True)
mk_z3(True)
init_project_def()
mk_dist_dir(True)
cp_license(True)
cp_vs_runtime(True)
mk_zip(True)
else:
mk_build_dirs()
mk_z3s()
init_project_def()
mk_dist_dirs()
cp_licenses()
cp_vs_runtimes()
mk_zips()
main()