diff options
Diffstat (limited to 'lib/spack/env/cc')
-rwxr-xr-x | lib/spack/env/cc | 472 |
1 files changed, 332 insertions, 140 deletions
diff --git a/lib/spack/env/cc b/lib/spack/env/cc index 266e41cb48..19ca31cace 100755 --- a/lib/spack/env/cc +++ b/lib/spack/env/cc @@ -1,140 +1,332 @@ -#!/usr/bin/env python -import sys -if not sys.version_info[:2] >= (2,6): - sys.exit("Spack requires Python 2.6. Version was %s." % sys.version_info) - -import os -import re -import subprocess -from contextlib import closing - -# Import spack parameters through the build environment. -spack_lib = os.environ.get("SPACK_LIB") -if not spack_lib: - print "Spack compiler must be run from spack!" - sys.exit(1) - -# Grab a minimal set of spack packages -sys.path.append(spack_lib) -from spack.compilation import * -from external import argparse -import llnl.util.tty as tty - -spack_prefix = get_env_var("SPACK_PREFIX") -spack_debug = get_env_flag("SPACK_DEBUG") -spack_deps = get_path("SPACK_DEPENDENCIES") -spack_env_path = get_path("SPACK_ENV_PATH") -spack_debug_log_dir = get_env_var("SPACK_DEBUG_LOG_DIR") -spack_spec = get_env_var("SPACK_SPEC") - -compiler_spec = get_env_var("SPACK_COMPILER_SPEC") -spack_cc = get_env_var("SPACK_CC", required=False) -spack_cxx = get_env_var("SPACK_CXX", required=False) -spack_f77 = get_env_var("SPACK_F77", required=False) -spack_fc = get_env_var("SPACK_FC", required=False) - -# Figure out what type of operation we're doing -command = os.path.basename(sys.argv[0]) - -cpp, cc, ccld, ld, version_check = range(5) - -if command == 'cpp': - mode = cpp -elif command == 'ld': - mode = ld -elif '-E' in sys.argv: - mode = cpp -elif '-c' in sys.argv: - mode = cc -else: - mode = ccld - - -if command in ('cc', 'gcc', 'c89', 'c99', 'clang'): - command = spack_cc - language = "C" -elif command in ('c++', 'CC', 'g++', 'clang++'): - command = spack_cxx - language = "C++" -elif command in ('f77'): - command = spack_f77 - language = "Fortran 77" -elif command in ('fc', 'f90', 'f95'): - command = spack_fc - language = "Fortran 90" -elif command in ('ld', 'cpp'): - pass # leave it the same. TODO: what's the right thing? -else: - raise Exception("Unknown compiler: %s" % command) - -if command is None: - print "ERROR: Compiler '%s' does not support compiling %s programs." % ( - compiler_spec, language) - sys.exit(1) - -version_args = ['-V', '-v', '--version', '-dumpversion'] -if any(arg in sys.argv for arg in version_args): - mode = version_check - -# Parse out the includes, libs, etc. so we can adjust them if need be. -parser = argparse.ArgumentParser(add_help=False) -parser.add_argument("-I", action='append', default=[], dest='include_path') -parser.add_argument("-L", action='append', default=[], dest='lib_path') -parser.add_argument("-l", action='append', default=[], dest='libs') - -options, other_args = parser.parse_known_args() -rpaths, other_args = parse_rpaths(other_args) - -# Add dependencies' include and lib paths to our compiler flags. -def add_if_dir(path_list, directory, index=None): - if os.path.isdir(directory): - if index is None: - path_list.append(directory) - else: - path_list.insert(index, directory) - -for dep_dir in spack_deps: - add_if_dir(options.include_path, os.path.join(dep_dir, "include")) - add_if_dir(options.lib_path, os.path.join(dep_dir, "lib")) - add_if_dir(options.lib_path, os.path.join(dep_dir, "lib64")) - -# Add our modified arguments to it. -arguments = ['-I%s' % path for path in options.include_path] -arguments += other_args -arguments += ['-L%s' % path for path in options.lib_path] -arguments += ['-l%s' % path for path in options.libs] - -# Add rpaths to install dir and its dependencies. We add both lib and lib64 -# here because we don't know which will be created. -rpaths.extend(options.lib_path) -rpaths.append('%s/lib' % spack_prefix) -rpaths.append('%s/lib64' % spack_prefix) -if mode == ccld: - arguments += ['-Wl,-rpath,%s' % p for p in rpaths] -elif mode == ld: - pairs = [('-rpath', '%s' % p) for p in rpaths] - arguments += [item for sublist in pairs for item in sublist] - -# Unset some pesky environment variables -for var in ["LD_LIBRARY_PATH", "LD_RUN_PATH", "DYLD_LIBRARY_PATH"]: - if var in os.environ: - os.environ.pop(var) - -# Ensure that the delegated command doesn't just call this script again. -remove_paths = ['.'] + spack_env_path -path = [p for p in get_path("PATH") if p not in remove_paths] -os.environ["PATH"] = ":".join(path) - -full_command = [command] + arguments - -if spack_debug: - input_log = os.path.join(spack_debug_log_dir, 'spack-cc-%s.in.log' % spack_spec) - output_log = os.path.join(spack_debug_log_dir, 'spack-cc-%s.out.log' % spack_spec) - with closing(open(input_log, 'a')) as log: - args = [os.path.basename(sys.argv[0])] + sys.argv[1:] - log.write("%s\n" % " ".join(arg.replace(' ', r'\ ') for arg in args)) - with closing(open(output_log, 'a')) as log: - log.write("%s\n" % " ".join(full_command)) - -rcode = subprocess.call(full_command) -sys.exit(rcode) +#!/bin/bash +############################################################################## +# Copyright (c) 2013, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://scalability-llnl.github.io/spack +# Please also see the LICENSE file 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 General Public License (as published by +# the Free Software Foundation) version 2.1 dated 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 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 +############################################################################## +# +# 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 arguments for dependency /include directories. +# -L arguments for dependency /lib directories. +# -Wl,-rpath arguments for dependency /lib directories. +# + +# This is the list of environment variables that need to be set before +# the script runs. They are set by routines in spack.build_environment +# as part of spack.package.Package.do_install(). +parameters=" +SPACK_PREFIX +SPACK_ENV_PATH +SPACK_DEBUG_LOG_DIR +SPACK_COMPILER_SPEC +SPACK_SHORT_SPEC" + +# The compiler input variables are checked for sanity later: +# SPACK_CC, SPACK_CXX, SPACK_F77, SPACK_FC +# Debug flag is optional; set to true for debug logging: +# SPACK_DEBUG +# Test command is used to unit test the compiler script. +# SPACK_TEST_COMMAND +# Dependencies can be empty for pkgs with no deps: +# SPACK_DEPENDENCIES + +# die() +# Prints a message and exits with error 1. +function die { + echo "$@" + exit 1 +} + +for param in $parameters; do + if [ -z "${!param}" ]; then + die "Spack compiler must be run from spack! Input $param was missing!" + fi +done + +# +# 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: +# cc compile +# ld link +# ccld compile & link +# cpp preprocessor +# vcheck version check +# +command=$(basename "$0") +case "$command" in + cc|gcc|c89|c99|clang) + command="$SPACK_CC" + language="C" + ;; + c++|CC|g++|clang++) + command="$SPACK_CXX" + language="C++" + ;; + f77) + command="$SPACK_F77" + language="Fortran 77" + ;; + fc|f90|f95) + command="$SPACK_FC" + language="Fortran 90" + ;; + cpp) + mode=cpp + ;; + ld) + mode=ld + ;; + *) + die "Unkown compiler: $command" + ;; +esac + +# Finish setting up the mode. +if [ -z "$mode" ]; then + mode=ccld + for arg in "$@"; do + if [ "$arg" = -v -o "$arg" = -V -o "$arg" = --version -o "$arg" = -dumpversion ]; then + mode=vcheck + break + elif [ "$arg" = -E ]; then + mode=cpp + break + elif [ "$arg" = -c ]; then + mode=cc + break + fi + done +fi + +# Dump the version and exist if we're in testing mode. +if [ "$SPACK_TEST_COMMAND" = "dump-mode" ]; then + echo "$mode" + exit +fi + +# Check that at least one of the real commands was actually selected, +# otherwise we don't know what to execute. +if [ -z "$command" ]; then + die "ERROR: Compiler '$SPACK_COMPILER_SPEC' does not support compiling $language programs." +fi + +# Save original command for debug logging +input_command="$@" + +# +# Now do real parsing of the command line args, trying hard to keep +# non-rpath linker arguments in the proper order w.r.t. other command +# line arguments. This is important for things like groups. +# +includes=() +libraries=() +libs=() +rpaths=() +other_args=() + +while [ -n "$1" ]; do + case "$1" in + -I*) + arg="${1#-I}" + if [ -z "$arg" ]; then shift; arg="$1"; fi + includes+=("$arg") + ;; + -L*) + arg="${1#-L}" + if [ -z "$arg" ]; then shift; arg="$1"; fi + libraries+=("$arg") + ;; + -l*) + arg="${1#-l}" + if [ -z "$arg" ]; then shift; arg="$1"; fi + libs+=("$arg") + ;; + -Wl,*) + arg="${1#-Wl,}" + if [ -z "$arg" ]; then shift; arg="$1"; fi + if [[ "$arg" = -rpath=* ]]; then + rpaths+=("${arg#-rpath=}") + elif [[ "$arg" = -rpath ]]; then + shift; arg="$1" + if [[ "$arg" != -Wl,* ]]; then + die "-Wl,-rpath was not followed by -Wl,*" + fi + rpaths+=("${arg#-Wl,}") + else + other_args+=("-Wl,$arg") + fi + ;; + -Xlinker,*) + arg="${1#-Xlinker,}" + if [ -z "$arg" ]; then shift; arg="$1"; fi + if [[ "$arg" = -rpath=* ]]; then + rpaths+=("${arg#-rpath=}") + elif [[ "$arg" = -rpath ]]; then + shift; arg="$1" + if [[ "$arg" != -Xlinker,* ]]; then + die "-Xlinker,-rpath was not followed by -Xlinker,*" + fi + rpaths+=("${arg#-Xlinker,}") + else + other_args+=("-Xlinker,$arg") + fi + ;; + *) + other_args+=("$1") + ;; + esac + shift +done + +# Dump parsed values for unit testing if asked for +if [ -n "$SPACK_TEST_COMMAND" ]; then + IFS=$'\n' + case "$SPACK_TEST_COMMAND" in + dump-includes) echo "${includes[*]}";; + dump-libraries) echo "${libraries[*]}";; + dump-libs) echo "${libs[*]}";; + dump-rpaths) echo "${rpaths[*]}";; + dump-other-args) echo "${other_args[*]}";; + dump-all) + echo "INCLUDES:" + echo "${includes[*]}" + echo + echo "LIBRARIES:" + echo "${libraries[*]}" + echo + echo "LIBS:" + echo "${libs[*]}" + echo + echo "RPATHS:" + echo "${rpaths[*]}" + echo + echo "ARGS:" + echo "${other_args[*]}" + ;; + *) + echo "ERROR: Unknown test command" + exit 1 ;; + esac + exit +fi + +# Read spack dependencies from the path environment variable +IFS=':' read -ra deps <<< "$SPACK_DEPENDENCIES" +for dep in "${deps[@]}"; do + if [ -d "$dep/include" ]; then + includes+=("$dep/include") + fi + + if [ -d "$dep/lib" ]; then + libraries+=("$dep/lib") + rpaths+=("$dep/lib") + fi + + if [ -d "$dep/lib64" ]; then + libraries+=("$dep/lib64") + rpaths+=("$dep/lib64") + fi +done + +# Include all -L's and prefix/whatever dirs in rpath +for dir in "${libraries[@]}"; do + [ "$dir" != "." ] && rpaths+=("$dir") +done +rpaths+=("$SPACK_PREFIX/lib") +rpaths+=("$SPACK_PREFIX/lib64") + +# Put the arguments together +args=() +for dir in "${includes[@]}"; do args+=("-I$dir"); done +args+=("${other_args[@]}") +for dir in "${libraries[@]}"; do args+=("-L$dir"); done +for lib in "${libs[@]}"; do args+=("-l$lib"); done + +if [ "$mode" = ccld ]; then + for dir in "${rpaths[@]}"; do + args+=("-Wl,-rpath") + args+=("-Wl,$dir"); + done +elif [ "$mode" = ld ]; then + for dir in "${rpaths[@]}"; do + args+=("-rpath") + args+=("$dir"); + done +fi + +# +# Unset pesky environment variables that could affect build sanity. +# +unset LD_LIBRARY_PATH +unset LD_RUN_PATH +unset DYLD_LIBRARY_PATH + +# +# 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="" +for dir in "${env_path[@]}"; do + remove="" + for rm_dir in "${spack_env_dirs[@]}"; do + if [ "$dir" = "$rm_dir" ]; then remove=True; fi + done + if [ -z "$remove" ]; then + if [ -z "$PATH" ]; then + PATH="$dir" + else + PATH="$PATH:$dir" + fi + fi +done +export PATH + +full_command=("$command") +full_command+=("${args[@]}") + +# +# 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_SHORT_SPEC.in.log" + output_log="$SPACK_DEBUG_LOG_DIR/spack-cc-$SPACK_SHORT_SPEC.out.log" + echo "$input_command" >> $input_log + echo "$mode ${full_command[@]}" >> $output_log +fi + +exec "${full_command[@]}" |