Turn the compiler wrapper into a package

Remove the compiler wrappers from core Spack, and move
the relevant code into the "compiler-wrapper" package.
This commit is contained in:
Massimiliano Culpo
2024-12-19 22:47:04 +01:00
parent e5f8049f3d
commit 95115d4290
97 changed files with 693 additions and 551 deletions

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cpp

View File

@@ -1 +0,0 @@
../fc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

1
lib/spack/env/c++ vendored
View File

@@ -1 +0,0 @@
cc

1
lib/spack/env/c89 vendored
View File

@@ -1 +0,0 @@
cc

1
lib/spack/env/c99 vendored
View File

@@ -1 +0,0 @@
cc

View File

@@ -1 +0,0 @@
../cc

963
lib/spack/env/cc vendored
View File

@@ -1,963 +0,0 @@
#!/bin/sh -f
# shellcheck disable=SC2034 # evals in this script fool shellcheck
#
# Copyright Spack Project Developers. See 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_LINKER_ARG
SPACK_SHORT_SPEC
SPACK_SYSTEM_DIRS
SPACK_MANAGED_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
#
# Prepend ELEMENT to the list stored in the variable LISTNAME.
# 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
}
execute() {
# 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
exit
}
# 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
# eval this because SPACK_MANAGED_DIRS and SPACK_SYSTEM_DIRS are inputs we don't wanna loop over.
# moving the eval inside the function would eval it every call.
eval "\
path_order() {
case \"\$1\" in
$SPACK_MANAGED_DIRS) return 0 ;;
$SPACK_SYSTEM_DIRS) return 2 ;;
/*) return 1 ;;
esac
}
"
# path_list functions. Path_lists have 3 parts: spack_store_<list>, <list> and system_<list>,
# which are used to prioritize paths when assembling the final command line.
# init_path_lists LISTNAME
# Set <LISTNAME>, spack_store_<LISTNAME>, and system_<LISTNAME> to "".
init_path_lists() {
eval "spack_store_$1=\"\""
eval "$1=\"\""
eval "system_$1=\"\""
}
# assign_path_lists LISTNAME1 LISTNAME2
# Copy contents of LISTNAME2 into LISTNAME1, for each path_list prefix.
assign_path_lists() {
eval "spack_store_$1=\"\${spack_store_$2}\""
eval "$1=\"\${$2}\""
eval "system_$1=\"\${system_$2}\""
}
# append_path_lists LISTNAME ELT
# Append the provided ELT to the appropriate list, based on the result of path_order().
append_path_lists() {
path_order "$2"
case $? in
0) eval "append spack_store_$1 \"\$2\"" ;;
1) eval "append $1 \"\$2\"" ;;
2) eval "append system_$1 \"\$2\"" ;;
esac
}
# 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
# Note. SPACK_ALWAYS_XFLAGS are applied for all compiler invocations,
# including version checks (SPACK_XFLAGS variants are not applied
# for version checks).
command="${0##*/}"
comp="CC"
vcheck_flags=""
case "$command" in
cpp)
mode=cpp
debug_flags="-g"
vcheck_flags="${SPACK_ALWAYS_CPPFLAGS}"
;;
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"
vcheck_flags="${SPACK_ALWAYS_CFLAGS}"
;;
c++|CC|g++|clang++|armclang++|icpc|icpx|pgc++|nvc++|xlc++|xlc++_r|FCC|amdclang++|crayCC)
command="$SPACK_CXX"
language="C++"
comp="CXX"
lang_flags=CXX
debug_flags="-g"
vcheck_flags="${SPACK_ALWAYS_CXXFLAGS}"
;;
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"
vcheck_flags="${SPACK_ALWAYS_FFLAGS}"
;;
f77|xlf|xlf_r|pgf77)
command="$SPACK_F77"
language="Fortran 77"
comp="F77"
lang_flags=F
debug_flags="-g"
vcheck_flags="${SPACK_ALWAYS_FFLAGS}"
;;
ld|ld.gold|ld.lld)
mode=ld
if [ -z "$SPACK_CC_RPATH_ARG" ]; then
comp="CXX"
fi
;;
*)
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, MAYBE THE PACKAGE DOES NOT DEPEND ON ${comp}?"
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
#
# 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
full_command_list="$command"
args="$@"
extend full_command_list vcheck_flags
extend full_command_list args
execute
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
append_path_lists return_rpath_dirs_list "$1"
wl_expect_rpath=no
else
case "$1" in
-rpath=*)
arg="${1#-rpath=}"
if [ -z "$arg" ]; then
shift; continue
fi
append_path_lists return_rpath_dirs_list "$arg"
;;
--rpath=*)
arg="${1#--rpath=}"
if [ -z "$arg" ]; then
shift; continue
fi
append_path_lists return_rpath_dirs_list "$arg"
;;
-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=""
init_path_lists return_isystem_include_dirs_list
init_path_lists return_include_dirs_list
init_path_lists return_lib_dirs_list
init_path_lists 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
append_path_lists return_isystem_include_dirs_list "$arg"
;;
-I*)
arg="${1#-I}"
if [ -z "$arg" ]; then shift; arg="$1"; fi
append_path_lists return_include_dirs_list "$arg"
;;
-L*)
arg="${1#-L}"
if [ -z "$arg" ]; then shift; arg="$1"; fi
append_path_lists return_lib_dirs_list "$arg"
;;
-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>
append_path_lists return_rpath_dirs_list "$1"
xlinker_expect_rpath=no
else
case "$1" in
-rpath=*)
arg="${1#-rpath=}"
append_path_lists return_rpath_dirs_list "$arg"
;;
--rpath=*)
arg="${1#--rpath=}"
append_path_lists return_rpath_dirs_list "$arg"
;;
-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")
;;
*)
# if mode is not ld, we can just add to other args
if [ "$mode" != "ld" ]; then
append return_other_args_list "$1"
shift
continue
fi
# if we're in linker mode, we need to parse raw RPATH args
case "$1" in
-rpath=*)
arg="${1#-rpath=}"
append_path_lists return_rpath_dirs_list "$arg"
;;
--rpath=*)
arg="${1#--rpath=}"
append_path_lists return_rpath_dirs_list "$arg"
;;
-rpath|--rpath)
if [ $# -eq 1 ]; then
# -rpath without value: let the linker raise an error.
append return_other_args_list "$1"
break
fi
shift
append_path_lists return_rpath_dirs_list "$1"
;;
*)
append return_other_args_list "$1"
;;
esac
;;
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 "$@"
assign_path_lists isystem_include_dirs_list return_isystem_include_dirs_list
assign_path_lists include_dirs_list return_include_dirs_list
assign_path_lists lib_dirs_list return_lib_dirs_list
assign_path_lists rpath_dirs_list return_rpath_dirs_list
isystem_was_used="$return_isystem_was_used"
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_ALWAYS_FFLAGS
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_ALWAYS_CPPFLAGS
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_ALWAYS_CFLAGS
extend spack_flags_list SPACK_CFLAGS
preextend flags_list SPACK_TARGET_ARGS_CC
;;
CXX)
extend spack_flags_list SPACK_ALWAYS_CXXFLAGS
extend spack_flags_list SPACK_CXXFLAGS
preextend flags_list SPACK_TARGET_ARGS_CXX
;;
F)
preextend flags_list SPACK_TARGET_ARGS_FORTRAN
;;
esac
;;
esac
# Linker flags
case "$mode" in
ccld)
extend spack_flags_list SPACK_LDFLAGS
;;
esac
IFS="$lsep"
categorize_arguments $spack_flags_list
unset IFS
assign_path_lists spack_flags_isystem_include_dirs_list return_isystem_include_dirs_list
assign_path_lists spack_flags_include_dirs_list return_include_dirs_list
assign_path_lists spack_flags_lib_dirs_list return_lib_dirs_list
assign_path_lists spack_flags_rpath_dirs_list return_rpath_dirs_list
spack_flags_isystem_was_used="$return_isystem_was_used"
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 spack_store_rpath_dirs_list SPACK_STORE_RPATH_DIRS
extend rpath_dirs_list SPACK_RPATH_DIRS
fi
fi
if [ "$mode" = ccld ] || [ "$mode" = ld ]; then
extend spack_store_lib_dirs_list SPACK_STORE_LINK_DIRS
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
case "$mode" in
cpp|cc|as|ccld)
if [ "$spack_flags_isystem_was_used" = "true" ] || [ "$isystem_was_used" = "true" ]; then
extend spack_store_isystem_include_dirs_list SPACK_STORE_INCLUDE_DIRS
extend isystem_include_dirs_list SPACK_INCLUDE_DIRS
else
extend spack_store_include_dirs_list SPACK_STORE_INCLUDE_DIRS
extend include_dirs_list SPACK_INCLUDE_DIRS
fi
;;
esac
#
# Finally, reassemble the command line.
#
args_list="$flags_list"
# Include search paths partitioned by (in store, non-sytem, system)
# NOTE: adding ${lsep} to the prefix here turns every added element into two
extend args_list spack_store_spack_flags_include_dirs_list -I
extend args_list spack_store_include_dirs_list -I
extend args_list spack_flags_include_dirs_list -I
extend args_list include_dirs_list -I
extend args_list spack_store_spack_flags_isystem_include_dirs_list "-isystem${lsep}"
extend args_list spack_store_isystem_include_dirs_list "-isystem${lsep}"
extend args_list spack_flags_isystem_include_dirs_list "-isystem${lsep}"
extend args_list isystem_include_dirs_list "-isystem${lsep}"
extend args_list system_spack_flags_include_dirs_list -I
extend args_list system_include_dirs_list -I
extend args_list system_spack_flags_isystem_include_dirs_list "-isystem${lsep}"
extend args_list system_isystem_include_dirs_list "-isystem${lsep}"
# Library search paths partitioned by (in store, non-sytem, system)
extend args_list spack_store_spack_flags_lib_dirs_list "-L"
extend args_list spack_store_lib_dirs_list "-L"
extend args_list spack_flags_lib_dirs_list "-L"
extend args_list lib_dirs_list "-L"
extend args_list system_spack_flags_lib_dirs_list "-L"
extend args_list system_lib_dirs_list "-L"
# RPATHs arguments
rpath_prefix=""
case "$mode" in
ccld)
if [ -n "$dtags_to_add" ] ; then
append args_list "$linker_arg$dtags_to_add"
fi
rpath_prefix="$rpath"
;;
ld)
if [ -n "$dtags_to_add" ] ; then
append args_list "$dtags_to_add"
fi
rpath_prefix="-rpath${lsep}"
;;
esac
# if mode is ccld or ld, extend RPATH lists with the prefix determined above
if [ -n "$rpath_prefix" ]; then
extend args_list spack_store_spack_flags_rpath_dirs_list "$rpath_prefix"
extend args_list spack_store_rpath_dirs_list "$rpath_prefix"
extend args_list spack_flags_rpath_dirs_list "$rpath_prefix"
extend args_list rpath_dirs_list "$rpath_prefix"
extend args_list system_spack_flags_rpath_dirs_list "$rpath_prefix"
extend args_list system_rpath_dirs_list "$rpath_prefix"
fi
# 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
execute

View File

@@ -1 +0,0 @@
../../cc

View File

@@ -1 +0,0 @@
../../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

1
lib/spack/env/cpp vendored
View File

@@ -1 +0,0 @@
cc

1
lib/spack/env/f77 vendored
View File

@@ -1 +0,0 @@
cc

1
lib/spack/env/f90 vendored
View File

@@ -1 +0,0 @@
cc

1
lib/spack/env/f95 vendored
View File

@@ -1 +0,0 @@
cc

1
lib/spack/env/fc vendored
View File

@@ -1 +0,0 @@
cc

View File

@@ -1 +0,0 @@
../../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

1
lib/spack/env/ftn vendored
View File

@@ -1 +0,0 @@
cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

1
lib/spack/env/ld vendored
View File

@@ -1 +0,0 @@
cc

View File

@@ -1 +0,0 @@
cc

View File

@@ -1 +0,0 @@
cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cpp

View File

@@ -1 +0,0 @@
../fc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -1 +0,0 @@
../cc

View File

@@ -21,6 +21,7 @@
import spack.platforms
import spack.spec
import spack.traverse
import spack.version
from .config import spec_for_current_python
@@ -126,6 +127,10 @@ def concretize(self) -> "spack.spec.Spec":
if node.name == "gcc-runtime":
node.versions = self.host_compiler.versions
# Can't use re2c@3.1 with Python 3.6
if self.host_python.satisfies("@3.6"):
s["re2c"].versions.versions = [spack.version.from_string("=2.2")]
for edge in spack.traverse.traverse_edges([s], cover="edges"):
if edge.spec.name == "python":
edge.spec = self.host_python

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -584,23 +584,6 @@ def set_package_py_globals(pkg, context: Context = Context.BUILD):
# Don't use which for this; we want to find it in the current dir.
module.configure = Executable("./configure")
# Put spack compiler paths in module scope. (Some packages use it
# in setup_run_environment etc, so don't put it context == build)
link_dir = spack.paths.build_env_path
# Set spack_cc, etc. for backward compatibility. This might change if the compiler wrapper
# is modeled as a package.
global_names = {
"c": ("spack_cc",),
"cxx": ("spack_cxx",),
"fortran": ("spack_fc", "spack_f77"),
}
for language in ("c", "cxx", "fortran"):
spec = pkg.spec.dependencies(virtuals=[language])
value = None if not spec else os.path.join(link_dir, spec[0].package.link_paths[language])
for name in global_names[language]:
setattr(module, name, value)
# Useful directories within the prefix are encapsulated in
# a Prefix object.
module.prefix = pkg.prefix

View File

@@ -4,22 +4,16 @@
import itertools
import os
import pathlib
import platform
import re
import sys
from typing import Dict, List, Optional, Sequence, Tuple, Union
import archspec.cpu
import llnl.util.tty as tty
from llnl.util.lang import classproperty, memoized
import spack
import spack.compilers.error
import spack.compilers.libraries
import spack.config
import spack.package_base
import spack.paths
import spack.util.executable
# Local "type" for type hints
@@ -109,7 +103,7 @@ def determine_version(cls, exe: Path) -> str:
@classmethod
def compiler_bindir(cls, prefix: Path) -> Path:
"""Overridable method for the location of the compiler bindir within the preifx"""
"""Overridable method for the location of the compiler bindir within the prefix"""
return os.path.join(prefix, "bin")
@classmethod
@@ -183,109 +177,6 @@ def standard_flag(self, *, language: str, standard: str) -> str:
def _standard_flag(self, *, language: str, standard: str) -> str:
raise NotImplementedError("Must be implemented by derived classes")
@property
def disable_new_dtags(self) -> str:
if platform.system() == "Darwin":
return ""
return "--disable-new-dtags"
@property
def enable_new_dtags(self) -> str:
if platform.system() == "Darwin":
return ""
return "--enable-new-dtags"
def setup_dependent_build_environment(self, env, dependent_spec):
# FIXME (compiler as nodes): check if this is good enough or should be made more general
# The package is not used as a compiler, so skip this setup
if not any(
lang in dependent_spec and dependent_spec[lang].name == self.spec.name
for lang in ("c", "cxx", "fortran")
):
return
# Populate an object with the list of environment modifications and return it
link_dir = pathlib.Path(spack.paths.build_env_path)
env_paths = []
for language, attr_name, wrapper_var_name, spack_var_name in [
("c", "cc", "CC", "SPACK_CC"),
("cxx", "cxx", "CXX", "SPACK_CXX"),
("fortran", "fortran", "F77", "SPACK_F77"),
("fortran", "fortran", "FC", "SPACK_FC"),
]:
if language not in dependent_spec or dependent_spec[language].name != self.spec.name:
continue
if not hasattr(self, attr_name):
continue
compiler = getattr(self, attr_name)
env.set(spack_var_name, compiler)
if language not in self.link_paths:
continue
wrapper_path = link_dir / self.link_paths.get(language)
env.set(wrapper_var_name, str(wrapper_path))
env.set(f"SPACK_{wrapper_var_name}_RPATH_ARG", self.rpath_arg)
uarch = dependent_spec.architecture.target
version_number, _ = archspec.cpu.version_components(
self.spec.version.dotted_numeric_string
)
try:
isa_arg = uarch.optimization_flags(self.archspec_name(), version_number)
except (ValueError, archspec.cpu.UnsupportedMicroarchitecture):
isa_arg = ""
if isa_arg:
env.set(f"SPACK_TARGET_ARGS_{attr_name.upper()}", isa_arg)
# Add spack build environment path with compiler wrappers first in
# the path. We add the compiler wrapper path, which includes default
# wrappers (cc, c++, f77, f90), AND a subdirectory containing
# compiler-specific symlinks. The latter ensures that builds that
# are sensitive to the *name* of the compiler see the right name when
# we're building with the wrappers.
#
# Conflicts on case-insensitive systems (like "CC" and "cc") are
# handled by putting one in the <build_env_path>/case-insensitive
# directory. Add that to the path too.
compiler_specific = os.path.join(
spack.paths.build_env_path, os.path.dirname(self.link_paths[language])
)
for item in [spack.paths.build_env_path, compiler_specific]:
env_paths.append(item)
ci = os.path.join(item, "case-insensitive")
if os.path.isdir(ci):
env_paths.append(ci)
# FIXME (compiler as nodes): make these paths language specific
env.set("SPACK_LINKER_ARG", self.linker_arg)
paths = _implicit_rpaths(pkg=self)
if paths:
env.set("SPACK_COMPILER_IMPLICIT_RPATHS", ":".join(paths))
# Check whether we want to force RPATH or RUNPATH
if spack.config.CONFIG.get("config:shared_linking:type") == "rpath":
env.set("SPACK_DTAGS_TO_STRIP", self.enable_new_dtags)
env.set("SPACK_DTAGS_TO_ADD", self.disable_new_dtags)
else:
env.set("SPACK_DTAGS_TO_STRIP", self.disable_new_dtags)
env.set("SPACK_DTAGS_TO_ADD", self.enable_new_dtags)
spec = self.spec
if spec.extra_attributes:
extra_rpaths = spec.extra_attributes.get("extra_rpaths")
if extra_rpaths:
env.append_path("SPACK_COMPILER_EXTRA_RPATHS", ":".join(extra_rpaths))
for item in env_paths:
env.prepend_path("SPACK_ENV_PATH", item)
def archspec_name(self) -> str:
"""Name that archspec uses to refer to this compiler"""
return self.spec.name
@@ -324,12 +215,6 @@ def _fortran_path(self) -> Optional[str]:
return None
def _implicit_rpaths(pkg: spack.package_base.PackageBase) -> List[str]:
detector = spack.compilers.libraries.CompilerPropertyDetector(pkg.spec)
paths = detector.implicit_rpaths()
return paths
@memoized
def _compiler_output(
compiler_path: Path, *, version_argument: str, ignore_errors: Tuple[int, ...] = ()

View File

@@ -38,7 +38,6 @@
r"^lib/spack/spack/.*\.sh$",
r"^lib/spack/spack/.*\.lp$",
r"^lib/spack/llnl/.*\.py$",
r"^lib/spack/env/cc$",
# special case some test data files that have license headers
r"^lib/spack/spack/test/data/style/broken.dummy",
r"^lib/spack/spack/test/data/unparse/.*\.txt",

View File

@@ -31,7 +31,6 @@
# spack directory hierarchy
lib_path = os.path.join(prefix, "lib", "spack")
external_path = os.path.join(lib_path, "external")
build_env_path = os.path.join(lib_path, "env")
module_path = os.path.join(lib_path, "spack")
command_path = os.path.join(module_path, "cmd")
analyzers_path = os.path.join(module_path, "analyzers")

View File

@@ -3149,6 +3149,16 @@ def define_runtime_constraints(self):
# Inject default flags for compilers
recorder("*").default_flags(compiler)
# FIXME (compiler as nodes): think of using isinstance(compiler_cls, WrappedCompiler)
# Add a dependency on the compiler wrapper
if sys.platform != "win32":
recorder("*").depends_on(
"compiler-wrapper",
when=f"%{compiler.name}@{compiler.versions}",
type="build",
description=f"Add the compiler wrapper when using {compiler}",
)
if not using_libc_compatibility():
continue

View File

@@ -2,8 +2,8 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import platform
import posixpath
import sys
import pytest
@@ -13,19 +13,16 @@
from llnl.util.filesystem import HeaderList, LibraryList
import spack.build_environment
import spack.build_systems.compiler
import spack.concretize
import spack.config
import spack.deptypes as dt
import spack.package_base
import spack.paths
import spack.spec
import spack.util.environment
import spack.util.spack_yaml as syaml
from spack.build_environment import UseMode, _static_to_shared_library, dso_suffix
from spack.context import Context
from spack.installer import PackageInstaller
from spack.paths import build_env_path
from spack.util.environment import EnvironmentModifications
from spack.util.executable import Executable
@@ -42,55 +39,41 @@ def prep_and_join(path, *pths):
@pytest.fixture
def build_environment(working_env):
cc = Executable(os.path.join(build_env_path, "cc"))
cxx = Executable(os.path.join(build_env_path, "c++"))
fc = Executable(os.path.join(build_env_path, "fc"))
def build_environment(monkeypatch, wrapper_dir, tmp_path):
realcc = "/bin/mycc"
prefix = "/spack-test-prefix"
prefix = str(tmp_path)
os.environ["SPACK_CC"] = realcc
os.environ["SPACK_CXX"] = realcc
os.environ["SPACK_FC"] = realcc
monkeypatch.setenv("SPACK_CC", realcc)
monkeypatch.setenv("SPACK_CXX", realcc)
monkeypatch.setenv("SPACK_FC", realcc)
os.environ["SPACK_PREFIX"] = prefix
os.environ["SPACK_ENV_PATH"] = "test"
os.environ["SPACK_DEBUG_LOG_DIR"] = "."
os.environ["SPACK_DEBUG_LOG_ID"] = "foo-hashabc"
os.environ["SPACK_SHORT_SPEC"] = "foo@1.2 arch=linux-rhel6-x86_64 /hashabc"
monkeypatch.setenv("SPACK_PREFIX", prefix)
monkeypatch.setenv("SPACK_ENV_PATH", "test")
monkeypatch.setenv("SPACK_DEBUG_LOG_DIR", ".")
monkeypatch.setenv("SPACK_DEBUG_LOG_ID", "foo-hashabc")
monkeypatch.setenv("SPACK_SHORT_SPEC", "foo@1.2 arch=linux-rhel6-x86_64 /hashabc")
os.environ["SPACK_CC_RPATH_ARG"] = "-Wl,-rpath,"
os.environ["SPACK_CXX_RPATH_ARG"] = "-Wl,-rpath,"
os.environ["SPACK_F77_RPATH_ARG"] = "-Wl,-rpath,"
os.environ["SPACK_FC_RPATH_ARG"] = "-Wl,-rpath,"
os.environ["SPACK_LINKER_ARG"] = "-Wl,"
os.environ["SPACK_DTAGS_TO_ADD"] = "--disable-new-dtags"
os.environ["SPACK_DTAGS_TO_STRIP"] = "--enable-new-dtags"
os.environ["SPACK_SYSTEM_DIRS"] = "/usr/include|/usr/lib"
os.environ["SPACK_MANAGED_DIRS"] = f"{prefix}/opt/spack"
os.environ["SPACK_TARGET_ARGS"] = ""
monkeypatch.setenv("SPACK_CC_RPATH_ARG", "-Wl,-rpath,")
monkeypatch.setenv("SPACK_CXX_RPATH_ARG", "-Wl,-rpath,")
monkeypatch.setenv("SPACK_F77_RPATH_ARG", "-Wl,-rpath,")
monkeypatch.setenv("SPACK_FC_RPATH_ARG", "-Wl,-rpath,")
monkeypatch.setenv("SPACK_CC_LINKER_ARG", "-Wl,")
monkeypatch.setenv("SPACK_CXX_LINKER_ARG", "-Wl,")
monkeypatch.setenv("SPACK_FC_LINKER_ARG", "-Wl,")
monkeypatch.setenv("SPACK_F77_LINKER_ARG", "-Wl,")
monkeypatch.setenv("SPACK_DTAGS_TO_ADD", "--disable-new-dtags")
monkeypatch.setenv("SPACK_DTAGS_TO_STRIP", "--enable-new-dtags")
monkeypatch.setenv("SPACK_SYSTEM_DIRS", "/usr/include|/usr/lib")
monkeypatch.setenv("SPACK_MANAGED_DIRS", f"{prefix}/opt/spack")
monkeypatch.setenv("SPACK_TARGET_ARGS", "")
if "SPACK_DEPENDENCIES" in os.environ:
del os.environ["SPACK_DEPENDENCIES"]
monkeypatch.delenv("SPACK_DEPENDENCIES", raising=False)
yield {"cc": cc, "cxx": cxx, "fc": fc}
cc = Executable(str(wrapper_dir / "cc"))
cxx = Executable(str(wrapper_dir / "c++"))
fc = Executable(str(wrapper_dir / "fc"))
for name in (
"SPACK_CC",
"SPACK_CXX",
"SPACK_FC",
"SPACK_PREFIX",
"SPACK_ENV_PATH",
"SPACK_DEBUG_LOG_DIR",
"SPACK_SHORT_SPEC",
"SPACK_CC_RPATH_ARG",
"SPACK_CXX_RPATH_ARG",
"SPACK_F77_RPATH_ARG",
"SPACK_FC_RPATH_ARG",
"SPACK_TARGET_ARGS",
):
del os.environ[name]
return {"cc": cc, "cxx": cxx, "fc": fc}
@pytest.fixture
@@ -322,14 +305,14 @@ def test_external_config_env(mock_packages, mutable_config, working_env):
@pytest.mark.regression("9107")
@pytest.mark.not_on_windows("Windows does not support module files")
def test_spack_paths_before_module_paths(
mutable_config, mock_packages, compiler_factory, monkeypatch, working_env
mutable_config, mock_packages, compiler_factory, monkeypatch, working_env, wrapper_dir
):
gcc_entry = compiler_factory(spec="gcc@14.0.1 languages=c,c++")
gcc_entry["modules"] = ["some_module"]
mutable_config.set("packages", {"gcc": {"externals": [gcc_entry]}})
module_path = os.path.join("path", "to", "module")
spack_path = os.path.join(spack.paths.prefix, os.path.join("lib", "spack", "env"))
monkeypatch.setenv("SPACK_ENV_PATH", wrapper_dir)
def _set_wrong_cc(x):
os.environ["PATH"] = module_path + os.pathsep + os.environ["PATH"]
@@ -341,7 +324,7 @@ def _set_wrong_cc(x):
spack.build_environment.setup_package(s.package, dirty=False)
paths = os.environ["PATH"].split(os.pathsep)
assert paths.index(spack_path) < paths.index(module_path)
assert paths.index(str(wrapper_dir)) < paths.index(module_path)
def test_package_inheritance_module_setup(config, mock_packages, working_env):
@@ -484,11 +467,9 @@ def test_parallel_false_is_not_propagating(default_mock_concretization):
@pytest.mark.parametrize(
"config_setting,expected_flag",
[
("runpath", "" if platform.system() == "Darwin" else "--enable-new-dtags"),
("rpath", "" if platform.system() == "Darwin" else "--disable-new-dtags"),
],
[("runpath", "--enable-new-dtags"), ("rpath", "--disable-new-dtags")],
)
@pytest.mark.skipif(sys.platform != "linux", reason="dtags make sense only on linux")
def test_setting_dtags_based_on_config(
config_setting, expected_flag, config, mock_packages, working_env
):
@@ -787,12 +768,11 @@ def test_optimization_flags_are_using_node_target(default_mock_concretization, m
"""Tests that we are using the target on the node to be compiled to retrieve the uarch
specific flags, and not the target of the compiler.
"""
monkeypatch.setattr(spack.build_systems.compiler, "_implicit_rpaths", lambda pkg: [])
gcc = default_mock_concretization("gcc target=core2")
compiler_wrapper_pkg = default_mock_concretization("compiler-wrapper target=core2").package
mpileaks = default_mock_concretization("mpileaks target=x86_64")
env = EnvironmentModifications()
gcc.package.setup_dependent_build_environment(env, mpileaks)
compiler_wrapper_pkg.setup_dependent_build_environment(env, mpileaks)
actions = env.group_by_name()["SPACK_TARGET_ARGS_CC"]
assert len(actions) == 1 and isinstance(actions[0], spack.util.environment.SetEnv)

View File

@@ -12,7 +12,6 @@
import spack.build_environment
import spack.config
from spack.paths import build_env_path
from spack.util.environment import SYSTEM_DIR_CASE_ENTRY, set_env
from spack.util.executable import Executable, ProcessError
@@ -110,12 +109,6 @@
#: The prefix of the package being mock installed
pkg_prefix = "/spack-test-prefix"
# Compilers to use during tests
cc = Executable(os.path.join(build_env_path, "cc"))
ld = Executable(os.path.join(build_env_path, "ld"))
cpp = Executable(os.path.join(build_env_path, "cpp"))
cxx = Executable(os.path.join(build_env_path, "c++"))
fc = Executable(os.path.join(build_env_path, "fc"))
#: the "real" compiler the wrapper is expected to invoke
real_cc = "/bin/mycc"
@@ -169,7 +162,10 @@ def wrapper_environment(working_env):
SPACK_TARGET_ARGS_CC="-march=znver2 -mtune=znver2",
SPACK_TARGET_ARGS_CXX="-march=znver2 -mtune=znver2",
SPACK_TARGET_ARGS_FORTRAN="-march=znver4 -mtune=znver4",
SPACK_LINKER_ARG="-Wl,",
SPACK_CC_LINKER_ARG="-Wl,",
SPACK_CXX_LINKER_ARG="-Wl,",
SPACK_FC_LINKER_ARG="-Wl,",
SPACK_F77_LINKER_ARG="-Wl,",
SPACK_DTAGS_TO_ADD="--disable-new-dtags",
SPACK_DTAGS_TO_STRIP="--enable-new-dtags",
SPACK_COMPILER_FLAGS_KEEP="",
@@ -198,6 +194,7 @@ def check_args(cc, args, expected):
per line, so that we see whether arguments that should (or shouldn't)
contain spaces are parsed correctly.
"""
cc = Executable(str(cc))
with set_env(SPACK_TEST_COMMAND="dump-args"):
cc_modified_args = cc(*args, output=str).strip().split("\n")
assert cc_modified_args == expected
@@ -210,9 +207,9 @@ def check_args_contents(cc, args, must_contain, must_not_contain):
per line, so that we see whether arguments that should (or shouldn't)
contain spaces are parsed correctly.
"""
cc = Executable(str(cc))
with set_env(SPACK_TEST_COMMAND="dump-args"):
cc_modified_args = cc(*args, output=str).strip().split("\n")
print(cc_modified_args)
for a in must_contain:
assert a in cc_modified_args
for a in must_not_contain:
@@ -225,6 +222,7 @@ def check_env_var(executable, var, expected):
This assumes that cc will print debug output when it's environment
contains SPACK_TEST_COMMAND=dump-env-<variable-to-debug>
"""
executable = Executable(str(executable))
with set_env(SPACK_TEST_COMMAND="dump-env-" + var):
output = executable(*test_args, output=str).strip()
assert executable.path + ": " + var + ": " + expected == output
@@ -232,17 +230,25 @@ def check_env_var(executable, var, expected):
def dump_mode(cc, args):
"""Make cc dump the mode it detects, and return it."""
cc = Executable(str(cc))
with set_env(SPACK_TEST_COMMAND="dump-mode"):
return cc(*args, output=str).strip()
def test_no_wrapper_environment():
def test_no_wrapper_environment(wrapper_dir):
cc = Executable(str(wrapper_dir / "cc"))
with pytest.raises(ProcessError):
output = cc(output=str)
assert "Spack compiler must be run from Spack" in output
def test_vcheck_mode(wrapper_environment):
def test_modes(wrapper_environment, wrapper_dir):
cc = wrapper_dir / "cc"
cxx = wrapper_dir / "c++"
cpp = wrapper_dir / "cpp"
ld = wrapper_dir / "ld"
# vcheck
assert dump_mode(cc, ["-I/include", "--version"]) == "vcheck"
assert dump_mode(cc, ["-I/include", "-V"]) == "vcheck"
assert dump_mode(cc, ["-I/include", "-v"]) == "vcheck"
@@ -250,38 +256,39 @@ def test_vcheck_mode(wrapper_environment):
assert dump_mode(cc, ["-I/include", "--version", "-c"]) == "vcheck"
assert dump_mode(cc, ["-I/include", "-V", "-o", "output"]) == "vcheck"
def test_cpp_mode(wrapper_environment):
# cpp
assert dump_mode(cc, ["-E"]) == "cpp"
assert dump_mode(cxx, ["-E"]) == "cpp"
assert dump_mode(cpp, []) == "cpp"
def test_as_mode(wrapper_environment):
# as
assert dump_mode(cc, ["-S"]) == "as"
def test_ccld_mode(wrapper_environment):
# ccld
assert dump_mode(cc, []) == "ccld"
assert dump_mode(cc, ["foo.c", "-o", "foo"]) == "ccld"
assert dump_mode(cc, ["foo.c", "-o", "foo", "-Wl,-rpath,foo"]) == "ccld"
assert dump_mode(cc, ["foo.o", "bar.o", "baz.o", "-o", "foo", "-Wl,-rpath,foo"]) == "ccld"
def test_ld_mode(wrapper_environment):
# ld
assert dump_mode(ld, []) == "ld"
assert dump_mode(ld, ["foo.o", "bar.o", "baz.o", "-o", "foo", "-Wl,-rpath,foo"]) == "ld"
def test_ld_unterminated_rpath(wrapper_environment):
@pytest.mark.regression("37179")
def test_expected_args(wrapper_environment, wrapper_dir):
cc = wrapper_dir / "cc"
fc = wrapper_dir / "fc"
ld = wrapper_dir / "ld"
# ld_unterminated_rpath
check_args(
ld,
["foo.o", "bar.o", "baz.o", "-o", "foo", "-rpath"],
["ld", "--disable-new-dtags", "foo.o", "bar.o", "baz.o", "-o", "foo", "-rpath"],
)
def test_xlinker_unterminated_rpath(wrapper_environment):
# xlinker_unterminated_rpath
check_args(
cc,
["foo.o", "bar.o", "baz.o", "-o", "foo", "-Xlinker", "-rpath"],
@@ -299,8 +306,7 @@ def test_xlinker_unterminated_rpath(wrapper_environment):
],
)
def test_wl_unterminated_rpath(wrapper_environment):
# wl_unterminated_rpath
check_args(
cc,
["foo.o", "bar.o", "baz.o", "-o", "foo", "-Wl,-rpath"],
@@ -309,99 +315,7 @@ def test_wl_unterminated_rpath(wrapper_environment):
+ ["-Wl,--disable-new-dtags", "foo.o", "bar.o", "baz.o", "-o", "foo", "-Wl,-rpath"],
)
def test_ld_flags(wrapper_environment, wrapper_flags):
check_args(
ld,
test_args,
["ld"]
+ test_include_paths
+ test_library_paths
+ ["--disable-new-dtags"]
+ test_rpaths
+ test_args_without_paths
+ spack_ldlibs,
)
def test_cpp_flags(wrapper_environment, wrapper_flags):
check_args(
cpp,
test_args,
["cpp"]
+ test_include_paths
+ test_library_paths
+ test_args_without_paths
+ spack_cppflags,
)
def test_cc_flags(wrapper_environment, wrapper_flags):
check_args(
cc,
test_args,
[real_cc]
+ target_args
+ test_include_paths
+ ["-Lfoo"]
+ test_library_paths
+ ["-Wl,--disable-new-dtags"]
+ test_wl_rpaths
+ test_args_without_paths
+ spack_cppflags
+ spack_cflags
+ ["-Wl,--gc-sections"]
+ spack_ldlibs,
)
def test_cxx_flags(wrapper_environment, wrapper_flags):
check_args(
cxx,
test_args,
[real_cc]
+ target_args
+ test_include_paths
+ ["-Lfoo"]
+ test_library_paths
+ ["-Wl,--disable-new-dtags"]
+ test_wl_rpaths
+ test_args_without_paths
+ spack_cppflags
+ ["-Wl,--gc-sections"]
+ spack_ldlibs,
)
def test_fc_flags(wrapper_environment, wrapper_flags):
check_args(
fc,
test_args,
[real_cc]
+ target_args_fc
+ test_include_paths
+ ["-Lfoo"]
+ test_library_paths
+ ["-Wl,--disable-new-dtags"]
+ test_wl_rpaths
+ test_args_without_paths
+ spack_fflags
+ spack_cppflags
+ ["-Wl,--gc-sections"]
+ spack_ldlibs,
)
def test_always_cflags(wrapper_environment, wrapper_flags):
with set_env(SPACK_ALWAYS_CFLAGS="-always1 -always2"):
check_args(
cc,
["-v", "--cmd-line-v-opt"],
[real_cc] + ["-always1", "-always2"] + ["-v", "--cmd-line-v-opt"],
)
def test_Wl_parsing(wrapper_environment):
# Wl_parsing
check_args(
cc,
["-Wl,-rpath,/a,--enable-new-dtags,-rpath=/b,--rpath", "-Wl,/c"],
@@ -410,26 +324,22 @@ def test_Wl_parsing(wrapper_environment):
+ ["-Wl,--disable-new-dtags", "-Wl,-rpath,/a", "-Wl,-rpath,/b", "-Wl,-rpath,/c"],
)
@pytest.mark.regression("37179")
def test_Wl_parsing_with_missing_value(wrapper_environment):
# Wl_parsing_with_missing_value
check_args(
cc,
["-Wl,-rpath=/a,-rpath=", "-Wl,--rpath="],
[real_cc] + target_args + ["-Wl,--disable-new-dtags", "-Wl,-rpath,/a"],
)
@pytest.mark.regression("37179")
def test_Wl_parsing_NAG_is_ignored(wrapper_environment):
# Wl_parsing_NAG_is_ignored
check_args(
fc,
["-Wl,-Wl,,x,,y,,z"],
[real_cc] + target_args_fc + ["-Wl,--disable-new-dtags", "-Wl,-Wl,,x,,y,,z"],
)
def test_Xlinker_parsing(wrapper_environment):
# Xlinker_parsing
#
# -Xlinker <x> ... -Xlinker <y> may have compiler flags inbetween, like -O3 in this
# example. Also check that a trailing -Xlinker (which is a compiler error) is not
# dropped or given an empty argument.
@@ -460,8 +370,8 @@ def test_Xlinker_parsing(wrapper_environment):
],
)
def test_rpath_without_value(wrapper_environment):
# rpath_without_value
#
# cc -Wl,-rpath without a value shouldn't drop -Wl,-rpath;
# same for -Xlinker
check_args(
@@ -475,14 +385,10 @@ def test_rpath_without_value(wrapper_environment):
[real_cc] + target_args + ["-Wl,--disable-new-dtags", "-O3", "-g", "-Xlinker", "-rpath"],
)
def test_dep_rpath(wrapper_environment):
"""Ensure RPATHs for root package are added."""
# dep_rapth
check_args(cc, test_args, [real_cc] + target_args + common_compile_args)
def test_dep_include(wrapper_environment):
"""Ensure a single dependency include directory is added."""
# dep_include
with set_env(SPACK_INCLUDE_DIRS="x"):
check_args(
cc,
@@ -497,29 +403,9 @@ def test_dep_include(wrapper_environment):
+ test_args_without_paths,
)
def test_system_path_cleanup(wrapper_environment):
"""Ensure SPACK_ENV_PATH is removed from PATH, even with trailing /
The compiler wrapper has to ensure that it is not called nested
like it would happen when gcc's collect2 looks in PATH for ld.
To prevent nested calls, the compiler wrapper removes the elements
of SPACK_ENV_PATH from PATH. Autotest's generated testsuite appends
a / to each element of PATH when adding AUTOTEST_PATH.
Thus, ensure that PATH cleanup works even with trailing /.
"""
system_path = "/bin:/usr/bin:/usr/local/bin"
cc_dir = os.path.dirname(cc.path)
with set_env(SPACK_ENV_PATH=cc_dir, SPACK_CC="true"):
with set_env(PATH=cc_dir + ":" + system_path):
check_env_var(cc, "PATH", system_path)
with set_env(PATH=cc_dir + "/:" + system_path):
check_env_var(cc, "PATH", system_path)
def test_dep_lib(wrapper_environment):
"""Ensure a single dependency RPATH is added."""
# dep_lib
#
# Ensure a single dependency RPATH is added
with set_env(SPACK_LINK_DIRS="x", SPACK_RPATH_DIRS="x"):
check_args(
cc,
@@ -535,9 +421,9 @@ def test_dep_lib(wrapper_environment):
+ test_args_without_paths,
)
def test_dep_lib_no_rpath(wrapper_environment):
"""Ensure a single dependency link flag is added with no dep RPATH."""
# dep_lib_no_rpath
#
# Ensure a single dependency link flag is added with no dep RPATH
with set_env(SPACK_LINK_DIRS="x"):
check_args(
cc,
@@ -552,9 +438,8 @@ def test_dep_lib_no_rpath(wrapper_environment):
+ test_args_without_paths,
)
def test_dep_lib_no_lib(wrapper_environment):
"""Ensure a single dependency RPATH is added with no -L."""
# dep_lib_no_lib
# Ensure a single dependency RPATH is added with no -L
with set_env(SPACK_RPATH_DIRS="x"):
check_args(
cc,
@@ -569,9 +454,8 @@ def test_dep_lib_no_lib(wrapper_environment):
+ test_args_without_paths,
)
def test_ccld_deps(wrapper_environment):
"""Ensure all flags are added in ccld mode."""
# ccld_deps
# Ensure all flags are added in ccld mode
with set_env(
SPACK_INCLUDE_DIRS="xinc:yinc:zinc",
SPACK_RPATH_DIRS="xlib:ylib:zlib",
@@ -592,13 +476,13 @@ def test_ccld_deps(wrapper_environment):
+ test_args_without_paths,
)
def test_ccld_deps_isystem(wrapper_environment):
"""Ensure all flags are added in ccld mode.
When a build uses -isystem, Spack should inject it's
include paths using -isystem. Spack will insert these
after any provided -isystem includes, but before any
system directories included using -isystem"""
# ccld_deps_isystem
#
# Ensure all flags are added in ccld mode.
# When a build uses -isystem, Spack should inject it's
# include paths using -isystem. Spack will insert these
# after any provided -isystem includes, but before any
# system directories included using -isystem
with set_env(
SPACK_INCLUDE_DIRS="xinc:yinc:zinc",
SPACK_RPATH_DIRS="xlib:ylib:zlib",
@@ -620,9 +504,8 @@ def test_ccld_deps_isystem(wrapper_environment):
+ test_args_without_paths,
)
def test_cc_deps(wrapper_environment):
"""Ensure -L and RPATHs are not added in cc mode."""
# cc_deps
# Ensure -L and RPATHs are not added in cc mode
with set_env(
SPACK_INCLUDE_DIRS="xinc:yinc:zinc",
SPACK_RPATH_DIRS="xlib:ylib:zlib",
@@ -640,9 +523,8 @@ def test_cc_deps(wrapper_environment):
+ test_args_without_paths,
)
def test_ccld_with_system_dirs(wrapper_environment):
"""Ensure all flags are added in ccld mode."""
# ccld_with_system_dirs
# Ensure all flags are added in ccld mode
with set_env(
SPACK_INCLUDE_DIRS="xinc:yinc:zinc",
SPACK_RPATH_DIRS="xlib:ylib:zlib",
@@ -673,12 +555,11 @@ def test_ccld_with_system_dirs(wrapper_environment):
+ test_args_without_paths,
)
def test_ccld_with_system_dirs_isystem(wrapper_environment):
"""Ensure all flags are added in ccld mode.
Ensure that includes are in the proper
place when a build uses -isystem, and uses
system directories in the include paths"""
# ccld_with_system_dirs_isystem
# Ensure all flags are added in ccld mode.
# Ensure that includes are in the proper
# place when a build uses -isystem, and uses
# system directories in the include paths
with set_env(
SPACK_INCLUDE_DIRS="xinc:yinc:zinc",
SPACK_RPATH_DIRS="xlib:ylib:zlib",
@@ -711,9 +592,8 @@ def test_ccld_with_system_dirs_isystem(wrapper_environment):
+ test_args_without_paths,
)
def test_ld_deps(wrapper_environment):
"""Ensure no (extra) -I args or -Wl, are passed in ld mode."""
# ld_deps
# Ensure no (extra) -I args or -Wl, are passed in ld mode
with set_env(
SPACK_INCLUDE_DIRS="xinc:yinc:zinc",
SPACK_RPATH_DIRS="xlib:ylib:zlib",
@@ -732,9 +612,8 @@ def test_ld_deps(wrapper_environment):
+ test_args_without_paths,
)
def test_ld_deps_no_rpath(wrapper_environment):
"""Ensure SPACK_LINK_DEPS controls -L for ld."""
# ld_deps_no_rpath
# Ensure SPACK_LINK_DEPS controls -L for ld
with set_env(SPACK_INCLUDE_DIRS="xinc:yinc:zinc", SPACK_LINK_DIRS="xlib:ylib:zlib"):
check_args(
ld,
@@ -748,9 +627,8 @@ def test_ld_deps_no_rpath(wrapper_environment):
+ test_args_without_paths,
)
def test_ld_deps_no_link(wrapper_environment):
"""Ensure SPACK_RPATH_DEPS controls -rpath for ld."""
# ld_deps_no_link
# Ensure SPACK_RPATH_DEPS controls -rpath for ld
with set_env(SPACK_INCLUDE_DIRS="xinc:yinc:zinc", SPACK_RPATH_DIRS="xlib:ylib:zlib"):
check_args(
ld,
@@ -765,10 +643,124 @@ def test_ld_deps_no_link(wrapper_environment):
)
def test_ld_deps_partial(wrapper_environment):
def test_expected_args_with_flags(wrapper_environment, wrapper_flags, wrapper_dir):
cc = wrapper_dir / "cc"
cxx = wrapper_dir / "c++"
cpp = wrapper_dir / "cpp"
fc = wrapper_dir / "fc"
ld = wrapper_dir / "ld"
# ld_flags
check_args(
ld,
test_args,
["ld"]
+ test_include_paths
+ test_library_paths
+ ["--disable-new-dtags"]
+ test_rpaths
+ test_args_without_paths
+ spack_ldlibs,
)
# cpp_flags
check_args(
cpp,
test_args,
["cpp"]
+ test_include_paths
+ test_library_paths
+ test_args_without_paths
+ spack_cppflags,
)
# cc_flags
check_args(
cc,
test_args,
[real_cc]
+ target_args
+ test_include_paths
+ ["-Lfoo"]
+ test_library_paths
+ ["-Wl,--disable-new-dtags"]
+ test_wl_rpaths
+ test_args_without_paths
+ spack_cppflags
+ spack_cflags
+ ["-Wl,--gc-sections"]
+ spack_ldlibs,
)
# cxx_flags
check_args(
cxx,
test_args,
[real_cc]
+ target_args
+ test_include_paths
+ ["-Lfoo"]
+ test_library_paths
+ ["-Wl,--disable-new-dtags"]
+ test_wl_rpaths
+ test_args_without_paths
+ spack_cppflags
+ ["-Wl,--gc-sections"]
+ spack_ldlibs,
)
# fc_flags
check_args(
fc,
test_args,
[real_cc]
+ target_args_fc
+ test_include_paths
+ ["-Lfoo"]
+ test_library_paths
+ ["-Wl,--disable-new-dtags"]
+ test_wl_rpaths
+ test_args_without_paths
+ spack_fflags
+ spack_cppflags
+ ["-Wl,--gc-sections"]
+ spack_ldlibs,
)
# always_cflags
with set_env(SPACK_ALWAYS_CFLAGS="-always1 -always2"):
check_args(
cc,
["-v", "--cmd-line-v-opt"],
[real_cc] + ["-always1", "-always2"] + ["-v", "--cmd-line-v-opt"],
)
def test_system_path_cleanup(wrapper_environment, wrapper_dir):
"""Ensure SPACK_ENV_PATH is removed from PATH, even with trailing /
The compiler wrapper has to ensure that it is not called nested
like it would happen when gcc's collect2 looks in PATH for ld.
To prevent nested calls, the compiler wrapper removes the elements
of SPACK_ENV_PATH from PATH. Autotest's generated testsuite appends
a / to each element of PATH when adding AUTOTEST_PATH.
Thus, ensure that PATH cleanup works even with trailing /.
"""
cc = wrapper_dir / "cc"
system_path = "/bin:/usr/bin:/usr/local/bin"
with set_env(SPACK_ENV_PATH=str(wrapper_dir), SPACK_CC="true"):
with set_env(PATH=str(wrapper_dir) + ":" + system_path):
check_env_var(cc, "PATH", system_path)
with set_env(PATH=str(wrapper_dir) + "/:" + system_path):
check_env_var(cc, "PATH", system_path)
def test_ld_deps_partial(wrapper_environment, wrapper_dir):
"""Make sure ld -r (partial link) is handled correctly on OS's where it
doesn't accept rpaths.
"""
ld = wrapper_dir / "ld"
with set_env(SPACK_INCLUDE_DIRS="xinc", SPACK_RPATH_DIRS="xlib", SPACK_LINK_DIRS="xlib"):
# TODO: do we need to add RPATHs on other platforms like Linux?
# TODO: Can't we treat them the same?
@@ -805,7 +797,8 @@ def test_ld_deps_partial(wrapper_environment):
)
def test_ccache_prepend_for_cc(wrapper_environment):
def test_ccache_prepend_for_cc(wrapper_environment, wrapper_dir):
cc = wrapper_dir / "cc"
with set_env(SPACK_CCACHE_BINARY="ccache"):
os.environ["SPACK_SHORT_SPEC"] = "foo@1.2=linux-x86_64"
check_args(
@@ -828,7 +821,8 @@ def test_ccache_prepend_for_cc(wrapper_environment):
)
def test_no_ccache_prepend_for_fc(wrapper_environment):
def test_no_ccache_prepend_for_fc(wrapper_environment, wrapper_dir):
fc = wrapper_dir / "fc"
os.environ["SPACK_SHORT_SPEC"] = "foo@1.2=linux-x86_64"
check_args(
fc,
@@ -845,7 +839,8 @@ def test_no_ccache_prepend_for_fc(wrapper_environment):
)
def test_keep_and_replace(wrapper_environment):
def test_keep_and_replace(wrapper_environment, wrapper_dir):
cc = wrapper_dir / "cc"
werror_specific = ["-Werror=meh"]
werror = ["-Werror"]
werror_all = werror_specific + werror
@@ -906,7 +901,8 @@ def test_keep_and_replace(wrapper_environment):
],
)
@pytest.mark.usefixtures("wrapper_environment", "mutable_config")
def test_flag_modification(cfg_override, initial, expected, must_be_gone):
def test_flag_modification(cfg_override, initial, expected, must_be_gone, wrapper_dir):
cc = wrapper_dir / "cc"
spack.config.add(cfg_override)
env = spack.build_environment.clean_environment()
@@ -917,7 +913,9 @@ def test_flag_modification(cfg_override, initial, expected, must_be_gone):
@pytest.mark.regression("9160")
def test_disable_new_dtags(wrapper_environment, wrapper_flags):
def test_disable_new_dtags(wrapper_environment, wrapper_flags, wrapper_dir):
cc = Executable(str(wrapper_dir / "cc"))
ld = Executable(str(wrapper_dir / "ld"))
with set_env(SPACK_TEST_COMMAND="dump-args"):
result = ld(*test_args, output=str).strip().split("\n")
assert "--disable-new-dtags" in result
@@ -926,7 +924,9 @@ def test_disable_new_dtags(wrapper_environment, wrapper_flags):
@pytest.mark.regression("9160")
def test_filter_enable_new_dtags(wrapper_environment, wrapper_flags):
def test_filter_enable_new_dtags(wrapper_environment, wrapper_flags, wrapper_dir):
cc = Executable(str(wrapper_dir / "cc"))
ld = Executable(str(wrapper_dir / "ld"))
with set_env(SPACK_TEST_COMMAND="dump-args"):
result = ld(*(test_args + ["--enable-new-dtags"]), output=str)
result = result.strip().split("\n")
@@ -938,7 +938,9 @@ def test_filter_enable_new_dtags(wrapper_environment, wrapper_flags):
@pytest.mark.regression("22643")
def test_linker_strips_loopopt(wrapper_environment, wrapper_flags):
def test_linker_strips_loopopt(wrapper_environment, wrapper_flags, wrapper_dir):
cc = Executable(str(wrapper_dir / "cc"))
ld = Executable(str(wrapper_dir / "ld"))
with set_env(SPACK_TEST_COMMAND="dump-args"):
# ensure that -loopopt=0 is not present in ld mode
result = ld(*(test_args + ["-loopopt=0"]), output=str)
@@ -958,7 +960,9 @@ def test_linker_strips_loopopt(wrapper_environment, wrapper_flags):
assert "-loopopt=0" in result
def test_spack_managed_dirs_are_prioritized(wrapper_environment):
def test_spack_managed_dirs_are_prioritized(wrapper_environment, wrapper_dir):
cc = Executable(str(wrapper_dir / "cc"))
# We have two different stores with 5 packages divided over them
pkg1 = "/path/to/spack-1/opt/spack/linux-ubuntu22.04-zen2/gcc-13.2.0/pkg-1.0-abcdef"
pkg2 = "/path/to/spack-1/opt/spack/linux-ubuntu22.04-zen2/gcc-13.2.0/pkg-2.0-abcdef"

View File

@@ -344,10 +344,20 @@ def test_get_spec_filter_list(mutable_mock_env_path, mutable_mock_repo):
"libelf",
"gcc",
"gcc-runtime",
"compiler-wrapper",
}
depth_2_set = {"mpich", "callpath", "dyninst", "libdwarf", "libelf", "gcc", "gcc-runtime"}
depth_1_set = {"dyninst", "libdwarf", "libelf", "gcc", "gcc-runtime"}
depth_0_set = {"libdwarf", "libelf", "gcc", "gcc-runtime"}
depth_2_set = {
"mpich",
"callpath",
"dyninst",
"libdwarf",
"libelf",
"gcc",
"gcc-runtime",
"compiler-wrapper",
}
depth_1_set = {"dyninst", "libdwarf", "libelf", "gcc", "gcc-runtime", "compiler-wrapper"}
depth_0_set = {"libdwarf", "libelf", "gcc", "gcc-runtime", "compiler-wrapper"}
expectations = {
None: full_set,

View File

@@ -223,7 +223,7 @@ def test_load_first(install_mockery, mock_fetch, mock_archive, mock_packages):
for dep in ("mpileaks", "callpath", "dyninst", "libelf", "libdwarf", "mpich")
)
assert all(
len([diff for diff in result["intersect"] if diff[0] == attr]) == 7
len([diff for diff in result["intersect"] if diff[0] == attr]) == 8
for attr in (
"version",
"node_target",

View File

@@ -170,7 +170,7 @@ def _check_json_output(spec_list):
def _check_json_output_deps(spec_list):
assert len(spec_list) == 15
assert len(spec_list) == 16
names = [spec["name"] for spec in spec_list]
assert names.count("mpileaks") == 3
@@ -272,6 +272,7 @@ def test_find_format_deps(database, config):
dyninst-8.2
libdwarf-20130729
libelf-0.8.13
compiler-wrapper-1.0
gcc-10.2.1
gcc-runtime-10.2.1
zmpi-1.0
@@ -293,6 +294,7 @@ def test_find_format_deps_paths(database, config):
dyninst-8.2 {mpileaks['dyninst'].prefix}
libdwarf-20130729 {mpileaks['libdwarf'].prefix}
libelf-0.8.13 {mpileaks['libelf'].prefix}
compiler-wrapper-1.0 {mpileaks['compiler-wrapper'].prefix}
gcc-10.2.1 {mpileaks['gcc'].prefix}
gcc-runtime-10.2.1 {mpileaks['gcc-runtime'].prefix}
zmpi-1.0 {mpileaks['zmpi'].prefix}

View File

@@ -61,7 +61,7 @@ def test_install_package_and_dependency(
assert filename in files
content = filename.open().read()
assert 'tests="3"' in content
assert 'tests="4"' in content
assert 'failures="0"' in content
assert 'errors="0"' in content
@@ -106,12 +106,12 @@ def test_install_package_already_installed(
content = filename.open().read()
print(content)
assert 'tests="4"' in content
assert 'tests="5"' in content
assert 'failures="0"' in content
assert 'errors="0"' in content
skipped = [line for line in content.split("\n") if "skipped" in line]
assert len(skipped) == 4
assert len(skipped) == 5
@pytest.mark.parametrize(
@@ -448,16 +448,16 @@ def just_throw(*args, **kwargs):
# Only libelf error is reported (through libdwarf root spec). libdwarf
# install is skipped and it is not an error.
assert 'tests="1"' in content
assert 'tests="0"' not in content
assert 'failures="0"' in content
assert 'errors="1"' in content
assert 'errors="0"' not in content
# Nothing should have succeeded
assert 'errors="0"' not in content
# We want to have both stdout and stderr
assert "<system-out>" in content
assert 'error message="{0}"'.format(msg) in content
assert f'error message="{msg}"' in content
@pytest.mark.usefixtures("noop_install", "mock_packages", "config")

View File

@@ -30,7 +30,7 @@ def test_mark_all_explicit(mutable_database):
mark("-e", "-a")
gc("-y")
all_specs = spack.store.STORE.layout.all_specs()
assert len(all_specs) == 16
assert len(all_specs) == 17
@pytest.mark.db
@@ -64,4 +64,4 @@ def test_mark_all_implicit_then_explicit(mutable_database):
mark("-e", "-a")
gc("-y")
all_specs = spack.store.STORE.layout.all_specs()
assert len(all_specs) == 16
assert len(all_specs) == 17

View File

@@ -90,7 +90,7 @@ def test_recursive_uninstall(mutable_database):
@pytest.mark.db
@pytest.mark.regression("3690")
@pytest.mark.parametrize("constraint,expected_number_of_specs", [("dyninst", 9), ("libelf", 7)])
@pytest.mark.parametrize("constraint,expected_number_of_specs", [("dyninst", 10), ("libelf", 8)])
def test_uninstall_spec_with_multiple_roots(
constraint, expected_number_of_specs, mutable_database
):
@@ -100,7 +100,7 @@ def test_uninstall_spec_with_multiple_roots(
@pytest.mark.db
@pytest.mark.parametrize("constraint,expected_number_of_specs", [("dyninst", 15), ("libelf", 15)])
@pytest.mark.parametrize("constraint,expected_number_of_specs", [("dyninst", 16), ("libelf", 16)])
def test_force_uninstall_spec_with_ref_count_not_zero(
constraint, expected_number_of_specs, mutable_database
):
@@ -170,7 +170,7 @@ def db_specs():
all_specs, mpileaks_specs, callpath_specs, mpi_specs = db_specs()
total_specs = len(all_specs)
assert total_specs == 15
assert total_specs == 16
assert len(mpileaks_specs) == 3
assert len(callpath_specs) == 2
assert len(mpi_specs) == 3

View File

@@ -466,7 +466,7 @@ def test_architecture_deep_inheritance(self, mock_targets, compiler_factory):
with spack.config.override("packages", {"gcc": {"externals": [cnl_compiler]}}):
spec_str = "mpileaks %gcc@4.5.0 os=CNL target=nocona ^dyninst os=CNL ^callpath os=CNL"
spec = spack.concretize.concretize_one(spec_str)
for s in spec.traverse(root=False):
for s in spec.traverse(root=False, deptype=("link", "run")):
assert s.architecture.target == spec.architecture.target
def test_compiler_flags_from_user_are_grouped(self):
@@ -1991,17 +1991,17 @@ def test_installed_specs_disregard_conflicts(self, mutable_database, monkeypatch
assert s.satisfies("~debug"), s
@pytest.mark.regression("32471")
def test_require_targets_are_allowed(self, mutable_database):
def test_require_targets_are_allowed(self, mutable_config, mutable_database):
"""Test that users can set target constraints under the require attribute."""
# Configuration to be added to packages.yaml
required_target = archspec.cpu.TARGETS[spack.platforms.test.Test.default].family
external_conf = {"all": {"require": f"target={required_target}"}}
spack.config.set("packages", external_conf)
mutable_config.set("packages", external_conf)
with spack.config.override("concretizer:reuse", False):
spec = spack.concretize.concretize_one("mpich")
for s in spec.traverse():
for s in spec.traverse(deptype=("link", "run")):
assert s.satisfies(f"target={required_target}")
def test_external_python_extensions_have_dependency(self):
@@ -2994,7 +2994,7 @@ def test_filtering_reused_specs(
@pytest.mark.usefixtures("mutable_database", "mock_store")
@pytest.mark.parametrize(
"reuse_yaml,expected_length",
[({"from": [{"type": "local"}]}, 19), ({"from": [{"type": "buildcache"}]}, 0)],
[({"from": [{"type": "local"}]}, 20), ({"from": [{"type": "buildcache"}]}, 0)],
)
@pytest.mark.not_on_windows("Expected length is different on Windows")
def test_selecting_reused_sources(

View File

@@ -2154,3 +2154,12 @@ def info(self):
@pytest.fixture()
def mock_runtimes(config, mock_packages):
return mock_packages.packages_with_tags("runtime")
@pytest.fixture()
def wrapper_dir(install_mockery):
"""Installs the compiler wrapper and returns the prefix where the script is installed."""
wrapper = spack.spec.Spec("compiler-wrapper").concretized()
wrapper_pkg = wrapper.package
PackageInstaller([wrapper_pkg], explicit=True).install()
return wrapper_pkg.bin_dir()

View File

@@ -95,11 +95,11 @@ def upstream_and_downstream_db(tmpdir, gen_mock_layout):
@pytest.mark.parametrize(
"install_tree,result",
[
("all", ["pkg-b", "pkg-c", "gcc-runtime", "gcc"]),
("all", ["pkg-b", "pkg-c", "gcc-runtime", "gcc", "compiler-wrapper"]),
("upstream", ["pkg-c"]),
("local", ["pkg-b", "gcc-runtime", "gcc"]),
("local", ["pkg-b", "gcc-runtime", "gcc", "compiler-wrapper"]),
("{u}", ["pkg-c"]),
("{d}", ["pkg-b", "gcc-runtime", "gcc"]),
("{d}", ["pkg-b", "gcc-runtime", "gcc", "compiler-wrapper"]),
],
ids=["all", "upstream", "local", "upstream_path", "downstream_path"],
)
@@ -491,7 +491,7 @@ def test_005_db_exists(database):
def test_010_all_install_sanity(database):
"""Ensure that the install layout reflects what we think it does."""
all_specs = spack.store.STORE.layout.all_specs()
assert len(all_specs) == 16
assert len(all_specs) == 17
# Query specs with multiple configurations
mpileaks_specs = [s for s in all_specs if s.satisfies("mpileaks")]
@@ -608,7 +608,7 @@ def test_050_basic_query(database):
"""Ensure querying database is consistent with what is installed."""
# query everything
total_specs = len(spack.store.STORE.db.query())
assert total_specs == 19
assert total_specs == 20
# query specs with multiple configurations
mpileaks_specs = database.query("mpileaks")
@@ -827,11 +827,11 @@ def check_unused(roots, deptype, expected):
assert set(u.name for u in unused) == set(expected)
default_dt = dt.LINK | dt.RUN
check_unused(None, default_dt, ["cmake", "gcc"])
check_unused(None, default_dt, ["cmake", "gcc", "compiler-wrapper"])
check_unused(
[si, ml_mpich, ml_mpich2, ml_zmpi, externaltest],
default_dt,
["trivial-smoke-test", "cmake", "gcc"],
["trivial-smoke-test", "cmake", "gcc", "compiler-wrapper"],
)
check_unused(
[si, ml_mpich, ml_mpich2, ml_zmpi, externaltest],
@@ -846,7 +846,15 @@ def check_unused(roots, deptype, expected):
check_unused(
[si, ml_mpich, ml_mpich2, ml_zmpi],
default_dt,
["trivial-smoke-test", "cmake", "externaltest", "externaltool", "externalvirtual", "gcc"],
[
"trivial-smoke-test",
"cmake",
"externaltest",
"externaltool",
"externalvirtual",
"gcc",
"compiler-wrapper",
],
)
@@ -1080,7 +1088,7 @@ def test_check_parents(spec_str, parent_name, expected_nparents, database):
def test_db_all_hashes(database):
# ensure we get the right number of hashes without a read transaction
hashes = database.all_hashes()
assert len(hashes) == 19
assert len(hashes) == 20
# and make sure the hashes match
with database.read_transaction():

View File

@@ -54,25 +54,34 @@ def test_ascii_graph_mpileaks(config, mock_packages, monkeypatch):
|\
| |\
| | |\
| | | o callpath
| |_|/|
|/| |/|
| |/|/|
o | | | mpich
|\| | |
| |/ /
|/| |
| | o dyninst
| |/|
|/|/|
| | |\
| | | o libdwarf
| | | |\
| | | | o callpath
| |_|_|/|
|/| |_|/|
| |/| |/|
| | |/|/|
| | | | o dyninst
| | |_|/|
| |/| |/|
| | |/|/|
| | | | |\
o | | | | | mpich
|\| | | | |
|\ \ \ \ \ \
| |_|/ / / /
|/| | | | |
| |/ / / /
| | | | o libdwarf
| |_|_|/|
|/| |_|/|
| |/| |/|
| | |/|/
| | | o libelf
| |_|/|
|/| |/|
| |/|/
| | o libelf
| |/|
|/|/
| o | compiler-wrapper
| /
| o gcc-runtime
|/
o gcc

View File

@@ -1022,7 +1022,7 @@ def test_install_fail_fast_on_detect(install_mockery, monkeypatch, capsys):
b, c = spack.concretize.concretize_one("pkg-b"), spack.concretize.concretize_one("pkg-c")
b_id, c_id = inst.package_id(b), inst.package_id(c)
installer = create_installer([b, c], {"fail_fast": True})
installer = create_installer([c, b], {"fail_fast": True})
# Make sure all packages are identified as failed
# This will prevent b from installing, which will cause the build of c to be skipped.
@@ -1031,7 +1031,7 @@ def test_install_fail_fast_on_detect(install_mockery, monkeypatch, capsys):
with pytest.raises(spack.error.InstallError, match="after first install failure"):
installer.install()
assert c_id in installer.failed, "Expected b to be marked as failed"
assert c_id in installer.failed
assert b_id not in installer.failed, "Expected no attempt to install pkg-c"
assert f"{c_id} failed to install" in capsys.readouterr().err

View File

@@ -15,12 +15,15 @@
import pytest
import spack
import spack.binary_distribution
import spack.database
import spack.deptypes as dt
import spack.environment as ev
import spack.error
import spack.oci.opener
import spack.spec
import spack.traverse
from spack.main import SpackCommand
from spack.oci.image import Digest, ImageReference, default_config, default_manifest
from spack.oci.oci import blob_exists, get_manifest_and_config, upload_blob, upload_manifest
@@ -82,7 +85,13 @@ def test_buildcache_tag(install_mockery, mock_fetch, mutable_mock_env_path):
name = ImageReference.from_string("example.com/image:full_env")
with ev.read("test") as e:
specs = [x for x in e.all_specs() if not x.external]
specs = [
x
for x in spack.traverse.traverse_nodes(
e.concrete_roots(), deptype=dt.LINK | dt.RUN
)
if not x.external
]
manifest, config = get_manifest_and_config(name)
@@ -99,7 +108,9 @@ def test_buildcache_tag(install_mockery, mock_fetch, mutable_mock_env_path):
name = ImageReference.from_string("example.com/image:single_spec")
manifest, config = get_manifest_and_config(name)
assert len(manifest["layers"]) == len([x for x in libelf.traverse() if not x.external])
assert len(manifest["layers"]) == len(
[x for x in libelf.traverse(deptype=dt.LINK | dt.RUN) if not x.external]
)
def test_buildcache_push_with_base_image_command(mutable_database, tmpdir):

View File

@@ -184,6 +184,7 @@ def test_conflicting_package_constraints(self, set_dependency):
[
(0, "mpileaks"),
(1, "callpath"),
(2, "compiler-wrapper"),
(2, "dyninst"),
(3, "gcc"),
(3, "gcc-runtime"),
@@ -199,23 +200,29 @@ def test_conflicting_package_constraints(self, set_dependency):
[
(0, "mpileaks"),
(1, "callpath"),
(2, "compiler-wrapper"),
(2, "dyninst"),
(3, "compiler-wrapper"),
(3, "gcc"),
(3, "gcc-runtime"),
(4, "gcc"),
(3, "libdwarf"),
(4, "compiler-wrapper"),
(4, "gcc"),
(4, "gcc-runtime"),
(4, "libelf"),
(5, "compiler-wrapper"),
(5, "gcc"),
(5, "gcc-runtime"),
(3, "libelf"),
(2, "gcc"),
(2, "gcc-runtime"),
(2, "zmpi"),
(3, "compiler-wrapper"),
(3, "fake"),
(3, "gcc"),
(3, "gcc-runtime"),
(1, "compiler-wrapper"),
(1, "gcc"),
(1, "gcc-runtime"),
(1, "zmpi"),
@@ -227,19 +234,24 @@ def test_conflicting_package_constraints(self, set_dependency):
[
(0, "mpileaks"),
(1, "callpath"),
(2, "compiler-wrapper"),
(2, "dyninst"),
(3, "compiler-wrapper"),
(3, "gcc"),
(3, "gcc-runtime"),
(4, "gcc"),
(3, "libdwarf"),
(4, "compiler-wrapper"),
(4, "gcc"),
(4, "gcc-runtime"),
(5, "gcc"),
(4, "libelf"),
(5, "compiler-wrapper"),
(5, "gcc"),
(5, "gcc-runtime"),
(6, "gcc"),
(3, "libelf"),
(4, "compiler-wrapper"),
(4, "gcc"),
(4, "gcc-runtime"),
(5, "gcc"),
@@ -247,14 +259,17 @@ def test_conflicting_package_constraints(self, set_dependency):
(2, "gcc-runtime"),
(3, "gcc"),
(2, "zmpi"),
(3, "compiler-wrapper"),
(3, "fake"),
(3, "gcc"),
(3, "gcc-runtime"),
(4, "gcc"),
(1, "compiler-wrapper"),
(1, "gcc"),
(1, "gcc-runtime"),
(2, "gcc"),
(1, "zmpi"),
(2, "compiler-wrapper"),
(2, "fake"),
(2, "gcc"),
(2, "gcc-runtime"),
@@ -265,6 +280,7 @@ def test_conflicting_package_constraints(self, set_dependency):
# Postorder node traversal
(
[
(2, "compiler-wrapper"),
(3, "gcc"),
(3, "gcc-runtime"),
(4, "libelf"),
@@ -280,11 +296,15 @@ def test_conflicting_package_constraints(self, set_dependency):
# Postorder edge traversal
(
[
(2, "compiler-wrapper"),
(3, "compiler-wrapper"),
(3, "gcc"),
(4, "gcc"),
(3, "gcc-runtime"),
(4, "compiler-wrapper"),
(4, "gcc"),
(4, "gcc-runtime"),
(5, "compiler-wrapper"),
(5, "gcc"),
(5, "gcc-runtime"),
(4, "libelf"),
@@ -293,11 +313,13 @@ def test_conflicting_package_constraints(self, set_dependency):
(2, "dyninst"),
(2, "gcc"),
(2, "gcc-runtime"),
(3, "compiler-wrapper"),
(3, "fake"),
(3, "gcc"),
(3, "gcc-runtime"),
(2, "zmpi"),
(1, "callpath"),
(1, "compiler-wrapper"),
(1, "gcc"),
(1, "gcc-runtime"),
(1, "zmpi"),
@@ -308,17 +330,22 @@ def test_conflicting_package_constraints(self, set_dependency):
# Postorder path traversal
(
[
(2, "compiler-wrapper"),
(3, "compiler-wrapper"),
(3, "gcc"),
(4, "gcc"),
(3, "gcc-runtime"),
(4, "compiler-wrapper"),
(4, "gcc"),
(5, "gcc"),
(4, "gcc-runtime"),
(5, "compiler-wrapper"),
(5, "gcc"),
(6, "gcc"),
(5, "gcc-runtime"),
(4, "libelf"),
(3, "libdwarf"),
(4, "compiler-wrapper"),
(4, "gcc"),
(5, "gcc"),
(4, "gcc-runtime"),
@@ -327,15 +354,18 @@ def test_conflicting_package_constraints(self, set_dependency):
(2, "gcc"),
(3, "gcc"),
(2, "gcc-runtime"),
(3, "compiler-wrapper"),
(3, "fake"),
(3, "gcc"),
(4, "gcc"),
(3, "gcc-runtime"),
(2, "zmpi"),
(1, "callpath"),
(1, "compiler-wrapper"),
(1, "gcc"),
(2, "gcc"),
(1, "gcc-runtime"),
(2, "compiler-wrapper"),
(2, "fake"),
(2, "gcc"),
(3, "gcc"),
@@ -350,47 +380,53 @@ def test_conflicting_package_constraints(self, set_dependency):
def test_traversal(self, pairs, traverse_kwargs, default_mock_concretization):
r"""Tests different traversals of the following graph
o mpileaks
o mpileaks@2.3/3qeg7jx
|\
| |\
| | |\
| | | |\
| | | | o callpath
| |_|_|/|
|/| |_|/|
| |/| |/|
| | |/|/|
o | | | | zmpi
|\| | | |
|\ \ \ \ \
| |_|/ / /
|/| | | |
| |\ \ \ \
| | |_|/ /
| |/| | |
| | o | | fake
| | / /
| | | o dyninst
| |_|/|
|/| |/|
| |/|/|
| | | |\
| | | | o libdwarf
| | | | |\
| | | | | o callpath@1.0/4gilijr
| |_|_|_|/|
|/| |_|_|/|
| |/| |_|/|
| | |/| |/|
| | | |/|/|
| | | | | o dyninst@8.2/u4oymb3
| | |_|_|/|
| |/| |_|/|
| | |/| |/|
| | | |/|/|
| | | | | |\
o | | | | | | mpich@3.0.4/g734fu6
|\| | | | | |
|\ \ \ \ \ \ \
| |_|/ / / / /
|/| | | | | |
| |\ \ \ \ \ \
| | |_|/ / / /
| |/| | | | |
| | |/ / / /
| | | | | o libdwarf@20130729/q5r7l2r
| |_|_|_|/|
|/| |_|_|/|
| |/| |_|/|
| | |/| |/|
| | | |/|/
| | | | o libelf@0.8.13/i2x6pya
| |_|_|/|
|/| |_|/|
| |/| |/|
| | |/|/
| | | o libel
| |_|/|
|/| |/|
| |/|/
o | | gcc-runtime
| | o | compiler-wrapper@1.0/njdili2
| | /
o | | gcc-runtime@10.5.0/iyytqeo
|\| |
| |/
|/|
o | glibc
/
o gcc
| o gcc@10.5.0/ljeisd4
|
o glibc@2.31/tbyn33w
"""
dag = default_mock_concretization("mpileaks ^zmpi")
names = [x for _, x in pairs]
@@ -843,10 +879,10 @@ def test_spec_tree_respect_deptypes(self):
"query,expected_length,expected_satisfies",
[
({"virtuals": ["mpi"]}, 1, ["mpich", "mpi"]),
({"depflag": dt.BUILD}, 3, ["mpich", "mpi", "callpath"]),
({"depflag": dt.BUILD}, 4, ["mpich", "mpi", "callpath"]),
({"depflag": dt.BUILD, "virtuals": ["mpi"]}, 1, ["mpich", "mpi"]),
({"depflag": dt.LINK}, 3, ["mpich", "mpi", "callpath"]),
({"depflag": dt.BUILD | dt.LINK}, 4, ["mpich", "mpi", "callpath"]),
({"depflag": dt.BUILD | dt.LINK}, 5, ["mpich", "mpi", "callpath"]),
({"virtuals": ["lapack"]}, 0, []),
],
)
@@ -928,7 +964,7 @@ def test_synthetic_construction_of_split_dependencies_from_same_package(mock_pac
root.add_dependency_edge(build_spec, depflag=dt.BUILD, virtuals=())
# Check dependencies from the perspective of root
assert len(root.dependencies()) == 4
assert len(root.dependencies()) == 5
assert len([x for x in root.dependencies() if x.name == "pkg-c"]) == 2
assert "@2.0" in root.dependencies(name="pkg-c", deptype=dt.BUILD)[0]

View File

@@ -1697,6 +1697,7 @@ def test_spec_trim(mock_packages, config):
top.trim("dt-diamond-left")
remaining = {x.name for x in top.traverse()}
assert {
"compiler-wrapper",
"dt-diamond",
"dt-diamond-right",
"dt-diamond-bottom",
@@ -1706,7 +1707,7 @@ def test_spec_trim(mock_packages, config):
top.trim("dt-diamond-right")
remaining = {x.name for x in top.traverse()}
assert {"dt-diamond", "gcc-runtime", "gcc"} == remaining
assert {"compiler-wrapper", "dt-diamond", "gcc-runtime", "gcc"} == remaining
@pytest.mark.regression("30861")