diff options
author | Todd Gamblin <tgamblin@llnl.gov> | 2018-08-06 01:40:54 -0700 |
---|---|---|
committer | Todd Gamblin <tgamblin@llnl.gov> | 2018-08-08 01:51:51 -0700 |
commit | 0e81f6cba5a1b718e4c65cbbd7f1ec512fbf89d1 (patch) | |
tree | 7202c1a629348ee3eebc655f8ee323405216a9b9 | |
parent | c8fb9b547940dcfbb3706cc8b34c93f634d41655 (diff) | |
download | spack-0e81f6cba5a1b718e4c65cbbd7f1ec512fbf89d1.tar.gz spack-0e81f6cba5a1b718e4c65cbbd7f1ec512fbf89d1.tar.bz2 spack-0e81f6cba5a1b718e4c65cbbd7f1ec512fbf89d1.tar.xz spack-0e81f6cba5a1b718e4c65cbbd7f1ec512fbf89d1.zip |
refactor: clean up and fix the cc test
- Add tests to ensure that RPATHs are not added in cc mode, which can
cause some builds to fail.
- Change cc.py to use pytest style
- Instead of writing out all the flags, break the flags down into
variables so that it's easy to read what each test is supposed to
check. This should make cc.py more maintainable in the future.
-rw-r--r-- | lib/spack/spack/test/cc.py | 888 | ||||
-rw-r--r-- | lib/spack/spack/util/environment.py | 29 |
2 files changed, 503 insertions, 414 deletions
diff --git a/lib/spack/spack/test/cc.py b/lib/spack/spack/test/cc.py index 12f8acc1a6..8435452642 100644 --- a/lib/spack/spack/test/cc.py +++ b/lib/spack/spack/test/cc.py @@ -27,16 +27,16 @@ This test checks that the Spack cc compiler wrapper is parsing arguments correctly. """ import os -import unittest -import tempfile -import shutil +import pytest from spack.paths import build_env_path -from llnl.util.filesystem import mkdirp +from spack.util.environment import system_dirs, set_env from spack.util.executable import Executable +# # Complicated compiler test command -test_command = [ +# +test_args = [ '-I/test/include', '-L/test/lib', '-L/other/lib', '-I/other/include', 'arg1', '-Wl,--start-group', @@ -45,422 +45,482 @@ test_command = [ '-llib1', '-llib2', 'arg4', '-Wl,--end-group', - '-Xlinker', '-rpath', '-Xlinker', '/third/rpath', '-Xlinker', - '-rpath', '-Xlinker', '/fourth/rpath', + '-Xlinker', '-rpath', '-Xlinker', '/third/rpath', + '-Xlinker', '-rpath', '-Xlinker', '/fourth/rpath', '-llib3', '-llib4', 'arg5', 'arg6'] +# +# Pieces of the test command above, as they should be parsed out. +# +# `_wl_rpaths` are for the compiler (with -Wl,), and `_rpaths` are raw +# -rpath arguments for the linker. +# +test_include_paths = [ + '-I/test/include', '-I/other/include'] + +test_library_paths = [ + '-L/test/lib', '-L/other/lib'] + +test_wl_rpaths = [ + '-Wl,-rpath,/first/rpath', '-Wl,-rpath,/second/rpath', + '-Wl,-rpath,/third/rpath', '-Wl,-rpath,/fourth/rpath'] + +test_rpaths = [ + '-rpath', '/first/rpath', '-rpath', '/second/rpath', + '-rpath', '/third/rpath', '-rpath', '/fourth/rpath'] + +test_args_without_paths = [ + 'arg1', + '-Wl,--start-group', + 'arg2', 'arg3', '-llib1', '-llib2', 'arg4', + '-Wl,--end-group', + '-llib3', '-llib4', 'arg5', 'arg6'] + +#: The prefix of the package being mock installed +pkg_prefix = '/spack-test-prefix' + +# +# Expected RPATHs for the package itself. The package is expected to +# have only one of /lib or /lib64, but we add both b/c we can't know +# before installing. +# +pkg_wl_rpaths = [ + '-Wl,-rpath,' + pkg_prefix + '/lib', + '-Wl,-rpath,' + pkg_prefix + '/lib64'] + +pkg_rpaths = [ + '-rpath', '/spack-test-prefix/lib', + '-rpath', '/spack-test-prefix/lib64'] + +# 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' -class CompilerWrapperTest(unittest.TestCase): - - def setUp(self): - self.cc = Executable(os.path.join(build_env_path, "cc")) - self.ld = Executable(os.path.join(build_env_path, "ld")) - self.cpp = Executable(os.path.join(build_env_path, "cpp")) - self.cxx = Executable(os.path.join(build_env_path, "c++")) - self.fc = Executable(os.path.join(build_env_path, "fc")) - - self.realcc = "/bin/mycc" - self.prefix = "/spack-test-prefix" - - os.environ['SPACK_CC'] = self.realcc - os.environ['SPACK_CXX'] = self.realcc - os.environ['SPACK_FC'] = self.realcc - - os.environ['SPACK_PREFIX'] = self.prefix - os.environ['SPACK_ENV_PATH'] = "test" - os.environ['SPACK_DEBUG_LOG_DIR'] = "." - os.environ['SPACK_DEBUG_LOG_ID'] = "foo-hashabc" - os.environ['SPACK_COMPILER_SPEC'] = "gcc@4.4.7" - os.environ['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," - - # Make some fake dependencies - self.tmp_deps = tempfile.mkdtemp() - self.dep1 = os.path.join(self.tmp_deps, 'dep1') - self.dep2 = os.path.join(self.tmp_deps, 'dep2') - self.dep3 = os.path.join(self.tmp_deps, 'dep3') - self.dep4 = os.path.join(self.tmp_deps, 'dep4') - - mkdirp(os.path.join(self.dep1, 'include')) - mkdirp(os.path.join(self.dep1, 'lib')) - - mkdirp(os.path.join(self.dep2, 'lib64')) - - mkdirp(os.path.join(self.dep3, 'include')) - mkdirp(os.path.join(self.dep3, 'lib64')) - - mkdirp(os.path.join(self.dep4, 'include')) - - if 'SPACK_DEPENDENCIES' in os.environ: - del os.environ['SPACK_DEPENDENCIES'] - - def tearDown(self): - shutil.rmtree(self.tmp_deps, True) - - def check_cc(self, command, args, expected): - os.environ['SPACK_TEST_COMMAND'] = command - self.assertEqual(self.cc(*args, output=str).strip(), expected) - - def check_cxx(self, command, args, expected): - os.environ['SPACK_TEST_COMMAND'] = command - self.assertEqual(self.cxx(*args, output=str).strip(), expected) - - def check_fc(self, command, args, expected): - os.environ['SPACK_TEST_COMMAND'] = command - self.assertEqual(self.fc(*args, output=str).strip(), expected) - - def check_ld(self, command, args, expected): - os.environ['SPACK_TEST_COMMAND'] = command - self.assertEqual(self.ld(*args, output=str).strip(), expected) - - def check_cpp(self, command, args, expected): - os.environ['SPACK_TEST_COMMAND'] = command - self.assertEqual(self.cpp(*args, output=str).strip(), expected) - - def test_vcheck_mode(self): - self.check_cc('dump-mode', ['-I/include', '--version'], "vcheck") - self.check_cc('dump-mode', ['-I/include', '-V'], "vcheck") - self.check_cc('dump-mode', ['-I/include', '-v'], "vcheck") - self.check_cc('dump-mode', ['-I/include', '-dumpversion'], "vcheck") - self.check_cc('dump-mode', ['-I/include', '--version', '-c'], "vcheck") - self.check_cc('dump-mode', ['-I/include', - '-V', '-o', 'output'], "vcheck") - - def test_cpp_mode(self): - self.check_cc('dump-mode', ['-E'], "cpp") - self.check_cpp('dump-mode', [], "cpp") - - def test_as_mode(self): - self.check_cc('dump-mode', ['-S'], "as") - - def test_ccld_mode(self): - self.check_cc('dump-mode', [], "ccld") - self.check_cc('dump-mode', ['foo.c', '-o', 'foo'], "ccld") - self.check_cc('dump-mode', ['foo.c', '-o', - 'foo', '-Wl,-rpath,foo'], "ccld") - self.check_cc( - 'dump-mode', - ['foo.o', 'bar.o', 'baz.o', '-o', 'foo', '-Wl,-rpath,foo'], - "ccld") - - def test_ld_mode(self): - self.check_ld('dump-mode', [], "ld") - self.check_ld( - 'dump-mode', - ['foo.o', 'bar.o', 'baz.o', '-o', 'foo', '-Wl,-rpath,foo'], - "ld") - - def test_flags(self): - os.environ['SPACK_LDFLAGS'] = '-L foo' - os.environ['SPACK_LDLIBS'] = '-lfoo' - os.environ['SPACK_CPPFLAGS'] = '-g -O1' - os.environ['SPACK_CFLAGS'] = '-Wall' - os.environ['SPACK_CXXFLAGS'] = '-Werror' - os.environ['SPACK_FFLAGS'] = '-w' - - # Test ldflags added properly in ld mode - self.check_ld('dump-args', test_command, - 'ld -L foo ' + - '-I/test/include -I/other/include arg1 ' + - '-Wl,--start-group arg2 arg3 -llib1 -llib2 arg4 ' + - '-Wl,--end-group ' + - '-llib3 -llib4 arg5 arg6 ' + - '-L/test/lib -L/other/lib ' + - '-lfoo ' + - '-rpath /first/rpath -rpath /second/rpath ' + - '-rpath /third/rpath -rpath /fourth/rpath ' + - '-rpath /spack-test-prefix/lib ' + - '-rpath /spack-test-prefix/lib64') - - # Test cppflags added properly in cpp mode - self.check_cpp('dump-args', test_command, - "cpp " + - '-g -O1 ' + - '-I/test/include -I/other/include arg1 ' + - '-Wl,--start-group arg2 arg3 -llib1 -llib2 arg4 ' + - '-Wl,--end-group ' + - '-llib3 -llib4 arg5 arg6 ' + - '-L/test/lib -L/other/lib') - - # Test ldflags, cppflags, and language specific flags are added in - # proper order - self.check_cc('dump-args', test_command, - self.realcc + ' ' + - '-g -O1 -Wall -L foo ' + - '-I/test/include -I/other/include arg1 ' + - '-Wl,--start-group arg2 arg3 -llib1 -llib2 arg4 ' + - '-Wl,--end-group ' + - '-llib3 -llib4 arg5 arg6 ' + - '-L/test/lib -L/other/lib ' + - '-lfoo ' + - '-Wl,-rpath,/first/rpath -Wl,-rpath,/second/rpath ' + - '-Wl,-rpath,/third/rpath -Wl,-rpath,/fourth/rpath ' + - '-Wl,-rpath,/spack-test-prefix/lib ' + - '-Wl,-rpath,/spack-test-prefix/lib64') - - self.check_cxx('dump-args', test_command, - self.realcc + ' ' + - '-g -O1 -Werror -L foo ' + - '-I/test/include -I/other/include arg1 ' + - '-Wl,--start-group arg2 arg3 -llib1 -llib2 arg4 ' + - '-Wl,--end-group ' + - '-llib3 -llib4 arg5 arg6 ' + - '-L/test/lib -L/other/lib ' + - '-lfoo ' + - '-Wl,-rpath,/first/rpath -Wl,-rpath,/second/rpath ' + - '-Wl,-rpath,/third/rpath -Wl,-rpath,/fourth/rpath ' + - '-Wl,-rpath,/spack-test-prefix/lib ' + - '-Wl,-rpath,/spack-test-prefix/lib64') - - self.check_fc('dump-args', test_command, - self.realcc + ' ' + - '-w -g -O1 -L foo ' + - '-I/test/include -I/other/include arg1 ' + - '-Wl,--start-group arg2 arg3 -llib1 -llib2 arg4 ' + - '-Wl,--end-group ' + - '-llib3 -llib4 arg5 arg6 ' + - '-L/test/lib -L/other/lib ' + - '-lfoo ' + - '-Wl,-rpath,/first/rpath -Wl,-rpath,/second/rpath ' + - '-Wl,-rpath,/third/rpath -Wl,-rpath,/fourth/rpath ' + - '-Wl,-rpath,/spack-test-prefix/lib ' + - '-Wl,-rpath,/spack-test-prefix/lib64') - - del os.environ['SPACK_CFLAGS'] - del os.environ['SPACK_CXXFLAGS'] - del os.environ['SPACK_FFLAGS'] - del os.environ['SPACK_CPPFLAGS'] - del os.environ['SPACK_LDFLAGS'] - del os.environ['SPACK_LDLIBS'] - - def test_dep_rpath(self): - """Ensure RPATHs for root package are added.""" - self.check_cc('dump-args', test_command, - self.realcc + ' ' + - '-I/test/include -I/other/include arg1 ' + - '-Wl,--start-group arg2 arg3 -llib1 -llib2 arg4 ' + - '-Wl,--end-group ' + - '-llib3 -llib4 arg5 arg6 ' + - '-L/test/lib -L/other/lib ' + - '-Wl,-rpath,/first/rpath -Wl,-rpath,/second/rpath ' + - '-Wl,-rpath,/third/rpath -Wl,-rpath,/fourth/rpath ' + - '-Wl,-rpath,/spack-test-prefix/lib ' + - '-Wl,-rpath,/spack-test-prefix/lib64') - - def test_dep_include(self): - """Ensure a single dependency include directory is added.""" - os.environ['SPACK_DEPENDENCIES'] = self.dep4 - os.environ['SPACK_RPATH_DEPS'] = os.environ['SPACK_DEPENDENCIES'] - os.environ['SPACK_LINK_DEPS'] = os.environ['SPACK_DEPENDENCIES'] - self.check_cc('dump-args', test_command, - self.realcc + ' ' + - '-I/test/include -I/other/include ' + - '-I' + self.dep4 + '/include arg1 ' + - '-Wl,--start-group arg2 arg3 -llib1 -llib2 arg4 ' + - '-Wl,--end-group ' + - '-llib3 -llib4 arg5 arg6 ' + - '-L/test/lib -L/other/lib ' + - '-Wl,-rpath,/first/rpath -Wl,-rpath,/second/rpath ' + - '-Wl,-rpath,/third/rpath -Wl,-rpath,/fourth/rpath ' + - '-Wl,-rpath,/spack-test-prefix/lib ' + - '-Wl,-rpath,/spack-test-prefix/lib64') - - def test_dep_lib(self): - """Ensure a single dependency RPATH is added.""" - os.environ['SPACK_DEPENDENCIES'] = self.dep2 - os.environ['SPACK_RPATH_DEPS'] = os.environ['SPACK_DEPENDENCIES'] - os.environ['SPACK_LINK_DEPS'] = os.environ['SPACK_DEPENDENCIES'] - self.check_cc('dump-args', test_command, - self.realcc + ' ' + - '-I/test/include -I/other/include arg1 ' + - '-Wl,--start-group arg2 arg3 -llib1 -llib2 arg4 ' + - '-Wl,--end-group ' + - '-llib3 -llib4 arg5 arg6 ' + - '-L/test/lib -L/other/lib ' + - '-L' + self.dep2 + '/lib64 ' + - '-Wl,-rpath,/first/rpath -Wl,-rpath,/second/rpath ' + - '-Wl,-rpath,/third/rpath -Wl,-rpath,/fourth/rpath ' + - '-Wl,-rpath,/spack-test-prefix/lib ' + - '-Wl,-rpath,/spack-test-prefix/lib64 ' + - '-Wl,-rpath,' + self.dep2 + '/lib64') - - def test_dep_lib_no_rpath(self): - """Ensure a single dependency link flag is added with no dep RPATH.""" - os.environ['SPACK_DEPENDENCIES'] = self.dep2 - os.environ['SPACK_LINK_DEPS'] = os.environ['SPACK_DEPENDENCIES'] - self.check_cc('dump-args', test_command, - self.realcc + ' ' + - '-I/test/include -I/other/include arg1 ' + - '-Wl,--start-group arg2 arg3 -llib1 -llib2 arg4 ' + - '-Wl,--end-group ' + - '-llib3 -llib4 arg5 arg6 ' + - '-L/test/lib -L/other/lib ' + - '-L' + self.dep2 + '/lib64 ' + - '-Wl,-rpath,/first/rpath -Wl,-rpath,/second/rpath ' + - '-Wl,-rpath,/third/rpath -Wl,-rpath,/fourth/rpath ' + - '-Wl,-rpath,/spack-test-prefix/lib ' + - '-Wl,-rpath,/spack-test-prefix/lib64') - - def test_dep_lib_no_lib(self): - """Ensure a single dependency RPATH is added with no -L.""" - os.environ['SPACK_DEPENDENCIES'] = self.dep2 - os.environ['SPACK_RPATH_DEPS'] = os.environ['SPACK_DEPENDENCIES'] - self.check_cc('dump-args', test_command, - self.realcc + ' ' + - '-I/test/include -I/other/include arg1 ' + - '-Wl,--start-group arg2 arg3 -llib1 -llib2 arg4 ' + - '-Wl,--end-group ' + - '-llib3 -llib4 arg5 arg6 ' + - '-L/test/lib -L/other/lib ' + - '-Wl,-rpath,/first/rpath -Wl,-rpath,/second/rpath ' + - '-Wl,-rpath,/third/rpath -Wl,-rpath,/fourth/rpath ' + - '-Wl,-rpath,/spack-test-prefix/lib ' + - '-Wl,-rpath,/spack-test-prefix/lib64 ' + - '-Wl,-rpath,' + self.dep2 + '/lib64') - - def test_all_deps(self): - """Ensure includes and RPATHs for all deps are added. """ - os.environ['SPACK_DEPENDENCIES'] = ':'.join([ - self.dep1, self.dep2, self.dep3, self.dep4]) - os.environ['SPACK_RPATH_DEPS'] = os.environ['SPACK_DEPENDENCIES'] - os.environ['SPACK_LINK_DEPS'] = os.environ['SPACK_DEPENDENCIES'] - - # This is probably more constrained than it needs to be; it - # checks order within prepended args and doesn't strictly have - # to. We could loosen that if it becomes necessary - self.check_cc('dump-args', test_command, - self.realcc + ' ' + - '-I/test/include -I/other/include ' + - '-I' + self.dep1 + '/include ' + - '-I' + self.dep3 + '/include ' + - '-I' + self.dep4 + '/include ' + - 'arg1 ' + - '-Wl,--start-group arg2 arg3 -llib1 -llib2 arg4 ' + - '-Wl,--end-group ' + - '-llib3 -llib4 arg5 arg6 ' + - '-L/test/lib -L/other/lib ' + - '-L' + self.dep1 + '/lib ' + - '-L' + self.dep2 + '/lib64 ' + - '-L' + self.dep3 + '/lib64 ' + - '-Wl,-rpath,/first/rpath -Wl,-rpath,/second/rpath ' + - '-Wl,-rpath,/third/rpath -Wl,-rpath,/fourth/rpath ' + - '-Wl,-rpath,/spack-test-prefix/lib ' + - '-Wl,-rpath,/spack-test-prefix/lib64 ' + - '-Wl,-rpath,' + self.dep1 + '/lib ' + - '-Wl,-rpath,' + self.dep2 + '/lib64 ' + - '-Wl,-rpath,' + self.dep3 + '/lib64') - - def test_ld_deps(self): - """Ensure no (extra) -I args or -Wl, are passed in ld mode.""" - os.environ['SPACK_DEPENDENCIES'] = ':'.join([ - self.dep1, self.dep2, self.dep3, self.dep4]) - os.environ['SPACK_RPATH_DEPS'] = os.environ['SPACK_DEPENDENCIES'] - os.environ['SPACK_LINK_DEPS'] = os.environ['SPACK_DEPENDENCIES'] - - self.check_ld('dump-args', test_command, - 'ld ' + - '-I/test/include -I/other/include ' + - 'arg1 ' + - '-Wl,--start-group arg2 arg3 -llib1 -llib2 arg4 ' + - '-Wl,--end-group ' + - '-llib3 -llib4 arg5 arg6 ' + - '-L/test/lib -L/other/lib ' + - '-L' + self.dep1 + '/lib ' + - '-L' + self.dep2 + '/lib64 ' + - '-L' + self.dep3 + '/lib64 ' + - '-rpath /first/rpath -rpath /second/rpath ' + - '-rpath /third/rpath -rpath /fourth/rpath ' + - '-rpath /spack-test-prefix/lib ' + - '-rpath /spack-test-prefix/lib64 ' + - '-rpath ' + self.dep1 + '/lib ' + - '-rpath ' + self.dep2 + '/lib64 ' + - '-rpath ' + self.dep3 + '/lib64') - - def test_ld_deps_no_rpath(self): - """Ensure SPACK_RPATH_DEPS controls RPATHs for ld.""" - os.environ['SPACK_DEPENDENCIES'] = ':'.join([ - self.dep1, self.dep2, self.dep3, self.dep4]) - os.environ['SPACK_LINK_DEPS'] = os.environ['SPACK_DEPENDENCIES'] - - self.check_ld('dump-args', test_command, - 'ld ' + - '-I/test/include -I/other/include ' + - 'arg1 ' + - '-Wl,--start-group arg2 arg3 -llib1 -llib2 arg4 ' + - '-Wl,--end-group ' + - '-llib3 -llib4 arg5 arg6 ' + - '-L/test/lib -L/other/lib ' + - '-L' + self.dep1 + '/lib ' + - '-L' + self.dep2 + '/lib64 ' + - '-L' + self.dep3 + '/lib64 ' + - '-rpath /first/rpath -rpath /second/rpath ' + - '-rpath /third/rpath -rpath /fourth/rpath ' + - '-rpath /spack-test-prefix/lib ' + - '-rpath /spack-test-prefix/lib64') - - def test_ld_deps_no_link(self): - """Ensure SPACK_LINK_DEPS controls -L for ld.""" - os.environ['SPACK_DEPENDENCIES'] = ':'.join([ - self.dep1, self.dep2, self.dep3, self.dep4]) - os.environ['SPACK_RPATH_DEPS'] = os.environ['SPACK_DEPENDENCIES'] - - self.check_ld('dump-args', test_command, - 'ld ' + - '-I/test/include -I/other/include ' + - 'arg1 ' + - '-Wl,--start-group arg2 arg3 -llib1 -llib2 arg4 ' + - '-Wl,--end-group ' + - '-llib3 -llib4 arg5 arg6 ' + - '-L/test/lib -L/other/lib ' + - '-rpath /first/rpath -rpath /second/rpath ' + - '-rpath /third/rpath -rpath /fourth/rpath ' + - '-rpath /spack-test-prefix/lib ' + - '-rpath /spack-test-prefix/lib64 ' + - '-rpath ' + self.dep1 + '/lib ' + - '-rpath ' + self.dep2 + '/lib64 ' + - '-rpath ' + self.dep3 + '/lib64') - - def test_ld_deps_reentrant(self): - """Make sure ld -r is handled correctly on OS's where it doesn't - support rpaths.""" - os.environ['SPACK_DEPENDENCIES'] = ':'.join([self.dep1]) - os.environ['SPACK_RPATH_DEPS'] = os.environ['SPACK_DEPENDENCIES'] - os.environ['SPACK_LINK_DEPS'] = os.environ['SPACK_DEPENDENCIES'] +# mock flags to use in the wrapper environment +spack_cppflags = ['-g', '-O1', '-DVAR=VALUE'] +spack_cflags = ['-Wall'] +spack_cxxflags = ['-Werror'] +spack_fflags = ['-w'] +spack_ldflags = ['-L', 'foo'] +spack_ldlibs = ['-lfoo'] + + +@pytest.fixture(scope='session') +def wrapper_environment(): + with set_env( + SPACK_CC=real_cc, + SPACK_CXX=real_cc, + SPACK_FC=real_cc, + SPACK_PREFIX=pkg_prefix, + SPACK_ENV_PATH='test', + SPACK_DEBUG_LOG_DIR='.', + SPACK_DEBUG_LOG_ID='foo-hashabc', + SPACK_COMPILER_SPEC='gcc@4.4.7', + SPACK_SHORT_SPEC='foo@1.2 arch=linux-rhel6-x86_64 /hashabc', + SPACK_SYSTEM_DIRS=' '.join(system_dirs), + SPACK_CC_RPATH_ARG='-Wl,-rpath,', + SPACK_CXX_RPATH_ARG='-Wl,-rpath,', + SPACK_F77_RPATH_ARG='-Wl,-rpath,', + SPACK_FC_RPATH_ARG='-Wl,-rpath,', + SPACK_DEPENDENCIES=None): + yield + +@pytest.fixture() +def wrapper_flags(): + with set_env( + SPACK_CPPFLAGS=' '.join(spack_cppflags), + SPACK_CFLAGS=' '.join(spack_cflags), + SPACK_CXXFLAGS=' '.join(spack_cxxflags), + SPACK_FFLAGS=' '.join(spack_fflags), + SPACK_LDFLAGS=' '.join(spack_ldflags), + SPACK_LDLIBS=' '.join(spack_ldlibs)): + yield + + +@pytest.fixture(scope='session') +def dep1(tmpdir_factory): + path = tmpdir_factory.mktemp('cc-dep1') + path.mkdir('include') + path.mkdir('lib') + yield str(path) + + +@pytest.fixture(scope='session') +def dep2(tmpdir_factory): + path = tmpdir_factory.mktemp('cc-dep2') + path.mkdir('lib64') + yield str(path) + + +@pytest.fixture(scope='session') +def dep3(tmpdir_factory): + path = tmpdir_factory.mktemp('cc-dep3') + path.mkdir('include') + path.mkdir('lib64') + yield str(path) + + +@pytest.fixture(scope='session') +def dep4(tmpdir_factory): + path = tmpdir_factory.mktemp('cc-dep4') + path.mkdir('include') + yield str(path) + + +pytestmark = pytest.mark.usefixtures('wrapper_environment') + + +def check_cc(command, args, expected): + with set_env(SPACK_TEST_COMMAND=command): + assert cc(*args, output=str).strip().split() == expected + + +def check_cxx(command, args, expected): + with set_env(SPACK_TEST_COMMAND=command): + assert cxx(*args, output=str).strip().split() == expected + + +def check_fc(command, args, expected): + with set_env(SPACK_TEST_COMMAND=command): + assert fc(*args, output=str).strip().split() == expected + + +def check_ld(command, args, expected): + with set_env(SPACK_TEST_COMMAND=command): + assert ld(*args, output=str).strip().split() == expected + + +def check_cpp(command, args, expected): + with set_env(SPACK_TEST_COMMAND=command): + assert cpp(*args, output=str).strip().split() == expected + + +def test_vcheck_mode(): + check_cc( + 'dump-mode', ['-I/include', '--version'], ['vcheck']) + check_cc( + 'dump-mode', ['-I/include', '-V'], ['vcheck']) + check_cc( + 'dump-mode', ['-I/include', '-v'], ['vcheck']) + check_cc( + 'dump-mode', ['-I/include', '-dumpversion'], ['vcheck']) + check_cc( + 'dump-mode', ['-I/include', '--version', '-c'], ['vcheck']) + check_cc( + 'dump-mode', ['-I/include', '-V', '-o', 'output'], ['vcheck']) + + +def test_cpp_mode(): + check_cc('dump-mode', ['-E'], ['cpp']) + check_cpp('dump-mode', [], ['cpp']) + + +def test_as_mode(): + check_cc('dump-mode', ['-S'], ['as']) + + +def test_ccld_mode(): + check_cc( + 'dump-mode', [], ['ccld']) + check_cc( + 'dump-mode', ['foo.c', '-o', 'foo'], ['ccld']) + check_cc( + 'dump-mode', ['foo.c', '-o', 'foo', '-Wl,-rpath,foo'], ['ccld']) + check_cc( + 'dump-mode', + ['foo.o', 'bar.o', 'baz.o', '-o', 'foo', '-Wl,-rpath,foo'], ['ccld']) + + +def test_ld_mode(): + check_ld('dump-mode', [], ['ld']) + check_ld( + 'dump-mode', + ['foo.o', 'bar.o', 'baz.o', '-o', 'foo', '-Wl,-rpath,foo'], ['ld']) + + +def test_ld_flags(wrapper_flags): + check_ld( + 'dump-args', test_args, + ['ld'] + + spack_ldflags + + test_include_paths + + test_library_paths + + test_rpaths + + pkg_rpaths + + test_args_without_paths + + spack_ldlibs) + + +def test_cpp_flags(wrapper_flags): + check_cpp( + 'dump-args', test_args, + ['cpp'] + + spack_cppflags + + test_include_paths + + test_library_paths + + test_args_without_paths) + + +def test_cc_flags(wrapper_flags): + check_cc( + 'dump-args', test_args, + [real_cc] + + spack_cppflags + + spack_cflags + + spack_ldflags + + test_include_paths + + test_library_paths + + test_wl_rpaths + + pkg_wl_rpaths + + test_args_without_paths + + spack_ldlibs) + + +def test_cxx_flags(wrapper_flags): + check_cxx( + 'dump-args', test_args, + [real_cc] + + spack_cppflags + + spack_cxxflags + + spack_ldflags + + test_include_paths + + test_library_paths + + test_wl_rpaths + + pkg_wl_rpaths + + test_args_without_paths + + spack_ldlibs) + + +def test_fc_flags(wrapper_flags): + check_fc( + 'dump-args', test_args, + [real_cc] + + spack_cppflags + + spack_fflags + + spack_ldflags + + test_include_paths + + test_library_paths + + test_wl_rpaths + + pkg_wl_rpaths + + test_args_without_paths + + spack_ldlibs) + + +def test_dep_rpath(): + """Ensure RPATHs for root package are added.""" + check_cc( + 'dump-args', test_args, + [real_cc] + + test_include_paths + + test_library_paths + + test_wl_rpaths + + pkg_wl_rpaths + + test_args_without_paths) + + +def test_dep_include(dep4): + """Ensure a single dependency include directory is added.""" + with set_env(SPACK_DEPENDENCIES=dep4, + SPACK_RPATH_DEPS=dep4, + SPACK_LINK_DEPS=dep4): + check_cc( + 'dump-args', test_args, + [real_cc] + + test_include_paths + + ['-I' + dep4 + '/include'] + + test_library_paths + + test_wl_rpaths + + pkg_wl_rpaths + + test_args_without_paths) + + +def test_dep_lib(dep2): + """Ensure a single dependency RPATH is added.""" + with set_env(SPACK_DEPENDENCIES=dep2, + SPACK_RPATH_DEPS=dep2, + SPACK_LINK_DEPS=dep2): + check_cc( + 'dump-args', test_args, + [real_cc] + + test_include_paths + + test_library_paths + + ['-L' + dep2 + '/lib64'] + + test_wl_rpaths + + pkg_wl_rpaths + + ['-Wl,-rpath,' + dep2 + '/lib64'] + + test_args_without_paths) + + +def test_dep_lib_no_rpath(dep2): + """Ensure a single dependency link flag is added with no dep RPATH.""" + with set_env(SPACK_DEPENDENCIES=dep2, + SPACK_LINK_DEPS=dep2): + check_cc( + 'dump-args', test_args, + [real_cc] + + test_include_paths + + test_library_paths + + ['-L' + dep2 + '/lib64'] + + test_wl_rpaths + + pkg_wl_rpaths + + test_args_without_paths) + + +def test_dep_lib_no_lib(dep2): + """Ensure a single dependency RPATH is added with no -L.""" + with set_env(SPACK_DEPENDENCIES=dep2, + SPACK_RPATH_DEPS=dep2): + check_cc( + 'dump-args', test_args, + [real_cc] + + test_include_paths + + test_library_paths + + test_wl_rpaths + + pkg_wl_rpaths + + ['-Wl,-rpath,' + dep2 + '/lib64'] + + test_args_without_paths) + + +def test_ccld_deps(dep1, dep2, dep3, dep4): + """Ensure all flags are added in ccld mode.""" + deps = ':'.join((dep1, dep2, dep3, dep4)) + with set_env(SPACK_DEPENDENCIES=deps, + SPACK_RPATH_DEPS=deps, + SPACK_LINK_DEPS=deps): + check_cc( + 'dump-args', test_args, + [real_cc] + + test_include_paths + + ['-I' + dep1 + '/include', + '-I' + dep3 + '/include', + '-I' + dep4 + '/include'] + + test_library_paths + + ['-L' + dep1 + '/lib', + '-L' + dep2 + '/lib64', + '-L' + dep3 + '/lib64'] + + test_wl_rpaths + + pkg_wl_rpaths + + ['-Wl,-rpath,' + dep1 + '/lib', + '-Wl,-rpath,' + dep2 + '/lib64', + '-Wl,-rpath,' + dep3 + '/lib64'] + + test_args_without_paths) + + +def test_cc_deps(dep1, dep2, dep3, dep4): + """Ensure -L and RPATHs are not added in cc mode.""" + deps = ':'.join((dep1, dep2, dep3, dep4)) + with set_env(SPACK_DEPENDENCIES=deps, + SPACK_RPATH_DEPS=deps, + SPACK_LINK_DEPS=deps): + check_cc( + 'dump-args', ['-c'] + test_args, + [real_cc] + + test_include_paths + + ['-I' + dep1 + '/include', + '-I' + dep3 + '/include', + '-I' + dep4 + '/include'] + + test_library_paths + + ['-c'] + + test_args_without_paths) + + +def test_ld_deps(dep1, dep2, dep3, dep4): + """Ensure no (extra) -I args or -Wl, are passed in ld mode.""" + deps = ':'.join((dep1, dep2, dep3, dep4)) + with set_env(SPACK_DEPENDENCIES=deps, + SPACK_RPATH_DEPS=deps, + SPACK_LINK_DEPS=deps): + check_ld( + 'dump-args', test_args, + ['ld'] + + test_include_paths + + test_library_paths + + ['-L' + dep1 + '/lib', + '-L' + dep2 + '/lib64', + '-L' + dep3 + '/lib64'] + + test_rpaths + + pkg_rpaths + + ['-rpath', dep1 + '/lib', + '-rpath', dep2 + '/lib64', + '-rpath', dep3 + '/lib64'] + + test_args_without_paths) + + +def test_ld_deps_no_rpath(dep1, dep2, dep3, dep4): + """Ensure SPACK_LINK_DEPS controls -L for ld.""" + deps = ':'.join((dep1, dep2, dep3, dep4)) + with set_env(SPACK_DEPENDENCIES=deps, + SPACK_LINK_DEPS=deps): + check_ld( + 'dump-args', test_args, + ['ld'] + + test_include_paths + + test_library_paths + + ['-L' + dep1 + '/lib', + '-L' + dep2 + '/lib64', + '-L' + dep3 + '/lib64'] + + test_rpaths + + pkg_rpaths + + test_args_without_paths) + + +def test_ld_deps_no_link(dep1, dep2, dep3, dep4): + """Ensure SPACK_RPATH_DEPS controls -rpath for ld.""" + deps = ':'.join((dep1, dep2, dep3, dep4)) + with set_env(SPACK_DEPENDENCIES=deps, + SPACK_RPATH_DEPS=deps): + check_ld( + 'dump-args', test_args, + ['ld'] + + test_include_paths + + test_library_paths + + test_rpaths + + pkg_rpaths + + ['-rpath', dep1 + '/lib', + '-rpath', dep2 + '/lib64', + '-rpath', dep3 + '/lib64'] + + test_args_without_paths) + + +def test_ld_deps_partial(dep1): + """Make sure ld -r (partial link) is handled correctly on OS's where it + doesn't accept rpaths. + """ + with set_env(SPACK_DEPENDENCIES=dep1, + SPACK_RPATH_DEPS=dep1, + SPACK_LINK_DEPS=dep1): + # TODO: do we need to add RPATHs on other platforms like Linux? + # TODO: Can't we treat them the same? os.environ['SPACK_SHORT_SPEC'] = "foo@1.2=linux-x86_64" - reentrant_test_command = ['-r'] + test_command - self.check_ld('dump-args', reentrant_test_command, - 'ld ' + - '-I/test/include -I/other/include ' + - '-r arg1 ' + - '-Wl,--start-group arg2 arg3 -llib1 -llib2 arg4 ' + - '-Wl,--end-group ' + - '-llib3 -llib4 arg5 arg6 ' + - '-L/test/lib -L/other/lib ' + - '-L' + self.dep1 + '/lib ' + - '-rpath /first/rpath -rpath /second/rpath ' + - '-rpath /third/rpath -rpath /fourth/rpath ' + - '-rpath /spack-test-prefix/lib ' + - '-rpath /spack-test-prefix/lib64 ' + - '-rpath ' + self.dep1 + '/lib') + check_ld( + 'dump-args', ['-r'] + test_args, + ['ld'] + + test_include_paths + + test_library_paths + + ['-L' + dep1 + '/lib'] + + test_rpaths + + pkg_rpaths + + ['-rpath', dep1 + '/lib'] + + ['-r'] + + test_args_without_paths) # rpaths from the underlying command will still appear # Spack will not add its own rpaths. os.environ['SPACK_SHORT_SPEC'] = "foo@1.2=darwin-x86_64" - self.check_ld('dump-args', reentrant_test_command, - 'ld ' + - '-I/test/include -I/other/include ' + - '-r arg1 ' + - '-Wl,--start-group arg2 arg3 -llib1 -llib2 arg4 ' + - '-Wl,--end-group ' + - '-llib3 -llib4 arg5 arg6 ' + - '-L/test/lib -L/other/lib ' + - '-L' + self.dep1 + '/lib ' + - '-rpath /first/rpath -rpath /second/rpath ' + - '-rpath /third/rpath -rpath /fourth/rpath') + check_ld( + 'dump-args', ['-r'] + test_args, + ['ld'] + + test_include_paths + + test_library_paths + + ['-L' + dep1 + '/lib'] + + test_rpaths + + ['-r'] + + test_args_without_paths) diff --git a/lib/spack/spack/util/environment.py b/lib/spack/spack/util/environment.py index 1b07b72873..751010a835 100644 --- a/lib/spack/spack/util/environment.py +++ b/lib/spack/spack/util/environment.py @@ -22,8 +22,10 @@ # License along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################## +import contextlib import os + system_paths = ['/', '/usr', '/usr/local'] suffixes = ['bin', 'bin64', 'include', 'lib', 'lib64'] system_dirs = [os.path.join(p, s) for s in suffixes for p in system_paths] + \ @@ -86,3 +88,30 @@ def dump_environment(path): with open(path, 'w') as env_file: for key, val in sorted(os.environ.items()): env_file.write('export %s="%s"\n' % (key, val)) + + +@contextlib.contextmanager +def set_env(**kwargs): + """Temporarily sets and restores environment variables. + + Variables can be set as keyword arguments to this function. + """ + saved = {} + for var, value in kwargs.items(): + if var in os.environ: + saved[var] = os.environ[var] + + if value is None: + if var in os.environ: + del os.environ[var] + else: + os.environ[var] = value + + yield + + for var, value in kwargs.items(): + if var in saved: + os.environ[var] = saved[var] + else: + if var in os.environ: + del os.environ[var] |