diff --git a/docs/markdown/External-commands.md b/docs/markdown/External-commands.md index 9336ec314..4c8c8e49f 100644 --- a/docs/markdown/External-commands.md +++ b/docs/markdown/External-commands.md @@ -16,6 +16,14 @@ output = r.stdout().strip() errortxt = r.stderr().strip() ``` +Additionally, since 0.50.0, you can pass the command [`environment`](Reference-manual.html#environment-object) object: + +```meson +env = environment() +env.set('FOO', 'bar') +run_command('command', 'arg1', 'arg2', env: env) +``` + The `run_command` function returns an object that can be queried for return value and text written to stdout and stderr. The `strip` method call is used to strip trailing and leading whitespace from diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index e913e25ca..db438134b 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -1183,12 +1183,14 @@ and Meson will set three environment variables `MESON_SOURCE_ROOT`, directory, build directory and subdirectory the target was defined in, respectively. -This function has one keyword argument. +This function supports the following keyword arguments: - `check` takes a boolean. If `true`, the exit status code of the command will be checked, and the configuration will fail if it is non-zero. The default is `false`. Since 0.47.0 + - `env` an [environment object](#environment-object) to use a custom environment + Since 0.50.0 See also [External commands](External-commands.md). @@ -2175,7 +2177,7 @@ and has the following methods: This object is returned by [`environment()`](#environment) and stores detailed information about how environment variables should be set during tests. It should be passed as the `env` keyword argument to -tests. It has the following methods. +tests and other functions. It has the following methods. - `append(varname, value1, value2, ...)` appends the given values to the old value of the environment variable, e.g. `env.append('FOO', diff --git a/docs/markdown/snippets/run_command_env.md b/docs/markdown/snippets/run_command_env.md new file mode 100644 index 000000000..dfa5ac5bd --- /dev/null +++ b/docs/markdown/snippets/run_command_env.md @@ -0,0 +1,9 @@ +## `run_command` accepts `env` kwarg + +You can pass [`environment`](Reference-manual.html#environment-object) object to [`run_command`](Reference-manual.html#run-command), just like to `test`: + +```meson +env = environment() +env.set('FOO', 'bar') +run_command('command', 'arg1', 'arg2', env: env) +``` diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index ee571fdc0..aff46d13f 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -143,31 +143,32 @@ class TryRunResultHolder(InterpreterObject): class RunProcess(InterpreterObject): - def __init__(self, cmd, args, source_dir, build_dir, subdir, mesonintrospect, in_builddir=False, check=False, capture=True): + def __init__(self, cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir=False, check=False, capture=True): super().__init__() if not isinstance(cmd, ExternalProgram): raise AssertionError('BUG: RunProcess must be passed an ExternalProgram') self.capture = capture - pc, self.stdout, self.stderr = self.run_command(cmd, args, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check) + pc, self.stdout, self.stderr = self.run_command(cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check) self.returncode = pc.returncode self.methods.update({'returncode': self.returncode_method, 'stdout': self.stdout_method, 'stderr': self.stderr_method, }) - def run_command(self, cmd, args, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check=False): + def run_command(self, cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check=False): command_array = cmd.get_command() + args - env = {'MESON_SOURCE_ROOT': source_dir, - 'MESON_BUILD_ROOT': build_dir, - 'MESON_SUBDIR': subdir, - 'MESONINTROSPECT': ' '.join([shlex.quote(x) for x in mesonintrospect]), - } + menv = {'MESON_SOURCE_ROOT': source_dir, + 'MESON_BUILD_ROOT': build_dir, + 'MESON_SUBDIR': subdir, + 'MESONINTROSPECT': ' '.join([shlex.quote(x) for x in mesonintrospect]), + } if in_builddir: cwd = os.path.join(build_dir, subdir) else: cwd = os.path.join(source_dir, subdir) child_env = os.environ.copy() - child_env.update(env) + child_env.update(menv) + child_env = env.get_env(child_env) stdout = subprocess.PIPE if self.capture else subprocess.DEVNULL mlog.debug('Running command:', ' '.join(command_array)) try: @@ -1954,7 +1955,7 @@ permitted_kwargs = {'add_global_arguments': {'language', 'native'}, 'install_subdir': {'exclude_files', 'exclude_directories', 'install_dir', 'install_mode', 'strip_directory'}, 'jar': build.known_jar_kwargs, 'project': {'version', 'meson_version', 'default_options', 'license', 'subproject_dir'}, - 'run_command': {'check', 'capture'}, + 'run_command': {'check', 'capture', 'env'}, 'run_target': {'command', 'depends'}, 'shared_library': build.known_shlib_kwargs, 'shared_module': build.known_shmod_kwargs, @@ -2259,6 +2260,7 @@ external dependencies (including libraries) must go to "dependencies".''') if not isinstance(actual, wanted): raise InvalidArguments('Incorrect argument type.') + @FeatureNewKwargs('run_command', '0.50.0', ['env']) @FeatureNewKwargs('run_command', '0.47.0', ['check', 'capture']) @permittedKwargs(permitted_kwargs['run_command']) def func_run_command(self, node, args, kwargs): @@ -2277,6 +2279,8 @@ external dependencies (including libraries) must go to "dependencies".''') if not isinstance(check, bool): raise InterpreterException('Check must be boolean.') + env = self.unpack_env_kwarg(kwargs) + m = 'must be a string, or the output of find_program(), files() '\ 'or configure_file(), or a compiler object; not {!r}' if isinstance(cmd, ExternalProgramHolder): @@ -2326,7 +2330,7 @@ external dependencies (including libraries) must go to "dependencies".''') if not a.startswith('..'): if a not in self.build_def_files: self.build_def_files.append(a) - return RunProcess(cmd, expanded_args, srcdir, builddir, self.subdir, + return RunProcess(cmd, expanded_args, env, srcdir, builddir, self.subdir, self.environment.get_build_command() + ['introspect'], in_builddir=in_builddir, check=check, capture=capture) diff --git a/test cases/common/36 run program/meson.build b/test cases/common/36 run program/meson.build index a05cea3fb..93897e3f8 100644 --- a/test cases/common/36 run program/meson.build +++ b/test cases/common/36 run program/meson.build @@ -65,6 +65,12 @@ ret = run_command(py3, '-c', 'print("some output")', capture : false) assert(ret.returncode() == 0, 'failed to run python3: ' + ret.stderr()) assert(ret.stdout() == '', 'stdout is "@0@" instead of empty'.format(ret.stdout())) +c_env = environment() +c_env.append('CUSTOM_ENV_VAR', 'FOOBAR') +ret = run_command(py3, '-c', 'import os; print(os.environ.get("CUSTOM_ENV_VAR"))', env : c_env) +assert(ret.returncode() == 0, 'failed to run python3: ' + ret.stderr()) +assert(ret.stdout() == 'FOOBAR\n', 'stdout is "@0@" instead of FOOBAR'.format(ret.stdout())) + dd = find_program('dd', required : false) if dd.found() ret = run_command(dd, 'if=/dev/urandom', 'bs=10', 'count=1', capture: false)