blob: 004accb21b5fe181df83de1fa748f8ad84a483a2 [file] [log] [blame]
#!/bin/bash
set -eo pipefail
# Copyright 2018-2019 by
# Armin Hasitzka.
#
# This file is part of the FreeType project, and may only be used, modified,
# and distributed under the terms of the FreeType project license,
# LICENSE.TXT. By continuing to use, modify, or distribute this file you
# indicate that you have read the license and understand and accept it
# fully.
dir="${PWD}"
cd "$( dirname "$( readlink -f "${0}" )" )" # go to `/fuzzing/scripts'
# ----------------------------------------------------------------------------
# collect parameters:
opt_help="0" # 0|1
opt_rebuild="0" # 0|1
while [[ "${#}" -gt "0" ]]; do
case "${1}" in
--help)
opt_help="1"
shift
;;
--rebuild)
opt_rebuild="1"
shift
;;
*) # show usage when invalid parameters are used:
opt_help="1"
shift
;;
esac
done
# ----------------------------------------------------------------------------
# usage:
if [[ "${opt_help}" == "1" ]]; then
cat <<EOF
usage: ${0} [OPTIONS]
This script listens to a few environmental variables. It is not mandatory to
set any of these variables but if they are set, they will be used.
CFLAGS Additional C compiler flags.
CXXFLAGS Additional C++ compiler flags.
LDFLAGS Additional linker flags.
OPTIONS:
--rebuild Rebuild the last build. Nothing will be reset, nothing will be
changed. Using this option calls 'make' in every module without
flushing or resetting anything. Useful for debugging.
--help Print usage information.
EOF
exit 66
fi
# ----------------------------------------------------------------------------
# rebuild shortcut:
if [[ "${opt_rebuild}" == "1" ]]; then
# Each project must be listed after any project it depends on.
bash build/zlib.sh --no-init
bash build/bzip2.sh --no-init
bash build/libarchive.sh --no-init
bash build/brotli.sh --no-init
bash build/libpng.sh --no-init
bash build/freetype.sh --no-init
bash build/libcxx.sh --no-init
#bash build/glog.sh --no-init
bash build/targets.sh --no-init
exit
fi
# ----------------------------------------------------------------------------
# settings:
ansi_reset="\e[0m"
ansi_bold="\e[1m"
ansi_underline="\e[4m"
ansi_red="\e[31m"
ansi_yellow="\e[33m"
build_type= # d|f
build_san= # a|m|t|u|n
build_ubsan= # y|n
build_coverage= # y|n
build_debugging= # 0|1|2|3|n
build_ft_trace= # y|n
build_ccache= # y|n
cflags="${CFLAGS}"
cxxflags="${CXXFLAGS} -std=c++11"
ldflags="${LDFLAGS}"
# Base name of the driver:
driver_name="driver"
# ----------------------------------------------------------------------------
# helpers:
# Print the new line character (\n).
function print_nl()
{
printf "\n"
}
# Print a question.
#
# $1: Question string.
function print_q()
{
printf "\n${ansi_bold}%s${ansi_reset}\n" "${1}"
}
# Print some information.
#
# $1: Name of an option.
# $2: Description of the option.
#
# .. or ...
#
# $1: General information.
function print_info()
{
if [[ "$#" == "1" ]]; then
printf " %s\n" "${1}"
elif [[ "$#" == "2" ]]; then
printf " ${ansi_yellow}%s${ansi_reset}: %s\n" "${1}" "${2}"
else
exit 66
fi
}
# Print a url.
#
# $1: URL.
function print_url()
{
printf " ${ansi_underline}%s${ansi_reset}\n" "${1}"
}
# Print (+ verify) the selection of an option.
#
# $1: The chosen option.
# ${2n + 2, n >= 0}: The nth option.
# ${2n + 3, n >= 0}: The nth description of an option.
#
# Example: $ print_sel ${selection} "y" "yes" "n" "no" ...
function print_sel()
{
i=2
while [[ i -le "$#" ]]; do
if [[ "${1}" == "${!i}" ]]; then
i=$(( i + 1 ))
printf "\n selection: ${ansi_yellow}%s${ansi_reset}\n" "${!i}"
return
fi
i=$(( i + 2 ))
done
printf "\n ${ansi_red}${ansi_bold}invalid selection: %s${ansi_reset}\n\n" "${1}"
exit 66
}
# Print (+ verify) the slection of an option that can either be "y" (yes) or
# "n" (no).
#
# $1: The chosen option.
function print_sel_yes_no()
{
print_sel "${1}" "y" "yes" "n" "no"
}
# Ask the user and print the (accepted + valid) result.
#
# $1: a list of options like "a|b|c"
# $2 (optional): a default option like "a"
function ask_user()
{
options="${1}"
if [[ "$#" == "2" ]]; then
options="${options}, default: ${2}"
fi
while : ; do
read -p "(${options}) > " answer
answer=$(echo "${answer}" | tr '[:upper:]' '[:lower:]')
if [[ "${answer}" == "" && "$#" == "2" ]]; then
echo "${2}"
break
fi
if [[ "${1}" == *"|${answer}|"* ||
"${1}" == *"|${answer}" ||
"${1}" == "${answer}|"* ]]; then
echo "${answer}"
break
fi
done
}
# ----------------------------------------------------------------------------
# interaction:
printf "${ansi_yellow}"
printf " ____ ____ ____ ____ ______ __ ___ ____\n"
printf "| __) _ \| __) __)_ __) / / _ \| __)\n"
printf "| |__| |_) ) |__| |__ | | \ \/ /| |_) ) |__\n"
printf "| __) /| __) __) | | \ / | __/| __)\n"
printf "| | | |\ \| |__| |__ | | / / | | | |__\n"
printf "|_| |_| \_\____)____) |_| /_/ |_| |____)\n\n"
printf "${ansi_reset}"
printf " Custom Build\n"
clang_version_regex='.*clang version ([0-9]+)\.[0-9]+.*'
if [[ "${CC}" =~ .*"clang".* ]]; then
cc="${CC}"
else
cc="clang"
fi
print_info "cc" "${cc}"
cc_version_output=$("${cc}" --version)
if [[ "${cc_version_output}" =~ ${clang_version_regex} ]]; then
cc_version="${BASH_REMATCH[1]}"
print_info "cc version" "${cc_version}"
else
print_info "cc version not detected"
fi
if [[ "${CXX}" =~ .*"clang".* ]]; then
cxx="${CXX}"
else
cxx="clang++"
fi
print_info "cxx" "${cxx}"
cxx_version_output=$("${cxx}" --version)
if [[ "${cxx_version_output}" =~ ${clang_version_regex} ]]; then
cxx_version="${BASH_REMATCH[1]}"
print_info "cxx version" "${cxx_version}"
if [ "${cxx_version}" -lt "10" ]; then
print_info "cxx version too old. Need clang 10 or newer."
exit 66
fi
else
print_info "cxx version not detected"
fi
print_q "Build the driver or the fuzzer?"
print_info "driver" "run selected samples or failed instances"
print_info "fuzzer" "fuzz a corpus of samples with libFuzzer"
print_nl
build_type=$( ask_user "d|f" )
print_sel "${build_type}" "d" "driver" "f" "fuzzer"
if [[ "${build_type}" == "f" ]]; then
cflags=" ${cflags} -fsanitize=fuzzer-no-link"
cxxflags="${cxxflags} -fsanitize=fuzzer-no-link"
export CMAKE_FUZZING_ENGINE="-fsanitize=fuzzer"
fi
llvm_sanitizer=""
# Address, Memory, and Thread Sanitizers are incompatibile.
# Also, UndefinedBehavior should only be used by itself or with Address.
# See clang/lib/Driver/SanitizerArgs.cpp#IncompatibleGroups
print_q "Sanitizer: Address, Memory, Thread, UndefinedBehavior, or None?"
print_url "https://clang.llvm.org/docs/AddressSanitizer.html"
print_url "https://clang.llvm.org/docs/MemorySanitizer.html"
print_url "https://clang.llvm.org/docs/ThreadSanitizer.html"
print_url "https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html"
print_nl
build_san=$( ask_user "a|m|t|u|n" "a" )
print_sel "${build_san}" \
"a" "AddressSanitizer" \
"m" "MemorySanitizer" \
"t" "ThreadSanitizer" \
"u" "UndefinedBehaviorSanitizer" \
"n" "None"
if [[ "${build_san}" == "a" ]]; then
cflags=" ${cflags} -fsanitize=address -fsanitize-address-use-after-scope"
cxxflags="${cxxflags} -fsanitize=address -fsanitize-address-use-after-scope"
ldflags=" ${ldflags} -fsanitize=address"
driver_name="${driver_name}-asan"
llvm_sanitizer="address"
print_q "Add the UndefinedBehaviorSanitizer?"
print_url "https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html"
print_nl
build_ubsan=$( ask_user "y|n" "y" )
print_sel_yes_no "${build_ubsan}"
elif [[ "${build_san}" == "m" ]]; then
cflags=" ${cflags} -fsanitize=memory -fsanitize-memory-track-origins"
cxxflags="${cxxflags} -fsanitize=memory -fsanitize-memory-track-origins"
ldflags=" ${ldflags} -fsanitize=memory"
driver_name="${driver_name}-msan"
llvm_sanitizer="memory"
elif [[ "${build_san}" == "t" ]]; then
cflags=" ${cflags} -fsanitize=thread"
cxxflags="${cxxflags} -fsanitize=thread"
ldflags=" ${ldflags} -fsanitize=thread"
driver_name="${driver_name}-tsan"
llvm_sanitizer="thread"
elif [[ "${build_san}" == "u" ]]; then
build_ubsan="y"
fi
if [[ "${build_ubsan}" == "y" ]]; then
cflags=" ${cflags} -fsanitize=undefined"
cxxflags="${cxxflags} -fsanitize=undefined"
ldflags=" ${ldflags} -fsanitize=undefined"
driver_name="${driver_name}-ubsan"
llvm_sanitizer="${llvm_sanitizer:+${llvm_sanitizer};}undefined"
fi
print_q "Add coverage instrumentation?"
print_url "https://clang.llvm.org/docs/SourceBasedCodeCoverage.html"
print_nl
build_coverage=$( ask_user "y|n" "n" )
print_sel_yes_no "${build_coverage}"
if [[ "${build_coverage}" == "y" ]]; then
cflags=" ${cflags} -fprofile-instr-generate -fcoverage-mapping"
cxxflags="${cxxflags} -fprofile-instr-generate -fcoverage-mapping"
ldflags=" ${ldflags} -fprofile-instr-generate"
driver_name="${driver_name}-cov"
fi
if [[ "${build_san}" != "n" ]]; then
print_q "Choose the optimisation level:"
print_info "0" "compile with '-g -O0'"
print_info "1" "compile with '-g -O1'"
print_info "2" "compile with '-g -O2'"
print_info "3" "compile with '-g -O3'"
print_nl
build_debugging=$( ask_user "0|1|2|3" "1" )
print_sel "${build_debugging}" \
"0" "-g -O0" \
"1" "-g -O1" \
"2" "-g -O2" \
"3" "-g -O3"
else
print_q "Add debugging flags?"
print_info "0" "compile with '-g -O0'"
print_info "1" "compile with '-g -O1'"
print_info "2" "compile with '-g -O2'"
print_info "3" "compile with '-g -O3'"
print_info "n" "compile without debugging flags"
print_nl
build_debugging=$( ask_user "0|1|2|3|n" "1" )
print_sel "${build_debugging}" \
"0" "-g -O0" \
"1" "-g -O1" \
"2" "-g -O2" \
"3" "-g -O3" \
"n" "no"
fi
if [[ "${build_debugging}" != "n" ]]; then
cflags=" ${cflags} -g -O${build_debugging}"
cxxflags="${cxxflags} -g -O${build_debugging}"
driver_name="${driver_name}-o${build_debugging}"
fi
if [[ "${build_type}" == "f" ]]; then
build_glog="n"
else
print_q "Use Glog logger?"
print_url "https://github.com/google/glog"
print_info "no" "logging will be compiled out and glog will not be linked"
print_nl
build_glog=$( ask_user "y|n" "n" )
print_sel_yes_no "${build_glog}"
fi
if [[ "${build_glog}" == "y" ]]; then
export CMAKE_USE_LOGGER_GLOG=1
driver_name="${driver_name}-glog"
fi
if [[ "${build_type}" == "f" ]]; then
build_ft_trace="n"
else
print_q "Add FreeType tracing?"
print_info "yes" "activate 'FT_DEBUG_LEVEL_{TRACE,ERROR}' and 'FT_DEBUG_{MEMORY,LOGGING}'"
print_nl
build_ft_trace=$( ask_user "y|n" "n" )
print_sel_yes_no "${build_ft_trace}"
fi
if [[ "${build_ft_trace}" == "y" ]]; then
cflags=" ${cflags} -DFT_DEBUG_LEVEL_TRACE -DFT_DEBUG_LEVEL_ERROR -DFT_DEBUG_MEMORY -DFT_DEBUG_LOGGING -pthread"
cxxflags="${cxxflags} -DFT_DEBUG_LEVEL_TRACE -DFT_DEBUG_LEVEL_ERROR -DFT_DEBUG_MEMORY -DFT_DEBUG_LOGGING -pthread"
ldflags=" ${ldflags} -pthread"
driver_name="${driver_name}-fttrace"
fi
if ! command -v "ccache" >"/dev/null"; then
build_ccache="n"
else
print_q "Use ccache?"
print_info "ccache seems to be available on this system"
print_nl
build_ccache=$( ask_user "y|n" "y" )
print_sel_yes_no "${build_ccache}"
fi
if [[ "${build_ccache}" == "y" ]]; then
cc="ccache ${cc}"
cxx="ccache ${cxx}"
driver_name="${driver_name}-ccache"
fi
print_nl
# ----------------------------------------------------------------------------
# export flags and build everything:
export CC="${cc}"
export CXX="${cxx}"
export CFLAGS="${cflags}"
export CXXFLAGS="${cxxflags}"
export LDFLAGS="${ldflags}"
export SANITIZER="${llvm_sanitizer}"
export CMAKE_DRIVER_EXE_NAME="${driver_name}"
# Each project must be listed after any project it depends on.
bash "build/zlib.sh"
bash "build/bzip2.sh"
bash "build/libarchive.sh"
bash "build/brotli.sh"
bash "build/libpng.sh"
bash "build/freetype.sh"
LDFLAGS= bash "build/libcxx.sh"
if [[ "${build_glog}" == "y" ]]; then
#TODO: use the static libcxx
bash "build/glog.sh"
fi
bash "build/targets.sh"
cd "${dir}"