blob: 1efcacbdb5a9236ef7162a0320975369f2e12bfe [file] [log] [blame]
#!/usr/bin/env python3
import argparse
import enum
import glob
import os
import shutil
import subprocess
import sys
import zipfile
import textwrap
@enum.unique
class Host(enum.Enum):
"""Enumeration of supported hosts."""
Darwin = 'darwin'
Linux = 'linux'
Windows = 'windows'
def get_default_host():
"""Returns the Host matching the current machine."""
if sys.platform.startswith('linux'):
return Host.Linux
elif sys.platform.startswith('darwin'):
return Host.Darwin
elif sys.platform.startswith('win'):
return Host.Windows
else:
raise RuntimeError('Unsupported host: {}'.format(sys.platform))
def parse_arguments():
parser = argparse.ArgumentParser()
parser.add_argument('src')
parser.add_argument('out_dir')
parser.add_argument('dest_dir')
parser.add_argument('build_id')
parser.add_argument('--cmake',
default='cmake',
help='Path to cmake binary.')
parser.add_argument('--ninja',
default='ninja',
help='Path to ninja binary.')
parser.add_argument('--android-cmake',
help='Path to android-cmake repository.')
parser.add_argument('--extra-notices',
default='',
help='Extra license files to install.')
return parser.parse_args()
def check_call(cmd, **kwargs):
print(subprocess.list2cmdline(cmd))
sys.stdout.flush()
subprocess.check_call(cmd, **kwargs)
def find_latest_clang(repo_path):
all_dirs = (f for f in os.listdir(repo_path)
if os.path.isdir(os.path.join(repo_path, f)))
clangs = (f for f in all_dirs if f.startswith('clang-r'))
latest_clang = max(clangs)
return os.path.join(repo_path, latest_clang)
def get_toolchain_flags(host):
cflags = []
ldflags = []
if host == Host.Windows:
cflags.append('/EHs')
if host == Host.Linux:
ldflags.append('-static-libstdc++')
ldflags.append('-static-libgcc')
ldflags.append('-pthread')
return (cflags, ldflags)
def normalize_cmake_path(path):
return path.replace('\\', '/')
def get_cmake_defines(host, args):
defines = {}
defines['CMAKE_BUILD_TYPE'] = 'Release'
cflags, ldflags = get_toolchain_flags(host)
cflags_str = ' '.join(cflags)
ldflags_str = ' '.join(ldflags)
defines['CMAKE_ASM_FLAGS'] = cflags_str
defines['CMAKE_C_FLAGS'] = cflags_str
defines['CMAKE_CXX_FLAGS'] = cflags_str
defines['CMAKE_EXE_LINKER_FLAGS'] = ldflags_str
defines['CMAKE_SHARED_LINKER_FLAGS'] = ldflags_str
defines['CMAKE_MODULE_LINKER_FLAGS'] = ldflags_str
if host == Host.Windows:
defines['CMAKE_MSVC_RUNTIME_LIBRARY'] = 'MultiThreaded$<$<CONFIG:Debug>:Debug>'
if host == Host.Linux:
defines['OPENSSL_USE_STATIC_LIBS'] = 'ON'
if host == Host.Darwin:
# This will be used to set -mmacosx-version-min. And helps to choose SDK.
# To specify a SDK, set CMAKE_OSX_SYSROOT or SDKROOT environment variable.
defines['CMAKE_OSX_DEPLOYMENT_TARGET'] = '10.9'
defines['CMAKE_OSX_ARCHITECTURES'] = 'x86_64;arm64'
return defines
def build_cmake_target(host, args):
build_dir = os.path.join(args.out_dir, 'build')
install_dir = os.path.join(args.out_dir, 'install')
print('## Building ##')
print('## Out Dir : {}'.format(args.out_dir))
print('## Src : {}'.format(args.src))
sys.stdout.flush()
os.makedirs(build_dir, exist_ok=True)
os.makedirs(install_dir, exist_ok=True)
defines = get_cmake_defines(host, args)
defines['CMAKE_INSTALL_PREFIX'] = install_dir
if args.ninja:
defines['CMAKE_MAKE_PROGRAM'] = args.ninja
config_cmd = [args.cmake, '-G', 'Ninja', args.src]
for key, value in defines.items():
config_cmd.append("-D{}={}".format(key, value))
check_call(config_cmd, cwd=build_dir)
if host == Host.Windows:
ninja_target = 'install'
else:
ninja_target = 'install/strip'
check_call([args.ninja, ninja_target], cwd=build_dir)
# e.g.: /path/to/openssl-1.1.1k/LICENSE:doc/openssl-1.1.1k/LICENSE
for notice in args.extra_notices.split():
(src, dst) = notice.split(':')
dst = os.path.join(install_dir, dst)
os.makedirs(os.path.dirname(dst), exist_ok=True)
shutil.copy2(src, dst)
return install_dir
def zip_dir(path, ziph):
"""Zip a folder with archive paths relative to the root"""
for root, dirs, files in os.walk(path):
for file in files:
install_file = os.path.join(root, file)
rel_file = os.path.relpath(install_file, path)
ziph.write(install_file, rel_file)
def package_target(install_dir, dest_dir):
os.makedirs(dest_dir, exist_ok=True)
package_path = os.path.join(dest_dir, 'cmake.zip')
print('## Packaging ##')
print('## Package : {}'.format(package_path))
print('## Install Dir : {}'.format(install_dir))
sys.stdout.flush()
with zipfile.ZipFile(package_path, 'w', zipfile.ZIP_DEFLATED) as zip:
zip_dir(install_dir, zip)
def package_target_for_studio(install_dir, cmake_version, ninja_path,
android_cmake, dest_dir):
"""Create a package with ninja.exe and source.properties for Android SDK"""
os.makedirs(dest_dir, exist_ok=True)
package_path = os.path.join(dest_dir, 'cmake-for-studio.zip')
source_properties = get_source_properties(cmake_version)
print('## Packaging with Ninja ##')
print('## Package : {}'.format(package_path))
print('## Install Dir : {}'.format(install_dir))
sys.stdout.flush()
module_path = glob.glob(os.path.join(install_dir, 'share', 'cmake-*'))[0]
module_path = os.path.basename(module_path)
with zipfile.ZipFile(package_path, 'w', zipfile.ZIP_DEFLATED) as zip:
zip_dir(install_dir, zip)
zip.writestr("source.properties", source_properties)
zip.write(ninja_path, os.path.join("bin",
os.path.basename(ninja_path)))
ninja_license_path = os.path.join(os.path.dirname(ninja_path),
"LICENSE")
zip.write(ninja_license_path, os.path.join("doc", "ninja", "LICENSE"))
for cmake_file in ["AndroidNdkModules.cmake", "AndroidNdkGdb.cmake"]:
file_path = os.path.join(android_cmake, cmake_file)
zip.write(
file_path,
os.path.join("share", module_path, "Modules", cmake_file))
def get_source_properties(cmake_target_version):
"""Return a source.properties for CMake version and build ID"""
source_properties = textwrap.dedent("""\
Pkg.Revision = {cmake_target_version}
Pkg.Path = cmake;{cmake_target_version}
Pkg.Desc = CMake {cmake_target_version}
""".format(cmake_target_version=cmake_target_version))
return source_properties
def get_cmake_version(install_dir):
"""Return result of 'cmake --version'"""
cmake_bin = os.path.join(install_dir, "bin")
if get_default_host() == Host.Windows:
cmake_exe = os.path.join(cmake_bin, "cmake.exe")
else:
cmake_exe = os.path.join(cmake_bin, "cmake")
cmd = [cmake_exe, "--version"]
print(subprocess.list2cmdline(cmd))
output_bytes = subprocess.check_output(cmd)
text = output_bytes.decode("UTF-8")
# Should be like 'cmake version 3.17.0-g6cb76b9'
first_line = text.splitlines()[0]
# Should be like '3.17.0-g6cb76b9'
version_with_sha = first_line.split()[2]
version = version_with_sha.split("-")[0] # Should be like '3.17.0'
print("## CMake Version = '{}'".format(version))
return version
def main():
args = parse_arguments()
host = get_default_host()
install_dir = build_cmake_target(host, args)
cmake_target_version = get_cmake_version(install_dir)
package_target(install_dir, args.dest_dir)
package_target_for_studio(install_dir, cmake_target_version, args.ninja,
args.android_cmake, args.dest_dir)
if __name__ == '__main__':
main()