aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2021-02-07 11:46:41 +0000
committerGitHub <noreply@github.com>2021-02-07 11:46:41 +0000
commit8b82ffa9e423558d7644c7135db4114f59537829 (patch)
tree90a326f8e796d4a81e98e0639fe67b6a0459ed08
parent8e73e5fe1c4fcd5a875cb4777c7938f9069e0102 (diff)
parentf63e168685c22a749502161355a7ea98a6c5344a (diff)
downloadmeson-8b82ffa9e423558d7644c7135db4114f59537829.zip
meson-8b82ffa9e423558d7644c7135db4114f59537829.tar.gz
meson-8b82ffa9e423558d7644c7135db4114f59537829.tar.bz2
Merge pull request #8305 from xclaesse/run-target-env
run_target: Add env kwarg
-rw-r--r--docs/markdown/Reference-manual.md4
-rw-r--r--docs/markdown/snippets/customtarget_env.md2
-rw-r--r--mesonbuild/backend/backends.py45
-rw-r--r--mesonbuild/backend/ninjabackend.py79
-rw-r--r--mesonbuild/backend/vs2010backend.py38
-rw-r--r--mesonbuild/build.py72
-rw-r--r--mesonbuild/interpreter.py7
-rw-r--r--mesonbuild/modules/gnome.py8
-rw-r--r--mesonbuild/modules/i18n.py6
-rw-r--r--mesonbuild/scripts/commandrunner.py84
-rw-r--r--mesonbuild/scripts/meson_exe.py11
-rwxr-xr-xrun_unittests.py1
-rw-r--r--test cases/common/52 run target/check-env.py9
-rw-r--r--test cases/common/52 run target/meson.build6
14 files changed, 131 insertions, 241 deletions
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index 14802eb..f623728 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -1504,6 +1504,10 @@ and subdirectory the target was defined in, respectively.
- `depends` is a list of targets that this target depends on but which
are not listed in the command array (because, for example, the
script does file globbing internally)
+- `env` *(since 0.57.0)*: environment variables to set, such as
+ `{'NAME1': 'value1', 'NAME2': 'value2'}` or `['NAME1=value1', 'NAME2=value2']`,
+ or an [`environment()` object](#environment-object) which allows more
+ sophisticated environment juggling.
### set_variable()
diff --git a/docs/markdown/snippets/customtarget_env.md b/docs/markdown/snippets/customtarget_env.md
index 69bfc0d..f2d651b 100644
--- a/docs/markdown/snippets/customtarget_env.md
+++ b/docs/markdown/snippets/customtarget_env.md
@@ -1,4 +1,4 @@
-## `custom_target()` now accepts an `env` keyword argument
+## `custom_target()` and `run_target()` now accepts an `env` keyword argument
Environment variables can now be passed to the `custom_target()` command.
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 233173f..e19afca 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -21,8 +21,6 @@ import json
import os
import pickle
import re
-import shlex
-import textwrap
import typing as T
import hashlib
import copy
@@ -34,7 +32,7 @@ from .. import mlog
from ..compilers import LANGUAGES_USING_LDFLAGS
from ..mesonlib import (
File, MachineChoice, MesonException, OptionType, OrderedSet, OptionOverrideProxy,
- classify_unity_sources, unholder, OptionKey
+ classify_unity_sources, unholder, OptionKey, join_args
)
if T.TYPE_CHECKING:
@@ -138,6 +136,7 @@ class ExecutableSerialisation:
self.capture = capture
self.pickled = False
self.skip_if_destdir = False
+ self.verbose = False
class TestSerialisation:
def __init__(self, name: str, project: str, suite: str, fname: T.List[str],
@@ -432,12 +431,14 @@ class Backend:
def as_meson_exe_cmdline(self, tname, exe, cmd_args, workdir=None,
extra_bdeps=None, capture=None, force_serialize=False,
- env: T.Optional[build.EnvironmentVariables] = None):
+ env: T.Optional[build.EnvironmentVariables] = None,
+ verbose: bool = False):
'''
Serialize an executable for running with a generator or a custom target
'''
cmd = [exe] + cmd_args
es = self.get_executable_serialisation(cmd, workdir, extra_bdeps, capture, env)
+ es.verbose = verbose
reasons = []
if es.extra_paths:
reasons.append('to set PATH')
@@ -987,18 +988,8 @@ class Backend:
if delta > 0.001:
raise MesonException('Clock skew detected. File {} has a time stamp {:.4f}s in the future.'.format(absf, delta))
- def build_target_to_cmd_array(self, bt, check_cross):
+ def build_target_to_cmd_array(self, bt):
if isinstance(bt, build.BuildTarget):
- if check_cross and isinstance(bt, build.Executable) and bt.for_machine is not MachineChoice.BUILD:
- if (self.environment.is_cross_build() and
- self.environment.exe_wrapper is None and
- self.environment.need_exe_wrapper()):
- s = textwrap.dedent('''
- Cannot use target {} as a generator because it is built for the
- host machine and no exe wrapper is defined or needs_exe_wrapper is
- true. You might want to set `native: true` instead to build it for
- the build machine.'''.format(bt.name))
- raise MesonException(s)
arr = [os.path.join(self.environment.get_build_dir(), self.get_target_filename(bt))]
else:
arr = bt.get_command()
@@ -1129,11 +1120,9 @@ class Backend:
inputs = self.get_custom_target_sources(target)
# Evaluate the command list
cmd = []
- index = -1
for i in target.command:
- index += 1
if isinstance(i, build.BuildTarget):
- cmd += self.build_target_to_cmd_array(i, (index == 0))
+ cmd += self.build_target_to_cmd_array(i)
continue
elif isinstance(i, build.CustomTarget):
# GIR scanner will attempt to execute this binary but
@@ -1146,10 +1135,7 @@ class Backend:
i = os.path.join(self.environment.get_build_dir(), i)
# FIXME: str types are blindly added ignoring 'target.absolute_paths'
# because we can't know if they refer to a file or just a string
- elif not isinstance(i, str):
- err_msg = 'Argument {0} is of unknown type {1}'
- raise RuntimeError(err_msg.format(str(i), str(type(i))))
- else:
+ elif isinstance(i, str):
if '@SOURCE_ROOT@' in i:
i = i.replace('@SOURCE_ROOT@', source_root)
if '@BUILD_ROOT@' in i:
@@ -1179,6 +1165,9 @@ class Backend:
else:
lead_dir = self.environment.get_build_dir()
i = i.replace(source, os.path.join(lead_dir, outdir))
+ else:
+ err_msg = 'Argument {0} is of unknown type {1}'
+ raise RuntimeError(err_msg.format(str(i), str(type(i))))
cmd.append(i)
# Substitute the rest of the template strings
values = mesonlib.get_filenames_templates_dict(inputs, outputs)
@@ -1204,11 +1193,21 @@ class Backend:
cmd = [i.replace('\\', '/') for i in cmd]
return inputs, outputs, cmd
+ def get_run_target_env(self, target: build.RunTarget) -> build.EnvironmentVariables:
+ env = target.env if target.env else build.EnvironmentVariables()
+ introspect_cmd = join_args(self.environment.get_build_command() + ['introspect'])
+ env.add_var(env.set, 'MESON_SOURCE_ROOT', [self.environment.get_source_dir()], {})
+ env.add_var(env.set, 'MESON_BUILD_ROOT', [self.environment.get_build_dir()], {})
+ env.add_var(env.set, 'MESON_SUBDIR', [target.subdir], {})
+ env.add_var(env.set, 'MESONINTROSPECT', [introspect_cmd], {})
+ return env
+
def run_postconf_scripts(self) -> None:
from ..scripts.meson_exe import run_exe
+ introspect_cmd = join_args(self.environment.get_build_command() + ['introspect'])
env = {'MESON_SOURCE_ROOT': self.environment.get_source_dir(),
'MESON_BUILD_ROOT': self.environment.get_build_dir(),
- 'MESONINTROSPECT': ' '.join([shlex.quote(x) for x in self.environment.get_build_command() + ['introspect']]),
+ 'MESONINTROSPECT': introspect_cmd,
}
for s in self.build.postconf_scripts:
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index ca17f19..3eca3c0 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -28,7 +28,6 @@ from .. import modules
from .. import environment, mesonlib
from .. import build
from .. import mlog
-from .. import dependencies
from .. import compilers
from ..arglist import CompilerArgs
from ..compilers import (
@@ -974,7 +973,6 @@ int dummy;
elem.add_item('DEPFILE', rel_dfile)
if target.console:
elem.add_item('pool', 'console')
- cmd = self.replace_paths(target, cmd)
elem.add_item('COMMAND', cmd)
elem.add_item('description', desc.format(target.name, cmd_type))
self.add_build(elem)
@@ -988,65 +986,28 @@ int dummy;
return '{}{}'.format(subproject_prefix, target.name)
def generate_run_target(self, target):
- cmd = self.environment.get_build_command() + ['--internal', 'commandrunner']
- deps = self.unwrap_dep_list(target)
- arg_strings = []
- for i in target.args:
- if isinstance(i, str):
- arg_strings.append(i)
- elif isinstance(i, (build.BuildTarget, build.CustomTarget)):
- relfname = self.get_target_filename(i)
- arg_strings.append(os.path.join(self.environment.get_build_dir(), relfname))
- deps.append(relfname)
- elif isinstance(i, mesonlib.File):
- relfname = i.rel_to_builddir(self.build_to_src)
- arg_strings.append(os.path.join(self.environment.get_build_dir(), relfname))
- else:
- raise AssertionError('Unreachable code in generate_run_target: ' + str(i))
- cmd += [self.environment.get_source_dir(),
- self.environment.get_build_dir(),
- target.subdir] + self.environment.get_build_command()
- texe = target.command
- try:
- texe = texe.held_object
- except AttributeError:
- pass
- if isinstance(texe, build.Executable):
- abs_exe = os.path.join(self.environment.get_build_dir(), self.get_target_filename(texe))
- deps.append(self.get_target_filename(texe))
- if self.environment.is_cross_build():
- exe_wrap = self.environment.get_exe_wrapper()
- if exe_wrap:
- if not exe_wrap.found():
- msg = 'The exe_wrapper {!r} defined in the cross file is ' \
- 'needed by run target {!r}, but was not found. ' \
- 'Please check the command and/or add it to PATH.'
- raise MesonException(msg.format(exe_wrap.name, target.name))
- cmd += exe_wrap.get_command()
- cmd.append(abs_exe)
- elif isinstance(texe, dependencies.ExternalProgram):
- cmd += texe.get_command()
- elif isinstance(texe, build.CustomTarget):
- deps.append(self.get_target_filename(texe))
- cmd += [os.path.join(self.environment.get_build_dir(), self.get_target_filename(texe))]
- elif isinstance(texe, mesonlib.File):
- cmd.append(texe.absolute_path(self.environment.get_source_dir(), self.environment.get_build_dir()))
+ target_name = self.build_run_target_name(target)
+ if not target.command:
+ # This is an alias target, it has no command, it just depends on
+ # other targets.
+ elem = NinjaBuildElement(self.all_outputs, target_name, 'phony', [])
else:
- cmd.append(target.command)
- cmd += arg_strings
-
- if texe:
- target_name = 'meson-{}'.format(self.build_run_target_name(target))
- elem = NinjaBuildElement(self.all_outputs, target_name, 'CUSTOM_COMMAND', [])
- elem.add_item('COMMAND', cmd)
- elem.add_item('description', 'Running external command {}'.format(target.name))
+ target_env = self.get_run_target_env(target)
+ _, _, cmd = self.eval_custom_target_command(target)
+ desc = 'Running external command {}{}'
+ meson_exe_cmd, reason = self.as_meson_exe_cmdline(target_name, cmd[0], cmd[1:],
+ force_serialize=True, env=target_env,
+ verbose=True)
+ cmd_type = ' (wrapped by meson {})'.format(reason)
+ internal_target_name = 'meson-{}'.format(target_name)
+ elem = NinjaBuildElement(self.all_outputs, internal_target_name, 'CUSTOM_COMMAND', [])
+ elem.add_item('COMMAND', meson_exe_cmd)
+ elem.add_item('description', desc.format(target.name, cmd_type))
elem.add_item('pool', 'console')
# Alias that runs the target defined above with the name the user specified
- self.create_target_alias(target_name)
- else:
- target_name = self.build_run_target_name(target)
- elem = NinjaBuildElement(self.all_outputs, target_name, 'phony', [])
-
+ self.create_target_alias(internal_target_name)
+ deps = self.unwrap_dep_list(target)
+ deps += self.get_custom_target_depend_files(target)
elem.add_dep(deps)
self.add_build(elem)
self.processed_targets[target.get_id()] = True
@@ -2105,7 +2066,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
generator = genlist.get_generator()
subdir = genlist.subdir
exe = generator.get_exe()
- exe_arr = self.build_target_to_cmd_array(exe, True)
+ exe_arr = self.build_target_to_cmd_array(exe)
infilelist = genlist.get_inputs()
outfilelist = genlist.get_outputs()
extra_dependencies = self.get_custom_target_depend_files(genlist)
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index c47fb4a..e94ab49 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -28,7 +28,7 @@ from .. import mlog
from .. import compilers
from ..interpreter import Interpreter
from ..mesonlib import (
- MesonException, File, python_command, replace_if_different, OptionKey,
+ MesonException, python_command, replace_if_different, OptionKey,
)
from ..environment import Environment, build_filename
@@ -121,7 +121,7 @@ class Vs2010Backend(backends.Backend):
infilelist = genlist.get_inputs()
outfilelist = genlist.get_outputs()
source_dir = os.path.join(down, self.build_to_src, genlist.subdir)
- exe_arr = self.build_target_to_cmd_array(exe, True)
+ exe_arr = self.build_target_to_cmd_array(exe)
idgroup = ET.SubElement(parent_node, 'ItemGroup')
for i in range(len(infilelist)):
if len(infilelist) == len(outfilelist):
@@ -257,9 +257,8 @@ class Vs2010Backend(backends.Backend):
for d in target.get_target_dependencies():
all_deps[d.get_id()] = d
elif isinstance(target, build.RunTarget):
- for d in [target.command] + target.args:
- if isinstance(d, (build.BuildTarget, build.CustomTarget)):
- all_deps[d.get_id()] = d
+ for d in target.get_dependencies():
+ all_deps[d.get_id()] = d
elif isinstance(target, build.BuildTarget):
for ldep in target.link_targets:
if isinstance(ldep, build.CustomTargetIndex):
@@ -534,27 +533,14 @@ class Vs2010Backend(backends.Backend):
# is probably a better way than running a this dummy command.
cmd_raw = python_command + ['-c', 'exit']
else:
- cmd_raw = [target.command] + target.args
- cmd = python_command + \
- [os.path.join(self.environment.get_script_dir(), 'commandrunner.py'),
- self.environment.get_source_dir(),
- self.environment.get_build_dir(),
- self.get_target_dir(target)] + self.environment.get_build_command()
- for i in cmd_raw:
- if isinstance(i, build.BuildTarget):
- cmd.append(os.path.join(self.environment.get_build_dir(), self.get_target_filename(i)))
- elif isinstance(i, dependencies.ExternalProgram):
- cmd += i.get_command()
- elif isinstance(i, File):
- relfname = i.rel_to_builddir(self.build_to_src)
- cmd.append(os.path.join(self.environment.get_build_dir(), relfname))
- elif isinstance(i, str):
- # Escape embedded quotes, because we quote the entire argument below.
- cmd.append(i.replace('"', '\\"'))
- else:
- cmd.append(i)
- cmd_templ = '''"%s" ''' * len(cmd)
- self.add_custom_build(root, 'run_target', cmd_templ % tuple(cmd))
+ _, _, cmd_raw = self.eval_custom_target_command(target)
+ depend_files = self.get_custom_target_depend_files(target)
+ target_env = self.get_run_target_env(target)
+ wrapper_cmd, _ = self.as_meson_exe_cmdline(target.name, cmd_raw[0], cmd_raw[1:],
+ force_serialize=True, env=target_env,
+ verbose=True)
+ self.add_custom_build(root, 'run_target', ' '.join(self.quote_arguments(wrapper_cmd)),
+ deps=depend_files)
ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.targets')
self.add_regen_dependency(root)
self.add_target_deps(root, target)
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 36bce1c..4abc800 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -29,7 +29,7 @@ from .mesonlib import (
File, MesonException, MachineChoice, PerMachine, OrderedSet, listify,
extract_as_list, typeslistify, stringlistify, classify_unity_sources,
get_filenames_templates_dict, substitute_values, has_path_sep, unholder,
- OptionKey,
+ OptionKey
)
from .compilers import (
Compiler, is_object, clink_langs, sort_clink, lang_suffixes,
@@ -2142,8 +2142,35 @@ class SharedModule(SharedLibrary):
def get_default_install_dir(self, environment):
return environment.get_shared_module_dir()
+class CommandBase:
+ def flatten_command(self, cmd):
+ cmd = unholder(listify(cmd))
+ final_cmd = []
+ for c in cmd:
+ if isinstance(c, str):
+ final_cmd.append(c)
+ elif isinstance(c, File):
+ self.depend_files.append(c)
+ final_cmd.append(c)
+ elif isinstance(c, dependencies.ExternalProgram):
+ if not c.found():
+ raise InvalidArguments('Tried to use not-found external program in "command"')
+ path = c.get_path()
+ if os.path.isabs(path):
+ # Can only add a dependency on an external program which we
+ # know the absolute path of
+ self.depend_files.append(File.from_absolute_file(path))
+ final_cmd += c.get_command()
+ elif isinstance(c, (BuildTarget, CustomTarget)):
+ self.dependencies.append(c)
+ final_cmd.append(c)
+ elif isinstance(c, list):
+ final_cmd += self.flatten_command(c)
+ else:
+ raise InvalidArguments('Argument {!r} in "command" is invalid'.format(c))
+ return final_cmd
-class CustomTarget(Target):
+class CustomTarget(Target, CommandBase):
known_kwargs = set([
'input',
'output',
@@ -2214,33 +2241,6 @@ class CustomTarget(Target):
bdeps.update(d.get_transitive_build_target_deps())
return bdeps
- def flatten_command(self, cmd):
- cmd = unholder(listify(cmd))
- final_cmd = []
- for c in cmd:
- if isinstance(c, str):
- final_cmd.append(c)
- elif isinstance(c, File):
- self.depend_files.append(c)
- final_cmd.append(c)
- elif isinstance(c, dependencies.ExternalProgram):
- if not c.found():
- raise InvalidArguments('Tried to use not-found external program in "command"')
- path = c.get_path()
- if os.path.isabs(path):
- # Can only add a dependency on an external program which we
- # know the absolute path of
- self.depend_files.append(File.from_absolute_file(path))
- final_cmd += c.get_command()
- elif isinstance(c, (BuildTarget, CustomTarget)):
- self.dependencies.append(c)
- final_cmd.append(c)
- elif isinstance(c, list):
- final_cmd += self.flatten_command(c)
- else:
- raise InvalidArguments('Argument {!r} in "command" is invalid'.format(c))
- return final_cmd
-
def process_kwargs(self, kwargs, backend):
self.process_kwargs_base(kwargs)
self.sources = unholder(extract_as_list(kwargs, 'input'))
@@ -2422,18 +2422,20 @@ class CustomTarget(Target):
for i in self.outputs:
yield CustomTargetIndex(self, i)
-class RunTarget(Target):
- def __init__(self, name, command, args, dependencies, subdir, subproject):
+class RunTarget(Target, CommandBase):
+ def __init__(self, name, command, dependencies, subdir, subproject, env=None):
self.typename = 'run'
# These don't produce output artifacts
super().__init__(name, subdir, subproject, False, MachineChoice.BUILD)
- self.command = command
- self.args = args
self.dependencies = dependencies
+ self.depend_files = []
+ self.command = self.flatten_command(command)
+ self.absolute_paths = False
+ self.env = env
def __repr__(self):
repr_str = "<{0} {1}: {2}>"
- return repr_str.format(self.__class__.__name__, self.get_id(), self.command)
+ return repr_str.format(self.__class__.__name__, self.get_id(), self.command[0])
def process_kwargs(self, kwargs):
return self.process_kwargs_base(kwargs)
@@ -2466,7 +2468,7 @@ class RunTarget(Target):
class AliasTarget(RunTarget):
def __init__(self, name, dependencies, subdir, subproject):
- super().__init__(name, '', [], dependencies, subdir, subproject)
+ super().__init__(name, [], dependencies, subdir, subproject)
class Jar(BuildTarget):
known_kwargs = known_jar_kwargs
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index e5404c2..f3d7502 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -2378,7 +2378,7 @@ permitted_kwargs = {'add_global_arguments': {'language', 'native'},
'jar': build.known_jar_kwargs,
'project': {'version', 'meson_version', 'default_options', 'license', 'subproject_dir'},
'run_command': {'check', 'capture', 'env'},
- 'run_target': {'command', 'depends'},
+ 'run_target': {'command', 'depends', 'env'},
'shared_library': build.known_shlib_kwargs,
'shared_module': build.known_shmod_kwargs,
'static_library': build.known_stlib_kwargs,
@@ -4058,6 +4058,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
self.add_target(name, tg.held_object)
return tg
+ @FeatureNewKwargs('run_target', '0.57.0', ['env'])
@permittedKwargs(permitted_kwargs['run_target'])
def func_run_target(self, node, args, kwargs):
if len(args) > 1:
@@ -4086,8 +4087,8 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
if not isinstance(d, (build.BuildTarget, build.CustomTarget)):
raise InterpreterException('Depends items must be build targets.')
cleaned_deps.append(d)
- command, *cmd_args = cleaned_args
- tg = RunTargetHolder(build.RunTarget(name, command, cmd_args, cleaned_deps, self.subdir, self.subproject), self)
+ env = self.unpack_env_kwarg(kwargs)
+ tg = RunTargetHolder(build.RunTarget(name, cleaned_args, cleaned_deps, self.subdir, self.subproject, env), self)
self.add_target(name, tg.held_object)
full_name = (self.subproject, name)
assert(full_name not in self.build.run_target_names)
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index e72944d..f966083 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -956,8 +956,8 @@ class GnomeModule(ExtensionModule):
'--id=' + project_id,
'--sources=' + source_str,
]
- pottarget = build.RunTarget('help-' + project_id + '-pot', potargs[0],
- potargs[1:], [], state.subdir, state.subproject)
+ pottarget = build.RunTarget('help-' + project_id + '-pot', potargs,
+ [], state.subdir, state.subproject)
poargs = state.environment.get_build_command() + [
'--internal', 'yelphelper', 'update-po',
@@ -966,8 +966,8 @@ class GnomeModule(ExtensionModule):
'--sources=' + source_str,
'--langs=' + '@@'.join(langs),
]
- potarget = build.RunTarget('help-' + project_id + '-update-po', poargs[0],
- poargs[1:], [], state.subdir, state.subproject)
+ potarget = build.RunTarget('help-' + project_id + '-update-po', poargs,
+ [], state.subdir, state.subproject)
rv = [inscript, pottarget, potarget]
return ModuleReturnValue(None, rv)
diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py
index ae24e6e..54faf4c 100644
--- a/mesonbuild/modules/i18n.py
+++ b/mesonbuild/modules/i18n.py
@@ -152,12 +152,12 @@ class I18nModule(ExtensionModule):
potargs.append(datadirs)
if extra_args:
potargs.append(extra_args)
- pottarget = build.RunTarget(packagename + '-pot', potargs[0], potargs[1:], [], state.subdir, state.subproject)
+ pottarget = build.RunTarget(packagename + '-pot', potargs, [], state.subdir, state.subproject)
gmoargs = state.environment.get_build_command() + ['--internal', 'gettext', 'gen_gmo']
if lang_arg:
gmoargs.append(lang_arg)
- gmotarget = build.RunTarget(packagename + '-gmo', gmoargs[0], gmoargs[1:], [], state.subdir, state.subproject)
+ gmotarget = build.RunTarget(packagename + '-gmo', gmoargs, [], state.subdir, state.subproject)
updatepoargs = state.environment.get_build_command() + ['--internal', 'gettext', 'update_po', pkg_arg]
if lang_arg:
@@ -166,7 +166,7 @@ class I18nModule(ExtensionModule):
updatepoargs.append(datadirs)
if extra_args:
updatepoargs.append(extra_args)
- updatepotarget = build.RunTarget(packagename + '-update-po', updatepoargs[0], updatepoargs[1:], [], state.subdir, state.subproject)
+ updatepotarget = build.RunTarget(packagename + '-update-po', updatepoargs, [], state.subdir, state.subproject)
targets = [pottarget, gmotarget, updatepotarget]
diff --git a/mesonbuild/scripts/commandrunner.py b/mesonbuild/scripts/commandrunner.py
deleted file mode 100644
index aeeaa3b..0000000
--- a/mesonbuild/scripts/commandrunner.py
+++ /dev/null
@@ -1,84 +0,0 @@
-# Copyright 2014 The Meson development team
-
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-
-# http://www.apache.org/licenses/LICENSE-2.0
-
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""This program is a wrapper to run external commands. It determines
-what to run, sets up the environment and executes the command."""
-
-import sys, os, subprocess, shutil, shlex
-import re
-import typing as T
-
-def run_command(source_dir: str, build_dir: str, subdir: str, meson_command: T.List[str], command: str, arguments: T.List[str]) -> subprocess.Popen:
- env = {'MESON_SOURCE_ROOT': source_dir,
- 'MESON_BUILD_ROOT': build_dir,
- 'MESON_SUBDIR': subdir,
- 'MESONINTROSPECT': ' '.join([shlex.quote(x) for x in meson_command + ['introspect']]),
- }
- cwd = os.path.join(source_dir, subdir)
- child_env = os.environ.copy()
- child_env.update(env)
-
- # Is the command an executable in path?
- exe = shutil.which(command)
- if exe is not None:
- command_array = [exe] + arguments
- else:# No? Maybe it is a script in the source tree.
- fullpath = os.path.join(source_dir, subdir, command)
- command_array = [fullpath] + arguments
- try:
- return subprocess.Popen(command_array, env=child_env, cwd=cwd)
- except FileNotFoundError:
- print('Could not execute command "%s". File not found.' % command)
- sys.exit(1)
- except PermissionError:
- print('Could not execute command "%s". File not executable.' % command)
- sys.exit(1)
- except OSError as err:
- print('Could not execute command "{}": {}'.format(command, err))
- sys.exit(1)
- except subprocess.SubprocessError as err:
- print('Could not execute command "{}": {}'.format(command, err))
- sys.exit(1)
-
-def is_python_command(cmdname: str) -> bool:
- end_py_regex = r'python(3|3\.\d+)?(\.exe)?$'
- return re.search(end_py_regex, cmdname) is not None
-
-def run(args: T.List[str]) -> int:
- if len(args) < 4:
- print('commandrunner.py <source dir> <build dir> <subdir> <command> [arguments]')
- return 1
- src_dir = args[0]
- build_dir = args[1]
- subdir = args[2]
- meson_bin = args[3]
- if is_python_command(meson_bin):
- meson_command = [meson_bin, args[4]]
- command = args[5]
- arguments = args[6:]
- else:
- meson_command = [meson_bin]
- command = args[4]
- arguments = args[5:]
- pc = run_command(src_dir, build_dir, subdir, meson_command, command, arguments)
- while True:
- try:
- pc.wait()
- break
- except KeyboardInterrupt:
- pass
- return pc.returncode
-
-if __name__ == '__main__':
- sys.exit(run(sys.argv[1:]))
diff --git a/mesonbuild/scripts/meson_exe.py b/mesonbuild/scripts/meson_exe.py
index 620f579..27db144 100644
--- a/mesonbuild/scripts/meson_exe.py
+++ b/mesonbuild/scripts/meson_exe.py
@@ -52,10 +52,13 @@ def run_exe(exe: ExecutableSerialisation, extra_env: T.Optional[dict] = None) ->
['Z:' + p for p in exe.extra_paths] + child_env.get('WINEPATH', '').split(';')
)
+ pipe = subprocess.PIPE
+ if exe.verbose:
+ assert not exe.capture, 'Cannot capture and print to console at the same time'
+ pipe = None
+
p = subprocess.Popen(cmd_args, env=child_env, cwd=exe.workdir,
- close_fds=False,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
+ close_fds=False, stdout=pipe, stderr=pipe)
stdout, stderr = p.communicate()
if p.returncode == 0xc0000135:
@@ -65,6 +68,8 @@ def run_exe(exe: ExecutableSerialisation, extra_env: T.Optional[dict] = None) ->
if p.returncode != 0:
if exe.pickled:
print('while executing {!r}'.format(cmd_args))
+ if exe.verbose:
+ return p.returncode
if not exe.capture:
print('--- stdout ---')
print(stdout.decode())
diff --git a/run_unittests.py b/run_unittests.py
index b2e7338..7981df6 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -2381,6 +2381,7 @@ class AllPlatformTests(BasePlatformTests):
testdir = os.path.join(self.common_test_dir, '52 run target')
self.init(testdir)
self.run_target('check_exists')
+ self.run_target('check-env')
def test_install_introspection(self):
'''
diff --git a/test cases/common/52 run target/check-env.py b/test cases/common/52 run target/check-env.py
new file mode 100644
index 0000000..8df3e28
--- /dev/null
+++ b/test cases/common/52 run target/check-env.py
@@ -0,0 +1,9 @@
+#!/usr/bin/env python3
+
+import os
+
+assert 'MESON_SOURCE_ROOT' in os.environ
+assert 'MESON_BUILD_ROOT' in os.environ
+assert 'MESON_SUBDIR' in os.environ
+assert 'MESONINTROSPECT' in os.environ
+assert 'MY_ENV' in os.environ
diff --git a/test cases/common/52 run target/meson.build b/test cases/common/52 run target/meson.build
index 9abe698..a28d218 100644
--- a/test cases/common/52 run target/meson.build
+++ b/test cases/common/52 run target/meson.build
@@ -72,3 +72,9 @@ run_target('ctags',
run_target('clang-format',
command : converter)
+
+# Check we can pass env to the program
+run_target('check-env',
+ command: [find_program('check-env.py')],
+ env: {'MY_ENV': '1'},
+)