 7dc485d288
			
		
	
	7dc485d288
	
	
	
		
			
			Spack flags supplied by users should supersede flags from package build systems and other places in Spack. However, Spack currently adds user-supplied flags to the beginning of the compile line, which means that in some cases build system flags will supersede user-supplied ones. The right place to add a flag to ensure it has highest precedence for the compiler really depends on the type of flag. For example, search paths like `-L` and `-I` are examined in order, so adding them first is highest precedence. Compilers take the *last* occurrence of optimization flags like `-O2`, so those should be placed *after* other such flags. Shim libraries with `-l` should go *before* other libraries on the command line, so we want user-supplied libs to go first, etc. `lib/spack/env/cc` already knows how to split arguments into categories like `libs_list`, `rpath_dirs_list`, etc., so we can leverage that functionality to merge user flags into the arg list correctly. The general rules for injected flags are: 1. All `-L`, `-I`, `-isystem`, `-l`, and `*-rpath` flags from `spack_flags_*` to appear before their regular counterparts. 2. All other flags ordered with the ones from flags after their regular counterparts, i.e. `other_flags` before `spack_flags_other_flags` - [x] Generalize argument categorization into its own function in the `cc` shell script - [x] Apply the same splitting logic to injected flags and flags from the original compile line. - [x] Use the resulting flag lists to merge user- and build-system-supplied flags by category. - [x] Add tests. Signed-off-by: Andrey Parfenov <andrey.parfenov@intel.com> Co-authored-by: iermolae <igor.ermolaev@intel.com>
		
			
				
	
	
		
			917 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			917 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
| #!/bin/sh -f
 | ||
| # shellcheck disable=SC2034  # evals in this script fool shellcheck
 | ||
| #
 | ||
| # Copyright 2013-2023 Lawrence Livermore National Security, LLC and other
 | ||
| # Spack Project Developers. See the top-level COPYRIGHT file for details.
 | ||
| #
 | ||
| # SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | ||
| 
 | ||
| #
 | ||
| # Spack compiler wrapper script.
 | ||
| #
 | ||
| # Compiler commands go through this compiler wrapper in Spack builds.
 | ||
| # The compiler wrapper is a thin layer around the standard compilers.
 | ||
| # It enables several key pieces of functionality:
 | ||
| #
 | ||
| # 1. It allows Spack to swap compilers into and out of builds easily.
 | ||
| # 2. It adds several options to the compile line so that spack
 | ||
| #    packages can find their dependencies at build time and run time:
 | ||
| #      -I and/or -isystem arguments for dependency /include directories.
 | ||
| #      -L                 arguments for dependency /lib directories.
 | ||
| #      -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.
 | ||
| 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
 | ||
| 
 | ||
| # Boolean (true/false/custom) if we want to add debug flags
 | ||
| # SPACK_ADD_DEBUG_FLAGS
 | ||
| 
 | ||
| # If a custom flag is requested, it will be defined
 | ||
| # SPACK_DEBUG_FLAGS
 | ||
| 
 | ||
| # The compiler input variables are checked for sanity later:
 | ||
| #   SPACK_CC, SPACK_CXX, SPACK_F77, SPACK_FC
 | ||
| # The default compiler flags are passed from these variables:
 | ||
| #   SPACK_CFLAGS, SPACK_CXXFLAGS, SPACK_FFLAGS,
 | ||
| #   SPACK_LDFLAGS, SPACK_LDLIBS
 | ||
| # Debug env var is optional; set to "TRUE" for debug logging:
 | ||
| #   SPACK_DEBUG
 | ||
| # Test command is used to unit test the compiler script.
 | ||
| #   SPACK_TEST_COMMAND
 | ||
| 
 | ||
| # die MESSAGE
 | ||
| # Print a message and exit with error code 1.
 | ||
| die() {
 | ||
|     echo "[spack cc] ERROR: $*"
 | ||
|     exit 1
 | ||
| }
 | ||
| 
 | ||
| # empty VARNAME
 | ||
| # Return whether the variable VARNAME is unset or set to the empty string.
 | ||
| empty() {
 | ||
|     eval "test -z \"\${$1}\""
 | ||
| }
 | ||
| 
 | ||
| # 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
 | ||
| system_dir() {
 | ||
|     IFS=':'  # SPACK_SYSTEM_DIRS is colon-separated
 | ||
|     path="$1"
 | ||
|     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
 | ||
| }
 | ||
| 
 | ||
| # Fail with a clear message if the input contains any bell characters.
 | ||
| if eval "[ \"\${*#*${lsep}}\" != \"\$*\" ]"; then
 | ||
|     die "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:-}" ]; 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
 | ||
|       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'."
 | ||
| fi
 | ||
| 
 | ||
| # Figure out the type of compiler, the language, and the mode so that
 | ||
| # the compiler script knows what to do.
 | ||
| #
 | ||
| # Possible languages are C, C++, Fortran 77, and Fortran 90.
 | ||
| # 'command' is set based on the input command to $SPACK_[CC|CXX|F77|F90]
 | ||
| #
 | ||
| # 'mode' is set to one of:
 | ||
| #    vcheck  version check
 | ||
| #    cpp     preprocess
 | ||
| #    cc      compile
 | ||
| #    as      assemble
 | ||
| #    ld      link
 | ||
| #    ccld    compile & link
 | ||
| 
 | ||
| command="${0##*/}"
 | ||
| comp="CC"
 | ||
| case "$command" in
 | ||
|     cpp)
 | ||
|         mode=cpp
 | ||
|         debug_flags="-g"
 | ||
|         ;;
 | ||
|     cc|c89|c99|gcc|clang|armclang|icc|icx|pgcc|nvc|xlc|xlc_r|fcc|amdclang|cl.exe|craycc)
 | ||
|         command="$SPACK_CC"
 | ||
|         language="C"
 | ||
|         comp="CC"
 | ||
|         lang_flags=C
 | ||
|         debug_flags="-g"
 | ||
|         ;;
 | ||
|     c++|CC|g++|clang++|armclang++|icpc|icpx|dpcpp|pgc++|nvc++|xlc++|xlc++_r|FCC|amdclang++|crayCC)
 | ||
|         command="$SPACK_CXX"
 | ||
|         language="C++"
 | ||
|         comp="CXX"
 | ||
|         lang_flags=CXX
 | ||
|         debug_flags="-g"
 | ||
|         ;;
 | ||
|     ftn|f90|fc|f95|gfortran|flang|armflang|ifort|ifx|pgfortran|nvfortran|xlf90|xlf90_r|nagfor|frt|amdflang|crayftn)
 | ||
|         command="$SPACK_FC"
 | ||
|         language="Fortran 90"
 | ||
|         comp="FC"
 | ||
|         lang_flags=F
 | ||
|         debug_flags="-g"
 | ||
|         ;;
 | ||
|     f77|xlf|xlf_r|pgf77)
 | ||
|         command="$SPACK_F77"
 | ||
|         language="Fortran 77"
 | ||
|         comp="F77"
 | ||
|         lang_flags=F
 | ||
|         debug_flags="-g"
 | ||
|         ;;
 | ||
|     ld|ld.gold|ld.lld)
 | ||
|         mode=ld
 | ||
|         ;;
 | ||
|     *)
 | ||
|         die "Unknown compiler: $command"
 | ||
|         ;;
 | ||
| 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
 | ||
|     for arg in "$@"; do
 | ||
|         case $arg in
 | ||
|             -v|-V|--version|-dumpversion)
 | ||
|                 mode=vcheck
 | ||
|                 break
 | ||
|                 ;;
 | ||
|         esac
 | ||
|     done
 | ||
| fi
 | ||
| 
 | ||
| # Finish setting up the mode.
 | ||
| if [ -z "$mode" ]; then
 | ||
|     mode=ccld
 | ||
|     for arg in "$@"; do
 | ||
|         if [ "$arg" = "-E" ]; then
 | ||
|             mode=cpp
 | ||
|             break
 | ||
|         elif [ "$arg" = "-S" ]; then
 | ||
|             mode=as
 | ||
|             break
 | ||
|         elif [ "$arg" = "-c" ]; then
 | ||
|             mode=cc
 | ||
|             break
 | ||
|         fi
 | ||
|     done
 | ||
| fi
 | ||
| 
 | ||
| # This is needed to ensure we set RPATH instead of RUNPATH
 | ||
| # (or the opposite, depending on the configuration in config.yaml)
 | ||
| #
 | ||
| # Documentation on this mechanism is lacking at best. A few sources
 | ||
| # of information are (note that some of them take explicitly the
 | ||
| # opposite stance that Spack does):
 | ||
| #
 | ||
| # http://blog.qt.io/blog/2011/10/28/rpath-and-runpath/
 | ||
| # https://wiki.debian.org/RpathIssue
 | ||
| #
 | ||
| # The only discussion I could find on enabling new dynamic tags by
 | ||
| # default on ld is the following:
 | ||
| #
 | ||
| # https://sourceware.org/ml/binutils/2013-01/msg00307.html
 | ||
| #
 | ||
| dtags_to_add="${SPACK_DTAGS_TO_ADD}"
 | ||
| dtags_to_strip="${SPACK_DTAGS_TO_STRIP}"
 | ||
| linker_arg="${SPACK_LINKER_ARG}"
 | ||
| 
 | ||
| # Set up rpath variable according to language.
 | ||
| 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
 | ||
|     echo "$mode"
 | ||
|     exit
 | ||
| fi
 | ||
| 
 | ||
| # If, say, SPACK_CC is set but SPACK_FC is not, we want to know. Compilers do not
 | ||
| # *have* to set up Fortran executables, so we need to tell the user when a build is
 | ||
| # about to attempt to use them unsuccessfully.
 | ||
| if [ -z "$command" ]; then
 | ||
|     die "Compiler '$SPACK_COMPILER_SPEC' does not have a $language compiler configured."
 | ||
| fi
 | ||
| 
 | ||
| #
 | ||
| # Filter '.' and Spack environment directories out of PATH so that
 | ||
| # this script doesn't just call itself
 | ||
| #
 | ||
| new_dirs=""
 | ||
| IFS=':'
 | ||
| for dir in $PATH; do
 | ||
|     addpath=true
 | ||
|     for spack_env_dir in $SPACK_ENV_PATH; do
 | ||
|         case "${dir%%/}" in
 | ||
|             "$spack_env_dir"|'.'|'')
 | ||
|                 addpath=false
 | ||
|                 break
 | ||
|                 ;;
 | ||
|         esac
 | ||
|     done
 | ||
|     if [ $addpath = true ]; then
 | ||
|         append new_dirs "$dir"
 | ||
|     fi
 | ||
| done
 | ||
| unset IFS
 | ||
| export PATH="$new_dirs"
 | ||
| 
 | ||
| if [ "$mode" = vcheck ]; then
 | ||
|     exec "${command}" "$@"
 | ||
| fi
 | ||
| 
 | ||
| # Darwin's linker has a -r argument that merges object files together.
 | ||
| # It doesn't work with -rpath.
 | ||
| # This variable controls whether they are added.
 | ||
| add_rpaths=true
 | ||
| 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
 | ||
| input_command="$*"
 | ||
| 
 | ||
| #
 | ||
| # Parse the command line arguments.
 | ||
| #
 | ||
| # We extract -L, -I, -isystem and -Wl,-rpath arguments from the
 | ||
| # command line and recombine them with Spack arguments later.  We
 | ||
| # parse these out so that we can make sure that system paths come
 | ||
| # last, that package arguments come first, and that Spack arguments
 | ||
| # are injected properly.
 | ||
| #
 | ||
| # All other arguments, including -l arguments, are treated as
 | ||
| # 'other_args' and left in their original order.  This ensures that
 | ||
| # --start-group, --end-group, and other order-sensitive flags continue to
 | ||
| # work as the caller expects.
 | ||
| #
 | ||
| # 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.
 | ||
| 
 | ||
| # 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.
 | ||
| 
 | ||
| parse_Wl() {
 | ||
|     while [ $# -ne 0 ]; do
 | ||
|     if [ "$wl_expect_rpath" = yes ]; then
 | ||
|         if system_dir "$1"; then
 | ||
|             append return_system_rpath_dirs_list "$1"
 | ||
|         else
 | ||
|             append return_rpath_dirs_list "$1"
 | ||
|         fi
 | ||
|         wl_expect_rpath=no
 | ||
|     else
 | ||
|         case "$1" in
 | ||
|             -rpath=*)
 | ||
|                 arg="${1#-rpath=}"
 | ||
|                 if [ -z "$arg" ]; then
 | ||
|                     shift; continue
 | ||
|                 elif system_dir "$arg"; then
 | ||
|                     append return_system_rpath_dirs_list "$arg"
 | ||
|                 else
 | ||
|                     append return_rpath_dirs_list "$arg"
 | ||
|                 fi
 | ||
|                 ;;
 | ||
|             --rpath=*)
 | ||
|                 arg="${1#--rpath=}"
 | ||
|                 if [ -z "$arg" ]; then
 | ||
|                     shift; continue
 | ||
|                 elif system_dir "$arg"; then
 | ||
|                     append return_system_rpath_dirs_list "$arg"
 | ||
|                 else
 | ||
|                     append return_rpath_dirs_list "$arg"
 | ||
|                 fi
 | ||
|                 ;;
 | ||
|             -rpath|--rpath)
 | ||
|                 wl_expect_rpath=yes
 | ||
|                 ;;
 | ||
|             "$dtags_to_strip")
 | ||
|                 ;;
 | ||
|             -Wl)
 | ||
|                 # Nested -Wl,-Wl means we're in NAG compiler territory, we don't support
 | ||
|                 # it.
 | ||
|                 return 1
 | ||
|                 ;;
 | ||
|             *)
 | ||
|                 append return_other_args_list "-Wl,$1"
 | ||
|                 ;;
 | ||
|         esac
 | ||
|     fi
 | ||
|     shift
 | ||
|     done
 | ||
| }
 | ||
| 
 | ||
| categorize_arguments() {
 | ||
| 
 | ||
|     unset IFS
 | ||
| 
 | ||
|     return_other_args_list=""
 | ||
|     return_isystem_was_used=""
 | ||
|     return_isystem_system_include_dirs_list=""
 | ||
|     return_isystem_include_dirs_list=""
 | ||
|     return_system_include_dirs_list=""
 | ||
|     return_include_dirs_list=""
 | ||
|     return_system_lib_dirs_list=""
 | ||
|     return_lib_dirs_list=""
 | ||
|     return_system_rpath_dirs_list=""
 | ||
|     return_rpath_dirs_list=""
 | ||
| 
 | ||
|     # Global state for keeping track of -Wl,-rpath -Wl,/path
 | ||
|     wl_expect_rpath=no
 | ||
| 
 | ||
|     # Same, but for -Xlinker -rpath -Xlinker /path
 | ||
|     xlinker_expect_rpath=no
 | ||
| 
 | ||
|     while [ $# -ne 0 ]; do
 | ||
| 
 | ||
|         # an RPATH to be added after the case statement.
 | ||
|         rp=""
 | ||
| 
 | ||
|         # Multiple consecutive spaces in the command line can
 | ||
|         # result in blank arguments
 | ||
|         if [ -z "$1" ]; then
 | ||
|             shift
 | ||
|             continue
 | ||
|         fi
 | ||
| 
 | ||
|         if [ -n "${SPACK_COMPILER_FLAGS_KEEP}" ] ; then
 | ||
|             # NOTE: the eval is required to allow `|` alternatives inside the variable
 | ||
|             eval "\
 | ||
|             case \"\$1\" in
 | ||
|                 $SPACK_COMPILER_FLAGS_KEEP)
 | ||
|                     append return_other_args_list \"\$1\"
 | ||
|                     shift
 | ||
|                     continue
 | ||
|                     ;;
 | ||
|             esac
 | ||
|             "
 | ||
|         fi
 | ||
|         # the replace list is a space-separated list of pipe-separated pairs,
 | ||
|         # the first in each pair is the original prefix to be matched, the
 | ||
|         # second is the replacement prefix
 | ||
|         if [ -n "${SPACK_COMPILER_FLAGS_REPLACE}" ] ; then
 | ||
|             for rep in ${SPACK_COMPILER_FLAGS_REPLACE} ; do
 | ||
|                 before=${rep%|*}
 | ||
|                 after=${rep#*|}
 | ||
|                 eval "\
 | ||
|                 stripped=\"\${1##$before}\"
 | ||
|                 "
 | ||
|                 if [ "$stripped" = "$1" ] ; then
 | ||
|                     continue
 | ||
|                 fi
 | ||
| 
 | ||
|                 replaced="$after$stripped" 
 | ||
| 
 | ||
|                 # it matched, remove it
 | ||
|                 shift
 | ||
| 
 | ||
|                 if [ -z "$replaced" ] ; then
 | ||
|                     # completely removed, continue OUTER loop
 | ||
|                     continue 2
 | ||
|                 fi
 | ||
| 
 | ||
|                 # re-build argument list with replacement
 | ||
|                 set -- "$replaced" "$@"
 | ||
|             done
 | ||
|         fi
 | ||
| 
 | ||
|         case "$1" in
 | ||
|             -isystem*)
 | ||
|                 arg="${1#-isystem}"
 | ||
|                 return_isystem_was_used=true
 | ||
|                 if [ -z "$arg" ]; then shift; arg="$1"; fi
 | ||
|                 if system_dir "$arg"; then
 | ||
|                     append return_isystem_system_include_dirs_list "$arg"
 | ||
|                 else
 | ||
|                     append return_isystem_include_dirs_list "$arg"
 | ||
|                 fi
 | ||
|                 ;;
 | ||
|             -I*)
 | ||
|                 arg="${1#-I}"
 | ||
|                 if [ -z "$arg" ]; then shift; arg="$1"; fi
 | ||
|                 if system_dir "$arg"; then
 | ||
|                     append return_system_include_dirs_list "$arg"
 | ||
|                 else
 | ||
|                     append return_include_dirs_list "$arg"
 | ||
|                 fi
 | ||
|                 ;;
 | ||
|             -L*)
 | ||
|                 arg="${1#-L}"
 | ||
|                 if [ -z "$arg" ]; then shift; arg="$1"; fi
 | ||
|                 if system_dir "$arg"; then
 | ||
|                     append return_system_lib_dirs_list "$arg"
 | ||
|                 else
 | ||
|                     append return_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
 | ||
|                 # 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.
 | ||
|                 if { [ "$mode" = "ccld" ] || [ $mode = "ld" ]; } \
 | ||
|                     && [ "$1" != "${1#-loopopt}" ]; then
 | ||
|                     shift
 | ||
|                     continue
 | ||
|                 fi
 | ||
|                 arg="${1#-l}"
 | ||
|                 if [ -z "$arg" ]; then shift; arg="$1"; fi
 | ||
|                 append return_other_args_list "-l$arg"
 | ||
|                 ;;
 | ||
|             -Wl,*)
 | ||
|                 IFS=,
 | ||
|                 if ! parse_Wl ${1#-Wl,}; then
 | ||
|                     append return_other_args_list "$1"
 | ||
|                 fi
 | ||
|                 unset IFS
 | ||
|                 ;;
 | ||
|             -Xlinker)
 | ||
|                 shift
 | ||
|                 if [ $# -eq 0 ]; then
 | ||
|                     # -Xlinker without value: let the compiler error about it.
 | ||
|                     append return_other_args_list -Xlinker
 | ||
|                     xlinker_expect_rpath=no
 | ||
|                     break
 | ||
|                 elif [ "$xlinker_expect_rpath" = yes ]; then
 | ||
|                     # Register the path of -Xlinker -rpath <other args> -Xlinker <path>
 | ||
|                     if system_dir "$1"; then
 | ||
|                         append return_system_rpath_dirs_list "$1"
 | ||
|                     else
 | ||
|                         append return_rpath_dirs_list "$1"
 | ||
|                     fi
 | ||
|                     xlinker_expect_rpath=no
 | ||
|                 else
 | ||
|                     case "$1" in
 | ||
|                         -rpath=*)
 | ||
|                             arg="${1#-rpath=}"
 | ||
|                             if system_dir "$arg"; then
 | ||
|                                 append return_system_rpath_dirs_list "$arg"
 | ||
|                             else
 | ||
|                                 append return_rpath_dirs_list "$arg"
 | ||
|                             fi
 | ||
|                             ;;
 | ||
|                         --rpath=*)
 | ||
|                             arg="${1#--rpath=}"
 | ||
|                             if system_dir "$arg"; then
 | ||
|                                 append return_system_rpath_dirs_list "$arg"
 | ||
|                             else
 | ||
|                                 append return_rpath_dirs_list "$arg"
 | ||
|                             fi
 | ||
|                             ;;
 | ||
|                         -rpath|--rpath)
 | ||
|                             xlinker_expect_rpath=yes
 | ||
|                             ;;
 | ||
|                         "$dtags_to_strip")
 | ||
|                             ;;
 | ||
|                         *)
 | ||
|                             append return_other_args_list -Xlinker
 | ||
|                             append return_other_args_list "$1"
 | ||
|                             ;;
 | ||
|                     esac
 | ||
|                 fi
 | ||
|                 ;;
 | ||
|             "$dtags_to_strip")
 | ||
|                 ;;
 | ||
|             *)
 | ||
|                 append return_other_args_list "$1"
 | ||
|                 ;;
 | ||
|         esac
 | ||
|         shift
 | ||
|     done
 | ||
| 
 | ||
|     # We found `-Xlinker -rpath` but no matching value `-Xlinker /path`. Just append
 | ||
|     # `-Xlinker -rpath` again and let the compiler or linker handle the error during arg
 | ||
|     # parsing.
 | ||
|     if [ "$xlinker_expect_rpath" = yes ]; then
 | ||
|         append return_other_args_list -Xlinker
 | ||
|         append return_other_args_list -rpath
 | ||
|     fi
 | ||
| 
 | ||
|     # Same, but for -Wl flags.
 | ||
|     if [ "$wl_expect_rpath" = yes ]; then
 | ||
|         append return_other_args_list -Wl,-rpath
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| categorize_arguments "$@"
 | ||
|     include_dirs_list="$return_include_dirs_list"
 | ||
|     lib_dirs_list="$return_lib_dirs_list"
 | ||
|     rpath_dirs_list="$return_rpath_dirs_list"
 | ||
|     system_include_dirs_list="$return_system_include_dirs_list"
 | ||
|     system_lib_dirs_list="$return_system_lib_dirs_list"
 | ||
|     system_rpath_dirs_list="$return_system_rpath_dirs_list"
 | ||
|     isystem_was_used="$return_isystem_was_used"
 | ||
|     isystem_system_include_dirs_list="$return_isystem_system_include_dirs_list"
 | ||
|     isystem_include_dirs_list="$return_isystem_include_dirs_list"
 | ||
|     other_args_list="$return_other_args_list"
 | ||
| 
 | ||
| #
 | ||
| # Add flags from Spack's cppflags, cflags, cxxflags, fcflags, fflags, and
 | ||
| # ldflags. We stick to the order that gmake puts the flags in by default.
 | ||
| #
 | ||
| # See the gmake manual on implicit rules for details:
 | ||
| # https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html
 | ||
| #
 | ||
| flags_list=""
 | ||
| 
 | ||
| # Add 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
 | ||
|     extend flags_list SPACK_DEBUG_FLAGS
 | ||
| fi
 | ||
| 
 | ||
| spack_flags_list=""
 | ||
| 
 | ||
| # Fortran flags come before CPPFLAGS
 | ||
| case "$mode" in
 | ||
|     cc|ccld)
 | ||
|         case $lang_flags in
 | ||
|             F)
 | ||
|                 extend spack_flags_list SPACK_FFLAGS
 | ||
|                 ;;
 | ||
|         esac
 | ||
|         ;;
 | ||
| esac
 | ||
| 
 | ||
| # C preprocessor flags come before any C/CXX flags
 | ||
| case "$mode" in
 | ||
|     cpp|as|cc|ccld)
 | ||
|         extend spack_flags_list SPACK_CPPFLAGS
 | ||
|         ;;
 | ||
| esac
 | ||
| 
 | ||
| 
 | ||
| # Add C and C++ flags
 | ||
| case "$mode" in
 | ||
|     cc|ccld)
 | ||
|         case $lang_flags in
 | ||
|             C)
 | ||
|                 extend spack_flags_list SPACK_CFLAGS
 | ||
|                 ;;
 | ||
|             CXX)
 | ||
|                 extend spack_flags_list SPACK_CXXFLAGS
 | ||
|                 ;;
 | ||
|         esac
 | ||
| 
 | ||
|         # prepend target args
 | ||
|         preextend flags_list SPACK_TARGET_ARGS
 | ||
|         ;;
 | ||
| esac
 | ||
| 
 | ||
| # Linker flags
 | ||
| case "$mode" in
 | ||
|     ld|ccld)
 | ||
|         extend spack_flags_list SPACK_LDFLAGS
 | ||
|         ;;
 | ||
| esac
 | ||
| 
 | ||
| IFS="$lsep"
 | ||
|     categorize_arguments $spack_flags_list
 | ||
| unset IFS
 | ||
|         spack_flags_include_dirs_list="$return_include_dirs_list"
 | ||
|         spack_flags_lib_dirs_list="$return_lib_dirs_list"
 | ||
|         spack_flags_rpath_dirs_list="$return_rpath_dirs_list"
 | ||
|         spack_flags_system_include_dirs_list="$return_system_include_dirs_list"
 | ||
|         spack_flags_system_lib_dirs_list="$return_system_lib_dirs_list"
 | ||
|         spack_flags_system_rpath_dirs_list="$return_system_rpath_dirs_list"
 | ||
|         spack_flags_isystem_was_used="$return_isystem_was_used"
 | ||
|         spack_flags_isystem_system_include_dirs_list="$return_isystem_system_include_dirs_list"
 | ||
|         spack_flags_isystem_include_dirs_list="$return_isystem_include_dirs_list"
 | ||
|         spack_flags_other_args_list="$return_other_args_list"
 | ||
| 
 | ||
| 
 | ||
| # On macOS insert headerpad_max_install_names linker flag
 | ||
| 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
 | ||
| 
 | ||
| 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.
 | ||
|         extend rpath_dirs_list SPACK_RPATH_DIRS
 | ||
|     fi
 | ||
| fi
 | ||
| 
 | ||
| if [ "$mode" = ccld ] || [ "$mode" = ld ]; then
 | ||
|     extend lib_dirs_list SPACK_LINK_DIRS
 | ||
| fi
 | ||
| 
 | ||
| libs_list=""
 | ||
| 
 | ||
| # add RPATHs if we're in in any linking mode
 | ||
| case "$mode" in
 | ||
|     ld|ccld)
 | ||
|         # Set 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
 | ||
|         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
 | ||
|             append libs_list "${lib#-l}"
 | ||
|         done
 | ||
|         ;;
 | ||
| esac
 | ||
| 
 | ||
| #
 | ||
| # Finally, reassemble the command line.
 | ||
| #
 | ||
| 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 spack_flags_include_dirs_list "-I"
 | ||
| extend args_list include_dirs_list "-I"
 | ||
| extend args_list spack_flags_isystem_include_dirs_list "-isystem${lsep}"
 | ||
| extend args_list isystem_include_dirs_list "-isystem${lsep}"
 | ||
| 
 | ||
| case "$mode" in
 | ||
|     cpp|cc|as|ccld)
 | ||
|         if [ "$spack_flags_isystem_was_used" = "true" ]; then
 | ||
|             extend args_list SPACK_INCLUDE_DIRS "-isystem${lsep}"
 | ||
|         elif [ "$isystem_was_used" = "true" ]; then
 | ||
|             extend args_list SPACK_INCLUDE_DIRS "-isystem${lsep}"
 | ||
|         else
 | ||
|             extend args_list SPACK_INCLUDE_DIRS "-I"
 | ||
|         fi
 | ||
|         ;;
 | ||
| esac
 | ||
| 
 | ||
| extend args_list spack_flags_system_include_dirs_list -I
 | ||
| extend args_list system_include_dirs_list -I
 | ||
| extend args_list spack_flags_isystem_system_include_dirs_list "-isystem${lsep}"
 | ||
| extend args_list isystem_system_include_dirs_list "-isystem${lsep}"
 | ||
| 
 | ||
| # Library search paths
 | ||
| extend args_list spack_flags_lib_dirs_list "-L"
 | ||
| extend args_list lib_dirs_list "-L"
 | ||
| extend args_list spack_flags_system_lib_dirs_list "-L"
 | ||
| extend args_list system_lib_dirs_list "-L"
 | ||
| 
 | ||
| # RPATHs arguments
 | ||
| case "$mode" in
 | ||
|     ccld)
 | ||
|         if [ -n "$dtags_to_add" ] ; then
 | ||
|             append args_list "$linker_arg$dtags_to_add"
 | ||
|         fi
 | ||
|         extend args_list spack_flags_rpath_dirs_list "$rpath"
 | ||
|         extend args_list rpath_dirs_list "$rpath"
 | ||
|         extend args_list spack_flags_system_rpath_dirs_list "$rpath"
 | ||
|         extend args_list system_rpath_dirs_list "$rpath"
 | ||
|         ;;
 | ||
|     ld)
 | ||
|         if [ -n "$dtags_to_add" ] ; then
 | ||
|             append args_list "$dtags_to_add"
 | ||
|         fi
 | ||
|         extend args_list spack_flags_rpath_dirs_list "-rpath${lsep}"
 | ||
|         extend args_list rpath_dirs_list "-rpath${lsep}"
 | ||
|         extend args_list spack_flags_system_rpath_dirs_list "-rpath${lsep}"
 | ||
|         extend args_list system_rpath_dirs_list "-rpath${lsep}"
 | ||
|         ;;
 | ||
| esac
 | ||
| 
 | ||
| # Other arguments from the input command
 | ||
| extend args_list other_args_list
 | ||
| extend args_list spack_flags_other_args_list
 | ||
| 
 | ||
| # Inject SPACK_LDLIBS, if supplied
 | ||
| extend args_list libs_list "-l"
 | ||
| 
 | ||
| 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
 | ||
|             prepend full_command_list "${SPACK_CCACHE_BINARY}"
 | ||
|             # workaround for stage being a temp folder
 | ||
|             # see #3761#issuecomment-294352232
 | ||
|             export CCACHE_NOHASHDIR=yes
 | ||
|             ;;
 | ||
|     esac
 | ||
| fi
 | ||
| 
 | ||
| # dump the full command if the caller supplies SPACK_TEST_COMMAND=dump-args
 | ||
| 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 "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
 | ||
|     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"
 | ||
|     IFS="$lsep"
 | ||
|     echo "[$mode] "$full_command_list >> "$output_log"
 | ||
|     unset IFS
 | ||
| fi
 | ||
| 
 | ||
| # Execute the full command, preserving spaces with IFS set
 | ||
| # to the alarm bell separator.
 | ||
| IFS="$lsep"; exec $full_command_list
 | ||
| 
 |