run_target: Add env kwarg

Re-implement it in backend using the same code path as for
custom_target(). This for example handle setting PATH on Windows when
command is an executable.
pull/8305/head
Xavier Claessens 4 years ago
parent 5d94d161ff
commit 522392e755
  1. 4
      docs/markdown/Reference-manual.md
  2. 2
      docs/markdown/snippets/customtarget_env.md
  3. 20
      mesonbuild/backend/backends.py
  4. 78
      mesonbuild/backend/ninjabackend.py
  5. 34
      mesonbuild/backend/vs2010backend.py
  6. 72
      mesonbuild/build.py
  7. 7
      mesonbuild/interpreter.py
  8. 8
      mesonbuild/modules/gnome.py
  9. 6
      mesonbuild/modules/i18n.py
  10. 84
      mesonbuild/scripts/commandrunner.py
  11. 11
      mesonbuild/scripts/meson_exe.py
  12. 1
      run_unittests.py
  13. 9
      test cases/common/52 run target/check-env.py
  14. 6
      test cases/common/52 run target/meson.build

@ -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 - `depends` is a list of targets that this target depends on but which
are not listed in the command array (because, for example, the are not listed in the command array (because, for example, the
script does file globbing internally) 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() ### set_variable()

@ -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. Environment variables can now be passed to the `custom_target()` command.

@ -21,7 +21,6 @@ import json
import os import os
import pickle import pickle
import re import re
import shlex
import textwrap import textwrap
import typing as T import typing as T
import hashlib import hashlib
@ -34,7 +33,7 @@ from .. import mlog
from ..compilers import LANGUAGES_USING_LDFLAGS from ..compilers import LANGUAGES_USING_LDFLAGS
from ..mesonlib import ( from ..mesonlib import (
File, MachineChoice, MesonException, OptionType, OrderedSet, OptionOverrideProxy, File, MachineChoice, MesonException, OptionType, OrderedSet, OptionOverrideProxy,
classify_unity_sources, unholder, OptionKey classify_unity_sources, unholder, OptionKey, join_args
) )
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
@ -138,6 +137,7 @@ class ExecutableSerialisation:
self.capture = capture self.capture = capture
self.pickled = False self.pickled = False
self.skip_if_destdir = False self.skip_if_destdir = False
self.verbose = False
class TestSerialisation: class TestSerialisation:
def __init__(self, name: str, project: str, suite: str, fname: T.List[str], def __init__(self, name: str, project: str, suite: str, fname: T.List[str],
@ -421,12 +421,14 @@ class Backend:
def as_meson_exe_cmdline(self, tname, exe, cmd_args, workdir=None, def as_meson_exe_cmdline(self, tname, exe, cmd_args, workdir=None,
extra_bdeps=None, capture=None, force_serialize=False, 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 Serialize an executable for running with a generator or a custom target
''' '''
cmd = [exe] + cmd_args cmd = [exe] + cmd_args
es = self.get_executable_serialisation(cmd, workdir, extra_bdeps, capture, env) es = self.get_executable_serialisation(cmd, workdir, extra_bdeps, capture, env)
es.verbose = verbose
reasons = [] reasons = []
if es.extra_paths: if es.extra_paths:
reasons.append('to set PATH') reasons.append('to set PATH')
@ -1193,11 +1195,21 @@ class Backend:
cmd = [i.replace('\\', '/') for i in cmd] cmd = [i.replace('\\', '/') for i in cmd]
return inputs, outputs, 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: def run_postconf_scripts(self) -> None:
from ..scripts.meson_exe import run_exe 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(), env = {'MESON_SOURCE_ROOT': self.environment.get_source_dir(),
'MESON_BUILD_ROOT': self.environment.get_build_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: for s in self.build.postconf_scripts:

@ -28,7 +28,6 @@ from .. import modules
from .. import environment, mesonlib from .. import environment, mesonlib
from .. import build from .. import build
from .. import mlog from .. import mlog
from .. import dependencies
from .. import compilers from .. import compilers
from ..arglist import CompilerArgs from ..arglist import CompilerArgs
from ..compilers import ( from ..compilers import (
@ -989,65 +988,28 @@ int dummy;
return '{}{}'.format(subproject_prefix, target.name) return '{}{}'.format(subproject_prefix, target.name)
def generate_run_target(self, target): 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()))
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))
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) 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', []) elem = NinjaBuildElement(self.all_outputs, target_name, 'phony', [])
else:
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(internal_target_name)
deps = self.unwrap_dep_list(target)
deps += self.get_custom_target_depend_files(target)
elem.add_dep(deps) elem.add_dep(deps)
self.add_build(elem) self.add_build(elem)
self.processed_targets[target.get_id()] = True self.processed_targets[target.get_id()] = True

@ -28,7 +28,7 @@ from .. import mlog
from .. import compilers from .. import compilers
from ..interpreter import Interpreter from ..interpreter import Interpreter
from ..mesonlib import ( from ..mesonlib import (
MesonException, File, python_command, replace_if_different, OptionKey, MesonException, python_command, replace_if_different, OptionKey,
) )
from ..environment import Environment, build_filename from ..environment import Environment, build_filename
@ -257,8 +257,7 @@ class Vs2010Backend(backends.Backend):
for d in target.get_target_dependencies(): for d in target.get_target_dependencies():
all_deps[d.get_id()] = d all_deps[d.get_id()] = d
elif isinstance(target, build.RunTarget): elif isinstance(target, build.RunTarget):
for d in [target.command] + target.args: for d in target.get_dependencies():
if isinstance(d, (build.BuildTarget, build.CustomTarget)):
all_deps[d.get_id()] = d all_deps[d.get_id()] = d
elif isinstance(target, build.BuildTarget): elif isinstance(target, build.BuildTarget):
for ldep in target.link_targets: for ldep in target.link_targets:
@ -534,27 +533,14 @@ class Vs2010Backend(backends.Backend):
# is probably a better way than running a this dummy command. # is probably a better way than running a this dummy command.
cmd_raw = python_command + ['-c', 'exit'] cmd_raw = python_command + ['-c', 'exit']
else: else:
cmd_raw = [target.command] + target.args _, _, cmd_raw = self.eval_custom_target_command(target)
cmd = python_command + \ depend_files = self.get_custom_target_depend_files(target)
[os.path.join(self.environment.get_script_dir(), 'commandrunner.py'), target_env = self.get_run_target_env(target)
self.environment.get_source_dir(), wrapper_cmd, _ = self.as_meson_exe_cmdline(target.name, cmd_raw[0], cmd_raw[1:],
self.environment.get_build_dir(), force_serialize=True, env=target_env,
self.get_target_dir(target)] + self.environment.get_build_command() verbose=True)
for i in cmd_raw: self.add_custom_build(root, 'run_target', ' '.join(self.quote_arguments(wrapper_cmd)),
if isinstance(i, build.BuildTarget): deps=depend_files)
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))
ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.targets') ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.targets')
self.add_regen_dependency(root) self.add_regen_dependency(root)
self.add_target_deps(root, target) self.add_target_deps(root, target)

@ -29,7 +29,7 @@ from .mesonlib import (
File, MesonException, MachineChoice, PerMachine, OrderedSet, listify, File, MesonException, MachineChoice, PerMachine, OrderedSet, listify,
extract_as_list, typeslistify, stringlistify, classify_unity_sources, extract_as_list, typeslistify, stringlistify, classify_unity_sources,
get_filenames_templates_dict, substitute_values, has_path_sep, unholder, get_filenames_templates_dict, substitute_values, has_path_sep, unholder,
OptionKey, OptionKey
) )
from .compilers import ( from .compilers import (
Compiler, is_object, clink_langs, sort_clink, lang_suffixes, Compiler, is_object, clink_langs, sort_clink, lang_suffixes,
@ -2142,8 +2142,35 @@ class SharedModule(SharedLibrary):
def get_default_install_dir(self, environment): def get_default_install_dir(self, environment):
return environment.get_shared_module_dir() 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([ known_kwargs = set([
'input', 'input',
'output', 'output',
@ -2214,33 +2241,6 @@ class CustomTarget(Target):
bdeps.update(d.get_transitive_build_target_deps()) bdeps.update(d.get_transitive_build_target_deps())
return bdeps 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): def process_kwargs(self, kwargs, backend):
self.process_kwargs_base(kwargs) self.process_kwargs_base(kwargs)
self.sources = unholder(extract_as_list(kwargs, 'input')) self.sources = unholder(extract_as_list(kwargs, 'input'))
@ -2422,18 +2422,20 @@ class CustomTarget(Target):
for i in self.outputs: for i in self.outputs:
yield CustomTargetIndex(self, i) yield CustomTargetIndex(self, i)
class RunTarget(Target): class RunTarget(Target, CommandBase):
def __init__(self, name, command, args, dependencies, subdir, subproject): def __init__(self, name, command, dependencies, subdir, subproject, env=None):
self.typename = 'run' self.typename = 'run'
# These don't produce output artifacts # These don't produce output artifacts
super().__init__(name, subdir, subproject, False, MachineChoice.BUILD) super().__init__(name, subdir, subproject, False, MachineChoice.BUILD)
self.command = command
self.args = args
self.dependencies = dependencies self.dependencies = dependencies
self.depend_files = []
self.command = self.flatten_command(command)
self.absolute_paths = False
self.env = env
def __repr__(self): def __repr__(self):
repr_str = "<{0} {1}: {2}>" 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): def process_kwargs(self, kwargs):
return self.process_kwargs_base(kwargs) return self.process_kwargs_base(kwargs)
@ -2466,7 +2468,7 @@ class RunTarget(Target):
class AliasTarget(RunTarget): class AliasTarget(RunTarget):
def __init__(self, name, dependencies, subdir, subproject): def __init__(self, name, dependencies, subdir, subproject):
super().__init__(name, '', [], dependencies, subdir, subproject) super().__init__(name, [], dependencies, subdir, subproject)
class Jar(BuildTarget): class Jar(BuildTarget):
known_kwargs = known_jar_kwargs known_kwargs = known_jar_kwargs

@ -2377,7 +2377,7 @@ permitted_kwargs = {'add_global_arguments': {'language', 'native'},
'jar': build.known_jar_kwargs, 'jar': build.known_jar_kwargs,
'project': {'version', 'meson_version', 'default_options', 'license', 'subproject_dir'}, 'project': {'version', 'meson_version', 'default_options', 'license', 'subproject_dir'},
'run_command': {'check', 'capture', 'env'}, 'run_command': {'check', 'capture', 'env'},
'run_target': {'command', 'depends'}, 'run_target': {'command', 'depends', 'env'},
'shared_library': build.known_shlib_kwargs, 'shared_library': build.known_shlib_kwargs,
'shared_module': build.known_shmod_kwargs, 'shared_module': build.known_shmod_kwargs,
'static_library': build.known_stlib_kwargs, 'static_library': build.known_stlib_kwargs,
@ -4059,6 +4059,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
self.add_target(name, tg.held_object) self.add_target(name, tg.held_object)
return tg return tg
@FeatureNewKwargs('run_target', '0.57.0', ['env'])
@permittedKwargs(permitted_kwargs['run_target']) @permittedKwargs(permitted_kwargs['run_target'])
def func_run_target(self, node, args, kwargs): def func_run_target(self, node, args, kwargs):
if len(args) > 1: if len(args) > 1:
@ -4087,8 +4088,8 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
if not isinstance(d, (build.BuildTarget, build.CustomTarget)): if not isinstance(d, (build.BuildTarget, build.CustomTarget)):
raise InterpreterException('Depends items must be build targets.') raise InterpreterException('Depends items must be build targets.')
cleaned_deps.append(d) cleaned_deps.append(d)
command, *cmd_args = cleaned_args env = self.unpack_env_kwarg(kwargs)
tg = RunTargetHolder(build.RunTarget(name, command, cmd_args, cleaned_deps, self.subdir, self.subproject), self) tg = RunTargetHolder(build.RunTarget(name, cleaned_args, cleaned_deps, self.subdir, self.subproject, env), self)
self.add_target(name, tg.held_object) self.add_target(name, tg.held_object)
full_name = (self.subproject, name) full_name = (self.subproject, name)
assert(full_name not in self.build.run_target_names) assert(full_name not in self.build.run_target_names)

@ -907,8 +907,8 @@ class GnomeModule(ExtensionModule):
'--id=' + project_id, '--id=' + project_id,
'--sources=' + source_str, '--sources=' + source_str,
] ]
pottarget = build.RunTarget('help-' + project_id + '-pot', potargs[0], pottarget = build.RunTarget('help-' + project_id + '-pot', potargs,
potargs[1:], [], state.subdir, state.subproject) [], state.subdir, state.subproject)
poargs = state.environment.get_build_command() + [ poargs = state.environment.get_build_command() + [
'--internal', 'yelphelper', 'update-po', '--internal', 'yelphelper', 'update-po',
@ -917,8 +917,8 @@ class GnomeModule(ExtensionModule):
'--sources=' + source_str, '--sources=' + source_str,
'--langs=' + '@@'.join(langs), '--langs=' + '@@'.join(langs),
] ]
potarget = build.RunTarget('help-' + project_id + '-update-po', poargs[0], potarget = build.RunTarget('help-' + project_id + '-update-po', poargs,
poargs[1:], [], state.subdir, state.subproject) [], state.subdir, state.subproject)
rv = [inscript, pottarget, potarget] rv = [inscript, pottarget, potarget]
return ModuleReturnValue(None, rv) return ModuleReturnValue(None, rv)

@ -152,12 +152,12 @@ class I18nModule(ExtensionModule):
potargs.append(datadirs) potargs.append(datadirs)
if extra_args: if extra_args:
potargs.append(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'] gmoargs = state.environment.get_build_command() + ['--internal', 'gettext', 'gen_gmo']
if lang_arg: if lang_arg:
gmoargs.append(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] updatepoargs = state.environment.get_build_command() + ['--internal', 'gettext', 'update_po', pkg_arg]
if lang_arg: if lang_arg:
@ -166,7 +166,7 @@ class I18nModule(ExtensionModule):
updatepoargs.append(datadirs) updatepoargs.append(datadirs)
if extra_args: if extra_args:
updatepoargs.append(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] targets = [pottarget, gmotarget, updatepotarget]

@ -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:]))

@ -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(';') ['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, p = subprocess.Popen(cmd_args, env=child_env, cwd=exe.workdir,
close_fds=False, close_fds=False, stdout=pipe, stderr=pipe)
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = p.communicate() stdout, stderr = p.communicate()
if p.returncode == 0xc0000135: if p.returncode == 0xc0000135:
@ -65,6 +68,8 @@ def run_exe(exe: ExecutableSerialisation, extra_env: T.Optional[dict] = None) ->
if p.returncode != 0: if p.returncode != 0:
if exe.pickled: if exe.pickled:
print('while executing {!r}'.format(cmd_args)) print('while executing {!r}'.format(cmd_args))
if exe.verbose:
return p.returncode
if not exe.capture: if not exe.capture:
print('--- stdout ---') print('--- stdout ---')
print(stdout.decode()) print(stdout.decode())

@ -2192,6 +2192,7 @@ class AllPlatformTests(BasePlatformTests):
testdir = os.path.join(self.common_test_dir, '52 run target') testdir = os.path.join(self.common_test_dir, '52 run target')
self.init(testdir) self.init(testdir)
self.run_target('check_exists') self.run_target('check_exists')
self.run_target('check-env')
def test_install_introspection(self): def test_install_introspection(self):
''' '''

@ -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

@ -72,3 +72,9 @@ run_target('ctags',
run_target('clang-format', run_target('clang-format',
command : converter) command : converter)
# Check we can pass env to the program
run_target('check-env',
command: [find_program('check-env.py')],
env: {'MY_ENV': '1'},
)

Loading…
Cancel
Save