diff options
Diffstat (limited to 'scripts/Xcode/build-llvm.py')
-rwxr-xr-x | scripts/Xcode/build-llvm.py | 373 |
1 files changed, 373 insertions, 0 deletions
diff --git a/scripts/Xcode/build-llvm.py b/scripts/Xcode/build-llvm.py new file mode 100755 index 000000000000..b594a8cfe17b --- /dev/null +++ b/scripts/Xcode/build-llvm.py @@ -0,0 +1,373 @@ +#!/usr/bin/env python + +import errno +import hashlib +import fnmatch +import os +import platform +import subprocess +import sys + +from lldbbuild import * + +#### SETTINGS #### + +def LLVM_HASH_INCLUDES_DIFFS (): + return False + +# The use of "x = "..."; return x" here is important because tooling looks for +# it with regexps. Only change how this works if you know what you are doing. + +def LLVM_REF (): + llvm_ref = "master" + return llvm_ref + +def CLANG_REF (): + clang_ref = "master" + return clang_ref + +# For use with Xcode-style builds + +def XCODE_REPOSITORIES (): + return [ + { 'name': "llvm", + 'vcs': VCS.git, + 'root': llvm_source_path(), + 'url': "http://llvm.org/git/llvm.git", + 'ref': LLVM_REF() }, + + { 'name': "clang", + 'vcs': VCS.git, + 'root': clang_source_path(), + 'url': "http://llvm.org/git/clang.git", + 'ref': CLANG_REF() }, + + { 'name': "ninja", + 'vcs': VCS.git, + 'root': ninja_source_path(), + 'url': "https://github.com/ninja-build/ninja.git", + 'ref': "master" } + ] + +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) + return [os.path.join(path, file) for file in files if file.endswith(".a")] + +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", "-p0", "-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 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()) + ] + 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 #### + +all_check_out_if_needed() +build_llvm_if_needed() +write_archives_txt() + +sys.exit(0) |