diff --git a/docs/markdown/snippets/environment_unset.md b/docs/markdown/snippets/environment_unset.md new file mode 100644 index 000000000..887bc0d47 --- /dev/null +++ b/docs/markdown/snippets/environment_unset.md @@ -0,0 +1,4 @@ +## New `unset()` method on `environment` objects + +[[@env]] now has an [[env.unset]] method to ensure an existing environment +is *not* defined. diff --git a/docs/yaml/objects/env.yaml b/docs/yaml/objects/env.yaml index d784c68b8..714da4fe4 100644 --- a/docs/yaml/objects/env.yaml +++ b/docs/yaml/objects/env.yaml @@ -82,3 +82,10 @@ methods: description: The values to set kwargs_inherit: env.append + +- name: unset + returns: void + since: 1.4.0 + description: | + Unset the specified environment variable. If this variable does not exist, + nothing happens. diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 4320cf52e..9aefc2f97 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -285,6 +285,7 @@ class EnvironmentVariablesHolder(ObjectHolder[mesonlib.EnvironmentVariables], Mu def __init__(self, obj: mesonlib.EnvironmentVariables, interpreter: 'Interpreter'): super().__init__(obj, interpreter) self.methods.update({'set': self.set_method, + 'unset': self.unset_method, 'append': self.append_method, 'prepend': self.prepend_method, }) @@ -309,6 +310,12 @@ class EnvironmentVariablesHolder(ObjectHolder[mesonlib.EnvironmentVariables], Mu name, values = args self.held_object.set(name, values, kwargs['separator']) + @FeatureNew('environment.unset', '1.4.0') + @typed_pos_args('environment.unset', str) + @noKwargs + def unset_method(self, args: T.Tuple[str], kwargs: TYPE_kwargs) -> None: + self.held_object.unset(args[0]) + @typed_pos_args('environment.append', str, varargs=str, min_varargs=1) @typed_kwargs('environment.append', ENV_SEPARATOR_KW) def append_method(self, args: T.Tuple[str, T.List[str]], kwargs: 'EnvironmentSeparatorKW') -> None: diff --git a/mesonbuild/utils/core.py b/mesonbuild/utils/core.py index 1b005cf95..92f9d2c70 100644 --- a/mesonbuild/utils/core.py +++ b/mesonbuild/utils/core.py @@ -63,6 +63,7 @@ class EnvironmentVariables(HoldableObject): self.envvars: T.List[T.Tuple[T.Callable[[T.Dict[str, str], str, T.List[str], str, T.Optional[str]], str], str, T.List[str], str]] = [] # The set of all env vars we have operations for. Only used for self.has_name() self.varnames: T.Set[str] = set() + self.unset_vars: T.Set[str] = set() if values: init_func = getattr(self, init_method) @@ -92,16 +93,30 @@ class EnvironmentVariables(HoldableObject): for method, name, values, separator in other.envvars: self.varnames.add(name) self.envvars.append((method, name, values, separator)) + if name in self.unset_vars: + self.unset_vars.remove(name) + self.unset_vars.update(other.unset_vars) def set(self, name: str, values: T.List[str], separator: str = os.pathsep) -> None: + if name in self.unset_vars: + raise MesonException(f'You cannot set the already unset variable {name!r}') self.varnames.add(name) self.envvars.append((self._set, name, values, separator)) + def unset(self, name: str) -> None: + if name in self.varnames: + raise MesonException(f'You cannot unset the {name!r} variable because it is already set') + self.unset_vars.add(name) + def append(self, name: str, values: T.List[str], separator: str = os.pathsep) -> None: + if name in self.unset_vars: + raise MesonException(f'You cannot append to unset variable {name!r}') self.varnames.add(name) self.envvars.append((self._append, name, values, separator)) def prepend(self, name: str, values: T.List[str], separator: str = os.pathsep) -> None: + if name in self.unset_vars: + raise MesonException(f'You cannot prepend to unset variable {name!r}') self.varnames.add(name) self.envvars.append((self._prepend, name, values, separator)) @@ -124,6 +139,8 @@ class EnvironmentVariables(HoldableObject): for method, name, values, separator in self.envvars: default_value = default_fmt.format(name) if default_fmt else None env[name] = method(env, name, values, separator, default_value) + for name in self.unset_vars: + env.pop(name, None) return env diff --git a/test cases/common/273 environment/meson.build b/test cases/common/273 environment/meson.build new file mode 100644 index 000000000..af15c1b55 --- /dev/null +++ b/test cases/common/273 environment/meson.build @@ -0,0 +1,50 @@ +project( + 'environment', + meson_version: '>=1.4.0', +) + +testenv = find_program(files('testenv.py')) + + +env = environment() +env.unset('foo') +test('not set', testenv, args: ['foo'], env: env) + +testcase expect_error('You cannot set the already unset variable \'foo\'') + env.set('foo', 'bar') +endtestcase + +testcase expect_error('You cannot append to unset variable \'foo\'') + env.append('foo', 'bar') +endtestcase + +testcase expect_error('You cannot prepend to unset variable \'foo\'') + env.prepend('foo', 'bar') +endtestcase + + +env1 = environment('foo=bar', method: 'append', separator: ':') +env1.append('foo', 'baz', separator: ':') +test('append', testenv, args: ['foo', 'bar:baz'], env: env1) + +testcase expect_error('You cannot unset the \'foo\' variable because it is already set') + env1.unset('foo') +endtestcase + + +env2 = environment(['foo=baz'], method: 'prepend', separator: ':') +env2.prepend('foo', 'bar', separator: ':') +test('prepend', testenv, args: ['foo', 'bar:baz'], env: env2) + +testcase expect_error('You cannot unset the \'foo\' variable because it is already set') + env2.unset('foo') +endtestcase + + +env3 = environment({'foo': 'foobar'}, method: 'set', separator: ':') +env3.set('foo', 'qux') +test('reset', testenv, args: ['foo', 'qux'], env: env3) + +testcase expect_error('You cannot unset the \'foo\' variable because it is already set') + env3.unset('foo') +endtestcase diff --git a/test cases/common/273 environment/testenv.py b/test cases/common/273 environment/testenv.py new file mode 100644 index 000000000..1a59b8237 --- /dev/null +++ b/test cases/common/273 environment/testenv.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 + +import os +import sys + +key = sys.argv[1] +expected = sys.argv[2] if len(sys.argv) > 2 else None + +if os.environ.get(key) == expected: + sys.exit(0) + +sys.exit(f'Expected {expected!r}, was {os.environ.get(key)!r}')