modify Xcode build to use cmake/ninja for internal llvm/clang

This change restores the Xcode build to working after Makefile support
was stripped from LLVM and clang recently.

With this change, the Xcode build now requires cmake (2.8.12.2+).
The cmake must either be on the path that Xcode sees, or it must
exist in one of the following locations:

* /usr/local/bin/cmake
* /opt/local/bin/cmake
* $HOME/bin/cmake

If the ninja build tool is present on the path, it will be used.
If not, ninja will be cloned (via git), bootstrap-built, and
used for the llvm/clang build.

LLDB now requires a minimum deployment target of OS X 10.9.  Prior
to this, it was 10.8.  The llvm/clang cmake build will not run
with Xcode 7.2 or Xcode 7.3 beta's compiler with the minimum
deployment target set to anything lower than 10.9.  This is
related to #include <atomic>.

When llvm or clang source code does not exist in the lldb tree,
it will be cloned via git using http://llvm.org/git/{project}.git.
Previously it used SVN.  If this causes any heartache, we can
make this smarter, autodetect an embedded svn and use svn instead.
(And/or use SVN if a git command is not available).

This change also fixes an lldb-mi linkage failure (needed
libncurses) as exposed by one of the LLVM libs.

llvm-svn: 259027
diff --git a/lldb/scripts/Xcode/build-llvm.py b/lldb/scripts/Xcode/build-llvm.py
new file mode 100755
index 0000000..72779bc
--- /dev/null
+++ b/lldb/scripts/Xcode/build-llvm.py
@@ -0,0 +1,343 @@
+#!/usr/bin/env python
+
+import errno
+import hashlib
+import os
+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",
+            ],
+    }
+
+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 check_out_if_needed(spec):
+    if not os.path.isdir(spec['root']):
+        vcs(spec).check_out()
+
+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")
+    ]
+    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(
+        "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)
diff --git a/lldb/scripts/Xcode/lldbbuild.py b/lldb/scripts/Xcode/lldbbuild.py
new file mode 100644
index 0000000..cf79a3e
--- /dev/null
+++ b/lldb/scripts/Xcode/lldbbuild.py
@@ -0,0 +1,135 @@
+import os
+import subprocess
+
+#### UTILITIES ####
+
+def enum (*sequential, **named):
+    enums = dict(zip(sequential, range(len(sequential))), **named)
+    return type('Enum', (), enums)
+
+#### SETTINGS ####
+
+#### INTERFACE TO THE XCODEPROJ ####
+
+def lldb_source_path ():
+    return os.environ.get('SRCROOT')
+
+def expected_llvm_build_path ():
+    if build_type() == BuildType.Xcode:
+        return package_build_path()
+    else:
+        return os.path.join(os.environ.get('LLDB_PATH_TO_LLVM_BUILD'), package_build_dir_name("llvm"))
+
+def archives_txt ():
+    return os.path.join(expected_package_build_path(), "archives.txt")
+
+def expected_package_build_path ():
+    return os.path.abspath(os.path.join(expected_llvm_build_path(), ".."))
+
+def architecture ():
+    platform_name = os.environ.get('RC_PLATFORM_NAME')
+    if not platform_name:
+        platform_name = os.environ.get('PLATFORM_NAME')
+    platform_arch = os.environ.get('ARCHS').split()[-1]
+    return platform_name + "-" + platform_arch
+
+def lldb_configuration ():
+    return os.environ.get('CONFIGURATION')
+
+def llvm_configuration ():
+    return os.environ.get('LLVM_CONFIGURATION')
+
+def llvm_build_dirtree ():
+    return os.environ.get('LLVM_BUILD_DIRTREE')
+
+# Edit the code below when adding build styles.
+
+BuildType = enum('Xcode')                # (Debug,DebugClang,Release)
+
+def build_type ():
+    return BuildType.Xcode
+
+#### VCS UTILITIES ####
+
+VCS = enum('git',
+           'svn')
+
+def run_in_directory(args, path):
+    return subprocess.check_output(args, cwd=path)
+
+class Git:
+    def __init__ (self, spec):
+        self.spec = spec
+    def status (self):
+        return run_in_directory(["git", "branch", "-v"], self.spec['root'])
+    def diff (self):
+        return run_in_directory(["git", "diff"], self.spec['root'])
+    def check_out (self):
+        run_in_directory(["git", "clone", self.spec['url'], self.spec['root']], lldb_source_path())
+        run_in_directory(["git", "fetch", "--all"], self.spec['root'])
+        run_in_directory(["git", "checkout", self.spec['ref']], self.spec['root'])
+
+class SVN:
+    def __init__ (self, spec):
+        self.spec = spec
+    def status (self):
+        return run_in_directory(["svn", "info"], self.spec['root'])
+    def diff (self):
+        return run_in_directory(["svn", "diff"], self.spec['root'])
+    # TODO implement check_out
+
+def vcs (spec):
+    if spec['vcs'] == VCS.git:
+        return Git(spec)
+    elif spec['vcs'] == VCS.svn:
+        return SVN(spec)
+    else:
+        return None
+
+#### SOURCE PATHS ####
+
+def llvm_source_path ():
+    if build_type() == BuildType.Xcode:
+        return os.path.join(lldb_source_path(), "llvm")
+
+def clang_source_path ():
+    if build_type() == BuildType.Xcode:
+        return os.path.join(llvm_source_path(), "tools", "clang")
+
+def ninja_source_path ():
+    if build_type() == BuildType.Xcode:
+        return os.path.join(lldb_source_path(), "ninja")
+
+#### BUILD PATHS ####
+
+def packages ():
+    return ["llvm"]
+
+def package_build_dir_name (package):
+    return package + "-" + architecture()
+
+def expected_package_build_path_for (package):
+    if build_type() == BuildType.Xcode:
+        if package != "llvm":
+            raise("On Xcode build, we only support the llvm package: requested {}".format(package))
+        return package_build_path()
+    return os.path.join(expected_package_build_path(), package_build_dir_name(package))
+
+def expected_package_build_paths ():
+    return [expected_package_build_path_for(package) for package in packages()]
+
+def library_path (build_path):
+    return build_path + "/lib"
+
+def library_paths ():
+    if build_type() == BuildType.Xcode:
+        package_build_paths = [package_build_path()]
+    else:
+        package_build_paths = expected_package_build_paths()
+    return [library_path(build_path) for build_path in package_build_paths]
+
+def package_build_path ():
+    return os.path.join(
+        llvm_build_dirtree(),
+        os.environ["LLVM_CONFIGURATION"],
+        os.environ["CURRENT_ARCH"])
diff --git a/lldb/scripts/Xcode/package-clang-headers.py b/lldb/scripts/Xcode/package-clang-headers.py
new file mode 100644
index 0000000..55ecc90
--- /dev/null
+++ b/lldb/scripts/Xcode/package-clang-headers.py
@@ -0,0 +1,82 @@
+#! /usr/bin/env python
+
+# package-clang-headers.py
+#
+# The Clang module loader depends on built-in headers for the Clang compiler.
+# We grab these from the Clang build and move them into the LLDB module.
+
+# TARGET_DIR is where the lldb framework/shared library gets put.
+# LLVM_BUILD_DIR is where LLVM and Clang got built
+# LLVM_BUILD_DIR/lib/clang should exist and contain headers
+
+import os
+import re
+import shutil
+import sys
+
+import lldbbuild
+
+if len(sys.argv) != 3:
+     print "usage: " + sys.argv[0] + " TARGET_DIR LLVM_BUILD_DIR"
+     sys.exit(1)
+
+target_dir = sys.argv[1]
+llvm_build_dir = lldbbuild.expected_package_build_path_for("llvm")
+
+if not os.path.isdir(target_dir):
+    print target_dir + " doesn't exist"
+    sys.exit(1) 
+
+if not os.path.isdir(llvm_build_dir):
+    llvm_build_dir = re.sub ("-macosx-", "-iphoneos-", llvm_build_dir)
+
+if not os.path.isdir(llvm_build_dir):
+    llvm_build_dir = re.sub ("-iphoneos-", "-appletvos-", llvm_build_dir)
+
+if not os.path.isdir(llvm_build_dir):
+    llvm_build_dir = re.sub ("-appletvos-", "-watchos-", llvm_build_dir)
+
+if not os.path.isdir(llvm_build_dir):
+    print llvm_build_dir + " doesn't exist"
+    sys.exit(1)
+
+resources = os.path.join(target_dir, "LLDB.framework", "Resources")
+
+if not os.path.isdir(resources):
+    print resources + " must exist"
+    sys.exit(1)
+
+clang_dir = os.path.join(llvm_build_dir, "lib", "clang")
+
+if not os.path.isdir(clang_dir):
+    print clang_dir + " must exist"
+    sys.exit(1)
+
+version_dir = None
+
+for subdir in os.listdir(clang_dir):
+    if (re.match("^[0-9]+(\.[0-9]+)*$", subdir)):
+        version_dir = os.path.join(clang_dir, subdir)
+        break
+
+if version_dir == None:
+    print "Couldn't find a subdirectory of the form #(.#)... in " + clang_dir
+    sys.exit(1)
+
+if not os.path.isdir(version_dir):
+    print version_dir + " is not a directory"
+    sys.exit(1)
+
+# Just checking... we're actually going to copy all of version_dir
+include_dir = os.path.join(version_dir, "include")
+
+if not os.path.isdir(include_dir):
+    print version_dir + " is not a directory"
+    sys.exit(1)
+
+clang_resources = os.path.join(resources, "Clang")
+
+if os.path.isdir(clang_resources):
+    shutil.rmtree(clang_resources)
+
+shutil.copytree(version_dir, clang_resources)