diff options
Diffstat (limited to 'wrappers/shadow-cc.sh')
-rwxr-xr-x | wrappers/shadow-cc.sh | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/wrappers/shadow-cc.sh b/wrappers/shadow-cc.sh new file mode 100755 index 00000000..586c9d52 --- /dev/null +++ b/wrappers/shadow-cc.sh @@ -0,0 +1,345 @@ +#!/bin/bash + +set -euf -o pipefail + +top="#TOP#" +shadow_top="#SHADOW_TOP#" + +orig_cc="#ORIG_TOOL#" +shadow_cc="#SHADOW_TOOL#" + +errors=() +warnings=() +skips=() + +mode=() +target=() +opt_level=() +lang=() +stdin_input=() + +orig_out=() +shadow_out=() +orig_input=() +shadow_input=() +orig_opts=() +shadow_opts=() + +dump_log () +{ + ( + set +u + IFS=";" + cat <<EOF +errors=(${errors[@]}) +warnings=(${warnings[@]}) +skips=(${skips[@]}) +mode=(${mode[@]}) +target=(${target[@]}) +opt_level=(${opt_level[@]}) +lang=(${lang[@]}) +stdin_input=(${stdin_input[@]}) +orig_out=(${orig_out[@]}) +shadow_out=(${shadow_out[@]}) +orig_input=(${orig_input[@]}) +shadow_input=(${shadow_input[@]}) +orig_opts=($0 ${orig_opts[@]}) +shadow_opts=(${shadow_opts[@]}) + +EOF + ) +} + +report_errors () { + res=$? + if [ $res != 0 ]; then + errors+=("wrapper failed with $res") + fi + if [ ${#errors[@]} != 0 ] \ + || [ ${#warnings[@]} != 0 ] \ + || [ ${#skips[@]} != 0 ]; then + ( + /usr/bin/flock -e 9 + + if [ ${#errors[@]} != 0 ]; then + log=$shadow_top.errors + elif [ ${#warnings[@]} != 0 ]; then + log=$shadow_top.warnings + else + log=$shadow_top.skips + fi + dump_log >>$log + ) 9>>$shadow_top.lock + fi + + # Log code-size + if [ ${#skips[@]} = 0 ] && [ -x "${orig_out[*]}" ]; then + if [ -x "${shadow_out[*]}" ]; then + text=$(/usr/bin/size "${shadow_out[*]}" | tail -n1 | awk '{ print $1 }') + else + text="-1" + fi + ( + /usr/bin/flock -e 9 + echo "${orig_out[*]},binary,$text" >> $shadow_top.size + ) 9>>$shadow_top.size + fi +} + +trap report_errors EXIT + +while [ "$#" -gt 0 ]; do + opt="$1" + shift + + orig_opts+=("$opt") + + case "$opt" in + "-c"|"-S"|"-E") mode+=("$opt") ;; + "-o") + if [ "$#" -gt 0 ]; then + orig_out=("$1" "${orig_out[@]}") + orig_opts+=("$1") + shift + else + errors+=("$opt with no argument") + fi + ;; + "-O"*) + opt_level+=("$opt") + ;; + "-target") + if [ "$#" -gt 0 ]; then + target=("$1" "${target[@]}") + orig_opts+=("$1") + shift + else + errors+=("$opt with no argument") + fi + ;; + "-x") + if [ "$#" -gt 0 ]; then + lang+=("$1") + orig_opts+=("$1") + shift + else + errors+=("$opt with no argument") + fi + ;; + "-isystem"|"--sysroot"|"-MF"|"-MQ"|"-I"|"-include"|"-z"|"-D"|"-iquote") + if [ "$#" -gt 0 ]; then + orig_opts+=("$1") + shift + else + errors+=("$opt with no argument") + fi + ;; + "-") + orig_input+=("$opt") + stdin_input+=("$opt") + ;; + "-"*) + ;; + *) + orig_input+=("$opt") + ;; + esac +done + +if [ ${#mode[@]} = 0 ]; then + # Link mode + mode=("-L") +elif [ ${#mode[@]} != 1 ]; then + warnings+=("Several modes: ${mode[*]}") +fi + +if [ ${#stdin_input[@]} = 1 ]; then + skips+=("Stdin input") + if [ x"${orig_input[*]}" != x"${stdin_input[*]}" ]; then + errors+=("Extra inputs in presence of stdin: ${orig_input[*]}") + fi + if [ x"${mode[*]}" != x"-E" ]; then + warnings+=("Stdin input with mode: ${mode[*]}") + fi +elif [ ${#stdin_input[@]} -gt 1 ]; then + errors+=("Several stdin inputs") +fi + +if [ ${#lang[@]} -gt 1 ]; then + warnings+=("Several -x LANG options: ${lang[*]}") +fi + +if [ ${#opt_level[@]} -gt 1 ]; then + warnings+=("Several opt_levels: ${opt_level[*]}") +fi + +if [ x"${mode[*]}" != x"-L" ]; then + if [ ${#orig_input[@]} != 1 ]; then + errors+=("Several inputs with mode ${mode[*]}: ${orig_input[*]}") + fi +fi + +if [ ${#orig_out[@]} = 0 ]; then + skips+=("No orig_out") +elif [ ${#orig_out[@]} != 1 ]; then + errors+=("Several orig_outs: ${orig_out[*]}") +fi + +if [ "${#target[@]}" -gt 1 ]; then + errors+=("Several targets: ${target[*]}") +fi + +if ! echo "${target[*]}" | grep -e "^aarch64" >/dev/null; then + skips+=("Skipping target: ${target[*]}") +fi + +if [ ${#errors[@]} = 0 ] && [ ${#skips[@]} != 0 ] \ + && [ ${#orig_out[@]} = 0 ] && [ ${#stdin_input[@]} != 0 ]; then + # Bionic has several cases where orig_cc [expectedly?] fails. + # Ignore these. + report_errors + exec "$orig_cc" "${orig_opts[@]}" +fi + +print_shadow_path () +{ + local path="$1" + local prefix="${2-}" + + case "$path" in + "/dev/null") echo "$path"; return ;; + "/"*) errors+=("Absolute path: $path") ;; + esac + + # Check that we are under $top + case "$(pwd -P | sed -e "s#^$top##")" in + "/"*) errors+=("Running outside of $top") ;; + esac + + echo "$(pwd -P)/$path" | sed -e "s#$top#$shadow_top${prefix}#" +} + +generate_shadow_rsp () +{ + local orig_rsp="$1" + + local shadow_rsp orig_file shadow_file + + shadow_rsp=$(print_shadow_path "$orig_rsp") + mkdir -p "$(dirname "$shadow_rsp")" + rm -f "$shadow_rsp" + + if [ "$(stat --format=%s "$orig_rsp")" = 0 ]; then + touch "$shadow_rsp" + else + local quote delim="" + while read -d '' -r orig_file; do + # unquote orig + case "$orig_file" in + \'*\') quote="'"; orig_file=${orig_file:1:-1} ;; + *) quote="" ;; + esac + shadow_file=$(print_shadow_path "$orig_file") + if ! [ -e "$shadow_file" ]; then + shadow_file="$orig_file" + fi + # quote shadow + echo -ne "$delim$quote$shadow_file$quote" >> "$shadow_rsp" + delim=" " + done < <({ cat "$orig_rsp"; echo; } | xargs printf '%s\0') + fi + + echo "$shadow_rsp" +} + +make_shadow_opts () +{ + local opt + local shadow_file + + while [ "$#" -gt 0 ]; do + opt="$1" + shift + + case "$opt" in + "-o") + shadow_opts+=("$opt") + opt=$(print_shadow_path "$1") + shadow_out+=("$opt") + shift + ;; + "-fuse-ld=lld") + opt="-fuse-ld=$(dirname "$shadow_cc")/ld.lld" + ;; + "-target"|"-x"|"-isystem"|"--sysroot"|"-MF"|"-MQ"|"-I"|"-include"|"-z"|"-D"|"-iquote") + shadow_opts+=("$opt") + opt="$1" + shift + ;; + "-Werror"*) continue ;; + "-") + errors+=("Stdin input should have been skipped") + ;; + "-"*) ;; + "@"*".rsp") + opt="${opt#@}" + if [ x"$(basename "$opt" .rsp)" = x"$(basename "${orig_out[*]}")" ]; then + opt=$(generate_shadow_rsp "$opt") + else + errors+=("RSP file $opt does not match orig_out ${orig_out[*]}") + fi + opt="@$opt" + ;; + "@"*) + errors+=("Unexpected option: $opt") + opt="${opt#@}" + # shellcheck disable=SC2046 + make_shadow_opts $(cat "$opt") + continue + ;; + *) + shadow_file=$(print_shadow_path "$opt") + if [ -e "$shadow_file" ]; then + opt="$shadow_file" + shadow_input+=("$shadow_file") + fi + ;; + esac + shadow_opts+=("$opt") + done +} + +if [ ${#errors[@]} = 0 ] && [ ${#skips[@]} = 0 ]; then + make_shadow_opts "${orig_opts[@]}" +fi + +if [ ${#errors[@]} = 0 ] && [ ${#skips[@]} = 0 ]; then + shadow_log=$(print_shadow_path "${orig_out[*]}" "-logs") + + mkdir -p "$(dirname "${shadow_out[*]}")" + mkdir -p "$(dirname "$shadow_log")" + + dump_log > "$shadow_log" + echo "$shadow_cc" "${shadow_opts[@]}" >> "$shadow_log" + "$shadow_cc" "${shadow_opts[@]}" >> "$shadow_log" 2>&1 & + shadow_res=0 && wait $! || shadow_res=$? + + if [ $shadow_res != 0 ]; then + errors+=("shadow_cc failed with $shadow_res") + elif ! [ -e "${shadow_out[*]}" ]; then + errors+=("shadow_cc failed to generate output") + elif [ x"${shadow_out[*]}" != x"/dev/null" ]; then + shadow_link=$(IFS=""; print_shadow_path "${orig_out[*]}" "-${target[*]-default}${mode[*]}") + mkdir -p "$(dirname "$shadow_link")" + ln -s -f "${shadow_out[*]}" "$shadow_link" + fi +fi + +#CCACHE# "$orig_cc" "${orig_opts[@]}" & +orig_res=0 && wait $! || orig_res=$? + +if [ $orig_res != 0 ]; then + errors+=("orig_cc failed with $orig_res") +fi + +exit $orig_res |