aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xdocs/genrelnotes.py5
-rw-r--r--docs/markdown/Commands.md2
-rw-r--r--docs/markdown/Windows-module.md1
-rw-r--r--docs/markdown/snippets/compiler_prefix_property_array.md18
-rw-r--r--docs/markdown/snippets/devenv.md15
-rw-r--r--docs/yaml/objects/compiler.yaml12
-rw-r--r--man/meson.12
-rw-r--r--mesonbuild/backend/backends.py28
-rw-r--r--mesonbuild/backend/ninjabackend.py13
-rw-r--r--mesonbuild/build.py1
-rw-r--r--mesonbuild/coredata.py2
-rw-r--r--mesonbuild/environment.py8
-rw-r--r--mesonbuild/interpreter/compiler.py8
-rw-r--r--mesonbuild/interpreter/interpreter.py3
-rw-r--r--mesonbuild/mdevenv.py55
-rw-r--r--mesonbuild/modules/gnome.py5
-rw-r--r--mesonbuild/modules/python.py1
-rw-r--r--mesonbuild/mtest.py35
-rw-r--r--mesonbuild/programs.py19
-rw-r--r--mesonbuild/scripts/clangformat.py12
-rw-r--r--mesonbuild/utils/core.py15
-rw-r--r--mesonbuild/utils/universal.py2
-rw-r--r--mesonbuild/wrap/wrap.py7
-rw-r--r--test cases/common/132 get define/meson.build7
-rw-r--r--test cases/common/51 run target/.clang-format1
-rw-r--r--test cases/common/51 run target/.clang-tidy0
-rw-r--r--test cases/common/51 run target/meson.build9
-rw-r--r--unittests/subprojectscommandtests.py18
-rw-r--r--unittests/taptests.py4
29 files changed, 214 insertions, 94 deletions
diff --git a/docs/genrelnotes.py b/docs/genrelnotes.py
index 6873019..bad6597 100755
--- a/docs/genrelnotes.py
+++ b/docs/genrelnotes.py
@@ -20,6 +20,7 @@ import argparse
import subprocess
import re
import shutil
+import datetime
from pathlib import Path
RELNOTE_TEMPLATE = '''---
@@ -73,6 +74,10 @@ def generate(relnotes, to_version, source_dir, output_dir):
output.parent.mkdir(exist_ok=True, parents=True)
with output.open('w', encoding='utf-8') as ofile:
ofile.write(RELNOTE_TEMPLATE.format(title, to_version, title_suffix))
+ if not output_dir:
+ date = datetime.date.today()
+ date_str = date.strftime("%d %B %Y")
+ ofile.write(f'Meson {to_version} was released on {date_str}\n')
for snippetfile in sorted(Path(source_dir, 'markdown/snippets').glob('*.md')):
snippet = snippetfile.read_text(encoding='utf-8')
ofile.write(snippet)
diff --git a/docs/markdown/Commands.md b/docs/markdown/Commands.md
index c328461..4a00c4f 100644
--- a/docs/markdown/Commands.md
+++ b/docs/markdown/Commands.md
@@ -345,6 +345,8 @@ These variables are set in environment in addition to those set using [[meson.ad
schemas is compiled. This is automatically set when using `gnome.compile_schemas()`.
Note that this requires GLib >= 2.64 when `gnome.compile_schemas()` is used in
more than one directory.
+- `QEMU_LD_PREFIX` *Since 1.0.0* is set to the `sys_root` value from cross file
+ when cross compiling and that property is defined.
Since *Since 0.62.0* if bash-completion scripts are being installed and the
shell is bash, they will be automatically sourced.
diff --git a/docs/markdown/Windows-module.md b/docs/markdown/Windows-module.md
index 06b3eb2..3d24d76 100644
--- a/docs/markdown/Windows-module.md
+++ b/docs/markdown/Windows-module.md
@@ -8,6 +8,7 @@ Windows.
### compile_resources
```
+ windows = import('windows')
windows.compile_resources(...(string | File | CustomTarget | CustomTargetIndex),
args: []string,
depend_files: [](string | File),
diff --git a/docs/markdown/snippets/compiler_prefix_property_array.md b/docs/markdown/snippets/compiler_prefix_property_array.md
new file mode 100644
index 0000000..75b7156
--- /dev/null
+++ b/docs/markdown/snippets/compiler_prefix_property_array.md
@@ -0,0 +1,18 @@
+## Compiler check functions `prefix` kwargs accepts arrays
+
+The `prefix` kwarg that most compiler check functions support
+now accepts an array in addition to a string. The elements of the
+array will be concatenated separated by a newline.
+
+This makes it more readable to write checks that need multiple headers
+to be included:
+
+```meson
+cc.check_header('GL/wglew.h', prefix : ['#include <windows.h>', '#include <GL/glew.h>'])
+```
+
+instead of
+
+```meson
+cc.check_header('GL/wglew.h', prefix : '#include <windows.h>\n#include <GL/glew.h>'])
+```
diff --git a/docs/markdown/snippets/devenv.md b/docs/markdown/snippets/devenv.md
new file mode 100644
index 0000000..d8a38ba
--- /dev/null
+++ b/docs/markdown/snippets/devenv.md
@@ -0,0 +1,15 @@
+## Developer environment improvements
+
+When cross compiling, the developer environment now sets all environment
+variables for the HOST machine. It now also sets `QEMU_LD_PREFIX` to the
+`sys_root` value from cross file if property is defined. That means that cross
+compiled executables can often be run transparently on the build machine, for
+example when cross compiling for aarch64 linux from x86_64 linux.
+
+A new argument `--workdir` has been added, by default it is set to build
+directory. For example, `meson devenv -C builddir --workdir .` can be used to
+remain in the current dir (often source dir) instead.
+
+`--dump` now prints shell commands like `FOO="/prepend/path:$FOO:/append/path"`,
+using the litteral `$FOO` instead of current value of `FOO` from environment.
+This makes easier to evaluate those expressions in a different environment.
diff --git a/docs/yaml/objects/compiler.yaml b/docs/yaml/objects/compiler.yaml
index e10e8fe..01283cd 100644
--- a/docs/yaml/objects/compiler.yaml
+++ b/docs/yaml/objects/compiler.yaml
@@ -78,13 +78,15 @@ methods:
description: You have found a bug if you can see this!
kwargs:
prefix:
- type: str
+ type: str | list[str]
description: |
Used to add `#include`s and other things that are required
- for the symbol to be declared. System definitions should be
- passed via compiler args (eg: `_GNU_SOURCE` is often required for
- some symbols to be exposed on Linux, and it should be passed via
- `args` keyword argument).
+ for the symbol to be declared. Since 1.0.0 an array is accepted
+ too. When an array is passed, the items are concatenated together
+ separated by a newline.
+ System definitions should be passed via compiler args
+ (eg: `_GNU_SOURCE` is often required for some symbols to be exposed
+ on Linux, and it should be passed via `args` keyword argument).
- name: _no_builtin_args
returns: void
diff --git a/man/meson.1 b/man/meson.1
index 4205bc9..7e7f486 100644
--- a/man/meson.1
+++ b/man/meson.1
@@ -1,4 +1,4 @@
-.TH MESON "1" "November 2022" "meson 0.64.0" "User Commands"
+.TH MESON "1" "December 2022" "meson 1.0.0" "User Commands"
.SH NAME
meson - a high productivity build system
.SH DESCRIPTION
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 747d80e..27004f8 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -1108,10 +1108,7 @@ class Backend:
break
is_cross = self.environment.is_cross_build(test_for_machine)
- if is_cross and self.environment.need_exe_wrapper():
- exe_wrapper = self.environment.get_exe_wrapper()
- else:
- exe_wrapper = None
+ exe_wrapper = self.environment.get_exe_wrapper()
machine = self.environment.machines[exe.for_machine]
if machine.is_windows() or machine.is_cygwin():
extra_bdeps: T.List[T.Union[build.BuildTarget, build.CustomTarget]] = []
@@ -1850,14 +1847,12 @@ class Backend:
env = build.EnvironmentVariables()
extra_paths = set()
library_paths = set()
+ build_machine = self.environment.machines[MachineChoice.BUILD]
host_machine = self.environment.machines[MachineChoice.HOST]
- need_exe_wrapper = self.environment.need_exe_wrapper()
- need_wine = need_exe_wrapper and host_machine.is_windows()
+ need_wine = not build_machine.is_windows() and host_machine.is_windows()
for t in self.build.get_targets().values():
- cross_built = not self.environment.machines.matches_build_machine(t.for_machine)
- can_run = not cross_built or not need_exe_wrapper or need_wine
in_default_dir = t.should_install() and not t.get_install_dir()[2]
- if not can_run or not in_default_dir:
+ if t.for_machine != MachineChoice.HOST or not in_default_dir:
continue
tdir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(t))
if isinstance(t, build.Executable):
@@ -1874,18 +1869,23 @@ class Backend:
# LD_LIBRARY_PATH. This allows running system applications using
# that library.
library_paths.add(tdir)
+ if need_wine:
+ # Executable paths should be in both PATH and WINEPATH.
+ # - Having them in PATH makes bash completion find it,
+ # and make running "foo.exe" find it when wine-binfmt is installed.
+ # - Having them in WINEPATH makes "wine foo.exe" find it.
+ library_paths.update(extra_paths)
if library_paths:
- if host_machine.is_windows() or host_machine.is_cygwin():
+ if need_wine:
+ env.prepend('WINEPATH', list(library_paths), separator=';')
+ elif host_machine.is_windows() or host_machine.is_cygwin():
extra_paths.update(library_paths)
elif host_machine.is_darwin():
env.prepend('DYLD_LIBRARY_PATH', list(library_paths))
else:
env.prepend('LD_LIBRARY_PATH', list(library_paths))
if extra_paths:
- if need_wine:
- env.prepend('WINEPATH', list(extra_paths), separator=';')
- else:
- env.prepend('PATH', list(extra_paths))
+ env.prepend('PATH', list(extra_paths))
return env
def compiler_to_generator(self, target: build.BuildTarget,
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 339867d..c583024 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -314,6 +314,7 @@ class NinjaBuildElement:
self.orderdeps = OrderedSet()
self.elems = []
self.all_outputs = all_outputs
+ self.output_errors = ''
def add_dep(self, dep):
if isinstance(dep, list):
@@ -362,7 +363,8 @@ class NinjaBuildElement:
self.rule.refcount += 1
def write(self, outfile):
- self.check_outputs()
+ if self.output_errors:
+ raise MesonException(self.output_errors)
ins = ' '.join([ninja_quote(i, True) for i in self.infilenames])
outs = ' '.join([ninja_quote(i, True) for i in self.outfilenames])
implicit_outs = ' '.join([ninja_quote(i, True) for i in self.implicit_outfilenames])
@@ -421,7 +423,7 @@ class NinjaBuildElement:
def check_outputs(self):
for n in self.outfilenames:
if n in self.all_outputs:
- raise MesonException(f'Multiple producers for Ninja target "{n}". Please rename your targets.')
+ self.output_errors = f'Multiple producers for Ninja target "{n}". Please rename your targets.'
self.all_outputs[n] = True
@dataclass
@@ -1283,6 +1285,7 @@ class NinjaBackend(backends.Backend):
self.ruledict[rule.name] = rule
def add_build(self, build):
+ build.check_outputs()
self.build_elements.append(build)
if build.rulename != 'phony':
@@ -3331,7 +3334,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
def generate_scanbuild(self):
if not environment.detect_scanbuild():
return
- if ('', 'scan-build') in self.build.run_target_names:
+ if 'scan-build' in self.all_outputs:
return
cmd = self.environment.get_build_command() + \
['--internal', 'scanbuild', self.environment.source_dir, self.environment.build_dir] + \
@@ -3352,8 +3355,6 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
return
if target_name in self.all_outputs:
return
- if ('', target_name) in self.build.run_target_names:
- return
cmd = self.environment.get_build_command() + \
['--internal', 'clang' + name, self.environment.source_dir, self.environment.build_dir] + \
extra_args
@@ -3378,8 +3379,6 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
import shutil
if not shutil.which(tool):
return
- if ('', target_name) in self.build.run_target_names:
- return
if target_name in self.all_outputs:
return
cmd = self.environment.get_build_command() + \
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 0ef5122..2fd5626 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -236,7 +236,6 @@ class Build:
self.environment = environment
self.projects = {}
self.targets: 'T.OrderedDict[str, T.Union[CustomTarget, BuildTarget]]' = OrderedDict()
- self.run_target_names: T.Set[T.Tuple[str, str]] = set()
self.global_args: PerMachine[T.Dict[str, T.List[str]]] = PerMachine({}, {})
self.global_link_args: PerMachine[T.Dict[str, T.List[str]]] = PerMachine({}, {})
self.projects_args: PerMachine[T.Dict[str, T.Dict[str, T.List[str]]]] = PerMachine({}, {})
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index d94e9ac..b90a567 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -53,7 +53,7 @@ if T.TYPE_CHECKING:
#
# Pip requires that RCs are named like this: '0.1.0.rc1'
# But the corresponding Git tag needs to be '0.1.0rc1'
-version = '0.99.99'
+version = '1.0.0.rc1'
backendlist = ['ninja', 'vs', 'vs2010', 'vs2012', 'vs2013', 'vs2015', 'vs2017', 'vs2019', 'vs2022', 'xcode']
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index a9df75e..9691cf1 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -25,9 +25,7 @@ from .mesonlib import (
search_version, MesonBugException
)
from . import mlog
-from .programs import (
- ExternalProgram, EmptyExternalProgram
-)
+from .programs import ExternalProgram
from .envconfig import (
BinaryTable, MachineInfo, Properties, known_cpu_families, CMakeVariables,
@@ -852,7 +850,7 @@ class Environment:
return value
return not machine_info_can_run(self.machines[for_machine])
- def get_exe_wrapper(self) -> ExternalProgram:
+ def get_exe_wrapper(self) -> T.Optional[ExternalProgram]:
if not self.need_exe_wrapper():
- return EmptyExternalProgram()
+ return None
return self.exe_wrapper
diff --git a/mesonbuild/interpreter/compiler.py b/mesonbuild/interpreter/compiler.py
index 7397321..8b6efd2 100644
--- a/mesonbuild/interpreter/compiler.py
+++ b/mesonbuild/interpreter/compiler.py
@@ -142,7 +142,13 @@ _INCLUDE_DIRS_KW: KwargInfo[T.List[build.IncludeDirs]] = KwargInfo(
default=[],
listify=True,
)
-_PREFIX_KW = KwargInfo('prefix', str, default='')
+_PREFIX_KW: KwargInfo[str] = KwargInfo(
+ 'prefix',
+ (str, ContainerTypeInfo(list, str)),
+ default='',
+ since_values={list: '1.0.0'},
+ convertor=lambda x: '\n'.join(x) if isinstance(x, list) else x)
+
_NO_BUILTIN_ARGS_KW = KwargInfo('no_builtin_args', bool, default=False)
_NAME_KW = KwargInfo('name', str, default='')
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py
index 552f51c..59cb6e0 100644
--- a/mesonbuild/interpreter/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -1990,9 +1990,6 @@ class Interpreter(InterpreterBase, HoldableObject):
tg = build.RunTarget(name, all_args, kwargs['depends'], self.subdir, self.subproject, self.environment,
kwargs['env'])
self.add_target(name, tg)
- full_name = (self.subproject, name)
- assert full_name not in self.build.run_target_names
- self.build.run_target_names.add(full_name)
return tg
@FeatureNew('alias_target', '0.52.0')
diff --git a/mesonbuild/mdevenv.py b/mesonbuild/mdevenv.py
index b75d18b..b25d73b 100644
--- a/mesonbuild/mdevenv.py
+++ b/mesonbuild/mdevenv.py
@@ -6,7 +6,8 @@ import itertools
from pathlib import Path
from . import build, minstall, dependencies
-from .mesonlib import MesonException, RealPathAction, is_windows, setup_vsenv, OptionKey, quote_arg, get_wine_shortpath
+from .mesonlib import (MesonException, is_windows, setup_vsenv, OptionKey,
+ get_wine_shortpath, MachineChoice)
from . import mlog
import typing as T
@@ -16,8 +17,10 @@ if T.TYPE_CHECKING:
POWERSHELL_EXES = {'pwsh.exe', 'powershell.exe'}
def add_arguments(parser: argparse.ArgumentParser) -> None:
- parser.add_argument('-C', dest='wd', action=RealPathAction,
- help='Directory to cd into before running')
+ parser.add_argument('-C', dest='builddir', type=Path, default='.',
+ help='Path to build directory')
+ parser.add_argument('--workdir', '-w', type=Path, default=None,
+ help='Directory to cd into before running (default: builddir, Since 1.0.0)')
parser.add_argument('--dump', action='store_true',
help='Only print required environment (Since 0.62.0)')
parser.add_argument('devcmd', nargs=argparse.REMAINDER, metavar='command',
@@ -45,15 +48,19 @@ def reduce_winepath(env: T.Dict[str, str]) -> None:
env['WINEPATH'] = get_wine_shortpath([winecmd], winepath.split(';'))
mlog.log('Meson detected wine and has set WINEPATH accordingly')
-def get_env(b: build.Build) -> T.Tuple[T.Dict[str, str], T.Set[str]]:
+def get_env(b: build.Build, dump: bool) -> T.Tuple[T.Dict[str, str], T.Set[str]]:
extra_env = build.EnvironmentVariables()
extra_env.set('MESON_DEVENV', ['1'])
extra_env.set('MESON_PROJECT_NAME', [b.project_name])
- env = os.environ.copy()
+ sysroot = b.environment.properties[MachineChoice.HOST].get_sys_root()
+ if sysroot:
+ extra_env.set('QEMU_LD_PREFIX', [sysroot])
+
+ env = {} if dump else os.environ.copy()
varnames = set()
for i in itertools.chain(b.devenv, {extra_env}):
- env = i.get_env(env)
+ env = i.get_env(env, dump)
varnames |= i.get_names()
reduce_winepath(env)
@@ -90,7 +97,7 @@ def add_gdb_auto_load(autoload_path: Path, gdb_helper: str, fname: Path) -> None
except (FileExistsError, shutil.SameFileError):
pass
-def write_gdb_script(privatedir: Path, install_data: 'InstallData') -> None:
+def write_gdb_script(privatedir: Path, install_data: 'InstallData', workdir: Path) -> None:
if not shutil.which('gdb'):
return
bdir = privatedir.parent
@@ -120,26 +127,44 @@ def write_gdb_script(privatedir: Path, install_data: 'InstallData') -> None:
gdbinit_path.write_text(gdbinit_line, encoding='utf-8')
first_time = True
if first_time:
- mlog.log('Meson detected GDB helpers and added config in', mlog.bold(str(gdbinit_path)))
+ gdbinit_path = gdbinit_path.resolve()
+ workdir_path = workdir.resolve()
+ rel_path = gdbinit_path.relative_to(workdir_path)
+ mlog.log('Meson detected GDB helpers and added config in', mlog.bold(str(rel_path)))
+ mlog.log('To load it automatically you might need to:')
+ mlog.log(' - Add', mlog.bold(f'add-auto-load-safe-path {gdbinit_path.parent}'),
+ 'in', mlog.bold('~/.gdbinit'))
+ if gdbinit_path.parent != workdir_path:
+ mlog.log(' - Change current workdir to', mlog.bold(str(rel_path.parent)),
+ 'or use', mlog.bold(f'--init-command {rel_path}'))
def run(options: argparse.Namespace) -> int:
- privatedir = Path(options.wd) / 'meson-private'
+ privatedir = Path(options.builddir) / 'meson-private'
buildfile = privatedir / 'build.dat'
if not buildfile.is_file():
- raise MesonException(f'Directory {options.wd!r} does not seem to be a Meson build directory.')
- b = build.load(options.wd)
+ raise MesonException(f'Directory {options.builddir!r} does not seem to be a Meson build directory.')
+ b = build.load(options.builddir)
+ workdir = options.workdir or options.builddir
- devenv, varnames = get_env(b)
+ devenv, varnames = get_env(b, options.dump)
if options.dump:
if options.devcmd:
raise MesonException('--dump option does not allow running other command.')
for name in varnames:
- print(f'{name}={quote_arg(devenv[name])}')
+ print(f'{name}="{devenv[name]}"')
print(f'export {name}')
return 0
+ if b.environment.need_exe_wrapper():
+ m = 'An executable wrapper could be required'
+ exe_wrapper = b.environment.get_exe_wrapper()
+ if exe_wrapper:
+ cmd = ' '.join(exe_wrapper.get_command())
+ m += f': {cmd}'
+ mlog.log(m)
+
install_data = minstall.load_install_data(str(privatedir / 'install.dat'))
- write_gdb_script(privatedir, install_data)
+ write_gdb_script(privatedir, install_data, workdir)
setup_vsenv(b.need_vsenv)
@@ -182,7 +207,7 @@ def run(options: argparse.Namespace) -> int:
try:
return subprocess.call(args, close_fds=False,
env=devenv,
- cwd=options.wd)
+ cwd=workdir)
except subprocess.CalledProcessError as e:
return e.returncode
except FileNotFoundError:
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index 1d5e746..d0d7dbf 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -39,7 +39,7 @@ from ..interpreterbase.decorators import typed_pos_args
from ..mesonlib import (
MachineChoice, MesonException, OrderedSet, Popen_safe, join_args,
)
-from ..programs import OverrideProgram, EmptyExternalProgram
+from ..programs import OverrideProgram
from ..scripts.gettext import read_linguas
if T.TYPE_CHECKING:
@@ -1464,9 +1464,8 @@ class GnomeModule(ExtensionModule):
t_args.append(f'--{program_name}={path}')
if namespace:
t_args.append('--namespace=' + namespace)
- # if not need_exe_wrapper, we get an EmptyExternalProgram. If none provided, we get NoneType
exe_wrapper = state.environment.get_exe_wrapper()
- if not isinstance(exe_wrapper, (NoneType, EmptyExternalProgram)):
+ if exe_wrapper:
t_args.append('--run=' + ' '.join(exe_wrapper.get_command()))
t_args.append(f'--htmlargs={"@@".join(kwargs["html_args"])}')
t_args.append(f'--scanargs={"@@".join(kwargs["scan_args"])}')
diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py
index 6bbfdb9..f74d10e 100644
--- a/mesonbuild/modules/python.py
+++ b/mesonbuild/modules/python.py
@@ -91,7 +91,6 @@ mod_kwargs -= {'name_prefix', 'name_suffix'}
class _PythonDependencyBase(_Base):
def __init__(self, python_holder: 'PythonInstallation', embed: bool):
- self.name = 'python' # override the name from the "real" dependency lookup
self.embed = embed
self.version: str = python_holder.version
self.platform = python_holder.platform
diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py
index f13e755..00812c1 100644
--- a/mesonbuild/mtest.py
+++ b/mesonbuild/mtest.py
@@ -275,6 +275,7 @@ TYPE_TAPResult = T.Union['TAPParser.Test',
'TAPParser.Error',
'TAPParser.Version',
'TAPParser.Plan',
+ 'TAPParser.UnknownLine',
'TAPParser.Bailout']
class TAPParser:
@@ -299,6 +300,10 @@ class TAPParser:
class Error(T.NamedTuple):
message: str
+ class UnknownLine(T.NamedTuple):
+ message: str
+ lineno: int
+
class Version(T.NamedTuple):
version: int
@@ -380,7 +385,7 @@ class TAPParser:
self.state = self._MAIN
assert self.state == self._MAIN
- if line.startswith('#'):
+ if not line or line.startswith('#'):
return
m = self._RE_TEST.match(line)
@@ -434,6 +439,9 @@ class TAPParser:
else:
yield self.Version(version=self.version)
return
+
+ # unknown syntax
+ yield self.UnknownLine(line, self.lineno)
else:
# end of file
if self.state == self._YAML:
@@ -673,6 +681,11 @@ class ConsoleLogger(TestLogger):
flush=True)
if result.verbose or result.res.is_bad():
self.print_log(harness, result)
+ if result.warnings:
+ print(flush=True)
+ for w in result.warnings:
+ print(w, flush=True)
+ print(flush=True)
if result.verbose or result.res.is_bad():
print(flush=True)
@@ -899,6 +912,7 @@ class TestRun:
self.junit = None # type: T.Optional[et.ElementTree]
self.is_parallel = is_parallel
self.verbose = verbose
+ self.warnings = [] # type: T.List[str]
def start(self, cmd: T.List[str]) -> None:
self.res = TestResult.RUNNING
@@ -1041,9 +1055,13 @@ class TestRunTAP(TestRun):
async def parse(self, harness: 'TestHarness', lines: T.AsyncIterator[str]) -> None:
res = None
+ warnings = [] # type: T.List[TAPParser.UnknownLine]
+ version: int
async for i in TAPParser().parse_async(lines):
- if isinstance(i, TAPParser.Bailout):
+ if isinstance(i, TAPParser.Version):
+ version = i.version
+ elif isinstance(i, TAPParser.Bailout):
res = TestResult.ERROR
harness.log_subtest(self, i.message, res)
elif isinstance(i, TAPParser.Test):
@@ -1051,10 +1069,23 @@ class TestRunTAP(TestRun):
if i.result.is_bad():
res = TestResult.FAIL
harness.log_subtest(self, i.name or f'subtest {i.number}', i.result)
+ elif isinstance(i, TAPParser.UnknownLine):
+ warnings.append(i)
elif isinstance(i, TAPParser.Error):
self.additional_error += 'TAP parsing error: ' + i.message
res = TestResult.ERROR
+ if warnings:
+ unknown = str(mlog.yellow('UNKNOWN'))
+ width = len(str(max(i.lineno for i in warnings)))
+ for w in warnings:
+ self.warnings.append(f'stdout: {w.lineno:{width}}: {unknown}: {w.message}')
+ if version > 13:
+ self.warnings.append('Unknown TAP output lines have been ignored. Please open a feature request to\n'
+ 'implement them, or prefix them with a # if they are not TAP syntax.')
+ else:
+ self.warnings.append(str(mlog.red('ERROR')) + ': Unknown TAP output lines for a supported TAP version.\n'
+ 'This is probably a bug in the test; if they are not TAP syntax, prefix them with a #')
if all(t.result is TestResult.SKIP for t in self.results):
# This includes the case where self.results is empty
res = TestResult.SKIP
diff --git a/mesonbuild/programs.py b/mesonbuild/programs.py
index 458faba..1d616aa 100644
--- a/mesonbuild/programs.py
+++ b/mesonbuild/programs.py
@@ -345,25 +345,6 @@ class NonExistingExternalProgram(ExternalProgram): # lgtm [py/missing-call-to-i
return False
-class EmptyExternalProgram(ExternalProgram): # lgtm [py/missing-call-to-init]
- '''
- A program object that returns an empty list of commands. Used for cases
- such as a cross file exe_wrapper to represent that it's not required.
- '''
-
- def __init__(self) -> None:
- self.name = None
- self.command = []
- self.path = None
-
- def __repr__(self) -> str:
- r = '<{} {!r} -> {!r}>'
- return r.format(self.__class__.__name__, self.name, self.command)
-
- def found(self) -> bool:
- return True
-
-
class OverrideProgram(ExternalProgram):
"""A script overriding a program."""
diff --git a/mesonbuild/scripts/clangformat.py b/mesonbuild/scripts/clangformat.py
index 9dc5466..f2f6a77 100644
--- a/mesonbuild/scripts/clangformat.py
+++ b/mesonbuild/scripts/clangformat.py
@@ -18,17 +18,25 @@ from pathlib import Path
from .run_tool import run_tool
from ..environment import detect_clangformat
+from ..mesonlib import version_compare
+from ..programs import ExternalProgram
import typing as T
def run_clang_format(fname: Path, exelist: T.List[str], check: bool) -> subprocess.CompletedProcess:
+ clangformat_10 = False
if check:
- original = fname.read_bytes()
+ cformat_ver = ExternalProgram('clang-format', exelist).get_version()
+ if version_compare(cformat_ver, '>=10'):
+ clangformat_10 = True
+ exelist = exelist + ['--dry-run', '--Werror']
+ else:
+ original = fname.read_bytes()
before = fname.stat().st_mtime
ret = subprocess.run(exelist + ['-style=file', '-i', str(fname)])
after = fname.stat().st_mtime
if before != after:
print('File reformatted: ', fname)
- if check:
+ if check and not clangformat_10:
# Restore the original if only checking.
fname.write_bytes(original)
ret.returncode = 1
diff --git a/mesonbuild/utils/core.py b/mesonbuild/utils/core.py
index ed413ca..5450cdc 100644
--- a/mesonbuild/utils/core.py
+++ b/mesonbuild/utils/core.py
@@ -119,23 +119,24 @@ class EnvironmentVariables(HoldableObject):
self.envvars.append((self._prepend, name, values, separator))
@staticmethod
- def _set(env: T.Dict[str, str], name: str, values: T.List[str], separator: str) -> str:
+ def _set(env: T.Dict[str, str], name: str, values: T.List[str], separator: str, default_value: T.Optional[str]) -> str:
return separator.join(values)
@staticmethod
- def _append(env: T.Dict[str, str], name: str, values: T.List[str], separator: str) -> str:
- curr = env.get(name)
+ def _append(env: T.Dict[str, str], name: str, values: T.List[str], separator: str, default_value: T.Optional[str]) -> str:
+ curr = env.get(name, default_value)
return separator.join(values if curr is None else [curr] + values)
@staticmethod
- def _prepend(env: T.Dict[str, str], name: str, values: T.List[str], separator: str) -> str:
- curr = env.get(name)
+ def _prepend(env: T.Dict[str, str], name: str, values: T.List[str], separator: str, default_value: T.Optional[str]) -> str:
+ curr = env.get(name, default_value)
return separator.join(values if curr is None else values + [curr])
- def get_env(self, full_env: T.MutableMapping[str, str]) -> T.Dict[str, str]:
+ def get_env(self, full_env: T.MutableMapping[str, str], dump: bool = False) -> T.Dict[str, str]:
env = full_env.copy()
for method, name, values, separator in self.envvars:
- env[name] = method(env, name, values, separator)
+ default_value = f'${name}' if dump else None
+ env[name] = method(env, name, values, separator, default_value)
return env
diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py
index 33c81c6..68ea0b7 100644
--- a/mesonbuild/utils/universal.py
+++ b/mesonbuild/utils/universal.py
@@ -1446,7 +1446,7 @@ def Popen_safe_legacy(args: T.List[str], write: T.Optional[str] = None,
input_ = write.encode('utf-8')
o, e = p.communicate(input_)
if o is not None:
- if sys.stdout.encoding:
+ if sys.stdout.encoding is not None:
o = o.decode(encoding=sys.stdout.encoding, errors='replace').replace('\r\n', '\n')
else:
o = o.decode(errors='replace').replace('\r\n', '\n')
diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py
index e8c955a..d949f43 100644
--- a/mesonbuild/wrap/wrap.py
+++ b/mesonbuild/wrap/wrap.py
@@ -582,8 +582,11 @@ class Resolver:
verbose_git(['fetch', self.wrap.get('url'), revno], self.dirname, check=True)
verbose_git(checkout_cmd, self.dirname, check=True)
else:
- verbose_git(['-c', 'advice.detachedHead=false', 'clone', *depth_option, '--branch', revno, self.wrap.get('url'),
- self.directory], self.subdir_root, check=True)
+ args = ['-c', 'advice.detachedHead=false', 'clone', *depth_option]
+ if revno.lower() != 'head':
+ args += ['--branch', revno]
+ args += [self.wrap.get('url'), self.directory]
+ verbose_git(args, self.subdir_root, check=True)
if self.wrap.values.get('clone-recursive', '').lower() == 'true':
verbose_git(['submodule', 'update', '--init', '--checkout', '--recursive', *depth_option],
self.dirname, check=True)
diff --git a/test cases/common/132 get define/meson.build b/test cases/common/132 get define/meson.build
index df3d02a..02e5a0c 100644
--- a/test cases/common/132 get define/meson.build
+++ b/test cases/common/132 get define/meson.build
@@ -66,6 +66,13 @@ foreach lang : ['c', 'cpp']
have = cc.get_define('MESON_FAIL_VALUE')
assert(have == '', 'MESON_FAIL_VALUE value is "@0@" instead of ""'.format(have))
+ # Check if prefix array works properly and has the expected order
+ have = cc.get_define('MESON_FAIL_VALUE', prefix: ['#define MESON_FAIL_VALUE 1', '#undef MESON_FAIL_VALUE'])
+ assert(have == '', 'MESON_FAIL_VALUE value is "@0@" instead of ""'.format(have))
+
+ have = cc.get_define('MESON_SUCCESS_VALUE', prefix: ['#undef MESON_SUCCESS_VALUE', '#define MESON_SUCCESS_VALUE 1'])
+ assert(have == '1', 'MESON_SUCCESS_VALUE value is "@0@" instead of ""'.format(have))
+
# This is used in the test_preprocessor_checks_CPPFLAGS() unit test.
have = cc.get_define('MESON_TEST_DEFINE_VALUE')
expect = get_option('MESON_TEST_DEFINE_VALUE')
diff --git a/test cases/common/51 run target/.clang-format b/test cases/common/51 run target/.clang-format
new file mode 100644
index 0000000..9b3aa8b
--- /dev/null
+++ b/test cases/common/51 run target/.clang-format
@@ -0,0 +1 @@
+BasedOnStyle: LLVM
diff --git a/test cases/common/51 run target/.clang-tidy b/test cases/common/51 run target/.clang-tidy
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test cases/common/51 run target/.clang-tidy
diff --git a/test cases/common/51 run target/meson.build b/test cases/common/51 run target/meson.build
index 85d30f0..dbb6732 100644
--- a/test cases/common/51 run target/meson.build
+++ b/test cases/common/51 run target/meson.build
@@ -78,8 +78,13 @@ custom_target('configure_script_ct',
run_target('ctags',
command : converter)
-run_target('clang-format',
- command : converter)
+clangf = run_target('clang-format',
+ command : [converter, files('.clang-format'), meson.current_build_dir() / 'clang-format'])
+custom_target('clang-tidy',
+ input: '.clang-tidy',
+ output: 'clang-tidy',
+ command : [converter, '@INPUT@', '@OUTPUT@'])
+alias_target('clang-format-check', clangf)
# Check we can pass env to the program. Also check some string substitutions
# that were added in 0.57.0 but not documented. This is documented behaviour
diff --git a/unittests/subprojectscommandtests.py b/unittests/subprojectscommandtests.py
index edd6ac1..bca124d 100644
--- a/unittests/subprojectscommandtests.py
+++ b/unittests/subprojectscommandtests.py
@@ -103,15 +103,20 @@ class SubprojectsCommandTests(BasePlatformTests):
self._git_remote(['commit', '--no-gpg-sign', '--allow-empty', '-m', f'tag {tag} commit'], name)
self._git_remote(['tag', '--no-sign', tag], name)
- def _wrap_create_git(self, name, revision='master'):
+ def _wrap_create_git(self, name, revision='master', depth=None):
path = self.root_dir / name
with open(str((self.subprojects_dir / name).with_suffix('.wrap')), 'w', encoding='utf-8') as f:
+ if depth is None:
+ depth_line = ''
+ else:
+ depth_line = 'depth = {}'.format(depth)
f.write(textwrap.dedent(
'''
[wrap-git]
url={}
revision={}
- '''.format(os.path.abspath(str(path)), revision)))
+ {}
+ '''.format(os.path.abspath(str(path)), revision, depth_line)))
def _wrap_create_file(self, name, tarball='dummy.tar.gz'):
path = self.root_dir / tarball
@@ -205,6 +210,15 @@ class SubprojectsCommandTests(BasePlatformTests):
self._subprojects_cmd(['update', '--reset'])
self.assertEqual(self._git_local_commit(subp_name), self._git_remote_commit(subp_name))
+ # Create a fake remote git repository and a wrap file targeting
+ # HEAD and depth = 1. Checks that "meson subprojects download" works.
+ subp_name = 'sub3'
+ self._git_create_remote_repo(subp_name)
+ self._wrap_create_git(subp_name, revision='head', depth='1')
+ self._subprojects_cmd(['download'])
+ self.assertPathExists(str(self.subprojects_dir / subp_name))
+ self._git_config(self.subprojects_dir / subp_name)
+
@skipIfNoExecutable('true')
def test_foreach(self):
self._create_project(self.subprojects_dir / 'sub_file')
diff --git a/unittests/taptests.py b/unittests/taptests.py
index 477d797..6c2ccb0 100644
--- a/unittests/taptests.py
+++ b/unittests/taptests.py
@@ -37,6 +37,9 @@ class TAPParserTests(unittest.TestCase):
def assert_error(self, events):
self.assertEqual(type(next(events)), TAPParser.Error)
+ def assert_unexpected(self, events, **kwargs):
+ self.assertEqual(next(events), TAPParser.UnknownLine(**kwargs))
+
def assert_bailout(self, events, **kwargs):
self.assertEqual(next(events), TAPParser.Bailout(**kwargs))
@@ -255,6 +258,7 @@ class TAPParserTests(unittest.TestCase):
def test_unexpected(self):
events = self.parse_tap('1..1\ninvalid\nok 1')
self.assert_plan(events, num_tests=1, late=False)
+ self.assert_unexpected(events, message='invalid', lineno=2)
self.assert_test(events, number=1, name='', result=TestResult.OK)
self.assert_last(events)