1#!/bin/bash
2set -eo pipefail
3
4# Copyright 2018-2019 by
5# Armin Hasitzka.
6#
7# This file is part of the FreeType project, and may only be used, modified,
8# and distributed under the terms of the FreeType project license,
9# LICENSE.TXT.  By continuing to use, modify, or distribute this file you
10# indicate that you have read the license and understand and accept it
11# fully.
12
13dir="${PWD}"
14cd $( dirname $( readlink -f "${0}" ) ) # go to `/fuzzing/scripts'
15
16# ----------------------------------------------------------------------------
17# collect parameters:
18
19opt_help="0"   # 0|1
20opt_rebuild="0" # 0|1
21
22while [[ "${#}" -gt "0" ]]; do
23    case "${1}" in
24        --help)
25            opt_help="1"
26            shift
27            ;;
28        --rebuild)
29            opt_rebuild="1"
30            shift
31            ;;
32        *) # show usage when invalid parameters are used:
33            opt_help="1"
34            shift
35            ;;
36    esac
37done
38
39# ----------------------------------------------------------------------------
40# usage:
41
42if [[ "${opt_help}" == "1" ]]; then
43    cat <<EOF
44
45usage: ${0} [OPTIONS]
46
47This script listens to a few environmental variables.  It is not mandatory to
48set any of these variables but if they are set, they will be used.
49
50  CFLAGS    Additional C compiler flags.
51  CXXFLAGS  Additional C++ compiler flags.
52  LDFLAGS   Additional linker flags.
53
54OPTIONS:
55
56  --rebuild  Rebuild the last build.  Nothing will be reset, nothing will be
57             changed.  Using this option calls 'make' in every module without
58             flushing or resetting anything.  Useful for debugging.
59
60  --help  Print usage information.
61
62EOF
63
64    exit 66
65fi
66
67# ----------------------------------------------------------------------------
68# rebuild shortcut:
69
70if [[ "${opt_rebuild}" == "1" ]]; then
71    bash build/glog.sh       --no-init
72    bash build/libarchive.sh --no-init
73    bash build/freetype.sh   --no-init
74    bash build/targets.sh    --no-init
75    exit
76fi
77
78# ----------------------------------------------------------------------------
79# settings:
80
81ansi_reset="\e[0m"
82ansi_bold="\e[1m"
83ansi_underline="\e[4m"
84ansi_red="\e[31m"
85ansi_yellow="\e[33m"
86
87build_type=      # d|f
88build_asan=      # y|n
89build_ubsan=     # y|n
90build_coverage=  # y|n
91build_debugging= # 0|1|2|3|n
92build_ft_trace=  # y|n
93build_ccache=    # y|n
94
95cc="clang"
96cxx="clang++"
97
98cflags="${CFLAGS}"
99cxxflags="${CXXFLAGS} -std=c++11"
100ldflags="${LDFLAGS}"
101
102# Base name of the driver:
103driver_name="driver"
104
105# ----------------------------------------------------------------------------
106# helpers:
107
108# Print the new line character (\n).
109function print_nl()
110{
111    printf "\n"
112}
113
114# Print a question.
115#
116# $1: Question string.
117function print_q()
118{
119    printf "\n${ansi_bold}%s${ansi_reset}\n" "${1}"
120}
121
122# Print some information.
123#
124# $1: Name of an option.
125# $2: Description of the option.
126#
127# .. or ...
128#
129# $1: General information.
130function print_info()
131{
132    if [[ "$#" == "1" ]]; then
133        printf "  %s\n" "${1}"
134    elif [[ "$#" == "2" ]]; then
135        printf "  ${ansi_yellow}%s${ansi_reset}: %s\n" "${1}" "${2}"
136    else
137        exit 66
138    fi
139}
140
141# Print a url.
142#
143# $1: URL.
144function print_url()
145{
146    printf "  ${ansi_underline}%s${ansi_reset}\n" "${1}"
147}
148
149# Print (+ verify) the selection of an option.
150#
151# $1: The chosen option.
152# ${2n + 2, n >= 0}: The nth option.
153# ${2n + 3, n >= 0}: The nth description of an option.
154#
155# Example: $ print_sel ${selection} "y" "yes" "n" "no" ...
156function print_sel()
157{
158    i=2
159    while [[ i -le "$#" ]]; do
160        if [[ "${1}" == "${!i}" ]]; then
161            i=$(( i + 1 ))
162            printf "\n  selection: ${ansi_yellow}%s${ansi_reset}\n" "${!i}"
163            return
164        fi
165        i=$(( i + 2 ))
166    done
167    printf "\n  ${ansi_red}${ansi_bold}invalid selection: %s${ansi_reset}\n\n" "${1}"
168    exit 66
169}
170
171# Print (+ verify) the slection of an option that can either be "y" (yes) or
172# "n" (no).
173#
174# $1: The chosen option.
175function print_sel_yes_no()
176{
177    print_sel "${1}" "y" "yes" "n" "no"
178}
179
180# Ask the user and print the (accepted + valid) result.
181#
182# $1: a list of options like "a|b|c"
183# $2 (optional): a default option like "a"
184function ask_user()
185{
186    options="${1}"
187    if [[ "$#" == "2" ]]; then
188        options="${options}, default: ${2}"
189    fi
190    while : ; do
191        read -p "(${options}) > " answer
192        answer=$(echo "${answer}" | tr '[:upper:]' '[:lower:]')
193        if [[ "${answer}" == "" && "$#" == "2" ]]; then
194            echo "${2}"
195            break
196        fi
197        if [[ "${1}" == *"|${answer}|"* ||
198              "${1}" == *"|${answer}"   ||
199              "${1}" ==   "${answer}|"* ]]; then
200            echo "${answer}"
201            break
202        fi
203    done
204}
205
206# ----------------------------------------------------------------------------
207# interaction:
208
209printf "${ansi_yellow}"
210printf " ____ ____  ____ ____ ______    __ ___  ____\n"
211printf "|  __)  _ \|  __)  __)_   __)  / /  _ \|  __)\n"
212printf "| |__| |_) ) |__| |__  | | \ \/ /| |_) ) |__\n"
213printf "|  __)    /|  __)  __) | |  \  / |  __/|  __)\n"
214printf "| |  | |\ \| |__| |__  | |  / /  | |   | |__\n"
215printf "|_|  |_| \_\____)____) |_| /_/   |_|   |____)\n\n"
216printf "${ansi_reset}"
217printf "               Custom Build\n"
218
219print_q    "Build the driver or the fuzzer?"
220print_info "driver" "run selected samples or failed instances"
221print_info "fuzzer" "fuzz a corpus of samples with libFuzzer"
222print_nl
223
224build_type=$( ask_user "d|f" )
225print_sel "${build_type}" "d" "driver" "f" "fuzzer"
226
227if [[ "${build_type}" == "f" ]]; then
228    cflags="  ${cflags}   -fsanitize=fuzzer-no-link"
229    cxxflags="${cxxflags} -fsanitize=fuzzer-no-link"
230
231    export CMAKE_FUZZING_ENGINE="-fsanitize=fuzzer"
232fi
233
234print_q   "Add the AddressSanitizer?"
235print_url "https://clang.llvm.org/docs/AddressSanitizer.html"
236print_nl
237
238build_asan=$( ask_user "y|n" "y" )
239print_sel_yes_no "${build_asan}"
240
241if [[ "${build_asan}" == "y" ]]; then
242    cflags="  ${cflags}   -fsanitize=address -fsanitize-address-use-after-scope"
243    cxxflags="${cxxflags} -fsanitize=address -fsanitize-address-use-after-scope"
244    ldflags=" ${ldflags}  -fsanitize=address"
245
246    driver_name="${driver_name}-asan"
247fi
248
249print_q   "Add the UndefinedBehaviorSanitizer?"
250print_url "https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html"
251print_nl
252
253build_ubsan=$( ask_user "y|n" "y" )
254print_sel_yes_no "${build_ubsan}"
255
256if [[ "${build_ubsan}" == "y" ]]; then
257    cflags="  ${cflags}   -fsanitize=undefined"
258    cxxflags="${cxxflags} -fsanitize=undefined"
259    ldflags=" ${ldflags}  -fsanitize=undefined"
260
261    driver_name="${driver_name}-ubsan"
262fi
263
264print_q   "Add coverage instrumentation?"
265print_url "https://clang.llvm.org/docs/SourceBasedCodeCoverage.html"
266print_nl
267
268build_coverage=$( ask_user "y|n" "n" )
269print_sel_yes_no "${build_coverage}"
270
271if [[ "${build_coverage}" == "y" ]]; then
272    cflags="  ${cflags}   -fprofile-instr-generate -fcoverage-mapping"
273    cxxflags="${cxxflags} -fprofile-instr-generate -fcoverage-mapping"
274    ldflags=" ${ldflags}  -fprofile-instr-generate"
275
276    driver_name="${driver_name}-cov"
277fi
278
279if [[ "${build_asan}" == "y" || "${build_ubsan}" == "y" ]]; then
280    print_q    "Choose the optimisation level:"
281    print_info "0" "compile with '-g -O0'"
282    print_info "1" "compile with '-g -O1'"
283    print_info "2" "compile with '-g -O2'"
284    print_info "3" "compile with '-g -O3'"
285    print_nl
286
287    build_debugging=$( ask_user "0|1|2|3" "1" )
288    print_sel "${build_debugging}" \
289              "0" "-g -O0"         \
290              "1" "-g -O1"         \
291              "2" "-g -O2"         \
292              "3" "-g -O3"
293else
294    print_q    "Add debugging flags?"
295    print_info "0" "compile with '-g -O0'"
296    print_info "1" "compile with '-g -O1'"
297    print_info "2" "compile with '-g -O2'"
298    print_info "3" "compile with '-g -O3'"
299    print_info "n" "compile without debugging flags"
300    print_nl
301
302    build_debugging=$( ask_user "0|1|2|3|n" "1" )
303    print_sel "${build_debugging}" \
304              "0" "-g -O0"         \
305              "1" "-g -O1"         \
306              "2" "-g -O2"         \
307              "3" "-g -O3"         \
308              "n" "no"
309fi
310
311if [[ "${build_debugging}" != "n" ]]; then
312    cflags="  ${cflags}   -g -O${build_debugging}"
313    cxxflags="${cxxflags} -g -O${build_debugging}"
314
315    driver_name="${driver_name}-o${build_debugging}"
316fi
317
318if [[ "${build_type}" == "f" ]]; then
319    build_glog="n"
320else
321    print_q    "Use Glog logger?"
322    print_url  "https://github.com/google/glog"
323    print_info "no" "logging will be compiled out and glog will not be linked"
324    print_nl
325
326    build_glog=$( ask_user "y|n" "n" )
327    print_sel_yes_no "${build_glog}"
328fi
329
330if [[ "${build_glog}" == "y" ]]; then
331    export CMAKE_USE_LOGGER_GLOG=1
332
333    driver_name="${driver_name}-glog"
334fi
335
336if [[ "${build_type}" == "f" ]]; then
337    build_ft_trace="n"
338else
339    print_q    "Add FreeType tracing?"
340    print_info "yes" "activate 'FT_DEBUG_LEVEL_{TRACE,ERROR}' and 'FT_DEBUG_MEMORY'"
341    print_nl
342
343    build_ft_trace=$( ask_user "y|n" "n" )
344    print_sel_yes_no "${build_ft_trace}"
345fi
346
347if [[ "${build_ft_trace}" == "y" ]]; then
348    cflags="  ${cflags}   -DFT_DEBUG_LEVEL_TRACE -DFT_DEBUG_LEVEL_ERROR -DFT_DEBUG_MEMORY"
349    cxxflags="${cxxflags} -DFT_DEBUG_LEVEL_TRACE -DFT_DEBUG_LEVEL_ERROR -DFT_DEBUG_MEMORY"
350
351    driver_name="${driver_name}-fttrace"
352fi
353
354if ! command -v "ccache" >"/dev/null"; then
355    build_ccache="n"
356else
357    print_q    "Use ccache?"
358    print_info "ccache seems to be available on this system"
359    print_nl
360
361    build_ccache=$( ask_user "y|n" "y" )
362    print_sel_yes_no "${build_ccache}"
363fi
364
365if [[ "${build_ccache}" == "y" ]]; then
366    cc="ccache ${cc}"
367    cxx="ccache ${cxx}"
368
369    driver_name="${driver_name}-ccache"
370fi
371
372print_nl
373
374# ----------------------------------------------------------------------------
375# export flags and build everything:
376
377export CC="${cc}"
378export CXX="${cxx}"
379
380export CFLAGS="${cflags}"
381export CXXFLAGS="${cxxflags}"
382export LDFLAGS="${ldflags}"
383
384export CMAKE_DRIVER_EXE_NAME="${driver_name}"
385
386if [[ "${build_glog}" == "y" ]]; then
387    bash "build/glog.sh"
388fi
389
390bash "build/libarchive.sh"
391bash "build/brotli.sh"
392bash "build/bzip2.sh"
393bash "build/freetype.sh"
394bash "build/targets.sh"
395
396cd "${dir}"
397