diff options
-rwxr-xr-x | lib/spack/env/cc | 563 | ||||
-rw-r--r-- | lib/spack/spack/test/cc.py | 163 |
2 files changed, 434 insertions, 292 deletions
diff --git a/lib/spack/env/cc b/lib/spack/env/cc index b603c3bc32..4bef0ddf9e 100755 --- a/lib/spack/env/cc +++ b/lib/spack/env/cc @@ -1,4 +1,5 @@ -#!/bin/bash +#!/bin/sh +# shellcheck disable=SC2034 # evals in this script fool shellcheck # # Copyright 2013-2021 Lawrence Livermore National Security, LLC and other # Spack Project Developers. See the top-level COPYRIGHT file for details. @@ -20,25 +21,33 @@ # -Wl,-rpath arguments for dependency /lib directories. # +# Reset IFS to the default: whitespace-separated lists. When we use +# other separators, we set and reset it. +unset IFS + +# Separator for lists whose names end with `_list`. +# We pick the alarm bell character, which is highly unlikely to +# conflict with anything. This is a literal bell character (which +# we have to use since POSIX sh does not convert escape sequences +# like '\a' outside of the format argument of `printf`). +# NOTE: Depending on your editor this may look empty, but it is not. +readonly lsep='' + # This is an array of environment variables that need to be set before # the script runs. They are set by routines in spack.build_environment # as part of the package installation process. -parameters=( - SPACK_ENV_PATH - SPACK_DEBUG_LOG_DIR - SPACK_DEBUG_LOG_ID - SPACK_COMPILER_SPEC - SPACK_CC_RPATH_ARG - SPACK_CXX_RPATH_ARG - SPACK_F77_RPATH_ARG - SPACK_FC_RPATH_ARG - SPACK_TARGET_ARGS - SPACK_DTAGS_TO_ADD - SPACK_DTAGS_TO_STRIP - SPACK_LINKER_ARG - SPACK_SHORT_SPEC - SPACK_SYSTEM_DIRS -) +readonly params="\ +SPACK_ENV_PATH +SPACK_DEBUG_LOG_DIR +SPACK_DEBUG_LOG_ID +SPACK_COMPILER_SPEC +SPACK_CC_RPATH_ARG +SPACK_CXX_RPATH_ARG +SPACK_F77_RPATH_ARG +SPACK_FC_RPATH_ARG +SPACK_LINKER_ARG +SPACK_SHORT_SPEC +SPACK_SYSTEM_DIRS" # Optional parameters that aren't required to be set @@ -58,60 +67,157 @@ parameters=( # Test command is used to unit test the compiler script. # SPACK_TEST_COMMAND -# die() -# Prints a message and exits with error 1. -function die { +# die MESSAGE +# Print a message and exit with error code 1. +die() { echo "$@" exit 1 } -# read input parameters into proper bash arrays. -# SYSTEM_DIRS is delimited by : -IFS=':' read -ra SPACK_SYSTEM_DIRS <<< "${SPACK_SYSTEM_DIRS}" +# empty VARNAME +# Return whether the variable VARNAME is unset or set to the empty string. +empty() { + eval "test -z \"\${$1}\"" +} -# SPACK_<LANG>FLAGS and SPACK_LDLIBS are split by ' ' -IFS=' ' read -ra SPACK_FFLAGS <<< "$SPACK_FFLAGS" -IFS=' ' read -ra SPACK_CPPFLAGS <<< "$SPACK_CPPFLAGS" -IFS=' ' read -ra SPACK_CFLAGS <<< "$SPACK_CFLAGS" -IFS=' ' read -ra SPACK_CXXFLAGS <<< "$SPACK_CXXFLAGS" -IFS=' ' read -ra SPACK_LDFLAGS <<< "$SPACK_LDFLAGS" -IFS=' ' read -ra SPACK_LDLIBS <<< "$SPACK_LDLIBS" +# setsep LISTNAME +# Set the global variable 'sep' to the separator for a list with name LISTNAME. +# There are three types of lists: +# 1. regular lists end with _list and are separated by $lsep +# 2. directory lists end with _dirs/_DIRS/PATH(S) and are separated by ':' +# 3. any other list is assumed to be separated by spaces: " " +setsep() { + case "$1" in + *_dirs|*_DIRS|*PATH|*PATHS) + sep=':' + ;; + *_list) + sep="$lsep" + ;; + *) + sep=" " + ;; + esac +} +# prepend LISTNAME ELEMENT [SEP] +# +# Prepend ELEMENT to the list stored in the variable LISTNAME, +# assuming the list is separated by SEP. +# Handles empty lists and single-element lists. +prepend() { + varname="$1" + elt="$2" + + if empty "$varname"; then + eval "$varname=\"\${elt}\"" + else + # Get the appropriate separator for the list we're appending to. + setsep "$varname" + eval "$varname=\"\${elt}${sep}\${$varname}\"" + fi +} + +# append LISTNAME ELEMENT [SEP] +# +# Append ELEMENT to the list stored in the variable LISTNAME, +# assuming the list is separated by SEP. +# Handles empty lists and single-element lists. +append() { + varname="$1" + elt="$2" + + if empty "$varname"; then + eval "$varname=\"\${elt}\"" + else + # Get the appropriate separator for the list we're appending to. + setsep "$varname" + eval "$varname=\"\${$varname}${sep}\${elt}\"" + fi +} + +# extend LISTNAME1 LISTNAME2 [PREFIX] +# +# Append the elements stored in the variable LISTNAME2 +# to the list stored in LISTNAME1. +# If PREFIX is provided, prepend it to each element. +extend() { + # Figure out the appropriate IFS for the list we're reading. + setsep "$2" + if [ "$sep" != " " ]; then + IFS="$sep" + fi + eval "for elt in \${$2}; do append $1 \"$3\${elt}\"; done" + unset IFS +} + +# preextend LISTNAME1 LISTNAME2 [PREFIX] +# +# Prepend the elements stored in the list at LISTNAME2 +# to the list at LISTNAME1, preserving order. +# If PREFIX is provided, prepend it to each element. +preextend() { + # Figure out the appropriate IFS for the list we're reading. + setsep "$2" + if [ "$sep" != " " ]; then + IFS="$sep" + fi + + # first, reverse the list to prepend + _reversed_list="" + eval "for elt in \${$2}; do prepend _reversed_list \"$3\${elt}\"; done" + + # prepend reversed list to preextend in order + IFS="${lsep}" + for elt in $_reversed_list; do prepend "$1" "$3${elt}"; done + unset IFS +} + +# system_dir PATH # test whether a path is a system directory -function system_dir { +system_dir() { + IFS=':' # SPACK_SYSTEM_DIRS is colon-separated path="$1" - for sd in "${SPACK_SYSTEM_DIRS[@]}"; do - if [ "${path}" == "${sd}" ] || [ "${path}" == "${sd}/" ]; then + for sd in $SPACK_SYSTEM_DIRS; do + if [ "${path}" = "${sd}" ] || [ "${path}" = "${sd}/" ]; then # success if path starts with a system prefix + unset IFS return 0 fi done + unset IFS return 1 # fail if path starts no system prefix } -for param in "${parameters[@]}"; do - if [[ -z ${!param+x} ]]; then +# Fail with a clear message if the input contains any bell characters. +if eval "[ \"\${*#*${lsep}}\" != \"\$*\" ]"; then + die "ERROR: Compiler command line contains our separator ('${lsep}'). Cannot parse." +fi + +# ensure required variables are set +for param in $params; do + if eval "test -z \"\${${param}:-}\""; then die "Spack compiler must be run from Spack! Input '$param' is missing." fi done # Check if optional parameters are defined # If we aren't asking for debug flags, don't add them -if [[ -z ${SPACK_ADD_DEBUG_FLAGS+x} ]]; then +if [ -z "${SPACK_ADD_DEBUG_FLAGS:-}" ]; then SPACK_ADD_DEBUG_FLAGS="false" fi # SPACK_ADD_DEBUG_FLAGS must be true/false/custom is_valid="false" for param in "true" "false" "custom"; do - if [ "$param" == "$SPACK_ADD_DEBUG_FLAGS" ]; then + if [ "$param" = "$SPACK_ADD_DEBUG_FLAGS" ]; then is_valid="true" fi done # Exit with error if we are given an incorrect value -if [ "$is_valid" == "false" ]; then - die "SPACK_ADD_DEBUG_FLAGS, if defined, must be one of 'true' 'false' or 'custom'" +if [ "$is_valid" = "false" ]; then + die "SPACK_ADD_DEBUG_FLAGS, if defined, must be one of 'true', 'false', or 'custom'." fi # Figure out the type of compiler, the language, and the mode so that @@ -174,7 +280,7 @@ esac # If any of the arguments below are present, then the mode is vcheck. # In vcheck mode, nothing is added in terms of extra search paths or # libraries. -if [[ -z $mode ]] || [[ $mode == ld ]]; then +if [ -z "$mode" ] || [ "$mode" = ld ]; then for arg in "$@"; do case $arg in -v|-V|--version|-dumpversion) @@ -186,16 +292,16 @@ if [[ -z $mode ]] || [[ $mode == ld ]]; then fi # Finish setting up the mode. -if [[ -z $mode ]]; then +if [ -z "$mode" ]; then mode=ccld for arg in "$@"; do - if [[ $arg == -E ]]; then + if [ "$arg" = "-E" ]; then mode=cpp break - elif [[ $arg == -S ]]; then + elif [ "$arg" = "-S" ]; then mode=as break - elif [[ $arg == -c ]]; then + elif [ "$arg" = "-c" ]; then mode=cc break fi @@ -222,17 +328,18 @@ dtags_to_strip="${SPACK_DTAGS_TO_STRIP}" linker_arg="${SPACK_LINKER_ARG}" # Set up rpath variable according to language. -eval rpath=\$SPACK_${comp}_RPATH_ARG +rpath="ERROR: RPATH ARG WAS NOT SET" +eval "rpath=\${SPACK_${comp}_RPATH_ARG:?${rpath}}" # Dump the mode and exit if the command is dump-mode. -if [[ $SPACK_TEST_COMMAND == dump-mode ]]; then +if [ "$SPACK_TEST_COMMAND" = "dump-mode" ]; then echo "$mode" exit fi # Check that at least one of the real commands was actually selected, # otherwise we don't know what to execute. -if [[ -z $command ]]; then +if [ -z "$command" ]; then die "ERROR: Compiler '$SPACK_COMPILER_SPEC' does not support compiling $language programs." fi @@ -240,24 +347,26 @@ fi # Filter '.' and Spack environment directories out of PATH so that # this script doesn't just call itself # -IFS=':' read -ra env_path <<< "$PATH" -IFS=':' read -ra spack_env_dirs <<< "$SPACK_ENV_PATH" -spack_env_dirs+=("" ".") -export PATH="" -for dir in "${env_path[@]}"; do +new_dirs="" +IFS=':' +for dir in $PATH; do addpath=true - for env_dir in "${spack_env_dirs[@]}"; do - if [[ "${dir%%/}" == "$env_dir" ]]; then - addpath=false - break - fi + for spack_env_dir in $SPACK_ENV_PATH; do + case "${dir%%/}" in + "$spack_env_dir"|'.'|'') + addpath=false + break + ;; + esac done - if $addpath; then - export PATH="${PATH:+$PATH:}$dir" + if [ $addpath = true ]; then + append new_dirs "$dir" fi done +unset IFS +export PATH="$new_dirs" -if [[ $mode == vcheck ]]; then +if [ "$mode" = vcheck ]; then exec "${command}" "$@" fi @@ -265,16 +374,20 @@ fi # It doesn't work with -rpath. # This variable controls whether they are added. add_rpaths=true -if [[ ($mode == ld || $mode == ccld) && "$SPACK_SHORT_SPEC" =~ "darwin" ]]; -then - for arg in "$@"; do - if [[ ($arg == -r && $mode == ld) || - ($arg == -r && $mode == ccld) || - ($arg == -Wl,-r && $mode == ccld) ]]; then - add_rpaths=false - break - fi - done +if [ "$mode" = ld ] || [ "$mode" = ccld ]; then + if [ "${SPACK_SHORT_SPEC#*darwin}" != "${SPACK_SHORT_SPEC}" ]; then + for arg in "$@"; do + if [ "$arg" = "-r" ]; then + if [ "$mode" = ld ] || [ "$mode" = ccld ]; then + add_rpaths=false + break + fi + elif [ "$arg" = "-Wl,-r" ] && [ "$mode" = ccld ]; then + add_rpaths=false + break + fi + done + fi fi # Save original command for debug logging @@ -297,17 +410,22 @@ input_command="$*" # The libs variable is initialized here for completeness, and it is also # used later to inject flags supplied via `ldlibs` on the command # line. These come into the wrappers via SPACK_LDLIBS. -# -includes=() -libdirs=() -rpaths=() -system_includes=() -system_libdirs=() -system_rpaths=() -libs=() -other_args=() -isystem_system_includes=() -isystem_includes=() + +# The loop below breaks up the command line into these lists of components. +# The lists are all bell-separated to be as flexible as possible, as their +# contents may come from the command line, from ' '-separated lists, +# ':'-separated lists, etc. +include_dirs_list="" +lib_dirs_list="" +rpath_dirs_list="" +system_include_dirs_list="" +system_lib_dirs_list="" +system_rpath_dirs_list="" +isystem_system_include_dirs_list="" +isystem_include_dirs_list="" +libs_list="" +other_args_list="" + while [ $# -ne 0 ]; do @@ -327,32 +445,32 @@ while [ $# -ne 0 ]; do isystem_was_used=true if [ -z "$arg" ]; then shift; arg="$1"; fi if system_dir "$arg"; then - isystem_system_includes+=("$arg") + append isystem_system_include_dirs_list "$arg" else - isystem_includes+=("$arg") + append isystem_include_dirs_list "$arg" fi ;; -I*) arg="${1#-I}" if [ -z "$arg" ]; then shift; arg="$1"; fi if system_dir "$arg"; then - system_includes+=("$arg") + append system_include_dirs_list "$arg" else - includes+=("$arg") + append include_dirs_list "$arg" fi ;; -L*) arg="${1#-L}" if [ -z "$arg" ]; then shift; arg="$1"; fi if system_dir "$arg"; then - system_libdirs+=("$arg") + append system_lib_dirs_list "$arg" else - libdirs+=("$arg") + append lib_dirs_list "$arg" fi ;; -l*) # -loopopt=0 is generated erroneously in autoconf <= 2.69, - # and passed by ifx to the linker, which confuses it with a + # and passed by ifx to the linker, which confuses it with a # library. Filter it out. # TODO: generalize filtering of args with an env var, so that # TODO: we do not have to special case this here. @@ -363,66 +481,76 @@ while [ $# -ne 0 ]; do fi arg="${1#-l}" if [ -z "$arg" ]; then shift; arg="$1"; fi - other_args+=("-l$arg") + append other_args_list "-l$arg" ;; -Wl,*) arg="${1#-Wl,}" if [ -z "$arg" ]; then shift; arg="$1"; fi - if [[ "$arg" = -rpath=* ]]; then - rp="${arg#-rpath=}" - elif [[ "$arg" = --rpath=* ]]; then - rp="${arg#--rpath=}" - elif [[ "$arg" = -rpath,* ]]; then - rp="${arg#-rpath,}" - elif [[ "$arg" = --rpath,* ]]; then - rp="${arg#--rpath,}" - elif [[ "$arg" =~ ^-?-rpath$ ]]; then - shift; arg="$1" - if [[ "$arg" != -Wl,* ]]; then - die "-Wl,-rpath was not followed by -Wl,*" - fi - rp="${arg#-Wl,}" - elif [[ "$arg" = "$dtags_to_strip" ]] ; then - : # We want to remove explicitly this flag - else - other_args+=("-Wl,$arg") - fi + case "$arg" in + -rpath=*) rp="${arg#-rpath=}" ;; + --rpath=*) rp="${arg#--rpath=}" ;; + -rpath,*) rp="${arg#-rpath,}" ;; + --rpath,*) rp="${arg#--rpath,}" ;; + -rpath|--rpath) + shift; arg="$1" + case "$arg" in + -Wl,*) + rp="${arg#-Wl,}" + ;; + *) + die "-Wl,-rpath was not followed by -Wl,*" + ;; + esac + ;; + "$dtags_to_strip") + : # We want to remove explicitly this flag + ;; + *) + append other_args_list "-Wl,$arg" + ;; + esac ;; -Xlinker,*) arg="${1#-Xlinker,}" if [ -z "$arg" ]; then shift; arg="$1"; fi - if [[ "$arg" = -rpath=* ]]; then - rp="${arg#-rpath=}" - elif [[ "$arg" = --rpath=* ]]; then - rp="${arg#--rpath=}" - elif [[ "$arg" = -rpath ]] || [[ "$arg" = --rpath ]]; then - shift; arg="$1" - if [[ "$arg" != -Xlinker,* ]]; then - die "-Xlinker,-rpath was not followed by -Xlinker,*" - fi - rp="${arg#-Xlinker,}" - else - other_args+=("-Xlinker,$arg") - fi + + case "$arg" in + -rpath=*) rp="${arg#-rpath=}" ;; + --rpath=*) rp="${arg#--rpath=}" ;; + -rpath|--rpath) + shift; arg="$1" + case "$arg" in + -Xlinker,*) + rp="${arg#-Xlinker,}" + ;; + *) + die "-Xlinker,-rpath was not followed by -Xlinker,*" + ;; + esac + ;; + *) + append other_args_list "-Xlinker,$arg" + ;; + esac ;; -Xlinker) - if [[ "$2" == "-rpath" ]]; then - if [[ "$3" != "-Xlinker" ]]; then + if [ "$2" = "-rpath" ]; then + if [ "$3" != "-Xlinker" ]; then die "-Xlinker,-rpath was not followed by -Xlinker,*" fi shift 3; rp="$1" - elif [[ "$2" = "$dtags_to_strip" ]] ; then + elif [ "$2" = "$dtags_to_strip" ]; then shift # We want to remove explicitly this flag else - other_args+=("$1") + append other_args_list "$1" fi ;; *) - if [[ "$1" = "$dtags_to_strip" ]] ; then + if [ "$1" = "$dtags_to_strip" ]; then : # We want to remove explicitly this flag else - other_args+=("$1") + append other_args_list "$1" fi ;; esac @@ -430,9 +558,9 @@ while [ $# -ne 0 ]; do # test rpaths against system directories in one place. if [ -n "$rp" ]; then if system_dir "$rp"; then - system_rpaths+=("$rp") + append system_rpath_dirs_list "$rp" else - rpaths+=("$rp") + append rpath_dirs_list "$rp" fi fi shift @@ -445,16 +573,15 @@ done # See the gmake manual on implicit rules for details: # https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html # -flags=() +flags_list="" # Add debug flags -if [ "${SPACK_ADD_DEBUG_FLAGS}" == "true" ]; then - flags=("${flags[@]}" "${debug_flags}") +if [ "${SPACK_ADD_DEBUG_FLAGS}" = "true" ]; then + extend flags_list debug_flags # If a custom flag is requested, derive from environment -elif [ "$SPACK_ADD_DEBUG_FLAGS" == "custom" ]; then - IFS=' ' read -ra SPACK_DEBUG_FLAGS <<< "$SPACK_DEBUG_FLAGS" - flags=("${flags[@]}" "${SPACK_DEBUG_FLAGS[@]}") +elif [ "$SPACK_ADD_DEBUG_FLAGS" = "custom" ]; then + extend flags_list SPACK_DEBUG_FLAGS fi # Fortran flags come before CPPFLAGS @@ -462,7 +589,8 @@ case "$mode" in cc|ccld) case $lang_flags in F) - flags=("${flags[@]}" "${SPACK_FFLAGS[@]}") ;; + extend flags_list SPACK_FFLAGS + ;; esac ;; esac @@ -470,7 +598,8 @@ esac # C preprocessor flags come before any C/CXX flags case "$mode" in cpp|as|cc|ccld) - flags=("${flags[@]}" "${SPACK_CPPFLAGS[@]}") ;; + extend flags_list SPACK_CPPFLAGS + ;; esac @@ -479,67 +608,67 @@ case "$mode" in cc|ccld) case $lang_flags in C) - flags=("${flags[@]}" "${SPACK_CFLAGS[@]}") ;; + extend flags_list SPACK_CFLAGS + ;; CXX) - flags=("${flags[@]}" "${SPACK_CXXFLAGS[@]}") ;; + extend flags_list SPACK_CXXFLAGS + ;; esac - flags=(${SPACK_TARGET_ARGS[@]} "${flags[@]}") + + # prepend target args + preextend flags_list SPACK_TARGET_ARGS ;; esac # Linker flags case "$mode" in ld|ccld) - flags=("${flags[@]}" "${SPACK_LDFLAGS[@]}") ;; + extend flags_list SPACK_LDFLAGS + ;; esac # On macOS insert headerpad_max_install_names linker flag -if [[ ($mode == ld || $mode == ccld) && "$SPACK_SHORT_SPEC" =~ "darwin" ]]; -then - case "$mode" in - ld) - flags=("${flags[@]}" -headerpad_max_install_names) ;; - ccld) - flags=("${flags[@]}" "-Wl,-headerpad_max_install_names") ;; - esac +if [ "$mode" = ld ] || [ "$mode" = ccld ]; then + if [ "${SPACK_SHORT_SPEC#*darwin}" != "${SPACK_SHORT_SPEC}" ]; then + case "$mode" in + ld) + append flags_list "-headerpad_max_install_names" ;; + ccld) + append flags_list "-Wl,-headerpad_max_install_names" ;; + esac + fi fi -IFS=':' read -ra rpath_dirs <<< "$SPACK_RPATH_DIRS" -if [[ $mode == ccld || $mode == ld ]]; then - - if [[ "$add_rpaths" != "false" ]] ; then +if [ "$mode" = ccld ] || [ "$mode" = ld ]; then + if [ "$add_rpaths" != "false" ]; then # Append RPATH directories. Note that in the case of the # top-level package these directories may not exist yet. For dependencies # it is assumed that paths have already been confirmed. - rpaths=("${rpaths[@]}" "${rpath_dirs[@]}") + extend rpath_dirs_list SPACK_RPATH_DIRS fi - fi -IFS=':' read -ra link_dirs <<< "$SPACK_LINK_DIRS" -if [[ $mode == ccld || $mode == ld ]]; then - libdirs=("${libdirs[@]}" "${link_dirs[@]}") +if [ "$mode" = ccld ] || [ "$mode" = ld ]; then + extend lib_dirs_list SPACK_LINK_DIRS fi # add RPATHs if we're in in any linking mode case "$mode" in ld|ccld) # Set extra RPATHs - IFS=':' read -ra extra_rpaths <<< "$SPACK_COMPILER_EXTRA_RPATHS" - libdirs+=("${extra_rpaths[@]}") - if [[ "$add_rpaths" != "false" ]] ; then - rpaths+=("${extra_rpaths[@]}") + extend lib_dirs_list SPACK_COMPILER_EXTRA_RPATHS + if [ "$add_rpaths" != "false" ]; then + extend rpath_dirs_list SPACK_COMPILER_EXTRA_RPATHS fi # Set implicit RPATHs - IFS=':' read -ra implicit_rpaths <<< "$SPACK_COMPILER_IMPLICIT_RPATHS" - if [[ "$add_rpaths" != "false" ]] ; then - rpaths+=("${implicit_rpaths[@]}") + if [ "$add_rpaths" != "false" ]; then + extend rpath_dirs_list SPACK_COMPILER_IMPLICIT_RPATHS fi # Add SPACK_LDLIBS to args - for lib in "${SPACK_LDLIBS[@]}"; do - libs+=("${lib#-l}") + for lib in $SPACK_LDLIBS; do + append libs_list "${lib#-l}" done ;; esac @@ -547,63 +676,62 @@ esac # # Finally, reassemble the command line. # - -# Includes and system includes first -args=() - -# flags assembled earlier -args+=("${flags[@]}") +args_list="$flags_list" # Insert include directories just prior to any system include directories +# NOTE: adding ${lsep} to the prefix here turns every added element into two +extend args_list include_dirs_list "-I" +extend args_list isystem_include_dirs_list "-isystem${lsep}" -for dir in "${includes[@]}"; do args+=("-I$dir"); done -for dir in "${isystem_includes[@]}"; do args+=("-isystem" "$dir"); done - -IFS=':' read -ra spack_include_dirs <<< "$SPACK_INCLUDE_DIRS" -if [[ $mode == cpp || $mode == cc || $mode == as || $mode == ccld ]]; then - if [[ "$isystem_was_used" == "true" ]] ; then - for dir in "${spack_include_dirs[@]}"; do args+=("-isystem" "$dir"); done - else - for dir in "${spack_include_dirs[@]}"; do args+=("-I$dir"); done - fi -fi +case "$mode" in + cpp|cc|as|ccld) + if [ "$isystem_was_used" = "true" ]; then + extend args_list SPACK_INCLUDE_DIRS "-isystem${lsep}" + else + extend args_list SPACK_INCLUDE_DIRS "-I" + fi + ;; +esac -for dir in "${system_includes[@]}"; do args+=("-I$dir"); done -for dir in "${isystem_system_includes[@]}"; do args+=("-isystem" "$dir"); done +extend args_list system_include_dirs_list -I +extend args_list isystem_system_include_dirs_list "-isystem${lsep}" # Library search paths -for dir in "${libdirs[@]}"; do args+=("-L$dir"); done -for dir in "${system_libdirs[@]}"; do args+=("-L$dir"); done +extend args_list lib_dirs_list "-L" +extend args_list system_lib_dirs_list "-L" # RPATHs arguments case "$mode" in ccld) - if [ -n "$dtags_to_add" ] ; then args+=("$linker_arg$dtags_to_add") ; fi - for dir in "${rpaths[@]}"; do args+=("$rpath$dir"); done - for dir in "${system_rpaths[@]}"; do args+=("$rpath$dir"); done + if [ -n "$dtags_to_add" ] ; then + append args_list "$linker_arg$dtags_to_add" + fi + extend args_list rpath_dirs_list "$rpath" + extend args_list system_rpath_dirs_list "$rpath" ;; ld) - if [ -n "$dtags_to_add" ] ; then args+=("$dtags_to_add") ; fi - for dir in "${rpaths[@]}"; do args+=("-rpath" "$dir"); done - for dir in "${system_rpaths[@]}"; do args+=("-rpath" "$dir"); done + if [ -n "$dtags_to_add" ] ; then + append args_list "$dtags_to_add" + fi + extend args_list rpath_dirs_list "-rpath${lsep}" + extend args_list system_rpath_dirs_list "-rpath${lsep}" ;; esac # Other arguments from the input command -args+=("${other_args[@]}") +extend args_list other_args_list # Inject SPACK_LDLIBS, if supplied -for lib in "${libs[@]}"; do - args+=("-l$lib"); -done +extend args_list libs_list "-l" -full_command=("$command" "${args[@]}") +full_command_list="$command" +extend full_command_list args_list # prepend the ccache binary if we're using ccache if [ -n "$SPACK_CCACHE_BINARY" ]; then case "$lang_flags" in C|CXX) # ccache only supports C languages - full_command=("${SPACK_CCACHE_BINARY}" "${full_command[@]}") + prepend full_command_list "${SPACK_CCACHE_BINARY}" # workaround for stage being a temp folder # see #3761#issuecomment-294352232 export CCACHE_NOHASHDIR=yes @@ -612,25 +740,36 @@ if [ -n "$SPACK_CCACHE_BINARY" ]; then fi # dump the full command if the caller supplies SPACK_TEST_COMMAND=dump-args -if [[ $SPACK_TEST_COMMAND == dump-args ]]; then - IFS=" -" && echo "${full_command[*]}" - exit -elif [[ $SPACK_TEST_COMMAND =~ dump-env-* ]]; then - var=${SPACK_TEST_COMMAND#dump-env-} - echo "$0: $var: ${!var}" -elif [[ -n $SPACK_TEST_COMMAND ]]; then - die "ERROR: Unknown test command" +if [ -n "${SPACK_TEST_COMMAND=}" ]; then + case "$SPACK_TEST_COMMAND" in + dump-args) + IFS="$lsep" + for arg in $full_command_list; do + echo "$arg" + done + unset IFS + exit + ;; + dump-env-*) + var=${SPACK_TEST_COMMAND#dump-env-} + eval "printf '%s\n' \"\$0: \$var: \$$var\"" + ;; + *) + die "ERROR: Unknown test command: '$SPACK_TEST_COMMAND'" + ;; + esac fi # # Write the input and output commands to debug logs if it's asked for. # -if [[ $SPACK_DEBUG == TRUE ]]; then +if [ "$SPACK_DEBUG" = TRUE ]; then input_log="$SPACK_DEBUG_LOG_DIR/spack-cc-$SPACK_DEBUG_LOG_ID.in.log" output_log="$SPACK_DEBUG_LOG_DIR/spack-cc-$SPACK_DEBUG_LOG_ID.out.log" echo "[$mode] $command $input_command" >> "$input_log" - echo "[$mode] ${full_command[*]}" >> "$output_log" + echo "[$mode] ${full_command_list}" >> "$output_log" fi -exec "${full_command[@]}" +# Execute the full command, preserving spaces with IFS set +# to the alarm bell separator. +IFS="$lsep"; exec $full_command_list diff --git a/lib/spack/spack/test/cc.py b/lib/spack/spack/test/cc.py index f202dcd62d..5381782480 100644 --- a/lib/spack/spack/test/cc.py +++ b/lib/spack/spack/test/cc.py @@ -13,13 +13,13 @@ import pytest from spack.paths import build_env_path from spack.util.environment import set_env, system_dirs -from spack.util.executable import Executable +from spack.util.executable import Executable, ProcessError # # Complicated compiler test command # test_args = [ - '-I/test/include', '-L/test/lib', '-L/other/lib', '-I/other/include', + '-I/test/include', '-L/test/lib', '-L/with space/lib', '-I/other/include', 'arg1', '-Wl,--start-group', 'arg2', @@ -31,7 +31,9 @@ test_args = [ '-Xlinker', '-rpath', '-Xlinker', '/fourth/rpath', '-Wl,--rpath,/fifth/rpath', '-Wl,--rpath', '-Wl,/sixth/rpath', '-llib3', '-llib4', - 'arg5', 'arg6'] + 'arg5', 'arg6', + '"-DDOUBLE_QUOTED_ARG"', "'-DSINGLE_QUOTED_ARG'", +] # # Pieces of the test command above, as they should be parsed out. @@ -43,7 +45,7 @@ test_include_paths = [ '-I/test/include', '-I/other/include'] test_library_paths = [ - '-L/test/lib', '-L/other/lib'] + '-L/test/lib', '-L/with space/lib'] test_wl_rpaths = [ '-Wl,-rpath,/first/rpath', '-Wl,-rpath,/second/rpath', @@ -60,7 +62,9 @@ test_args_without_paths = [ '-Wl,--start-group', 'arg2', 'arg3', '-llib1', '-llib2', 'arg4', '-Wl,--end-group', - '-llib3', '-llib4', 'arg5', 'arg6'] + '-llib3', '-llib4', 'arg5', 'arg6', + '"-DDOUBLE_QUOTED_ARG"', "'-DSINGLE_QUOTED_ARG'", +] #: The prefix of the package being mock installed pkg_prefix = '/spack-test-prefix' @@ -86,6 +90,17 @@ spack_ldlibs = ['-lfoo'] lheaderpad = ['-Wl,-headerpad_max_install_names'] headerpad = ['-headerpad_max_install_names'] +target_args = ["-march=znver2", "-mtune=znver2"] + +# common compile arguments: includes, libs, -Wl linker args, other args +common_compile_args = ( + test_include_paths + + test_library_paths + + ['-Wl,--disable-new-dtags'] + + test_wl_rpaths + + test_args_without_paths +) + @pytest.fixture(scope='session') def wrapper_environment(): @@ -107,7 +122,7 @@ def wrapper_environment(): SPACK_LINK_DIRS=None, SPACK_INCLUDE_DIRS=None, SPACK_RPATH_DIRS=None, - SPACK_TARGET_ARGS='', + SPACK_TARGET_ARGS="-march=znver2 -mtune=znver2", SPACK_LINKER_ARG='-Wl,', SPACK_DTAGS_TO_ADD='--disable-new-dtags', SPACK_DTAGS_TO_STRIP='--enable-new-dtags'): @@ -126,9 +141,6 @@ def wrapper_flags(): yield -pytestmark = pytest.mark.usefixtures('wrapper_environment') - - def check_args(cc, args, expected): """Check output arguments that cc produces when called with args. @@ -149,7 +161,7 @@ def check_env_var(executable, var, expected): """ with set_env(SPACK_TEST_COMMAND='dump-env-' + var): output = executable(*test_args, output=str).strip() - assert output == executable.path + ': ' + var + ': ' + expected + assert executable.path + ': ' + var + ': ' + expected == output def dump_mode(cc, args): @@ -158,7 +170,13 @@ def dump_mode(cc, args): return cc(*args, output=str).strip() -def test_vcheck_mode(): +def test_no_wrapper_environment(): + with pytest.raises(ProcessError): + output = cc(output=str) + assert "Spack compiler must be run from Spack" in output + + +def test_vcheck_mode(wrapper_environment): assert dump_mode(cc, ['-I/include', '--version']) == 'vcheck' assert dump_mode(cc, ['-I/include', '-V']) == 'vcheck' assert dump_mode(cc, ['-I/include', '-v']) == 'vcheck' @@ -167,17 +185,17 @@ def test_vcheck_mode(): assert dump_mode(cc, ['-I/include', '-V', '-o', 'output']) == 'vcheck' -def test_cpp_mode(): +def test_cpp_mode(wrapper_environment): assert dump_mode(cc, ['-E']) == 'cpp' assert dump_mode(cxx, ['-E']) == 'cpp' assert dump_mode(cpp, []) == 'cpp' -def test_as_mode(): +def test_as_mode(wrapper_environment): assert dump_mode(cc, ['-S']) == 'as' -def test_ccld_mode(): +def test_ccld_mode(wrapper_environment): assert dump_mode(cc, []) == 'ccld' assert dump_mode(cc, ['foo.c', '-o', 'foo']) == 'ccld' assert dump_mode(cc, ['foo.c', '-o', 'foo', '-Wl,-rpath,foo']) == 'ccld' @@ -185,13 +203,13 @@ def test_ccld_mode(): 'foo.o', 'bar.o', 'baz.o', '-o', 'foo', '-Wl,-rpath,foo']) == 'ccld' -def test_ld_mode(): +def test_ld_mode(wrapper_environment): assert dump_mode(ld, []) == 'ld' assert dump_mode(ld, [ 'foo.o', 'bar.o', 'baz.o', '-o', 'foo', '-Wl,-rpath,foo']) == 'ld' -def test_ld_flags(wrapper_flags): +def test_ld_flags(wrapper_environment, wrapper_flags): check_args( ld, test_args, ['ld'] + @@ -204,7 +222,7 @@ def test_ld_flags(wrapper_flags): spack_ldlibs) -def test_cpp_flags(wrapper_flags): +def test_cpp_flags(wrapper_environment, wrapper_flags): check_args( cpp, test_args, ['cpp'] + @@ -214,69 +232,58 @@ def test_cpp_flags(wrapper_flags): test_args_without_paths) -def test_cc_flags(wrapper_flags): +def test_cc_flags(wrapper_environment, wrapper_flags): check_args( cc, test_args, [real_cc] + + target_args + spack_cppflags + spack_cflags + spack_ldflags + - test_include_paths + - test_library_paths + - ['-Wl,--disable-new-dtags'] + - test_wl_rpaths + - test_args_without_paths + + common_compile_args + spack_ldlibs) -def test_cxx_flags(wrapper_flags): +def test_cxx_flags(wrapper_environment, wrapper_flags): check_args( cxx, test_args, [real_cc] + + target_args + spack_cppflags + spack_cxxflags + spack_ldflags + - test_include_paths + - test_library_paths + - ['-Wl,--disable-new-dtags'] + - test_wl_rpaths + - test_args_without_paths + + common_compile_args + spack_ldlibs) -def test_fc_flags(wrapper_flags): +def test_fc_flags(wrapper_environment, wrapper_flags): check_args( fc, test_args, [real_cc] + + target_args + spack_fflags + spack_cppflags + spack_ldflags + - test_include_paths + - test_library_paths + - ['-Wl,--disable-new-dtags'] + - test_wl_rpaths + - test_args_without_paths + + common_compile_args + spack_ldlibs) -def test_dep_rpath(): +def test_dep_rpath(wrapper_environment): """Ensure RPATHs for root package are added.""" check_args( cc, test_args, [real_cc] + - test_include_paths + - test_library_paths + - ['-Wl,--disable-new-dtags'] + - test_wl_rpaths + - test_args_without_paths) + target_args + + common_compile_args) -def test_dep_include(): +def test_dep_include(wrapper_environment): """Ensure a single dependency include directory is added.""" with set_env(SPACK_INCLUDE_DIRS='x'): check_args( cc, test_args, [real_cc] + + target_args + test_include_paths + ['-Ix'] + test_library_paths + @@ -285,7 +292,7 @@ def test_dep_include(): test_args_without_paths) -def test_system_path_cleanup(): +def test_system_path_cleanup(wrapper_environment): """Ensure SPACK_ENV_PATH is removed from PATH, even with trailing / The compiler wrapper has to ensure that it is not called nested @@ -305,13 +312,14 @@ def test_system_path_cleanup(): check_env_var(cc, 'PATH', system_path) -def test_dep_lib(): +def test_dep_lib(wrapper_environment): """Ensure a single dependency RPATH is added.""" with set_env(SPACK_LINK_DIRS='x', SPACK_RPATH_DIRS='x'): check_args( cc, test_args, [real_cc] + + target_args + test_include_paths + test_library_paths + ['-Lx'] + @@ -321,12 +329,13 @@ def test_dep_lib(): test_args_without_paths) -def test_dep_lib_no_rpath(): +def test_dep_lib_no_rpath(wrapper_environment): """Ensure a single dependency link flag is added with no dep RPATH.""" with set_env(SPACK_LINK_DIRS='x'): check_args( cc, test_args, [real_cc] + + target_args + test_include_paths + test_library_paths + ['-Lx'] + @@ -335,12 +344,13 @@ def test_dep_lib_no_rpath(): test_args_without_paths) -def test_dep_lib_no_lib(): +def test_dep_lib_no_lib(wrapper_environment): """Ensure a single dependency RPATH is added with no -L.""" with set_env(SPACK_RPATH_DIRS='x'): check_args( cc, test_args, [real_cc] + + target_args + test_include_paths + test_library_paths + ['-Wl,--disable-new-dtags'] + @@ -349,7 +359,7 @@ def test_dep_lib_no_lib(): test_args_without_paths) -def test_ccld_deps(): +def test_ccld_deps(wrapper_environment): """Ensure all flags are added in ccld mode.""" with set_env(SPACK_INCLUDE_DIRS='xinc:yinc:zinc', SPACK_RPATH_DIRS='xlib:ylib:zlib', @@ -357,6 +367,7 @@ def test_ccld_deps(): check_args( cc, test_args, [real_cc] + + target_args + test_include_paths + ['-Ixinc', '-Iyinc', @@ -373,7 +384,7 @@ def test_ccld_deps(): test_args_without_paths) -def test_ccld_deps_isystem(): +def test_ccld_deps_isystem(wrapper_environment): """Ensure all flags are added in ccld mode. When a build uses -isystem, Spack should inject it's include paths using -isystem. Spack will insert these @@ -386,6 +397,7 @@ def test_ccld_deps_isystem(): check_args( cc, mytest_args, [real_cc] + + target_args + test_include_paths + ['-isystem', 'fooinc', '-isystem', 'xinc', @@ -403,7 +415,7 @@ def test_ccld_deps_isystem(): test_args_without_paths) -def test_cc_deps(): +def test_cc_deps(wrapper_environment): """Ensure -L and RPATHs are not added in cc mode.""" with set_env(SPACK_INCLUDE_DIRS='xinc:yinc:zinc', SPACK_RPATH_DIRS='xlib:ylib:zlib', @@ -411,6 +423,7 @@ def test_cc_deps(): check_args( cc, ['-c'] + test_args, [real_cc] + + target_args + test_include_paths + ['-Ixinc', '-Iyinc', @@ -420,7 +433,7 @@ def test_cc_deps(): test_args_without_paths) -def test_ccld_with_system_dirs(): +def test_ccld_with_system_dirs(wrapper_environment): """Ensure all flags are added in ccld mode.""" with set_env(SPACK_INCLUDE_DIRS='xinc:yinc:zinc', SPACK_RPATH_DIRS='xlib:ylib:zlib', @@ -434,6 +447,7 @@ def test_ccld_with_system_dirs(): check_args( cc, sys_path_args + test_args, [real_cc] + + target_args + test_include_paths + ['-Ixinc', '-Iyinc', @@ -455,7 +469,7 @@ def test_ccld_with_system_dirs(): test_args_without_paths) -def test_ccld_with_system_dirs_isystem(): +def test_ccld_with_system_dirs_isystem(wrapper_environment): """Ensure all flags are added in ccld mode. Ensure that includes are in the proper place when a build uses -isystem, and uses @@ -472,6 +486,7 @@ def test_ccld_with_system_dirs_isystem(): check_args( cc, sys_path_args + test_args, [real_cc] + + target_args + test_include_paths + ['-isystem', 'xinc', '-isystem', 'yinc', @@ -493,7 +508,7 @@ def test_ccld_with_system_dirs_isystem(): test_args_without_paths) -def test_ld_deps(): +def test_ld_deps(wrapper_environment): """Ensure no (extra) -I args or -Wl, are passed in ld mode.""" with set_env(SPACK_INCLUDE_DIRS='xinc:yinc:zinc', SPACK_RPATH_DIRS='xlib:ylib:zlib', @@ -514,7 +529,7 @@ def test_ld_deps(): test_args_without_paths) -def test_ld_deps_no_rpath(): +def test_ld_deps_no_rpath(wrapper_environment): """Ensure SPACK_LINK_DEPS controls -L for ld.""" with set_env(SPACK_INCLUDE_DIRS='xinc:yinc:zinc', SPACK_LINK_DIRS='xlib:ylib:zlib'): @@ -531,7 +546,7 @@ def test_ld_deps_no_rpath(): test_args_without_paths) -def test_ld_deps_no_link(): +def test_ld_deps_no_link(wrapper_environment): """Ensure SPACK_RPATH_DEPS controls -rpath for ld.""" with set_env(SPACK_INCLUDE_DIRS='xinc:yinc:zinc', SPACK_RPATH_DIRS='xlib:ylib:zlib'): @@ -548,7 +563,7 @@ def test_ld_deps_no_link(): test_args_without_paths) -def test_ld_deps_partial(): +def test_ld_deps_partial(wrapper_environment): """Make sure ld -r (partial link) is handled correctly on OS's where it doesn't accept rpaths. """ @@ -586,57 +601,45 @@ def test_ld_deps_partial(): test_args_without_paths) -def test_ccache_prepend_for_cc(): +def test_ccache_prepend_for_cc(wrapper_environment): with set_env(SPACK_CCACHE_BINARY='ccache'): os.environ['SPACK_SHORT_SPEC'] = "foo@1.2=linux-x86_64" check_args( cc, test_args, ['ccache'] + # ccache prepended in cc mode [real_cc] + - test_include_paths + - test_library_paths + - ['-Wl,--disable-new-dtags'] + - test_wl_rpaths + - test_args_without_paths) + target_args + + common_compile_args) os.environ['SPACK_SHORT_SPEC'] = "foo@1.2=darwin-x86_64" check_args( cc, test_args, ['ccache'] + # ccache prepended in cc mode [real_cc] + + target_args + lheaderpad + - test_include_paths + - test_library_paths + - ['-Wl,--disable-new-dtags'] + - test_wl_rpaths + - test_args_without_paths) + common_compile_args) -def test_no_ccache_prepend_for_fc(): +def test_no_ccache_prepend_for_fc(wrapper_environment): os.environ['SPACK_SHORT_SPEC'] = "foo@1.2=linux-x86_64" check_args( fc, test_args, # no ccache for Fortran [real_cc] + - test_include_paths + - test_library_paths + - ['-Wl,--disable-new-dtags'] + - test_wl_rpaths + - test_args_without_paths) + target_args + + common_compile_args) os.environ['SPACK_SHORT_SPEC'] = "foo@1.2=darwin-x86_64" check_args( fc, test_args, # no ccache for Fortran [real_cc] + + target_args + lheaderpad + - test_include_paths + - test_library_paths + - ['-Wl,--disable-new-dtags'] + - test_wl_rpaths + - test_args_without_paths) + common_compile_args) @pytest.mark.regression('9160') -def test_disable_new_dtags(wrapper_flags): +def test_disable_new_dtags(wrapper_environment, wrapper_flags): with set_env(SPACK_TEST_COMMAND='dump-args'): result = ld(*test_args, output=str).strip().split('\n') assert '--disable-new-dtags' in result @@ -645,7 +648,7 @@ def test_disable_new_dtags(wrapper_flags): @pytest.mark.regression('9160') -def test_filter_enable_new_dtags(wrapper_flags): +def test_filter_enable_new_dtags(wrapper_environment, wrapper_flags): with set_env(SPACK_TEST_COMMAND='dump-args'): result = ld(*(test_args + ['--enable-new-dtags']), output=str) result = result.strip().split('\n') @@ -657,7 +660,7 @@ def test_filter_enable_new_dtags(wrapper_flags): @pytest.mark.regression('22643') -def test_linker_strips_loopopt(wrapper_flags): +def test_linker_strips_loopopt(wrapper_environment, wrapper_flags): with set_env(SPACK_TEST_COMMAND='dump-args'): # ensure that -loopopt=0 is not present in ld mode result = ld(*(test_args + ["-loopopt=0"]), output=str) |