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)
16source "$cwd"/coreconf/nspr.sh
17source "$cwd"/coreconf/sanitizers.sh
18GYP=${GYP:-gyp}
19
20# Usage info
21show_help()
22{
23    cat "$cwd"/help.txt
24}
25
26run_verbose()
27{
28    if [ "$verbose" = 1 ]; then
29        echo "$@"
30        exec 3>&1
31    else
32        exec 3>/dev/null
33    fi
34    "$@" 1>&3 2>&3
35    exec 3>&-
36}
37
38if [ -n "$CCC" ] && [ -z "$CXX" ]; then
39    export CXX="$CCC"
40fi
41
42opt_build=0
43build_64=0
44clean=0
45rebuild_gyp=0
46rebuild_nspr=0
47target=Debug
48verbose=0
49fuzz=0
50fuzz_tls=0
51fuzz_oss=0
52no_local_nspr=0
53armhf=0
54
55gyp_params=(--depth="$cwd" --generator-output=".")
56nspr_params=()
57ninja_params=()
58
59# try to guess sensible defaults
60arch=$(python "$cwd"/coreconf/detect_host_arch.py)
61if [ "$arch" = "x64" -o "$arch" = "aarch64" ]; then
62    build_64=1
63elif [ "$arch" = "arm" ]; then
64    armhf=1
65fi
66
67# parse command line arguments
68while [ $# -gt 0 ]; do
69    case $1 in
70        -c) clean=1 ;;
71        -cc) clean_only=1 ;;
72        --gyp|-g) rebuild_gyp=1 ;;
73        --nspr) nspr_clean; rebuild_nspr=1 ;;
74        -j) ninja_params+=(-j "$2"); shift ;;
75        -v) ninja_params+=(-v); verbose=1 ;;
76        --test) gyp_params+=(-Dtest_build=1) ;;
77        --clang) export CC=clang; export CCC=clang++; export CXX=clang++ ;;
78        --gcc) export CC=gcc; export CCC=g++; export CXX=g++ ;;
79        --fuzz) fuzz=1 ;;
80        --fuzz=oss) fuzz=1; fuzz_oss=1 ;;
81        --fuzz=tls) fuzz=1; fuzz_tls=1 ;;
82        --scan-build) enable_scanbuild  ;;
83        --scan-build=?*) enable_scanbuild "${1#*=}" ;;
84        --opt|-o) opt_build=1 ;;
85        -m32|--m32) build_64=0 ;;
86        --asan) enable_sanitizer asan ;;
87        --msan) enable_sanitizer msan ;;
88        --ubsan) enable_ubsan ;;
89        --ubsan=?*) enable_ubsan "${1#*=}" ;;
90        --sancov) enable_sancov ;;
91        --sancov=?*) enable_sancov "${1#*=}" ;;
92        --pprof) gyp_params+=(-Duse_pprof=1) ;;
93        --ct-verif) gyp_params+=(-Dct_verif=1) ;;
94        --emit-llvm) gyp_params+=(-Demit_llvm=1 -Dsign_libs=0) ;;
95        --disable-tests) gyp_params+=(-Ddisable_tests=1) ;;
96        --no-zdefs) gyp_params+=(-Dno_zdefs=1) ;;
97        --system-sqlite) gyp_params+=(-Duse_system_sqlite=1) ;;
98        --with-nspr=?*) set_nspr_path "${1#*=}"; no_local_nspr=1 ;;
99        --system-nspr) set_nspr_path "/usr/include/nspr/:"; no_local_nspr=1 ;;
100        --enable-libpkix) gyp_params+=(-Ddisable_libpkix=0) ;;
101        --enable-fips) gyp_params+=(-Ddisable_fips=0) ;;
102        *) show_help; exit 2 ;;
103    esac
104    shift
105done
106
107if [ "$opt_build" = 1 ]; then
108    target=Release
109else
110    target=Debug
111fi
112if [ "$build_64" = 1 ]; then
113    nspr_params+=(--enable-64bit)
114elif [ ! "$armhf" = 1 ]; then
115    gyp_params+=(-Dtarget_arch=ia32)
116fi
117if [ "$fuzz" = 1 ]; then
118    source "$cwd"/coreconf/fuzz.sh
119fi
120
121# set paths
122target_dir="$cwd"/out/$target
123mkdir -p "$target_dir"
124dist_dir="$cwd"/../dist
125dist_dir=$(mkdir -p "$dist_dir"; cd "$dist_dir"; pwd -P)
126gyp_params+=(-Dnss_dist_dir="$dist_dir")
127
128# -c = clean first
129if [ "$clean" = 1 -o "$clean_only" = 1 ]; then
130    nspr_clean
131    rm -rf "$cwd"/out
132    rm -rf "$dist_dir"
133    # -cc = only clean, don't build
134    if [ "$clean_only" = 1 ]; then
135        echo "Cleaned"
136        exit 0
137    fi
138fi
139
140# This saves a canonical representation of arguments that we are passing to gyp
141# or the NSPR build so that we can work out if a rebuild is needed.
142# Caveat: This can fail for arguments that are position-dependent.
143# e.g., "-e 2 -f 1" and "-e 1 -f 2" canonicalize the same.
144check_config()
145{
146    local newconf="$1".new oldconf="$1"
147    shift
148    mkdir -p $(dirname "$newconf")
149    echo CC="$CC" >"$newconf"
150    echo CCC="$CCC" >>"$newconf"
151    echo CXX="$CXX" >>"$newconf"
152    for i in "$@"; do echo $i; done | sort >>"$newconf"
153
154    # Note: The following diff fails if $oldconf isn't there as well, which
155    # happens if we don't have a previous successful build.
156    ! diff -q "$newconf" "$oldconf" >/dev/null 2>&1
157}
158
159gyp_config="$cwd"/out/gyp_config
160nspr_config="$cwd"/out/$target/nspr_config
161
162# If we don't have a build directory make sure that we rebuild.
163if [ ! -d "$target_dir" ]; then
164    rebuild_nspr=1
165    rebuild_gyp=1
166elif [ ! -d "$dist_dir"/$target ]; then
167    rebuild_nspr=1
168fi
169
170# Update NSPR ${C,CXX,LD}FLAGS.
171nspr_set_flags $sanitizer_flags
172
173if check_config "$nspr_config" "${nspr_params[@]}" \
174                 nspr_cflags="$nspr_cflags" \
175                 nspr_cxxflags="$nspr_cxxflags" \
176                 nspr_ldflags="$nspr_ldflags"; then
177    rebuild_nspr=1
178fi
179
180# Forward sanitizer flags.
181if [ ! -z "$sanitizer_flags" ]; then
182    gyp_params+=(-Dsanitizer_flags="$sanitizer_flags")
183fi
184
185if check_config "$gyp_config" "${gyp_params[@]}"; then
186    rebuild_gyp=1
187fi
188
189# save the chosen target
190mkdir -p "$dist_dir"
191echo $target > "$dist_dir"/latest
192
193if [[ "$rebuild_nspr" = 1 && "$no_local_nspr" = 0 ]]; then
194    nspr_build "${nspr_params[@]}"
195    mv -f "$nspr_config".new "$nspr_config"
196fi
197if [ "$rebuild_gyp" = 1 ]; then
198    if ! hash ${GYP} 2> /dev/null; then
199        echo "Please install gyp" 1>&2
200        exit 1
201    fi
202    # These extra arguments aren't used in determining whether to rebuild.
203    obj_dir="$dist_dir"/$target
204    gyp_params+=(-Dnss_dist_obj_dir=$obj_dir)
205    if [ "$no_local_nspr" = 0 ]; then
206        set_nspr_path "$obj_dir/include/nspr:$obj_dir/lib"
207    fi
208
209    run_verbose run_scanbuild ${GYP} -f ninja "${gyp_params[@]}" "$cwd"/nss.gyp
210
211    mv -f "$gyp_config".new "$gyp_config"
212fi
213
214# Run ninja.
215if hash ninja 2>/dev/null; then
216    ninja=ninja
217elif hash ninja-build 2>/dev/null; then
218    ninja=ninja-build
219else
220    echo "Please install ninja" 1>&2
221    exit 1
222fi
223run_scanbuild $ninja -C "$target_dir" "${ninja_params[@]}"
224