summaryrefslogblamecommitdiff
path: root/lib/spack/env/cc
blob: f72a6663a37bb344ac230554646c1f186b6d8ecd (plain) (tree)
1
2
3
4
5
6
7
8
            
                                                                   
 
                                                                         
                                                                         
 

                                              









                                                                     


                                                                        

 











                                                                    
                                                                      
                                                                      
                                              











                   
 







                                                           

                                                            
                                                             
                                               
                               
                                                             


                                                        
 


                                             
                               


          




                                                                          
 


















                                                                               
 









































































                                                                           
                                           

                                                   
             

                                                                    
                                                         
                     


                    
             


                                                    

                                                                      
                                                                                 




                                              
                                                                               


      

                                                     
                                            





                                                 
                                                   




                                                    

                                                                                         

  






                                                                        
                          
                       
                    
                     

                           
 
                  
         
                  

                
                        
          
                                                                                        

                           
                 
                    
                        
          
                                                                                                  

                            
                  
                      
                        
          
                                                                                                                   

                             
                 
                    
                        
          
                        

                             
                  
                    
                        
          
                      


               
                                        


          

                                                                     
            
                                           
                       





                                         



                             
                       

                       
                                  

                    
                                    

                   
                                    


                   


        


















                                                                   
                                              

                                                  
 
                                                     
                                                



                


                                                                                   
                          
                                                                                        

  



                                                                  


                    
                






                                            
        

                                

      

                       
 
                             
                          

  



                                                                      













                                                                       

  
                                         
                  

 

                                   



                                                                   
                        
 



                                                                         
 


                                                                        




                                                                           
 
            

                                         
                                
                                                     
            
                                              
          

                          

                     
                                  


                                            
                                                               
                    
                                                        
                  

                      
                                   


                                            
                                                               
                    
                                                        
                  





                                   




                                                                                        
              
                                                      


                  

         

 
                        
 
             
 









                                              
 

                                                            
 
















                                                                                      
                   






                                                         
             













                                                                             
 
                                           
 

                                       
 



                                                             
 



                                                         
 



















































                                                                             
                     












                                                                                       
                    


























                                                                           
                  








                                                  
 






                                                                                        
 
















                                                                               
 
 




                                                                            
 
             
 
                 

                                                

                                                        

                                                  

  

                   
                                    
               


                           
                                                    
                  






                                                  
                                              
          
    
 

                     

               

                           
                                                    
                  
                
                                                      
                  
            


                                              
          
    
 
              
               
            
                                             
          
    
 














                                                                                               
                                                         








                                                                       

  

                                               


                                                                                 
                                               
      
  
 

                                               
  
 

            
                                            

               
                          


                                                              
          
 
                             

                                                                 

          
                                  

                                        

            

    


                                       
                       
 
                                                                         
                                                                            
                                                   
                                       
                                                                        
                                                            
 

                   


                                                                 





                                                                 
 
                                                        
                                            
                                                                               
                                                                   

                      
                                               
                                   
                                                      
                                          

                  

               


                                                       
                                                             
                                                 
                                                                    
                                                        

          


                                            
                                                                    
                                                        
                                                                           
                                                               

          
 
                                        
                                
                                            

                                  
                               
 

                                  




                                                  
                                                              





                                                      
 
                                                                           














                                                       
                                                             

              

  


                                                                      
                                  

                                                                          
                                                          


                                                      

  


                                                          
 
#!/bin/sh -f
# shellcheck disable=SC2034  # evals in this script fool shellcheck
#
# Copyright 2013-2024 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