Unified environment modifications in config files (#14372)
* Unified environment modifications in config files fixes #13357 This commit factors all the code that is involved in the validation (schema) and parsing of environment modifications from configuration files in a single place. The factored out code is then used for module files and compiler configuration. Attributes were separated by dashes in `compilers.yaml` files and by underscores in `modules.yaml` files. This PR unifies the syntax on attributes separated by underscores. Unit testing of environment modifications in compilers has been refactored and simplified.
This commit is contained in:
		
				
					committed by
					
						
						Greg Becker
					
				
			
			
				
	
			
			
			
						parent
						
							0f3ae864a5
						
					
				
				
					commit
					b9629c36f2
				
			@@ -929,11 +929,13 @@ in GNU Autotools. If all flags are set, the order is
 | 
			
		||||
Compiler environment variables and additional RPATHs
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
In the exceptional case a compiler requires setting special environment
 | 
			
		||||
variables, like an explicit library load path. These can bet set in an
 | 
			
		||||
extra section in the compiler configuration (the supported environment
 | 
			
		||||
modification commands are: ``set``, ``unset``, ``append-path``, and
 | 
			
		||||
``prepend-path``). The user can also specify additional ``RPATHs`` that the
 | 
			
		||||
Sometimes compilers require setting special environment variables to
 | 
			
		||||
operate correctly. Spack handles these cases by allowing custom environment
 | 
			
		||||
modifications in the ``environment`` attribute of the compiler configuration
 | 
			
		||||
section. See also the :ref:`configuration_environment_variables` section
 | 
			
		||||
of the configuration files docs for more information.
 | 
			
		||||
 | 
			
		||||
It is also possible to specify additional ``RPATHs`` that the
 | 
			
		||||
compiler will add to all executables generated by that compiler.  This is
 | 
			
		||||
useful for forcing certain compilers to RPATH their own runtime libraries, so
 | 
			
		||||
that executables will run without the need to set ``LD_LIBRARY_PATH``.
 | 
			
		||||
@@ -950,28 +952,19 @@ that executables will run without the need to set ``LD_LIBRARY_PATH``.
 | 
			
		||||
          fc: /opt/gcc/bin/gfortran
 | 
			
		||||
        environment:
 | 
			
		||||
          unset:
 | 
			
		||||
            BAD_VARIABLE: # The colon is required but the value must be empty
 | 
			
		||||
            - BAD_VARIABLE
 | 
			
		||||
          set:
 | 
			
		||||
            GOOD_VARIABLE_NUM: 1
 | 
			
		||||
            GOOD_VARIABLE_STR: good
 | 
			
		||||
          prepend-path:
 | 
			
		||||
          prepend_path:
 | 
			
		||||
            PATH: /path/to/binutils
 | 
			
		||||
          append-path:
 | 
			
		||||
          append_path:
 | 
			
		||||
            LD_LIBRARY_PATH: /opt/gcc/lib
 | 
			
		||||
        extra_rpaths:
 | 
			
		||||
        - /path/to/some/compiler/runtime/directory
 | 
			
		||||
        - /path/to/some/other/compiler/runtime/directory
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
   The section `environment` is interpreted as an ordered dictionary, which
 | 
			
		||||
   means two things. First, environment modification are applied in the order
 | 
			
		||||
   they are specified in the configuration file. Second, you cannot express
 | 
			
		||||
   environment modifications that require mixing different commands, i.e. you
 | 
			
		||||
   cannot `set` one variable, than `prepend-path` to another one, and than
 | 
			
		||||
   again `set` a third one.
 | 
			
		||||
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
Architecture specifiers
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 
 | 
			
		||||
@@ -427,6 +427,33 @@ home directory, and ``~user`` will expand to a specified user's home
 | 
			
		||||
directory. The ``~`` must appear at the beginning of the path, or Spack
 | 
			
		||||
will not expand it.
 | 
			
		||||
 | 
			
		||||
.. _configuration_environment_variables:
 | 
			
		||||
 | 
			
		||||
-------------------------
 | 
			
		||||
Environment Modifications
 | 
			
		||||
-------------------------
 | 
			
		||||
 | 
			
		||||
Spack allows to prescribe custom environment modifications in a few places
 | 
			
		||||
within its configuration files. Every time these modifications are allowed
 | 
			
		||||
they are specified as a dictionary, like in the following example:
 | 
			
		||||
 | 
			
		||||
.. code-block:: yaml
 | 
			
		||||
 | 
			
		||||
   environment:
 | 
			
		||||
     set:
 | 
			
		||||
       LICENSE_FILE: '/path/to/license'
 | 
			
		||||
     unset:
 | 
			
		||||
     - CPATH
 | 
			
		||||
     - LIBRARY_PATH
 | 
			
		||||
     append_path:
 | 
			
		||||
       PATH: '/new/bin/dir'
 | 
			
		||||
 | 
			
		||||
The possible actions that are permitted are ``set``, ``unset``, ``append_path``,
 | 
			
		||||
``prepend_path`` and finally ``remove_path``. They all require a dictionary
 | 
			
		||||
of variable names mapped to the values used for the modification.
 | 
			
		||||
The only exception is ``unset`` that requires just a list of variable names.
 | 
			
		||||
No particular order is ensured on the execution of each of these modifications.
 | 
			
		||||
 | 
			
		||||
----------------------------
 | 
			
		||||
Seeing Spack's Configuration
 | 
			
		||||
----------------------------
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,6 @@
 | 
			
		||||
import sys
 | 
			
		||||
import traceback
 | 
			
		||||
import types
 | 
			
		||||
from six import iteritems
 | 
			
		||||
from six import StringIO
 | 
			
		||||
 | 
			
		||||
import llnl.util.tty as tty
 | 
			
		||||
@@ -52,6 +51,7 @@
 | 
			
		||||
import spack.config
 | 
			
		||||
import spack.main
 | 
			
		||||
import spack.paths
 | 
			
		||||
import spack.schema.environment
 | 
			
		||||
import spack.store
 | 
			
		||||
from spack.util.string import plural
 | 
			
		||||
from spack.util.environment import (
 | 
			
		||||
@@ -342,21 +342,7 @@ def set_build_environment_variables(pkg, env, dirty):
 | 
			
		||||
    # Set environment variables if specified for
 | 
			
		||||
    # the given compiler
 | 
			
		||||
    compiler = pkg.compiler
 | 
			
		||||
    environment = compiler.environment
 | 
			
		||||
 | 
			
		||||
    for command, variable in iteritems(environment):
 | 
			
		||||
        if command == 'set':
 | 
			
		||||
            for name, value in iteritems(variable):
 | 
			
		||||
                env.set(name, value)
 | 
			
		||||
        elif command == 'unset':
 | 
			
		||||
            for name, _ in iteritems(variable):
 | 
			
		||||
                env.unset(name)
 | 
			
		||||
        elif command == 'prepend-path':
 | 
			
		||||
            for name, value in iteritems(variable):
 | 
			
		||||
                env.prepend_path(name, value)
 | 
			
		||||
        elif command == 'append-path':
 | 
			
		||||
            for name, value in iteritems(variable):
 | 
			
		||||
                env.append_path(name, value)
 | 
			
		||||
    env.extend(spack.schema.environment.parse(compiler.environment))
 | 
			
		||||
 | 
			
		||||
    if compiler.extra_rpaths:
 | 
			
		||||
        extra_rpaths = ':'.join(compiler.extra_rpaths)
 | 
			
		||||
 
 | 
			
		||||
@@ -28,26 +28,24 @@
 | 
			
		||||
Each of the four classes needs to be sub-classed when implementing a new
 | 
			
		||||
module type.
 | 
			
		||||
"""
 | 
			
		||||
import collections
 | 
			
		||||
import copy
 | 
			
		||||
import datetime
 | 
			
		||||
import inspect
 | 
			
		||||
import os.path
 | 
			
		||||
import re
 | 
			
		||||
import collections
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
import llnl.util.filesystem
 | 
			
		||||
import llnl.util.tty as tty
 | 
			
		||||
 | 
			
		||||
import spack.paths
 | 
			
		||||
import spack.build_environment as build_environment
 | 
			
		||||
import spack.util.environment
 | 
			
		||||
import spack.tengine as tengine
 | 
			
		||||
import spack.util.path
 | 
			
		||||
import spack.util.environment
 | 
			
		||||
import spack.error
 | 
			
		||||
import spack.util.spack_yaml as syaml
 | 
			
		||||
import spack.paths
 | 
			
		||||
import spack.schema.environment
 | 
			
		||||
import spack.tengine as tengine
 | 
			
		||||
import spack.util.environment
 | 
			
		||||
import spack.util.file_permissions as fp
 | 
			
		||||
import spack.util.path
 | 
			
		||||
import spack.util.spack_yaml as syaml
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#: config section for this file
 | 
			
		||||
@@ -415,22 +413,7 @@ def env(self):
 | 
			
		||||
        """List of environment modifications that should be done in the
 | 
			
		||||
        module.
 | 
			
		||||
        """
 | 
			
		||||
        env_mods = spack.util.environment.EnvironmentModifications()
 | 
			
		||||
        actions = self.conf.get('environment', {})
 | 
			
		||||
 | 
			
		||||
        def process_arglist(arglist):
 | 
			
		||||
            if method == 'unset':
 | 
			
		||||
                for x in arglist:
 | 
			
		||||
                    yield (x,)
 | 
			
		||||
            else:
 | 
			
		||||
                for x in six.iteritems(arglist):
 | 
			
		||||
                    yield x
 | 
			
		||||
 | 
			
		||||
        for method, arglist in actions.items():
 | 
			
		||||
            for args in process_arglist(arglist):
 | 
			
		||||
                getattr(env_mods, method)(*args)
 | 
			
		||||
 | 
			
		||||
        return env_mods
 | 
			
		||||
        return spack.schema.environment.parse(self.conf.get('environment', {}))
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def suffixes(self):
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@
 | 
			
		||||
.. literalinclude:: _spack_root/lib/spack/spack/schema/compilers.py
 | 
			
		||||
   :lines: 13-
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import spack.schema.environment
 | 
			
		||||
 | 
			
		||||
#: Properties for inclusion in other schemas
 | 
			
		||||
properties = {
 | 
			
		||||
@@ -68,50 +68,7 @@
 | 
			
		||||
                                {'type': 'boolean'}
 | 
			
		||||
                            ]
 | 
			
		||||
                        },
 | 
			
		||||
                        'environment': {
 | 
			
		||||
                            'type': 'object',
 | 
			
		||||
                            'default': {},
 | 
			
		||||
                            'additionalProperties': False,
 | 
			
		||||
                            'properties': {
 | 
			
		||||
                                'set': {
 | 
			
		||||
                                    'type': 'object',
 | 
			
		||||
                                    'patternProperties': {
 | 
			
		||||
                                        # Variable name
 | 
			
		||||
                                        r'\w[\w-]*': {
 | 
			
		||||
                                            'anyOf': [{'type': 'string'},
 | 
			
		||||
                                                      {'type': 'number'}]
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                },
 | 
			
		||||
                                'unset': {
 | 
			
		||||
                                    'type': 'object',
 | 
			
		||||
                                    'patternProperties': {
 | 
			
		||||
                                        # Variable name
 | 
			
		||||
                                        r'\w[\w-]*': {'type': 'null'}
 | 
			
		||||
                                    }
 | 
			
		||||
                                },
 | 
			
		||||
                                'prepend-path': {
 | 
			
		||||
                                    'type': 'object',
 | 
			
		||||
                                    'patternProperties': {
 | 
			
		||||
                                        # Variable name
 | 
			
		||||
                                        r'\w[\w-]*': {
 | 
			
		||||
                                            'anyOf': [{'type': 'string'},
 | 
			
		||||
                                                      {'type': 'number'}]
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                },
 | 
			
		||||
                                'append-path': {
 | 
			
		||||
                                    'type': 'object',
 | 
			
		||||
                                    'patternProperties': {
 | 
			
		||||
                                        # Variable name
 | 
			
		||||
                                        r'\w[\w-]*': {
 | 
			
		||||
                                            'anyOf': [{'type': 'string'},
 | 
			
		||||
                                                      {'type': 'number'}]
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        'environment': spack.schema.environment.definition,
 | 
			
		||||
                        'extra_rpaths': {
 | 
			
		||||
                            'type': 'array',
 | 
			
		||||
                            'default': [],
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										58
									
								
								lib/spack/spack/schema/environment.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								lib/spack/spack/schema/environment.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
# Copyright 2013-2019 Lawrence Livermore National Security, LLC and other
 | 
			
		||||
# Spack Project Developers. See the top-level COPYRIGHT file for details.
 | 
			
		||||
#
 | 
			
		||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
			
		||||
"""Schema for environment modifications. Meant for inclusion in other
 | 
			
		||||
schemas.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
array_of_strings_or_num = {
 | 
			
		||||
    'type': 'array', 'default': [], 'items':
 | 
			
		||||
        {'anyOf': [{'type': 'string'}, {'type': 'number'}]}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dictionary_of_strings_or_num = {
 | 
			
		||||
    'type': 'object', 'patternProperties':
 | 
			
		||||
        {r'\w[\w-]*': {'anyOf': [{'type': 'string'}, {'type': 'number'}]}}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
definition = {
 | 
			
		||||
    'type': 'object',
 | 
			
		||||
    'default': {},
 | 
			
		||||
    'additionalProperties': False,
 | 
			
		||||
    'properties': {
 | 
			
		||||
        'set': dictionary_of_strings_or_num,
 | 
			
		||||
        'unset': array_of_strings_or_num,
 | 
			
		||||
        'prepend_path': dictionary_of_strings_or_num,
 | 
			
		||||
        'append_path': dictionary_of_strings_or_num,
 | 
			
		||||
        'remove_path': dictionary_of_strings_or_num
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parse(config_obj):
 | 
			
		||||
    """Returns an EnvironmentModifications object containing the modifications
 | 
			
		||||
    parsed from input.
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        config_obj: a configuration dictionary conforming to the
 | 
			
		||||
            schema definition for environment modifications
 | 
			
		||||
    """
 | 
			
		||||
    import spack.util.environment as ev
 | 
			
		||||
    try:
 | 
			
		||||
        from collections import Sequence  # novm
 | 
			
		||||
    except ImportError:
 | 
			
		||||
        from collections.abc import Sequence  # novm
 | 
			
		||||
 | 
			
		||||
    env = ev.EnvironmentModifications()
 | 
			
		||||
    for command, variable in config_obj.items():
 | 
			
		||||
        # Distinguish between commands that take only a name as argument
 | 
			
		||||
        # (e.g. unset) and commands that take a name and a value.
 | 
			
		||||
        if isinstance(variable, Sequence):
 | 
			
		||||
            for name in variable:
 | 
			
		||||
                getattr(env, command)(name)
 | 
			
		||||
        else:
 | 
			
		||||
            for name, value in variable.items():
 | 
			
		||||
                getattr(env, command)(name, value)
 | 
			
		||||
 | 
			
		||||
    return env
 | 
			
		||||
@@ -8,6 +8,8 @@
 | 
			
		||||
.. literalinclude:: _spack_root/lib/spack/spack/schema/modules.py
 | 
			
		||||
   :lines: 13-
 | 
			
		||||
"""
 | 
			
		||||
import spack.schema.environment
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#: Matches a spec or a multi-valued variant but not another
 | 
			
		||||
#: valid keyword.
 | 
			
		||||
@@ -66,17 +68,7 @@
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        'environment': {
 | 
			
		||||
            'type': 'object',
 | 
			
		||||
            'default': {},
 | 
			
		||||
            'additionalProperties': False,
 | 
			
		||||
            'properties': {
 | 
			
		||||
                'set': dictionary_of_strings,
 | 
			
		||||
                'unset': array_of_strings,
 | 
			
		||||
                'prepend_path': dictionary_of_strings,
 | 
			
		||||
                'append_path': dictionary_of_strings
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        'environment': spack.schema.environment.definition
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,6 @@
 | 
			
		||||
from spack.paths import build_env_path
 | 
			
		||||
from spack.build_environment import dso_suffix, _static_to_shared_library
 | 
			
		||||
from spack.util.executable import Executable
 | 
			
		||||
from spack.util.spack_yaml import syaml_dict, syaml_str
 | 
			
		||||
from spack.util.environment import EnvironmentModifications
 | 
			
		||||
 | 
			
		||||
from llnl.util.filesystem import LibraryList, HeaderList
 | 
			
		||||
@@ -65,6 +64,18 @@ def build_environment(working_env):
 | 
			
		||||
        del os.environ[name]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture
 | 
			
		||||
def ensure_env_variables(config, mock_packages, monkeypatch, working_env):
 | 
			
		||||
    """Returns a function that takes a dictionary and updates os.environ
 | 
			
		||||
    for the test lifetime accordingly. Plugs-in mock config and repo.
 | 
			
		||||
    """
 | 
			
		||||
    def _ensure(env_mods):
 | 
			
		||||
        for name, value in env_mods.items():
 | 
			
		||||
            monkeypatch.setenv(name, value)
 | 
			
		||||
 | 
			
		||||
    return _ensure
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_static_to_shared_library(build_environment):
 | 
			
		||||
    os.environ['SPACK_TEST_COMMAND'] = 'dump-args'
 | 
			
		||||
 | 
			
		||||
@@ -119,79 +130,58 @@ def _set_wrong_cc(x):
 | 
			
		||||
    assert os.environ['ANOTHER_VAR'] == 'THIS_IS_SET'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.usefixtures('config', 'mock_packages')
 | 
			
		||||
def test_compiler_config_modifications(monkeypatch, working_env):
 | 
			
		||||
    s = spack.spec.Spec('cmake')
 | 
			
		||||
    s.concretize()
 | 
			
		||||
    pkg = s.package
 | 
			
		||||
@pytest.mark.parametrize('initial,modifications,expected', [
 | 
			
		||||
    # Set and unset variables
 | 
			
		||||
    ({'SOME_VAR_STR': '', 'SOME_VAR_NUM': '0'},
 | 
			
		||||
     {'set': {'SOME_VAR_STR': 'SOME_STR', 'SOME_VAR_NUM': 1}},
 | 
			
		||||
     {'SOME_VAR_STR': 'SOME_STR', 'SOME_VAR_NUM': '1'}),
 | 
			
		||||
    ({'SOME_VAR_STR': ''},
 | 
			
		||||
     {'unset': ['SOME_VAR_STR']},
 | 
			
		||||
     {'SOME_VAR_STR': None}),
 | 
			
		||||
    ({},  # Set a variable that was not defined already
 | 
			
		||||
     {'set': {'SOME_VAR_STR': 'SOME_STR'}},
 | 
			
		||||
     {'SOME_VAR_STR': 'SOME_STR'}),
 | 
			
		||||
    # Append and prepend to the same variable
 | 
			
		||||
    ({'EMPTY_PATH_LIST': '/path/middle'},
 | 
			
		||||
     {'prepend_path': {'EMPTY_PATH_LIST': '/path/first'},
 | 
			
		||||
      'append_path': {'EMPTY_PATH_LIST': '/path/last'}},
 | 
			
		||||
     {'EMPTY_PATH_LIST': '/path/first:/path/middle:/path/last'}),
 | 
			
		||||
    # Append and prepend from empty variables
 | 
			
		||||
    ({'EMPTY_PATH_LIST': '', 'SOME_VAR_STR': ''},
 | 
			
		||||
     {'prepend_path': {'EMPTY_PATH_LIST': '/path/first'},
 | 
			
		||||
      'append_path': {'SOME_VAR_STR': '/path/last'}},
 | 
			
		||||
     {'EMPTY_PATH_LIST': '/path/first', 'SOME_VAR_STR': '/path/last'}),
 | 
			
		||||
    ({},  # Same as before but on variables that were not defined
 | 
			
		||||
     {'prepend_path': {'EMPTY_PATH_LIST': '/path/first'},
 | 
			
		||||
      'append_path': {'SOME_VAR_STR': '/path/last'}},
 | 
			
		||||
     {'EMPTY_PATH_LIST': '/path/first', 'SOME_VAR_STR': '/path/last'}),
 | 
			
		||||
    # Remove a path from a list
 | 
			
		||||
    ({'EMPTY_PATH_LIST': '/path/first:/path/middle:/path/last'},
 | 
			
		||||
     {'remove_path': {'EMPTY_PATH_LIST': '/path/middle'}},
 | 
			
		||||
     {'EMPTY_PATH_LIST': '/path/first:/path/last'}),
 | 
			
		||||
    ({'EMPTY_PATH_LIST': '/only/path'},
 | 
			
		||||
     {'remove_path': {'EMPTY_PATH_LIST': '/only/path'}},
 | 
			
		||||
     {'EMPTY_PATH_LIST': ''}),
 | 
			
		||||
])
 | 
			
		||||
def test_compiler_config_modifications(
 | 
			
		||||
        initial, modifications, expected, ensure_env_variables, monkeypatch
 | 
			
		||||
):
 | 
			
		||||
    # Set the environment as per prerequisites
 | 
			
		||||
    ensure_env_variables(initial)
 | 
			
		||||
 | 
			
		||||
    os.environ['SOME_VAR_STR'] = ''
 | 
			
		||||
    os.environ['SOME_VAR_NUM'] = '0'
 | 
			
		||||
    os.environ['PATH_LIST'] = '/path/third:/path/forth'
 | 
			
		||||
    os.environ['EMPTY_PATH_LIST'] = ''
 | 
			
		||||
    os.environ.pop('NEW_PATH_LIST', None)
 | 
			
		||||
    # Monkeypatch a pkg.compiler.environment with the required modifications
 | 
			
		||||
    pkg = spack.spec.Spec('cmake').concretized().package
 | 
			
		||||
    monkeypatch.setattr(pkg.compiler, 'environment', modifications)
 | 
			
		||||
 | 
			
		||||
    env_mod = syaml_dict()
 | 
			
		||||
    set_cmd = syaml_dict()
 | 
			
		||||
    env_mod[syaml_str('set')] = set_cmd
 | 
			
		||||
 | 
			
		||||
    set_cmd[syaml_str('SOME_VAR_STR')] = syaml_str('SOME_STR')
 | 
			
		||||
    set_cmd[syaml_str('SOME_VAR_NUM')] = 1
 | 
			
		||||
 | 
			
		||||
    monkeypatch.setattr(pkg.compiler, 'environment', env_mod)
 | 
			
		||||
    # Trigger the modifications
 | 
			
		||||
    spack.build_environment.setup_package(pkg, False)
 | 
			
		||||
    assert os.environ['SOME_VAR_STR'] == 'SOME_STR'
 | 
			
		||||
    assert os.environ['SOME_VAR_NUM'] == str(1)
 | 
			
		||||
 | 
			
		||||
    env_mod = syaml_dict()
 | 
			
		||||
    unset_cmd = syaml_dict()
 | 
			
		||||
    env_mod[syaml_str('unset')] = unset_cmd
 | 
			
		||||
 | 
			
		||||
    unset_cmd[syaml_str('SOME_VAR_STR')] = None
 | 
			
		||||
 | 
			
		||||
    monkeypatch.setattr(pkg.compiler, 'environment', env_mod)
 | 
			
		||||
    assert 'SOME_VAR_STR' in os.environ
 | 
			
		||||
    spack.build_environment.setup_package(pkg, False)
 | 
			
		||||
    assert 'SOME_VAR_STR' not in os.environ
 | 
			
		||||
 | 
			
		||||
    env_mod = syaml_dict()
 | 
			
		||||
    set_cmd = syaml_dict()
 | 
			
		||||
    env_mod[syaml_str('set')] = set_cmd
 | 
			
		||||
    append_cmd = syaml_dict()
 | 
			
		||||
    env_mod[syaml_str('append-path')] = append_cmd
 | 
			
		||||
    unset_cmd = syaml_dict()
 | 
			
		||||
    env_mod[syaml_str('unset')] = unset_cmd
 | 
			
		||||
    prepend_cmd = syaml_dict()
 | 
			
		||||
    env_mod[syaml_str('prepend-path')] = prepend_cmd
 | 
			
		||||
 | 
			
		||||
    set_cmd[syaml_str('EMPTY_PATH_LIST')] = syaml_str('/path/middle')
 | 
			
		||||
 | 
			
		||||
    append_cmd[syaml_str('PATH_LIST')] = syaml_str('/path/last')
 | 
			
		||||
    append_cmd[syaml_str('EMPTY_PATH_LIST')] = syaml_str('/path/last')
 | 
			
		||||
    append_cmd[syaml_str('NEW_PATH_LIST')] = syaml_str('/path/last')
 | 
			
		||||
 | 
			
		||||
    unset_cmd[syaml_str('SOME_VAR_NUM')] = None
 | 
			
		||||
 | 
			
		||||
    prepend_cmd[syaml_str('PATH_LIST')] = syaml_str('/path/first:/path/second')
 | 
			
		||||
    prepend_cmd[syaml_str('EMPTY_PATH_LIST')] = syaml_str('/path/first')
 | 
			
		||||
    prepend_cmd[syaml_str('NEW_PATH_LIST')] = syaml_str('/path/first')
 | 
			
		||||
    prepend_cmd[syaml_str('SOME_VAR_NUM')] = syaml_str('/8')
 | 
			
		||||
 | 
			
		||||
    assert 'SOME_VAR_NUM' in os.environ
 | 
			
		||||
    monkeypatch.setattr(pkg.compiler, 'environment', env_mod)
 | 
			
		||||
    spack.build_environment.setup_package(pkg, False)
 | 
			
		||||
    # Check that the order of modifications is respected and the
 | 
			
		||||
    # variable was unset before it was prepended.
 | 
			
		||||
    assert os.environ['SOME_VAR_NUM'] == '/8'
 | 
			
		||||
 | 
			
		||||
    expected = '/path/first:/path/second:/path/third:/path/forth:/path/last'
 | 
			
		||||
    assert os.environ['PATH_LIST'] == expected
 | 
			
		||||
 | 
			
		||||
    expected = '/path/first:/path/middle:/path/last'
 | 
			
		||||
    assert os.environ['EMPTY_PATH_LIST'] == expected
 | 
			
		||||
 | 
			
		||||
    expected = '/path/first:/path/last'
 | 
			
		||||
    assert os.environ['NEW_PATH_LIST'] == expected
 | 
			
		||||
    # Check they were applied
 | 
			
		||||
    for name, value in expected.items():
 | 
			
		||||
        if value is not None:
 | 
			
		||||
            assert os.environ[name] == value
 | 
			
		||||
            continue
 | 
			
		||||
        assert name not in os.environ
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.regression('9107')
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user