diff options
Diffstat (limited to 'lib/spack/env/cc')
-rwxr-xr-x | lib/spack/env/cc | 424 |
1 files changed, 291 insertions, 133 deletions
diff --git a/lib/spack/env/cc b/lib/spack/env/cc index b06c6fd6fc..3b83291cc2 100755 --- a/lib/spack/env/cc +++ b/lib/spack/env/cc @@ -1,28 +1,10 @@ #!/bin/bash -############################################################################## -# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC. -# Produced at the Lawrence Livermore National Laboratory. # -# This file is part of Spack. -# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. -# LLNL-CODE-647188 +# Copyright 2013-2018 Lawrence Livermore National Security, LLC and other +# Spack Project Developers. See the top-level COPYRIGHT file for details. # -# For details, see https://github.com/spack/spack -# Please also see the NOTICE and LICENSE files for our notice and the LGPL. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License (as -# published by the Free Software Foundation) version 2.1, February 1999. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and -# conditions of the GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -############################################################################## +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + # # Spack compiler wrapper script. # @@ -52,6 +34,7 @@ parameters=( SPACK_F77_RPATH_ARG SPACK_FC_RPATH_ARG SPACK_SHORT_SPEC + SPACK_SYSTEM_DIRS ) # The compiler input variables are checked for sanity later: @@ -73,7 +56,31 @@ function die { exit 1 } -for param in ${parameters[@]}; do +# read input parameters into proper bash arrays. +# SYSTEM_DIRS is delimited by : +IFS=':' read -ra SPACK_SYSTEM_DIRS <<< "${SPACK_SYSTEM_DIRS}" + +# 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" + +# test whether a path is a system directory +function system_dir { + path="$1" + for sd in "${SPACK_SYSTEM_DIRS[@]}"; do + if [ "${path}" == "${sd}" ] || [ "${path}" == "${sd}/" ]; then + # success if path starts with a system prefix + return 0 + fi + done + return 1 # fail if path starts no system prefix +} + +for param in "${parameters[@]}"; do if [[ -z ${!param} ]]; then die "Spack compiler must be run from Spack! Input '$param' is missing." fi @@ -117,7 +124,7 @@ case "$command" in comp="FC" lang_flags=F ;; - f77|gfortran|flang|ifort|pgfortran|xlf|xlf_r|nagfor|ftn) + f77|xlf|xlf_r|pgf77) command="$SPACK_F77" language="Fortran 77" comp="F77" @@ -136,10 +143,12 @@ esac # libraries. if [[ -z $mode ]] || [[ $mode == ld ]]; then for arg in "$@"; do - if [[ $arg == -v || $arg == -V || $arg == --version || $arg == -dumpversion ]]; then - mode=vcheck - break - fi + case $arg in + -v|-V|--version|-dumpversion) + mode=vcheck + break + ;; + esac done fi @@ -163,7 +172,7 @@ fi # Set up rpath variable according to language. eval rpath=\$SPACK_${comp}_RPATH_ARG -# Dump the version and exit if we're in testing mode. +# Dump the mode and exit if the command is dump-mode. if [[ $SPACK_TEST_COMMAND == dump-mode ]]; then echo "$mode" exit @@ -176,50 +185,40 @@ if [[ -z $command ]]; then fi # -# Set paths as defined in the 'environment' section of the compiler config -# names are stored in SPACK_ENV_TO_SET -# values are stored in SPACK_ENV_SET_<varname> -# -IFS=':' read -ra env_set_varnames <<< "$SPACK_ENV_TO_SET" -for varname in "${env_set_varnames[@]}"; do - spack_varname="SPACK_ENV_SET_$varname" - export $varname=${!spack_varname} - unset $spack_varname -done - -# # 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+=("" ".") -PATH="" +export PATH="" for dir in "${env_path[@]}"; do addpath=true for env_dir in "${spack_env_dirs[@]}"; do - if [[ $dir == $env_dir ]]; then + if [[ "$dir" == "$env_dir" ]]; then addpath=false break fi done if $addpath; then - PATH="${PATH:+$PATH:}$dir" + export PATH="${PATH:+$PATH:}$dir" fi done -export PATH if [[ $mode == vcheck ]]; then - exec ${command} "$@" + 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) && "$SPACK_SHORT_SPEC" =~ "darwin" ]]; then +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 + if [[ ($arg == -r && $mode == ld) || + ($arg == -r && $mode == ccld) || + ($arg == -Wl,-r && $mode == ccld) ]]; then add_rpaths=false break fi @@ -227,126 +226,285 @@ if [[ ($mode == ld || $mode == ccld) && "$SPACK_SHORT_SPEC" =~ "darwin" ]]; then fi # Save original command for debug logging -input_command="$@" -args=("$@") +input_command="$*" + +# +# Parse the command line arguments. +# +# We extract -L, -I, 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. +# +includes=() +libdirs=() +rpaths=() +system_includes=() +system_libdirs=() +system_rpaths=() +libs=() +other_args=() -# Prepend cppflags, cflags, cxxflags, fcflags, fflags, and ldflags +while [ -n "$1" ]; do + # an RPATH to be added after the case statement. + rp="" -# Add ldflags + case "$1" in + -I*) + arg="${1#-I}" + if [ -z "$arg" ]; then shift; arg="$1"; fi + if system_dir "$arg"; then + system_includes+=("$arg") + else + includes+=("$arg") + fi + ;; + -L*) + arg="${1#-L}" + if [ -z "$arg" ]; then shift; arg="$1"; fi + if system_dir "$arg"; then + system_libdirs+=("$arg") + else + libdirs+=("$arg") + fi + ;; + -l*) + arg="${1#-l}" + if [ -z "$arg" ]; then shift; arg="$1"; fi + other_args+=("-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 + shift; arg="$1" + if [[ "$arg" != -Wl,* ]]; then + die "-Wl,-rpath was not followed by -Wl,*" + fi + rp="${arg#-Wl,}" + else + other_args+=("-Wl,$arg") + fi + ;; + -Xlinker,*) + arg="${1#-Xlinker,}" + if [ -z "$arg" ]; then shift; arg="$1"; fi + if [[ "$arg" = -rpath=* ]]; then + rp="${arg#-rpath=}" + elif [[ "$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 + ;; + -Xlinker) + if [[ "$2" == "-rpath" ]]; then + if [[ "$3" != "-Xlinker" ]]; then + die "-Xlinker,-rpath was not followed by -Xlinker,*" + fi + shift 3; + rp="$1" + else + other_args+=("$1") + fi + ;; + *) + other_args+=("$1") + ;; + esac + + # test rpaths against system directories in one place. + if [ -n "$rp" ]; then + if system_dir "$rp"; then + system_rpaths+=("$rp") + else + rpaths+=("$rp") + fi + fi + shift +done + +# +# 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=() + +# Fortran flags come before CPPFLAGS case "$mode" in - ld|ccld) - args=(${SPACK_LDFLAGS[@]} "${args[@]}") ;; + cc|ccld) + case $lang_flags in + F) + flags=("${flags[@]}" "${SPACK_FFLAGS[@]}") ;; + esac + ;; +esac + +# C preprocessor flags come before any C/CXX flags +case "$mode" in + cpp|as|cc|ccld) + flags=("${flags[@]}" "${SPACK_CPPFLAGS[@]}") ;; esac -# Add compiler flags. + +# Add C and C++ flags case "$mode" in cc|ccld) - # Add c, cxx, fc, and f flags case $lang_flags in C) - args=(${SPACK_CFLAGS[@]} "${args[@]}") ;; + flags=("${flags[@]}" "${SPACK_CFLAGS[@]}") ;; CXX) - args=(${SPACK_CXXFLAGS[@]} "${args[@]}") ;; + flags=("${flags[@]}" "${SPACK_CXXFLAGS[@]}") ;; esac ;; esac -# Add cppflags +# Linker flags case "$mode" in - cpp|as|cc|ccld) - args=(${SPACK_CPPFLAGS[@]} "${args[@]}") ;; + ld|ccld) + flags=("${flags[@]}" "${SPACK_LDFLAGS[@]}") ;; esac -case "$mode" in cc|ccld) - # Add fortran flags - case $lang_flags in - F) - args=(${SPACK_FFLAGS[@]} "${args[@]}") ;; - esac + +# Include the package's prefix/lib[64] dirs in rpath. We don't know until +# *after* installation which one's correct, so we include both lib and +# lib64, assuming that only one will be present. +case "$mode" in + ld|ccld) + $add_rpaths && rpaths+=("$SPACK_PREFIX/lib") + $add_rpaths && rpaths+=("$SPACK_PREFIX/lib64") ;; esac -# Read spack dependencies from the path environment variable +# Read spack dependencies from the environment. This is a list of prefixes. IFS=':' read -ra deps <<< "$SPACK_DEPENDENCIES" for dep in "${deps[@]}"; do - # Prepend include directories - if [[ -d $dep/include ]]; then - if [[ $mode == cpp || $mode == cc || $mode == as || $mode == ccld ]]; then - args=("-I$dep/include" "${args[@]}") - fi - fi - - # Prepend lib and RPATH directories - if [[ -d $dep/lib ]]; then - if [[ $mode == ccld ]]; then - if [[ $SPACK_RPATH_DEPS == *$dep* ]]; then - $add_rpaths && args=("$rpath$dep/lib" "${args[@]}") - fi - if [[ $SPACK_LINK_DEPS == *$dep* ]]; then - args=("-L$dep/lib" "${args[@]}") - fi - elif [[ $mode == ld ]]; then - if [[ $SPACK_RPATH_DEPS == *$dep* ]]; then - $add_rpaths && args=("-rpath" "$dep/lib" "${args[@]}") + # Append include directories in any compilation mode + case "$mode" in + cpp|cc|as|ccld) + if [[ -d $dep/include ]]; then + includes=("${includes[@]}" "$dep/include") fi - if [[ $SPACK_LINK_DEPS == *$dep* ]]; then - args=("-L$dep/lib" "${args[@]}") - fi - fi - fi + ;; + esac - # Prepend lib64 and RPATH directories - if [[ -d $dep/lib64 ]]; then - if [[ $mode == ccld ]]; then - if [[ $SPACK_RPATH_DEPS == *$dep* ]]; then - $add_rpaths && args=("$rpath$dep/lib64" "${args[@]}") + # Append lib/lib64 and RPATH directories, but only if we're linking + case "$mode" in + ld|ccld) + if [[ -d $dep/lib ]]; then + if [[ $SPACK_RPATH_DEPS == *$dep* ]]; then + $add_rpaths && rpaths=("${rpaths[@]}" "$dep/lib") + fi + if [[ $SPACK_LINK_DEPS == *$dep* ]]; then + libdirs=("${libdirs[@]}" "$dep/lib") + fi fi - if [[ $SPACK_LINK_DEPS == *$dep* ]]; then - args=("-L$dep/lib64" "${args[@]}") - fi - elif [[ $mode == ld ]]; then - if [[ $SPACK_RPATH_DEPS == *$dep* ]]; then - $add_rpaths && args=("-rpath" "$dep/lib64" "${args[@]}") - fi - if [[ $SPACK_LINK_DEPS == *$dep* ]]; then - args=("-L$dep/lib64" "${args[@]}") + + if [[ -d $dep/lib64 ]]; then + if [[ $SPACK_RPATH_DEPS == *$dep* ]]; then + $add_rpaths && rpaths+=("$dep/lib64") + fi + if [[ $SPACK_LINK_DEPS == *$dep* ]]; then + libdirs+=("$dep/lib64") + fi fi - fi - fi + ;; + esac done -# Include all -L's and prefix/whatever dirs in rpath -if [[ $mode == ccld ]]; then - $add_rpaths && args=("$rpath$SPACK_PREFIX/lib64" "${args[@]}") - $add_rpaths && args=("$rpath$SPACK_PREFIX/lib" "${args[@]}") -elif [[ $mode == ld ]]; then - $add_rpaths && args=("-rpath" "$SPACK_PREFIX/lib64" "${args[@]}") - $add_rpaths && args=("-rpath" "$SPACK_PREFIX/lib" "${args[@]}") -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" + for extra_rpath in "${extra_rpaths[@]}"; do + $add_rpaths && rpaths+=("$extra_rpath") + libdirs+=("$extra_rpath") + done -# Set extra RPATHs -IFS=':' read -ra extra_rpaths <<< "$SPACK_COMPILER_EXTRA_RPATHS" -for extra_rpath in "${extra_rpaths[@]}"; do - if [[ $mode == ccld ]]; then - $add_rpaths && args=("$rpath$extra_rpath" "${args[@]}") - args=("-L$extra_rpath" "${args[@]}") - elif [[ $mode == ld ]]; then - $add_rpaths && args=("-rpath" "$extra_rpath" "${args[@]}") - args=("-L$extra_rpath" "${args[@]}") - fi -done + # Add SPACK_LDLIBS to args + for lib in "${SPACK_LDLIBS[@]}"; do + libs+=("${lib#-l}") + done + ;; +esac + +# +# Finally, reassemble the command line. +# + +# Includes and system includes first +args=() + +# flags assembled earlier +args+=("${flags[@]}") -# Add SPACK_LDLIBS to args +# include directory search paths +for dir in "${includes[@]}"; do args+=("-I$dir"); done +for dir in "${system_includes[@]}"; do args+=("-I$dir"); done + +# Library search paths +for dir in "${libdirs[@]}"; do args+=("-L$dir"); done +for dir in "${system_libdirs[@]}"; do args+=("-L$dir"); done + +# RPATHs arguments case "$mode" in - ld|ccld) - args=("${args[@]}" ${SPACK_LDLIBS[@]}) ;; + ccld) + for dir in "${rpaths[@]}"; do args+=("$rpath$dir"); done + for dir in "${system_rpaths[@]}"; do args+=("$rpath$dir"); done + ;; + ld) + for dir in "${rpaths[@]}"; do args+=("-rpath" "$dir"); done + for dir in "${system_rpaths[@]}"; do args+=("-rpath" "$dir"); done + ;; esac +# Other arguments from the input command +args+=("${other_args[@]}") + +# Inject SPACK_LDLIBS, if supplied +for lib in "${libs[@]}"; do + args+=("-l$lib"); +done + full_command=("$command" "${args[@]}") -# In test command mode, write out full command for Spack tests. +# 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[@]}") + # 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 [[ $SPACK_TEST_COMMAND == dump-args ]]; then - echo "${full_command[@]}" + IFS=" +" && echo "${full_command[*]}" exit elif [[ -n $SPACK_TEST_COMMAND ]]; then die "ERROR: Unknown test command" @@ -359,7 +517,7 @@ 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[*]}" >> "$output_log" fi exec "${full_command[@]}" |