Make all functionality invokable via the main Meson binary,

which can be a Windows .exe file.
0.42-msi
Jussi Pakkanen 7 years ago
parent f5d530ef71
commit 40156e422a
  1. 6
      mesonbuild/backend/backends.py
  2. 78
      mesonbuild/backend/ninjabackend.py
  3. 19
      mesonbuild/backend/vs2010backend.py
  4. 12
      mesonbuild/environment.py
  5. 36
      mesonbuild/interpreter.py
  6. 9
      mesonbuild/mesonlib.py
  7. 16
      mesonbuild/modules/gnome.py
  8. 16
      mesonbuild/modules/i18n.py
  9. 21
      mesonbuild/scripts/commandrunner.py
  10. 5
      mesonbuild/scripts/meson_install.py
  11. 12
      test cases/common/147 mesonintrospect from scripts/check_env.py

@ -20,10 +20,11 @@ from .. import mlog
from .. import compilers
import json
import subprocess
from ..mesonlib import MesonException, get_meson_script
from ..mesonlib import MesonException
from ..mesonlib import get_compiler_for_source, classify_unity_sources
from ..compilers import CompilerArgs
from collections import OrderedDict
import shlex
class CleanTrees:
'''
@ -771,7 +772,8 @@ class Backend:
def run_postconf_scripts(self):
env = {'MESON_SOURCE_ROOT': self.environment.get_source_dir(),
'MESON_BUILD_ROOT': self.environment.get_build_dir(),
'MESONINTROSPECT': get_meson_script(self.environment, 'mesonintrospect')}
'MESONINTROSPECT': ' '.join([shlex.quote(x) for x in self.environment.get_build_command() + ['introspect']]),
}
child_env = os.environ.copy()
child_env.update(env)

@ -25,7 +25,7 @@ from .. import compilers
from ..compilers import CompilerArgs
from ..linkers import ArLinker
from ..mesonlib import File, MesonException, OrderedSet
from ..mesonlib import get_meson_script, get_compiler_for_source
from ..mesonlib import get_compiler_for_source
from .backends import CleanTrees, InstallData
from ..build import InvalidArguments
@ -520,8 +520,7 @@ int dummy;
# All targets are built from the build dir
self.environment.get_build_dir(),
capture=ofilenames[0] if target.capture else None)
cmd = [sys.executable, self.environment.get_build_command(),
'--internal', 'exe', exe_data]
cmd = self.environment.get_build_command() + ['--internal', 'exe', exe_data]
cmd_type = 'meson_exe.py custom'
else:
cmd_type = 'custom'
@ -537,7 +536,7 @@ int dummy;
self.processed_targets[target.name + target.type_suffix()] = True
def generate_run_target(self, target, outfile):
cmd = [sys.executable, self.environment.get_build_command(), '--internal', 'commandrunner']
cmd = self.environment.get_build_command() + ['--internal', 'commandrunner']
deps = self.unwrap_dep_list(target)
arg_strings = []
for i in target.args:
@ -555,8 +554,7 @@ int dummy;
elem = NinjaBuildElement(self.all_outputs, 'meson-' + target.name, 'CUSTOM_COMMAND', [])
cmd += [self.environment.get_source_dir(),
self.environment.get_build_dir(),
target.subdir,
get_meson_script(self.environment, 'mesonintrospect')]
target.subdir] + self.environment.get_build_command()
texe = target.command
try:
texe = texe.held_object
@ -594,12 +592,11 @@ int dummy;
def generate_coverage_rules(self, outfile):
e = NinjaBuildElement(self.all_outputs, 'meson-coverage', 'CUSTOM_COMMAND', 'PHONY')
e.add_item('COMMAND', [sys.executable,
self.environment.get_build_command(),
'--internal', 'coverage',
self.environment.get_source_dir(),
self.environment.get_build_dir(),
self.environment.get_log_dir()])
e.add_item('COMMAND', self.environment.get_build_command() +
['--internal', 'coverage',
self.environment.get_source_dir(),
self.environment.get_build_dir(),
self.environment.get_log_dir()])
e.add_item('description', 'Generates coverage reports.')
e.write(outfile)
# Alias that runs the target defined above
@ -659,12 +656,11 @@ int dummy;
d = InstallData(self.environment.get_source_dir(),
self.environment.get_build_dir(),
self.environment.get_prefix(),
strip_bin,
get_meson_script(self.environment, 'mesonintrospect'))
strip_bin, self.environment.get_build_command() + ['introspect'])
elem = NinjaBuildElement(self.all_outputs, 'meson-install', 'CUSTOM_COMMAND', 'PHONY')
elem.add_dep('all')
elem.add_item('DESC', 'Installing files.')
elem.add_item('COMMAND', [sys.executable, self.environment.get_build_command(), '--internal', 'install', install_data_file])
elem.add_item('COMMAND', self.environment.get_build_command() + ['--internal', 'install', install_data_file])
elem.add_item('pool', 'console')
self.generate_depmf_install(d)
self.generate_target_install(d)
@ -844,7 +840,7 @@ int dummy;
def generate_tests(self, outfile):
self.serialize_tests()
cmd = [sys.executable, '-u', self.environment.get_build_command(), 'test', '--no-rebuild']
cmd = self.environment.get_build_command(True) + ['test', '--no-rebuild']
if not self.environment.coredata.get_builtin_option('stdsplit'):
cmd += ['--no-stdsplit']
if self.environment.coredata.get_builtin_option('errorlogs'):
@ -858,7 +854,7 @@ int dummy;
self.create_target_alias('meson-test', outfile)
# And then benchmarks.
cmd = [sys.executable, '-u', self.environment.get_build_command(), 'test', '--benchmark', '--logbase',
cmd = self.environment.get_build_command(True) + ['test', '--benchmark', '--logbase',
'benchmarklog', '--num-processes=1', '--no-rebuild']
elem = NinjaBuildElement(self.all_outputs, 'meson-benchmark', 'CUSTOM_COMMAND', ['all', 'PHONY'])
elem.add_item('COMMAND', cmd)
@ -896,13 +892,12 @@ int dummy;
outfile.write(' depfile = $DEPFILE\n')
outfile.write(' restat = 1\n\n')
outfile.write('rule REGENERATE_BUILD\n')
c = (ninja_quote(quote_func(sys.executable)),
ninja_quote(quote_func(self.environment.get_build_command())),
'--internal',
c = [ninja_quote(quote_func(x)) for x in self.environment.get_build_command()] + \
['--internal',
'regenerate',
ninja_quote(quote_func(self.environment.get_source_dir())),
ninja_quote(quote_func(self.environment.get_build_dir())))
outfile.write(" command = %s %s %s %s %s %s --backend ninja\n" % c)
ninja_quote(quote_func(self.environment.get_build_dir()))]
outfile.write(" command = " + ' '.join(c) + ' --backend ninja\n')
outfile.write(' description = Regenerating build files.\n')
outfile.write(' generator = 1\n\n')
outfile.write('\n')
@ -1500,13 +1495,13 @@ int dummy;
outfile.write(description)
outfile.write('\n')
outfile.write('\n')
args = [ninja_quote(quote_func(x)) for x in self.environment.get_build_command()] + \
['--internal',
'symbolextractor',
'$in',
'$out']
symrule = 'rule SHSYM\n'
symcmd = ' command = "%s" "%s" %s %s %s %s $CROSS\n' % (ninja_quote(sys.executable),
self.environment.get_build_command(),
'--internal',
'symbolextractor',
'$in',
'$out')
symcmd = ' command = ' + ' '.join(args) + ' $CROSS\n'
synstat = ' restat = 1\n'
syndesc = ' description = Generating symbol file $out.\n'
outfile.write(symrule)
@ -1564,8 +1559,7 @@ int dummy;
def generate_swift_compile_rules(self, compiler, outfile):
rule = 'rule %s_COMPILER\n' % compiler.get_language()
full_exe = [ninja_quote(sys.executable),
ninja_quote(self.environment.get_build_command()),
full_exe = [ninja_quote(x) for x in self.environment.get_build_command()] + [
'--internal',
'dirchanger',
'$RUNDIR']
@ -2493,9 +2487,7 @@ rule FORTRAN_DEP_HACK
e = NinjaBuildElement(self.all_outputs, 'meson-clean-ctlist', 'CUSTOM_COMMAND', 'PHONY')
d = CleanTrees(self.environment.get_build_dir(), trees)
d_file = os.path.join(self.environment.get_scratch_dir(), 'cleantrees.dat')
e.add_item('COMMAND', [sys.executable,
self.environment.get_build_command(),
'--internal', 'cleantrees', d_file])
e.add_item('COMMAND', self.environment.get_build_command() + ['--internal', 'cleantrees', d_file])
e.add_item('description', 'Cleaning custom target directories.')
e.write(outfile)
# Alias that runs the target defined above
@ -2536,13 +2528,10 @@ rule FORTRAN_DEP_HACK
def generate_dist(self, outfile):
elem = NinjaBuildElement(self.all_outputs, 'meson-dist', 'CUSTOM_COMMAND', 'PHONY')
elem.add_item('DESC', 'Creating source packages')
elem.add_item('COMMAND', [sys.executable,
self.environment.get_build_command(),
'--internal', 'dist',
self.environment.source_dir,
self.environment.build_dir,
sys.executable,
self.environment.get_build_command()])
elem.add_item('COMMAND', self.environment.get_build_command() +
['--internal', 'dist',
self.environment.source_dir,
self.environment.build_dir] + self.environment.get_build_command())
elem.add_item('pool', 'console')
elem.write(outfile)
# Alias that runs the target defined above
@ -2550,17 +2539,16 @@ rule FORTRAN_DEP_HACK
# For things like scan-build and other helper tools we might have.
def generate_utils(self, outfile):
cmd = [sys.executable, self.environment.get_build_command(),
'--internal', 'scanbuild', self.environment.source_dir, self.environment.build_dir,
sys.executable, self.environment.get_build_command()] + self.get_user_option_args()
cmd = self.environment.get_build_command() + \
['--internal', 'scanbuild', self.environment.source_dir, self.environment.build_dir] + \
self.environment.get_build_command() + self.get_user_option_args()
elem = NinjaBuildElement(self.all_outputs, 'meson-scan-build', 'CUSTOM_COMMAND', 'PHONY')
elem.add_item('COMMAND', cmd)
elem.add_item('pool', 'console')
elem.write(outfile)
# Alias that runs the target defined above
self.create_target_alias('meson-scan-build', outfile)
cmd = [sys.executable, self.environment.get_build_command(),
'--internal', 'uninstall']
cmd = self.environment.get_build_command() + ['--internal', 'uninstall']
elem = NinjaBuildElement(self.all_outputs, 'meson-uninstall', 'CUSTOM_COMMAND', 'PHONY')
elem.add_item('COMMAND', cmd)
elem.add_item('pool', 'console')

@ -24,7 +24,7 @@ from .. import mlog
from .. import compilers
from ..build import BuildTarget
from ..compilers import CompilerArgs
from ..mesonlib import MesonException, File, get_meson_script
from ..mesonlib import MesonException, File
from ..environment import Environment
def autodetect_vs_version(build):
@ -413,8 +413,8 @@ class Vs2010Backend(backends.Backend):
cmd = [sys.executable, os.path.join(self.environment.get_script_dir(), 'commandrunner.py'),
self.environment.get_build_dir(),
self.environment.get_source_dir(),
self.get_target_dir(target),
get_meson_script(self.environment, 'mesonintrospect')]
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)))
@ -447,8 +447,7 @@ class Vs2010Backend(backends.Backend):
# All targets run from the target dir
tdir_abs,
capture=ofilenames[0] if target.capture else None)
wrapper_cmd = [sys.executable, self.environment.get_build_command(),
'--internal', 'exe', exe_data]
wrapper_cmd = self.environment.get_build_command() + ['--internal', 'exe', exe_data]
ET.SubElement(customstep, 'Command').text = ' '.join(self.quote_arguments(wrapper_cmd))
ET.SubElement(customstep, 'Outputs').text = ';'.join(ofilenames)
ET.SubElement(customstep, 'Inputs').text = ';'.join([exe_data] + srcs + depend_files)
@ -1095,10 +1094,7 @@ class Vs2010Backend(backends.Backend):
ET.SubElement(midl, 'TypeLibraryName').text = '%(Filename).tlb'
ET.SubElement(midl, 'InterfaceIdentifierFilename').text = '%(Filename)_i.c'
ET.SubElement(midl, 'ProxyFileName').text = '%(Filename)_p.c'
regen_command = [sys.executable,
self.environment.get_build_command(),
'--internal',
'regencheck']
regen_command = self.environment.get_build_command() + ['--internal', 'regencheck']
private_dir = self.environment.get_scratch_dir()
cmd_templ = '''setlocal
"%s" "%s"
@ -1178,10 +1174,7 @@ if %%errorlevel%% neq 0 goto :VCEnd'''
postbuild = ET.SubElement(action, 'PostBuildEvent')
ET.SubElement(postbuild, 'Message')
# FIXME: No benchmarks?
test_command = [sys.executable,
self.environment.get_build_command(),
'test',
'--no-rebuild']
test_command = self.environment.get_build_command() + ['test', '--no-rebuild']
if not self.environment.coredata.get_builtin_option('stdsplit'):
test_command += ['--no-stdsplit']
if self.environment.coredata.get_builtin_option('errorlogs'):

@ -19,6 +19,7 @@ from .linkers import ArLinker, VisualStudioLinker
from . import mesonlib
from .mesonlib import EnvironmentException, Popen_safe
from . import mlog
import sys
from . import compilers
from .compilers import (
@ -361,8 +362,15 @@ class Environment:
def get_coredata(self):
return self.coredata
def get_build_command(self):
return self.meson_script_launcher
def get_build_command(self, unbuffered=False):
# If running an executable created with cx_freeze,
# Python might not be installed so don't prefix
# the command with it.
if sys.executable.endswith('meson.exe'):
return [sys.executable]
if unbuffered:
[sys.executable, '-u', self.meson_script_launcher]
return [sys.executable, self.meson_script_launcher]
def is_header(self, fname):
return is_header(fname)

@ -21,7 +21,7 @@ from . import optinterpreter
from . import compilers
from .wrap import wrap, WrapMode
from . import mesonlib
from .mesonlib import FileMode, Popen_safe, get_meson_script
from .mesonlib import FileMode, Popen_safe
from .dependencies import ExternalProgram
from .dependencies import InternalDependency, Dependency, DependencyException
from .interpreterbase import InterpreterBase
@ -31,7 +31,7 @@ from .interpreterbase import InterpreterObject, MutableInterpreterObject
from .modules import ModuleReturnValue
import os, sys, shutil, uuid
import re
import re, shlex
from collections import namedtuple
import importlib
@ -88,7 +88,8 @@ class RunProcess(InterpreterObject):
env = {'MESON_SOURCE_ROOT': source_dir,
'MESON_BUILD_ROOT': build_dir,
'MESON_SUBDIR': subdir,
'MESONINTROSPECT': mesonintrospect}
'MESONINTROSPECT': ' '.join([shlex.quote(x) for x in mesonintrospect]),
}
if in_builddir:
cwd = os.path.join(build_dir, subdir)
else:
@ -97,7 +98,13 @@ class RunProcess(InterpreterObject):
child_env.update(env)
mlog.debug('Running command:', ' '.join(command_array))
try:
return Popen_safe(command_array, env=child_env, cwd=cwd)
p, o, e = Popen_safe(command_array, env=child_env, cwd=cwd)
mlog.debug('--- stdout----')
mlog.debug(o)
mlog.debug('----stderr----')
mlog.debug(e)
mlog.debug('')
return p, o, e
except FileNotFoundError:
raise InterpreterException('Could not execute command "%s".' % ' '.join(command_array))
@ -1605,7 +1612,7 @@ class Interpreter(InterpreterBase):
else:
raise InterpreterException('Arguments ' + m.format(a))
return RunProcess(cmd, expanded_args, srcdir, builddir, self.subdir,
get_meson_script(self.environment, 'mesonintrospect'), in_builddir)
self.environment.get_build_command() + ['introspect'], in_builddir)
@stringArgs
def func_gettext(self, nodes, args, kwargs):
@ -2188,16 +2195,15 @@ class Interpreter(InterpreterBase):
else:
vcs_cmd = [' '] # executing this cmd will fail in vcstagger.py and force to use the fallback string
# vcstagger.py parameters: infile, outfile, fallback, source_dir, replace_string, regex_selector, command...
kwargs['command'] = [sys.executable,
self.environment.get_build_command(),
'--internal',
'vcstagger',
'@INPUT0@',
'@OUTPUT0@',
fallback,
source_dir,
replace_string,
regex_selector] + vcs_cmd
kwargs['command'] = self.environment.get_build_command() + \
['--internal',
'vcstagger',
'@INPUT0@',
'@OUTPUT0@',
fallback,
source_dir,
replace_string,
regex_selector] + vcs_cmd
kwargs.setdefault('build_always', True)
return self.func_custom_target(node, [kwargs['output']], kwargs)

@ -182,15 +182,6 @@ class File:
def relative_name(self):
return os.path.join(self.subdir, self.fname)
def get_meson_script(env, script):
'''
Given the path of `meson.py`/`meson`, get the path of a meson script such
as `mesonintrospect` or `mesontest`.
'''
meson_py = env.get_build_command()
(base, ext) = os.path.splitext(meson_py)
return os.path.join(os.path.dirname(base), script + ext)
def get_compiler_for_source(compilers, src):
for comp in compilers:
if comp.can_compile(src):

@ -660,7 +660,7 @@ class GnomeModule(ExtensionModule):
if kwargs:
raise MesonException('Unknown arguments passed: {}'.format(', '.join(kwargs.keys())))
script = [sys.executable, state.environment.get_build_command()]
script = state.environment.get_build_command()
args = ['--internal',
'yelphelper',
'install',
@ -676,20 +676,20 @@ class GnomeModule(ExtensionModule):
args.append('--langs=' + '@@'.join(langs))
inscript = build.RunScript(script, args)
potargs = [state.environment.get_build_command(), '--internal', 'yelphelper', 'pot',
potargs = state.environment.get_build_command() + ['--internal', 'yelphelper', 'pot',
'--subdir=' + state.subdir,
'--id=' + project_id,
'--sources=' + source_str]
pottarget = build.RunTarget('help-' + project_id + '-pot', sys.executable,
potargs, [], state.subdir)
pottarget = build.RunTarget('help-' + project_id + '-pot', potargs[0],
potargs[1:], [], state.subdir)
poargs = [state.environment.get_build_command(), '--internal', 'yelphelper', 'update-po',
poargs = state.environment.get_build_command() + ['--internal', 'yelphelper', 'update-po',
'--subdir=' + state.subdir,
'--id=' + project_id,
'--sources=' + source_str,
'--langs=' + '@@'.join(langs)]
potarget = build.RunTarget('help-' + project_id + '-update-po', sys.executable,
poargs, [], state.subdir)
potarget = build.RunTarget('help-' + project_id + '-update-po', poargs[0],
poargs[1:], [], state.subdir)
rv = [inscript, pottarget, potarget]
return ModuleReturnValue(None, rv)
@ -717,7 +717,7 @@ class GnomeModule(ExtensionModule):
raise MesonException('You can only specify main_xml or main_sgml, not both.')
main_file = main_xml
targetname = modulename + '-doc'
command = [sys.executable, state.environment.get_build_command()]
command = state.environment.get_build_command()
namespace = kwargs.get('namespace', '')
mode = kwargs.get('mode', 'auto')

@ -72,7 +72,7 @@ class I18nModule(ExtensionModule):
datadirs = self._get_data_dirs(state, mesonlib.stringlistify(kwargs.pop('data_dirs', [])))
datadirs = '--datadirs=' + ':'.join(datadirs) if datadirs else None
command = [state.environment.get_build_command(), '--internal', 'msgfmthelper',
command = state.environment.get_build_command() + ['--internal', 'msgfmthelper',
'@INPUT@', '@OUTPUT@', file_type, podir]
if datadirs:
command.append(datadirs)
@ -105,28 +105,28 @@ class I18nModule(ExtensionModule):
datadirs = '--datadirs=' + ':'.join(datadirs) if datadirs else None
extra_args = '--extra-args=' + '@@'.join(extra_args) if extra_args else None
potargs = [state.environment.get_build_command(), '--internal', 'gettext', 'pot', pkg_arg]
potargs = state.environment.get_build_command() + ['--internal', 'gettext', 'pot', pkg_arg]
if datadirs:
potargs.append(datadirs)
if extra_args:
potargs.append(extra_args)
pottarget = build.RunTarget(packagename + '-pot', sys.executable, potargs, [], state.subdir)
pottarget = build.RunTarget(packagename + '-pot', potargs[0], potargs[1:], [], state.subdir)
gmoargs = [state.environment.get_build_command(), '--internal', 'gettext', 'gen_gmo']
gmoargs = state.environment.get_build_command() + ['--internal', 'gettext', 'gen_gmo']
if lang_arg:
gmoargs.append(lang_arg)
gmotarget = build.RunTarget(packagename + '-gmo', sys.executable, gmoargs, [], state.subdir)
gmotarget = build.RunTarget(packagename + '-gmo', gmoargs[0], gmoargs[1:], [], state.subdir)
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:
updatepoargs.append(lang_arg)
if datadirs:
updatepoargs.append(datadirs)
if extra_args:
updatepoargs.append(extra_args)
updatepotarget = build.RunTarget(packagename + '-update-po', sys.executable, updatepoargs, [], state.subdir)
updatepotarget = build.RunTarget(packagename + '-update-po', updatepoargs[0], updatepoargs[1:], [], state.subdir)
script = [sys.executable, state.environment.get_build_command()]
script = state.environment.get_build_command()
args = ['--internal', 'gettext', 'install',
'--subdir=' + state.subdir,
'--localedir=' + state.environment.coredata.get_builtin_option('localedir'),

@ -15,13 +15,14 @@
"""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
import sys, os, subprocess, shutil, shlex
def run_command(source_dir, build_dir, subdir, mesonintrospect, command, arguments):
def run_command(source_dir, build_dir, subdir, meson_command, command, arguments):
env = {'MESON_SOURCE_ROOT': source_dir,
'MESON_BUILD_ROOT': build_dir,
'MESON_SUBDIR': subdir,
'MESONINTROSPECT': mesonintrospect}
'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)
@ -47,10 +48,16 @@ def run(args):
src_dir = args[0]
build_dir = args[1]
subdir = args[2]
mesonintrospect = args[3]
command = args[4]
arguments = args[5:]
pc = run_command(src_dir, build_dir, subdir, mesonintrospect, command, arguments)
meson_command = args[3]
if 'python' in meson_command: # Hack.
meson_command = [meson_command, args[4]]
command = args[5]
arguments = args[6:]
else:
meson_command = [meson_command]
command = args[4]
arguments = args[5:]
pc = run_command(src_dir, build_dir, subdir, meson_command, command, arguments)
pc.wait()
return pc.returncode

@ -13,6 +13,7 @@
# limitations under the License.
import sys, pickle, os, shutil, subprocess, gzip, platform, errno
import shlex
from glob import glob
from . import depfixer
from . import destdir_join
@ -247,7 +248,9 @@ def run_install_script(d):
'MESON_BUILD_ROOT': d.build_dir,
'MESON_INSTALL_PREFIX': d.prefix,
'MESON_INSTALL_DESTDIR_PREFIX': d.fullprefix,
'MESONINTROSPECT': d.mesonintrospect}
'MESONINTROSPECT': ' '.join([shlex.quote(x) for x in d.mesonintrospect]),
}
print(env)
child_env = os.environ.copy()
child_env.update(env)

@ -2,6 +2,7 @@
import os
import sys
import shlex
do_print = False
@ -13,8 +14,15 @@ if 'MESONINTROSPECT' not in os.environ:
mesonintrospect = os.environ['MESONINTROSPECT']
if not os.path.isfile(mesonintrospect):
introspect_arr = shlex.split(mesonintrospect)
#print(mesonintrospect)
#print(introspect_arr)
some_executable = introspect_arr[0]
if not os.path.isfile(some_executable):
raise RuntimeError('{!r} does not exist'.format(mesonintrospect))
if do_print:
print(mesonintrospect, end='')
print(some_executable, end='')

Loading…
Cancel
Save