1#!/usr/bin/env bash
2#===----------------------------------------------------------------------===##
3#
4# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5# See https://llvm.org/LICENSE.txt for license information.
6# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7#
8#===----------------------------------------------------------------------===##
9
10set -e
11
12PROGNAME="$(basename "${0}")"
13
14function error() { printf "error: %s\n" "$*"; exit 1; }
15
16function usage() {
17cat <<EOF
18Usage:
19${PROGNAME} [options]
20
21[-h|--help]                  Display this help and exit.
22
23--llvm-root <DIR>            Path to the root of the LLVM monorepo. Only the libcxx
24                             and libcxxabi directories are required.
25
26--build-dir <DIR>            Path to the directory to use for building. This will
27                             contain intermediate build products.
28
29--install-dir <DIR>          Path to the directory to install the library to.
30
31--symbols-dir <DIR>          Path to the directory to install the .dSYM bundle to.
32
33--sdk <SDK>                  SDK used for building the library. This represents
34                             the target platform that the library will run on.
35                             You can get a list of SDKs with \`xcodebuild -showsdks\`.
36
37--architectures "<arch>..."  A whitespace separated list of architectures to build for.
38                             The library will be built for each architecture independently,
39                             and a universal binary containing all architectures will be
40                             created from that.
41
42--version X[.Y[.Z]]          The version of the library to encode in the dylib.
43EOF
44}
45
46while [[ $# -gt 0 ]]; do
47    case ${1} in
48        -h|--help)
49            usage
50            exit 0
51            ;;
52        --llvm-root)
53            llvm_root="${2}"
54            shift; shift
55            ;;
56        --build-dir)
57            build_dir="${2}"
58            shift; shift
59            ;;
60        --symbols-dir)
61            symbols_dir="${2}"
62            shift; shift
63            ;;
64        --install-dir)
65            install_dir="${2}"
66            shift; shift
67            ;;
68        --sdk)
69            sdk="${2}"
70            shift; shift
71            ;;
72        --architectures)
73            architectures="${2}"
74            shift; shift
75            ;;
76        --version)
77            version="${2}"
78            shift; shift
79            ;;
80        *)
81            error "Unknown argument '${1}'"
82            ;;
83    esac
84done
85
86for arg in llvm_root build_dir symbols_dir install_dir sdk architectures version; do
87    if [ -z ${!arg+x} ]; then
88        error "Missing required argument '--${arg//_/-}'"
89    elif [ "${!arg}" == "" ]; then
90        error "Argument to --${arg//_/-} must not be empty"
91    fi
92done
93
94# Allow using relative paths
95function realpath() {
96    if [[ $1 = /* ]]; then echo "$1"; else echo "$(pwd)/${1#./}"; fi
97}
98for arg in llvm_root build_dir symbols_dir install_dir; do
99    path="$(realpath "${!arg}")"
100    eval "${arg}=\"${path}\""
101done
102
103function step() {
104    separator="$(printf "%0.s-" $(seq 1 ${#1}))"
105    echo
106    echo "${separator}"
107    echo "${1}"
108    echo "${separator}"
109}
110
111for arch in ${architectures}; do
112    step "Building libc++.dylib and libc++abi.dylib for architecture ${arch}"
113    mkdir -p "${build_dir}/${arch}"
114    (cd "${build_dir}/${arch}" &&
115        xcrun --sdk "${sdk}" cmake "${llvm_root}/libcxx/utils/ci/runtimes" \
116            -GNinja \
117            -DCMAKE_MAKE_PROGRAM="$(xcrun --sdk "${sdk}" --find ninja)" \
118            -DLLVM_ENABLE_PROJECTS="libcxx;libcxxabi" \
119            -C "${llvm_root}/libcxx/cmake/caches/Apple.cmake" \
120            -DCMAKE_INSTALL_PREFIX="${build_dir}/${arch}-install" \
121            -DCMAKE_INSTALL_NAME_DIR="/usr/lib" \
122            -DCMAKE_OSX_ARCHITECTURES="${arch}" \
123            -DLIBCXXABI_LIBRARY_VERSION="${version}" \
124            -DLIBCXX_INCLUDE_BENCHMARKS=OFF
125    )
126
127    xcrun --sdk "${sdk}" cmake --build "${build_dir}/${arch}" --target install-cxx install-cxxabi -- -v
128done
129
130function universal_dylib() {
131    dylib=${1}
132
133    inputs=$(for arch in ${architectures}; do echo "${build_dir}/${arch}-install/lib/${dylib}"; done)
134
135    step "Creating a universal dylib ${dylib} from the dylibs for all architectures"
136    xcrun --sdk "${sdk}" lipo -create ${inputs} -output "${build_dir}/${dylib}"
137
138    step "Installing the (stripped) universal dylib to ${install_dir}/usr/lib"
139    mkdir -p "${install_dir}/usr/lib"
140    cp "${build_dir}/${dylib}" "${install_dir}/usr/lib/${dylib}"
141    xcrun --sdk "${sdk}" strip -S "${install_dir}/usr/lib/${dylib}"
142
143    step "Installing the unstripped dylib and the dSYM bundle to ${symbols_dir}"
144    xcrun --sdk "${sdk}" dsymutil "${build_dir}/${dylib}" -o "${symbols_dir}/${dylib}.dSYM"
145    cp "${build_dir}/${dylib}" "${symbols_dir}/${dylib}"
146}
147
148universal_dylib libc++.1.dylib
149universal_dylib libc++abi.dylib
150(cd "${install_dir}/usr/lib" && ln -s "libc++.1.dylib" libc++.dylib)
151
152# Install the headers by copying the headers from one of the built architectures
153# into the install directory. Headers from all architectures should be the same.
154step "Installing the libc++ and libc++abi headers to ${install_dir}/usr/include"
155any_arch=$(echo ${architectures} | cut -d ' ' -f 1)
156mkdir -p "${install_dir}/usr/include"
157ditto "${build_dir}/${any_arch}-install/include" "${install_dir}/usr/include"
158ditto "${llvm_root}/libcxxabi/include" "${install_dir}/usr/include" # TODO: libcxxabi should install its headers in CMake
159if [[ $EUID -eq 0 ]]; then # Only chown if we're running as root
160    chown -R root:wheel "${install_dir}/usr/include"
161fi
162
163step "Installing the libc++ and libc++abi licenses"
164mkdir -p "${install_dir}/usr/local/OpenSourceLicenses"
165cp "${llvm_root}/libcxx/LICENSE.TXT" "${install_dir}/usr/local/OpenSourceLicenses/libcxx.txt"
166cp "${llvm_root}/libcxxabi/LICENSE.TXT" "${install_dir}/usr/local/OpenSourceLicenses/libcxxabi.txt"
167
168# Also install universal static archives for libc++ and libc++abi
169libcxx_archives=$(for arch in ${architectures}; do echo "${build_dir}/${arch}-install/lib/libc++.a"; done)
170libcxxabi_archives=$(for arch in ${architectures}; do echo "${build_dir}/${arch}-install/lib/libc++abi.a"; done)
171step "Creating universal static archives for libc++ and libc++abi from the static archives for each architecture"
172mkdir -p "${install_dir}/usr/local/lib/libcxx"
173xcrun --sdk "${sdk}" libtool -static ${libcxx_archives} -o "${install_dir}/usr/local/lib/libcxx/libc++-static.a"
174xcrun --sdk "${sdk}" libtool -static ${libcxxabi_archives} -o "${install_dir}/usr/local/lib/libcxx/libc++abi-static.a"
175