1#!/usr/bin/env bash
2#
3# This Source Code Form is subject to the terms of the Mozilla Public
4# License, v. 2.0. If a copy of the MPL was not distributed with this
5# file, You can obtain one at http://mozilla.org/MPL/2.0/.
6################################################################################
7#
8# This script builds NSS with gyp and ninja.
9#
10# This build system is still under development.  It does not yet support all
11# the features or platforms that NSS supports.
12
13set -e
14
15cwd=$(cd $(dirname $0); pwd -P)
16dist_dir="$cwd/../dist"
17argsfile="$dist_dir/build_args"
18source "$cwd/coreconf/nspr.sh"
19source "$cwd/coreconf/sanitizers.sh"
20GYP=${GYP:-gyp}
21
22# Usage info
23show_help()
24{
25    cat "$cwd/help.txt"
26}
27
28run_verbose()
29{
30    if [ "$verbose" = 1 ]; then
31        echo "$@"
32        exec 3>&1
33    else
34        exec 3>/dev/null
35    fi
36    "$@" 1>&3 2>&3
37    exec 3>&-
38}
39
40# The prehistoric bash on Mac doesn't support @Q quoting.
41# The consequences aren't that serious, unless there are odd arrangements of spaces.
42if /usr/bin/env bash -c 'x=1;echo "${x@Q}"' >/dev/null 2>&1; then
43    Q() { echo "${@@Q}"; }
44else
45    Q() { echo "$@"; }
46fi
47
48if [ -n "$CCC" ] && [ -z "$CXX" ]; then
49    export CXX="$CCC"
50fi
51
52opt_build=0
53build_64=0
54clean=0
55rebuild_gyp=0
56rebuild_nspr=0
57build_nspr_tests=0
58run_nspr_tests=0
59exit_after_nspr=0
60target=Debug
61verbose=0
62fuzz=0
63fuzz_tls=0
64fuzz_oss=0
65no_local_nspr=0
66sslkeylogfile=1
67
68gyp_params=(--depth="$cwd" --generator-output=".")
69ninja_params=()
70
71# Assume that the target architecture is the same as the host by default.
72host_arch=$(python "$cwd/coreconf/detect_host_arch.py")
73target_arch=$host_arch
74
75# Assume that MSVC is wanted if this is running on windows.
76platform=$(uname -s)
77if [ "${platform%-*}" = "MINGW32_NT" -o "${platform%-*}" = "MINGW64_NT" ]; then
78    msvc=1
79fi
80
81# Parse command line arguments.
82all_args=("$@")
83while [ $# -gt 0 ]; do
84    case "$1" in
85        --rebuild)
86            if [[ ! -e "$argsfile" ]]; then
87                echo "Unable to rebuild" 1>&2
88                exit 2
89            fi
90            IFS=$'\r\n' GLOBIGNORE='*' command eval  'previous_args=($(<"$argsfile"))'
91            exec /usr/bin/env bash -c "$(Q "$0")"' "$@"' "$0" "${previous_args[@]}"
92            ;;
93        -c) clean=1 ;;
94        -cc) clean_only=1 ;;
95        -v) ninja_params+=(-v); verbose=1 ;;
96        -j) ninja_params+=(-j "$2"); shift ;;
97        --gyp|-g) rebuild_gyp=1 ;;
98        --opt|-o) opt_build=1 ;;
99        -m32|--m32) target_arch=ia32; echo 'Warning: use -t instead of -m32' 1>&2 ;;
100        -t|--target) target_arch="$2"; shift ;;
101        --target=*) target_arch="${1#*=}" ;;
102        --clang) export CC=clang; export CCC=clang++; export CXX=clang++; msvc=0 ;;
103        --gcc) export CC=gcc; export CCC=g++; export CXX=g++; msvc=0 ;;
104        --msvc) msvc=1 ;;
105        --scan-build) enable_scanbuild  ;;
106        --scan-build=?*) enable_scanbuild "${1#*=}" ;;
107        --disable-tests) gyp_params+=(-Ddisable_tests=1) ;;
108        --pprof) gyp_params+=(-Duse_pprof=1) ;;
109        --asan) enable_sanitizer asan ;;
110        --msan) enable_sanitizer msan ;;
111        --ubsan) enable_ubsan ;;
112        --ubsan=?*) enable_ubsan "${1#*=}" ;;
113        --fuzz) fuzz=1 ;;
114        --fuzz=oss) fuzz=1; fuzz_oss=1 ;;
115        --fuzz=tls) fuzz=1; fuzz_tls=1 ;;
116        --sancov) enable_sancov; gyp_params+=(-Dcoverage=1) ;;
117        --sancov=?*) enable_sancov "${1#*=}"; gyp_params+=(-Dcoverage=1) ;;
118        --emit-llvm) gyp_params+=(-Demit_llvm=1 -Dsign_libs=0) ;;
119        --no-zdefs) gyp_params+=(-Dno_zdefs=1) ;;
120        --static) gyp_params+=(-Dstatic_libs=1) ;;
121        --ct-verif) gyp_params+=(-Dct_verif=1) ;;
122        --nspr) nspr_clean; rebuild_nspr=1 ;;
123        --nspr-test-build) build_nspr_tests=1 ;;
124        --nspr-test-run) run_nspr_tests=1 ;;
125        --nspr-only) exit_after_nspr=1 ;;
126        --with-nspr=?*) set_nspr_path "${1#*=}"; no_local_nspr=1 ;;
127        --system-nspr) set_nspr_path "/usr/include/nspr/:"; no_local_nspr=1 ;;
128        --system-sqlite) gyp_params+=(-Duse_system_sqlite=1) ;;
129        --enable-fips) gyp_params+=(-Ddisable_fips=0) ;;
130        --enable-libpkix) gyp_params+=(-Ddisable_libpkix=0) ;;
131        --mozpkix-only) gyp_params+=(-Dmozpkix_only=1 -Ddisable_tests=1 -Dsign_libs=0) ;;
132        --disable-keylog) sslkeylogfile=0 ;;
133        --enable-legacy-db) gyp_params+=(-Ddisable_dbm=0) ;;
134        -D*) gyp_params+=("$1") ;;
135        *) show_help; exit 2 ;;
136    esac
137    shift
138done
139
140# Set the target architecture and build type.
141gyp_params+=(-Dtarget_arch="$target_arch")
142if [ "$opt_build" = 1 ]; then
143    target=Release
144else
145    target=Debug
146fi
147
148gyp_params+=(-Denable_sslkeylogfile="$sslkeylogfile")
149
150# Do special setup.
151if [ "$fuzz" = 1 ]; then
152    source "$cwd/coreconf/fuzz.sh"
153fi
154nspr_set_flags $sanitizer_flags
155if [ ! -z "$sanitizer_flags" ]; then
156    gyp_params+=(-Dsanitizer_flags="$sanitizer_flags")
157fi
158
159if [ "$msvc" = 1 ]; then
160    source "$cwd/coreconf/msvc.sh"
161fi
162
163# -c = clean first
164if [ "$clean" = 1 -o "$clean_only" = 1 ]; then
165    nspr_clean
166    rm -rf "$cwd/out"
167    rm -rf "$dist_dir"
168    # -cc = only clean, don't build
169    if [ "$clean_only" = 1 ]; then
170        echo "Cleaned"
171        exit 0
172    fi
173fi
174
175# Setup build paths.
176target_dir="$cwd/out/$target"
177mkdir -p "$target_dir"
178dist_dir=$(mkdir -p "$dist_dir"; cd "$dist_dir"; pwd -P)
179gyp_params+=(-Dnss_dist_dir="$dist_dir")
180
181# This saves a canonical representation of arguments that we are passing to gyp
182# or the NSPR build so that we can work out if a rebuild is needed.
183# Caveat: This can fail for arguments that are position-dependent.
184# e.g., "-e 2 -f 1" and "-e 1 -f 2" canonicalize the same.
185check_config()
186{
187    local newconf="$1".new oldconf="$1"
188    shift
189    mkdir -p $(dirname "$newconf")
190    echo CC="$(Q "$CC")" >"$newconf"
191    echo CCC="$(Q "$CCC")" >>"$newconf"
192    echo CXX="$(Q "$CXX")" >>"$newconf"
193    echo target_arch="$(Q "$target_arch")" >>"$newconf"
194    for i in "$@"; do echo "$i"; done | sort >>"$newconf"
195
196    # Note: The following diff fails if $oldconf isn't there as well, which
197    # happens if we don't have a previous successful build.
198    ! diff -q "$newconf" "$oldconf" >/dev/null 2>&1
199}
200
201gyp_config="$cwd/out/gyp_config"
202nspr_config="$cwd/out/$target/nspr_config"
203
204# Now check what needs to be rebuilt.
205# If we don't have a build directory make sure that we rebuild.
206if [ ! -d "$target_dir" ]; then
207    rebuild_nspr=1
208    rebuild_gyp=1
209elif [ ! -d "$dist_dir/$target" ]; then
210    rebuild_nspr=1
211fi
212
213if check_config "$nspr_config" \
214                 nspr_cflags="$(Q "$nspr_cflags")" \
215                 nspr_cxxflags="$(Q "$nspr_cxxflags")" \
216                 nspr_ldflags="$(Q "$nspr_ldflags")"; then
217    rebuild_nspr=1
218fi
219
220if check_config "$gyp_config" "$(Q "${gyp_params[@]}")"; then
221    rebuild_gyp=1
222fi
223
224# Save the chosen target.
225echo "$target" > "$dist_dir/latest"
226for i in "${all_args[@]}"; do echo "$i"; done > "$argsfile"
227
228# Build.
229# NSPR.
230if [[ "$rebuild_nspr" = 1 && "$no_local_nspr" = 0 ]]; then
231    nspr_clean
232    nspr_build
233    mv -f "$nspr_config.new" "$nspr_config"
234fi
235
236if [ "$exit_after_nspr" = 1 ]; then
237  exit 0
238fi
239
240# gyp.
241if [ "$rebuild_gyp" = 1 ]; then
242    if ! hash "$GYP" 2> /dev/null; then
243        echo "Building NSS requires an installation of gyp: https://gyp.gsrc.io/" 1>&2
244        exit 3
245    fi
246    # These extra arguments aren't used in determining whether to rebuild.
247    obj_dir="$dist_dir/$target"
248    gyp_params+=(-Dnss_dist_obj_dir="$obj_dir")
249    if [ "$no_local_nspr" = 0 ]; then
250        set_nspr_path "$obj_dir/include/nspr:$obj_dir/lib"
251    fi
252
253    run_verbose run_scanbuild ${GYP} -f ninja "${gyp_params[@]}" "$cwd/nss.gyp"
254
255    mv -f "$gyp_config.new" "$gyp_config"
256fi
257
258# ninja.
259if hash ninja-build 2>/dev/null; then
260    ninja=ninja-build
261elif hash ninja 2>/dev/null; then
262    ninja=ninja
263else
264    echo "Building NSS requires an installation of ninja: https://ninja-build.org/" 1>&2
265    exit 3
266fi
267run_scanbuild "$ninja" -C "$target_dir" "${ninja_params[@]}"
268