#!/usr/bin/env python import errno import hashlib import fnmatch import os import platform import re import repo import subprocess import sys from lldbbuild import * #### SETTINGS #### def LLVM_HASH_INCLUDES_DIFFS(): return False # For use with Xcode-style builds def process_vcs(vcs): return { "svn": VCS.svn, "git": VCS.git }[vcs] def process_root(name): return { "llvm": llvm_source_path(), "clang": clang_source_path(), "ninja": ninja_source_path() }[name] def process_repo(r): return { 'name': r["name"], 'vcs': process_vcs(r["vcs"]), 'root': process_root(r["name"]), 'url': r["url"], 'ref': r["ref"] } def fallback_repo(name): return { 'name': name, 'vcs': None, 'root': process_root(name), 'url': None, 'ref': None } def dirs_exist(names): for name in names: if not os.path.isdir(process_root(name)): return False return True def XCODE_REPOSITORIES(): names = ["llvm", "clang", "ninja"] if dirs_exist(names): return [fallback_repo(n) for n in names] override = repo.get_override() if override: return [process_repo(r) for r in override] identifier = repo.identifier() if identifier == None: identifier = "" # repo.find will just use the fallback file set = repo.find(identifier) return [process_repo(r) for r in set] def get_c_compiler(): return subprocess.check_output([ 'xcrun', '--sdk', 'macosx', '-find', 'clang' ]).rstrip() def get_cxx_compiler(): return subprocess.check_output([ 'xcrun', '--sdk', 'macosx', '-find', 'clang++' ]).rstrip() # CFLAGS="-isysroot $(xcrun --sdk macosx --show-sdk-path) -mmacosx-version-min=${DARWIN_DEPLOYMENT_VERSION_OSX}" \ # LDFLAGS="-mmacosx-version-min=${DARWIN_DEPLOYMENT_VERSION_OSX}" \ def get_deployment_target(): return os.environ.get('MACOSX_DEPLOYMENT_TARGET', None) def get_c_flags(): cflags = '' # sdk_path = subprocess.check_output([ # 'xcrun', # '--sdk', 'macosx', # '--show-sdk-path']).rstrip() # cflags += '-isysroot {}'.format(sdk_path) deployment_target = get_deployment_target() if deployment_target: # cflags += ' -mmacosx-version-min={}'.format(deployment_target) pass return cflags def get_cxx_flags(): return get_c_flags() def get_common_linker_flags(): linker_flags = "" deployment_target = get_deployment_target() if deployment_target: # if len(linker_flags) > 0: # linker_flags += ' ' # linker_flags += '-mmacosx-version-min={}'.format(deployment_target) pass return linker_flags def get_exe_linker_flags(): return get_common_linker_flags() def get_shared_linker_flags(): return get_common_linker_flags() def CMAKE_FLAGS(): return { "Debug": [ "-DCMAKE_BUILD_TYPE=RelWithDebInfo", "-DLLVM_ENABLE_ASSERTIONS=ON", ], "DebugClang": [ "-DCMAKE_BUILD_TYPE=Debug", "-DLLVM_ENABLE_ASSERTIONS=ON", ], "Release": [ "-DCMAKE_BUILD_TYPE=Release", "-DLLVM_ENABLE_ASSERTIONS=ON", ], "BuildAndIntegration": [ "-DCMAKE_BUILD_TYPE=Release", "-DLLVM_ENABLE_ASSERTIONS=OFF", ], } def CMAKE_ENVIRONMENT(): return { } #### COLLECTING ALL ARCHIVES #### def collect_archives_in_path(path): files = os.listdir(path) # Only use libclang and libLLVM archives, and exclude libclang_rt regexp = "^lib(clang[^_]|LLVM|gtest).*$" return [ os.path.join( path, file) for file in files if file.endswith(".a") and re.match( regexp, file)] def archive_list(): paths = library_paths() archive_lists = [collect_archives_in_path(path) for path in paths] return [archive for archive_list in archive_lists for archive in archive_list] def write_archives_txt(): f = open(archives_txt(), 'w') for archive in archive_list(): f.write(archive + "\n") f.close() #### COLLECTING REPOSITORY MD5S #### def source_control_status(spec): vcs_for_spec = vcs(spec) if LLVM_HASH_INCLUDES_DIFFS(): return vcs_for_spec.status() + vcs_for_spec.diff() else: return vcs_for_spec.status() def source_control_status_for_specs(specs): statuses = [source_control_status(spec) for spec in specs] return "".join(statuses) def all_source_control_status(): return source_control_status_for_specs(XCODE_REPOSITORIES()) def md5(string): m = hashlib.md5() m.update(string) return m.hexdigest() def all_source_control_status_md5(): return md5(all_source_control_status()) #### CHECKING OUT AND BUILDING LLVM #### def apply_patches(spec): files = os.listdir(os.path.join(lldb_source_path(), 'scripts')) patches = [ f for f in files if fnmatch.fnmatch( f, spec['name'] + '.*.diff')] for p in patches: run_in_directory(["patch", "-p1", "-i", os.path.join(lldb_source_path(), 'scripts', p)], spec['root']) def check_out_if_needed(spec): if not os.path.isdir(spec['root']): vcs(spec).check_out() apply_patches(spec) def all_check_out_if_needed(): map(check_out_if_needed, XCODE_REPOSITORIES()) def should_build_llvm(): if build_type() == BuildType.Xcode: # TODO use md5 sums return True def do_symlink(source_path, link_path): print "Symlinking " + source_path + " to " + link_path if os.path.islink(link_path): os.remove(link_path) if not os.path.exists(link_path): os.symlink(source_path, link_path) def setup_source_symlink(repo): source_path = repo["root"] link_path = os.path.join(lldb_source_path(), os.path.basename(source_path)) do_symlink(source_path, link_path) def setup_source_symlinks(): map(setup_source_symlink, XCODE_REPOSITORIES()) def setup_build_symlink(): # We don't use the build symlinks in llvm.org Xcode-based builds. if build_type() != BuildType.Xcode: source_path = package_build_path() link_path = expected_package_build_path() do_symlink(source_path, link_path) def should_run_cmake(cmake_build_dir): # We need to run cmake if our llvm build directory doesn't yet exist. if not os.path.exists(cmake_build_dir): return True # Wee also need to run cmake if for some reason we don't have a ninja # build file. (Perhaps the cmake invocation failed, which this current # build may have fixed). ninja_path = os.path.join(cmake_build_dir, "build.ninja") return not os.path.exists(ninja_path) def cmake_environment(): cmake_env = join_dicts(os.environ, CMAKE_ENVIRONMENT()) return cmake_env def is_executable(path): return os.path.isfile(path) and os.access(path, os.X_OK) def find_executable_in_paths(program, paths_to_check): program_dir, program_name = os.path.split(program) if program_dir: if is_executable(program): return program else: for path_dir in paths_to_check: path_dir = path_dir.strip('"') executable_file = os.path.join(path_dir, program) if is_executable(executable_file): return executable_file return None def find_cmake(): # First check the system PATH env var for cmake cmake_binary = find_executable_in_paths( "cmake", os.environ["PATH"].split(os.pathsep)) if cmake_binary: # We found it there, use it. return cmake_binary # Check a few more common spots. Xcode launched from Finder # will have the default environment, and may not have # all the normal places present. extra_cmake_dirs = [ "/usr/local/bin", "/opt/local/bin", os.path.join(os.path.expanduser("~"), "bin") ] if platform.system() == "Darwin": # Add locations where an official CMake.app package may be installed. extra_cmake_dirs.extend([ os.path.join( os.path.expanduser("~"), "Applications", "CMake.app", "Contents", "bin"), os.path.join( os.sep, "Applications", "CMake.app", "Contents", "bin")]) cmake_binary = find_executable_in_paths("cmake", extra_cmake_dirs) if cmake_binary: # We found it in one of the usual places. Use that. return cmake_binary # We couldn't find cmake. Tell the user what to do. raise Exception( "could not find cmake in PATH ({}) or in any of these locations ({}), " "please install cmake or add a link to it in one of those locations".format( os.environ["PATH"], extra_cmake_dirs)) def cmake_flags(): cmake_flags = CMAKE_FLAGS()[lldb_configuration()] cmake_flags += ["-GNinja", "-DCMAKE_C_COMPILER={}".format(get_c_compiler()), "-DCMAKE_CXX_COMPILER={}".format(get_cxx_compiler()), "-DCMAKE_INSTALL_PREFIX={}".format(expected_package_build_path_for("llvm")), "-DCMAKE_C_FLAGS={}".format(get_c_flags()), "-DCMAKE_CXX_FLAGS={}".format(get_cxx_flags()), "-DCMAKE_EXE_LINKER_FLAGS={}".format(get_exe_linker_flags()), "-DCMAKE_SHARED_LINKER_FLAGS={}".format(get_shared_linker_flags()), "-DHAVE_CRASHREPORTER_INFO=1"] deployment_target = get_deployment_target() if deployment_target: cmake_flags.append( "-DCMAKE_OSX_DEPLOYMENT_TARGET={}".format(deployment_target)) return cmake_flags def run_cmake(cmake_build_dir, ninja_binary_path): cmake_binary = find_cmake() print "found cmake binary: using \"{}\"".format(cmake_binary) command_line = [cmake_binary] + cmake_flags() + [ "-DCMAKE_MAKE_PROGRAM={}".format(ninja_binary_path), llvm_source_path()] print "running cmake like so: ({}) in dir ({})".format(command_line, cmake_build_dir) subprocess.check_call( command_line, cwd=cmake_build_dir, env=cmake_environment()) def create_directories_as_needed(path): try: os.makedirs(path) except OSError as error: # An error indicating that the directory exists already is fine. # Anything else should be passed along. if error.errno != errno.EEXIST: raise error def run_cmake_if_needed(ninja_binary_path): cmake_build_dir = package_build_path() if should_run_cmake(cmake_build_dir): # Create the build directory as needed create_directories_as_needed(cmake_build_dir) run_cmake(cmake_build_dir, ninja_binary_path) def build_ninja_if_needed(): # First check if ninja is in our path. If so, there's nothing to do. ninja_binary_path = find_executable_in_paths( "ninja", os.environ["PATH"].split(os.pathsep)) if ninja_binary_path: # It's on the path. cmake will find it. We're good. print "found ninja here: \"{}\"".format(ninja_binary_path) return ninja_binary_path # Figure out if we need to build it. ninja_build_dir = ninja_source_path() ninja_binary_path = os.path.join(ninja_build_dir, "ninja") if not is_executable(ninja_binary_path): # Build ninja command_line = ["python", "configure.py", "--bootstrap"] print "building ninja like so: ({}) in dir ({})".format(command_line, ninja_build_dir) subprocess.check_call( command_line, cwd=ninja_build_dir, env=os.environ) return ninja_binary_path def join_dicts(dict1, dict2): d = dict1.copy() d.update(dict2) return d def build_llvm(ninja_binary_path): cmake_build_dir = package_build_path() subprocess.check_call( [ninja_binary_path], cwd=cmake_build_dir, env=cmake_environment()) def build_llvm_if_needed(): if should_build_llvm(): ninja_binary_path = build_ninja_if_needed() run_cmake_if_needed(ninja_binary_path) build_llvm(ninja_binary_path) setup_build_symlink() #### MAIN LOGIC #### if __name__ == "__main__": all_check_out_if_needed() build_llvm_if_needed() write_archives_txt() sys.exit(0)