summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xlib/spack/env/cc563
-rw-r--r--lib/spack/spack/test/cc.py163
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)