diff options
-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] |