diff --git a/docs/markdown/snippets/env_kwarg_generator_process.md b/docs/markdown/snippets/env_kwarg_generator_process.md new file mode 100644 index 000000000..cdf73a302 --- /dev/null +++ b/docs/markdown/snippets/env_kwarg_generator_process.md @@ -0,0 +1,4 @@ +## generator.process() gains 'env' keyword argument + +Like the kwarg of the same name in `custom_target()`, `env` allows +you to set the environment in which the generator will process inputs. diff --git a/docs/yaml/objects/generator.yaml b/docs/yaml/objects/generator.yaml index e7b866acc..fbef95fa4 100644 --- a/docs/yaml/objects/generator.yaml +++ b/docs/yaml/objects/generator.yaml @@ -34,3 +34,12 @@ methods: `subdir/one.input` is processed it generates a file `{target private directory}/subdir/one.out` as opposed to `{target private directory}/one.out`. + + env: + type: env | list[str] | dict[str] + since: 1.3.0 + description: | + environment variables to set, such as + `{'NAME1': 'value1', 'NAME2': 'value2'}` or `['NAME1=value1', 'NAME2=value2']`, + or an [[@env]] object which allows more + sophisticated environment juggling. diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 2b9a64eb5..a994d7388 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2649,7 +2649,8 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) args = self.replace_paths(target, args, override_subdir=subdir) cmdlist, reason = self.as_meson_exe_cmdline(exe, self.replace_extra_args(args, genlist), - capture=outfiles[0] if generator.capture else None) + capture=outfiles[0] if generator.capture else None, + env=genlist.env) abs_pdir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(target)) os.makedirs(abs_pdir, exist_ok=True) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index fd19ff3d3..c9c21a135 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -211,7 +211,8 @@ class Vs2010Backend(backends.Backend): self.replace_extra_args(args, genlist), workdir=tdir_abs, capture=outfiles[0] if generator.capture else None, - force_serialize=True + force_serialize=True, + env=genlist.env ) deps = cmd[-1:] + deps abs_pdir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(target)) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index d1888d7b2..8208fa0d9 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1822,8 +1822,14 @@ class Generator(HoldableObject): def process_files(self, files: T.Iterable[T.Union[str, File, 'CustomTarget', 'CustomTargetIndex', 'GeneratedList']], state: T.Union['Interpreter', 'ModuleState'], preserve_path_from: T.Optional[str] = None, - extra_args: T.Optional[T.List[str]] = None) -> 'GeneratedList': - output = GeneratedList(self, state.subdir, preserve_path_from, extra_args=extra_args if extra_args is not None else []) + extra_args: T.Optional[T.List[str]] = None, + env: T.Optional[EnvironmentVariables] = None) -> 'GeneratedList': + output = GeneratedList( + self, + state.subdir, + preserve_path_from, + extra_args=extra_args if extra_args is not None else [], + env=env if env is not None else EnvironmentVariables()) for e in files: if isinstance(e, CustomTarget): @@ -1862,6 +1868,7 @@ class GeneratedList(HoldableObject): subdir: str preserve_path_from: T.Optional[str] extra_args: T.List[str] + env: T.Optional[EnvironmentVariables] def __post_init__(self) -> None: self.name = self.generator.exe @@ -1875,6 +1882,9 @@ class GeneratedList(HoldableObject): if self.extra_args is None: self.extra_args: T.List[str] = [] + if self.env is None: + self.env: EnvironmentVariables = EnvironmentVariables() + if isinstance(self.generator.exe, programs.ExternalProgram): if not self.generator.exe.found(): raise InvalidArguments('Tried to use not-found external program as generator') diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 46b4cc1e5..effeebb20 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -21,7 +21,7 @@ from ..interpreterbase import ( typed_pos_args, typed_kwargs, typed_operator, noArgsFlattening, noPosargs, noKwargs, unholder_return, flatten, resolve_second_level_holders, InterpreterException, InvalidArguments, InvalidCode) -from ..interpreter.type_checking import NoneType, ENV_SEPARATOR_KW, PKGCONFIG_DEFINE_KW +from ..interpreter.type_checking import NoneType, ENV_KW, ENV_SEPARATOR_KW, PKGCONFIG_DEFINE_KW from ..dependencies import Dependency, ExternalLibrary, InternalDependency from ..programs import ExternalProgram from ..mesonlib import HoldableObject, OptionKey, listify, Popen_safe @@ -1043,6 +1043,7 @@ class GeneratorHolder(ObjectHolder[build.Generator]): 'generator.process', KwargInfo('preserve_path_from', (str, NoneType), since='0.45.0'), KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]), + ENV_KW.evolve(since='1.3.0') ) def process_method(self, args: T.Tuple[T.List[T.Union[str, mesonlib.File, 'build.GeneratedTypes']]], @@ -1060,7 +1061,7 @@ class GeneratorHolder(ObjectHolder[build.Generator]): '0.57.0', self.interpreter.subproject) gl = self.held_object.process_files(args[0], self.interpreter, - preserve_path_from, extra_args=kwargs['extra_args']) + preserve_path_from, extra_args=kwargs['extra_args'], env=kwargs['env']) return gl diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 7396229db..241a08b76 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -103,6 +103,7 @@ class GeneratorProcess(TypedDict): preserve_path_from: T.Optional[str] extra_args: T.List[str] + env: EnvironmentVariables class DependencyMethodPartialDependency(TypedDict): diff --git a/test cases/common/272 env in generator.process/generate_main.py b/test cases/common/272 env in generator.process/generate_main.py new file mode 100644 index 000000000..993c5ac03 --- /dev/null +++ b/test cases/common/272 env in generator.process/generate_main.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 +import os +import sys + +ENV_VAR_VALUE = os.environ.get('ENV_VAR_VALUE') +assert ENV_VAR_VALUE is not None + +with open(sys.argv[1], 'r') as infile, \ + open(sys.argv[2], 'w') as outfile: + + outfile.write(infile.read().replace('ENV_VAR_VALUE', ENV_VAR_VALUE)) diff --git a/test cases/common/272 env in generator.process/main.template b/test cases/common/272 env in generator.process/main.template new file mode 100644 index 000000000..3c3340efc --- /dev/null +++ b/test cases/common/272 env in generator.process/main.template @@ -0,0 +1,3 @@ +int main(void) { + return ENV_VAR_VALUE; +} \ No newline at end of file diff --git a/test cases/common/272 env in generator.process/meson.build b/test cases/common/272 env in generator.process/meson.build new file mode 100644 index 000000000..5d7404024 --- /dev/null +++ b/test cases/common/272 env in generator.process/meson.build @@ -0,0 +1,21 @@ +project('test_env_in_generator_process', 'c') + +generate_main_py = find_program('generate_main.py') + +main_generator = generator(generate_main_py, + arguments: ['@INPUT@', '@OUTPUT@'], + output: '@BASENAME@' + '.c' +) + +main_template = files('main.template') + +# With explicit values +my_executable = executable('myexecutable', main_generator.process(main_template, env: {'ENV_VAR_VALUE': '0'})) +test('explicit_value', my_executable) + +# With env object +env = environment() +env.set('ENV_VAR_VALUE', '0') + +my_executable2 = executable('myexecutable2', main_generator.process(main_template, env: env)) +test('env_object', my_executable2)