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--architectures "<arch>..."  A whitespace separated list of architectures to build for.
34                             The library will be built for each architecture independently,
35                             and a universal binary containing all architectures will be
36                             created from that.
37
38--headers-only               Only install the header part of the library -- don't actually
39                             build the full library.
40
41--version X[.Y[.Z]]          The version of the library to encode in the dylib.
42EOF
43}
44
45while [[ $# -gt 0 ]]; do
46    case ${1} in
47        -h|--help)
48            usage
49            exit 0
50            ;;
51        --llvm-root)
52            llvm_root="${2}"
53            shift; shift
54            ;;
55        --build-dir)
56            build_dir="${2}"
57            shift; shift
58            ;;
59        --symbols-dir)
60            symbols_dir="${2}"
61            shift; shift
62            ;;
63        --install-dir)
64            install_dir="${2}"
65            shift; shift
66            ;;
67        --architectures)
68            architectures="${2}"
69            shift; shift
70            ;;
71        --headers-only)
72            headers_only=true
73            shift
74            ;;
75        --version)
76            version="${2}"
77            shift; shift
78            ;;
79        *)
80            error "Unknown argument '${1}'"
81            ;;
82    esac
83done
84
85for arg in llvm_root build_dir symbols_dir install_dir architectures version; do
86    if [ -z ${!arg+x} ]; then
87        error "Missing required argument '--${arg//_/-}'"
88    elif [ "${!arg}" == "" ]; then
89        error "Argument to --${arg//_/-} must not be empty"
90    fi
91done
92
93# Allow using relative paths
94function realpath() {
95    if [[ $1 = /* ]]; then echo "$1"; else echo "$(pwd)/${1#./}"; fi
96}
97for arg in llvm_root build_dir symbols_dir install_dir; do
98    path="$(realpath "${!arg}")"
99    eval "${arg}=\"${path}\""
100done
101
102function step() {
103    separator="$(printf "%0.s-" $(seq 1 ${#1}))"
104    echo
105    echo "${separator}"
106    echo "${1}"
107    echo "${separator}"
108}
109
110for arch in ${architectures}; do
111    step "Building libc++.dylib and libc++abi.dylib for architecture ${arch}"
112    mkdir -p "${build_dir}/${arch}"
113    xcrun cmake -S "${llvm_root}/runtimes" \
114                -B "${build_dir}/${arch}" \
115                -GNinja \
116                -DCMAKE_MAKE_PROGRAM="$(xcrun --find ninja)" \
117                -C "${llvm_root}/libcxx/cmake/caches/Apple.cmake" \
118                -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi" \
119                -DCMAKE_INSTALL_PREFIX="${build_dir}/${arch}-install" \
120                -DCMAKE_INSTALL_NAME_DIR="/usr/lib" \
121                -DCMAKE_OSX_ARCHITECTURES="${arch}" \
122                -DLIBCXXABI_LIBRARY_VERSION="${version}"
123
124    if [ "$headers_only" = true ]; then
125        xcrun cmake --build "${build_dir}/${arch}" --target install-cxx-headers install-cxxabi-headers -- -v
126    else
127        xcrun cmake --build "${build_dir}/${arch}" --target install-cxx install-cxxabi -- -v
128    fi
129done
130
131function universal_dylib() {
132    dylib=${1}
133
134    inputs=$(for arch in ${architectures}; do echo "${build_dir}/${arch}-install/lib/${dylib}"; done)
135
136    step "Creating a universal dylib ${dylib} from the dylibs for all architectures"
137    xcrun lipo -create ${inputs} -output "${build_dir}/${dylib}"
138
139    step "Installing the (stripped) universal dylib to ${install_dir}/usr/lib"
140    mkdir -p "${install_dir}/usr/lib"
141    cp "${build_dir}/${dylib}" "${install_dir}/usr/lib/${dylib}"
142    xcrun strip -S "${install_dir}/usr/lib/${dylib}"
143
144    step "Installing the unstripped dylib and the dSYM bundle to ${symbols_dir}"
145    xcrun dsymutil "${build_dir}/${dylib}" -o "${symbols_dir}/${dylib}.dSYM"
146    cp "${build_dir}/${dylib}" "${symbols_dir}/${dylib}"
147}
148
149if [ "$headers_only" != true ]; then
150    universal_dylib libc++.1.dylib
151    universal_dylib libc++abi.dylib
152    (cd "${install_dir}/usr/lib" && ln -s "libc++.1.dylib" libc++.dylib)
153fi
154
155# Install the headers by copying the headers from one of the built architectures
156# into the install directory. Headers from all architectures should be the same.
157step "Installing the libc++ and libc++abi headers to ${install_dir}/usr/include"
158any_arch=$(echo ${architectures} | cut -d ' ' -f 1)
159mkdir -p "${install_dir}/usr/include"
160ditto "${build_dir}/${any_arch}-install/include" "${install_dir}/usr/include"
161if [[ $EUID -eq 0 ]]; then # Only chown if we're running as root
162    chown -R root:wheel "${install_dir}/usr/include"
163fi
164
165if [ "$headers_only" != true ]; then
166    step "Installing the libc++ and libc++abi licenses"
167    mkdir -p "${install_dir}/usr/local/OpenSourceLicenses"
168    cp "${llvm_root}/libcxx/LICENSE.TXT" "${install_dir}/usr/local/OpenSourceLicenses/libcxx.txt"
169    cp "${llvm_root}/libcxxabi/LICENSE.TXT" "${install_dir}/usr/local/OpenSourceLicenses/libcxxabi.txt"
170
171    # Also install universal static archives for libc++ and libc++abi
172    libcxx_archives=$(for arch in ${architectures}; do echo "${build_dir}/${arch}-install/lib/libc++.a"; done)
173    libcxxabi_archives=$(for arch in ${architectures}; do echo "${build_dir}/${arch}-install/lib/libc++abi.a"; done)
174    step "Creating universal static archives for libc++ and libc++abi from the static archives for each architecture"
175    mkdir -p "${install_dir}/usr/local/lib/libcxx"
176    xcrun libtool -static ${libcxx_archives} -o "${install_dir}/usr/local/lib/libcxx/libc++-static.a"
177    xcrun libtool -static ${libcxxabi_archives} -o "${install_dir}/usr/local/lib/libcxx/libc++abi-static.a"
178fi
179