aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/markdown/snippets/fix_backend_startup_project.md4
-rw-r--r--docs/yaml/functions/shared_library.yaml7
-rw-r--r--docs/yaml/functions/shared_module.yaml9
-rw-r--r--mesonbuild/backend/backends.py23
-rw-r--r--mesonbuild/backend/ninjabackend.py2
-rw-r--r--mesonbuild/backend/vs2010backend.py13
-rw-r--r--mesonbuild/backend/vs2012backend.py2
-rw-r--r--mesonbuild/backend/vs2013backend.py2
-rw-r--r--mesonbuild/backend/vs2015backend.py2
-rw-r--r--mesonbuild/backend/vs2017backend.py4
-rw-r--r--mesonbuild/backend/vs2019backend.py2
-rw-r--r--mesonbuild/backend/vs2022backend.py2
-rw-r--r--mesonbuild/build.py23
-rw-r--r--mesonbuild/compilers/d.py2
-rw-r--r--mesonbuild/compilers/mixins/c2000.py12
-rw-r--r--mesonbuild/compilers/mixins/visualstudio.py2
-rw-r--r--mesonbuild/dependencies/misc.py2
-rw-r--r--mesonbuild/interpreter/interpreter.py259
-rw-r--r--mesonbuild/interpreter/interpreterobjects.py3
-rw-r--r--mesonbuild/interpreter/kwargs.py48
-rw-r--r--mesonbuild/interpreter/mesonmain.py2
-rw-r--r--mesonbuild/interpreter/type_checking.py11
-rw-r--r--mesonbuild/mesonlib/universal.py2
-rw-r--r--mesonbuild/minstall.py4
-rw-r--r--mesonbuild/modules/gnome.py2
-rw-r--r--mesonbuild/modules/qt.py2
-rw-r--r--mesonbuild/modules/unstable_rust.py10
-rw-r--r--test cases/failing/106 feature require.bis/meson.build2
-rw-r--r--test cases/failing/106 feature require.bis/meson_options.txt1
-rw-r--r--test cases/failing/106 feature require.bis/test.json8
-rw-r--r--test cases/failing/115 nonsensical bindgen/meson.build20
-rw-r--r--test cases/failing/115 nonsensical bindgen/src/header.h8
-rw-r--r--test cases/failing/115 nonsensical bindgen/src/source.c8
-rw-r--r--test cases/failing/115 nonsensical bindgen/test.json8
-rw-r--r--test cases/failing/75 link with shared module on osx/test.json2
-rw-r--r--test cases/unit/1 soname/main.c5
-rw-r--r--test cases/unit/1 soname/meson.build13
-rw-r--r--test cases/unit/17 prebuilt shared/meson.build27
-rw-r--r--test cases/unit/17 prebuilt shared/meson_options.txt1
-rw-r--r--test cases/unit/17 prebuilt shared/rejected.c8
-rw-r--r--test cases/unit/17 prebuilt shared/rejected.h6
-rw-r--r--test cases/unit/17 prebuilt shared/rejected_main.c6
-rw-r--r--unittests/allplatformstests.py58
-rw-r--r--unittests/internaltests.py19
-rw-r--r--unittests/linuxliketests.py18
45 files changed, 495 insertions, 179 deletions
diff --git a/docs/markdown/snippets/fix_backend_startup_project.md b/docs/markdown/snippets/fix_backend_startup_project.md
new file mode 100644
index 0000000..8269ef6
--- /dev/null
+++ b/docs/markdown/snippets/fix_backend_startup_project.md
@@ -0,0 +1,4 @@
+## backend_startup_project
+
+`backend_startup_project` will no longer erase the last project in a VS
+solution if it is not the specified project.
diff --git a/docs/yaml/functions/shared_library.yaml b/docs/yaml/functions/shared_library.yaml
index 46e5a1c..15ac782 100644
--- a/docs/yaml/functions/shared_library.yaml
+++ b/docs/yaml/functions/shared_library.yaml
@@ -2,13 +2,6 @@ name: shared_library
returns: lib
description: Builds a shared library with the given sources.
-notes:
- - |
- Linking to a shared module is not supported on some
- platforms, notably OSX. Consider using a
- [[shared_library]] instead, if you need to both
- `dlopen()` and link with a library.
-
posargs_inherit: _build_target_base
varargs_inherit: _build_target_base
kwargs_inherit: _build_target_base
diff --git a/docs/yaml/functions/shared_module.yaml b/docs/yaml/functions/shared_module.yaml
index 8909c2f..ff374e7 100644
--- a/docs/yaml/functions/shared_module.yaml
+++ b/docs/yaml/functions/shared_module.yaml
@@ -13,6 +13,15 @@ description: |
you will need to set the `export_dynamic` argument of the executable to
`true`.
+notes:
+ - |
+ *Linking to a shared module is deprecated, and will be an error in the future*.
+ It used to be allowed because it was the only way to have a shared-library-like target that
+ contained references to undefined symbols. However, since 0.40.0, the `override_options:`
+ [[build_target]] keyword argument can be used to create such a [[shared_library]], and shared
+ modules have other characteristics that make them incompatible with linking, such as a lack of
+ SONAME. Linking to shared modules also does not work on some platforms, such as on macOS / iOS.
+
posargs_inherit: _build_target_base
varargs_inherit: _build_target_base
kwargs_inherit: _build_target_base
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 6c15678..769ee6c 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -395,15 +395,15 @@ class Backend:
return os.path.join(self.build_to_src, target_dir)
return self.build_to_src
- def get_target_private_dir(self, target: build.Target) -> str:
+ def get_target_private_dir(self, target: T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]) -> str:
return os.path.join(self.get_target_filename(target, warn_multi_output=False) + '.p')
- def get_target_private_dir_abs(self, target: build.Target) -> str:
+ def get_target_private_dir_abs(self, target: T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]) -> str:
return os.path.join(self.environment.get_build_dir(), self.get_target_private_dir(target))
@lru_cache(maxsize=None)
def get_target_generated_dir(
- self, target: build.Target,
+ self, target: T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex],
gensrc: T.Union[build.CustomTarget, build.CustomTargetIndex, build.GeneratedList],
src: str) -> str:
"""
@@ -418,7 +418,8 @@ class Backend:
# target that the GeneratedList is used in
return os.path.join(self.get_target_private_dir(target), src)
- def get_unity_source_file(self, target: build.Target, suffix: str, number: int) -> mesonlib.File:
+ def get_unity_source_file(self, target: T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex],
+ suffix: str, number: int) -> mesonlib.File:
# There is a potential conflict here, but it is unlikely that
# anyone both enables unity builds and has a file called foo-unity.cpp.
osrc = f'{target.name}-unity{number}.{suffix}'
@@ -763,18 +764,24 @@ class Backend:
paths.append(os.path.join(self.build_to_src, rel_to_src))
else:
paths.append(libdir)
+ for i in chain(target.link_targets, target.link_whole_targets):
+ if isinstance(i, build.BuildTarget):
+ paths.extend(self.rpaths_for_bundled_shared_libraries(i, exclude_system))
return paths
- def determine_rpath_dirs(self, target: build.BuildTarget) -> T.Tuple[str, ...]:
+ # This may take other types
+ def determine_rpath_dirs(self, target: T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]
+ ) -> T.Tuple[str, ...]:
result: OrderedSet[str]
if self.environment.coredata.get_option(OptionKey('layout')) == 'mirror':
- # NEed a copy here
+ # Need a copy here
result = OrderedSet(target.get_link_dep_subdirs())
else:
result = OrderedSet()
result.add('meson-out')
- result.update(self.rpaths_for_bundled_shared_libraries(target))
- target.rpath_dirs_to_remove.update([d.encode('utf-8') for d in result])
+ if isinstance(target, build.BuildTarget):
+ result.update(self.rpaths_for_bundled_shared_libraries(target))
+ target.rpath_dirs_to_remove.update([d.encode('utf-8') for d in result])
return tuple(result)
@staticmethod
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index a1d3e50..b6621c9 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -2785,7 +2785,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
commands += linker.get_std_shared_lib_link_args()
# All shared libraries are PIC
commands += linker.get_pic_args()
- if not isinstance(target, build.SharedModule):
+ if not isinstance(target, build.SharedModule) or target.backwards_compat_want_soname:
# Add -Wl,-soname arguments on Linux, -install_name on OS X
commands += linker.get_soname_args(
self.environment, target.prefix, target.name, target.suffix,
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index 4597a5a..7a56041 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -98,6 +98,8 @@ class Vs2010Backend(backends.Backend):
super().__init__(build, interpreter)
self.name = 'vs2010'
self.project_file_version = '10.0.30319.1'
+ self.sln_file_version = '11.00'
+ self.sln_version_comment = '2010'
self.platform_toolset = None
self.vs_version = '2010'
self.windows_target_platform_version = None
@@ -348,10 +350,11 @@ class Vs2010Backend(backends.Backend):
def generate_solution(self, sln_filename, projlist):
default_projlist = self.get_build_by_default_targets()
sln_filename_tmp = sln_filename + '~'
- with open(sln_filename_tmp, 'w', encoding='utf-8') as ofile:
- ofile.write('Microsoft Visual Studio Solution File, Format '
- 'Version 11.00\n')
- ofile.write('# Visual Studio ' + self.vs_version + '\n')
+ # Note using the utf-8 BOM requires the blank line, otherwise Visual Studio Version Selector fails.
+ # Without the BOM, VSVS fails if there is a blank line.
+ with open(sln_filename_tmp, 'w', encoding='utf-8-sig') as ofile:
+ ofile.write('\nMicrosoft Visual Studio Solution File, Format Version %s\n' % self.sln_file_version)
+ ofile.write('# Visual Studio %s\n' % self.sln_version_comment)
prj_templ = 'Project("{%s}") = "%s", "%s", "{%s}"\n'
for prj in projlist:
coredata = self.environment.coredata
@@ -467,7 +470,7 @@ class Vs2010Backend(backends.Backend):
# Put the startup project first in the project list
if startup_idx:
- projlist = [projlist[startup_idx]] + projlist[0:startup_idx] + projlist[startup_idx + 1:-1]
+ projlist.insert(0, projlist.pop(startup_idx))
return projlist
diff --git a/mesonbuild/backend/vs2012backend.py b/mesonbuild/backend/vs2012backend.py
index a9ba5f4..ee2f022 100644
--- a/mesonbuild/backend/vs2012backend.py
+++ b/mesonbuild/backend/vs2012backend.py
@@ -24,6 +24,8 @@ class Vs2012Backend(Vs2010Backend):
super().__init__(build, interpreter)
self.name = 'vs2012'
self.vs_version = '2012'
+ self.sln_file_version = '12.00'
+ self.sln_version_comment = '2012'
if self.environment is not None:
# TODO: we assume host == build
comps = self.environment.coredata.compilers.host
diff --git a/mesonbuild/backend/vs2013backend.py b/mesonbuild/backend/vs2013backend.py
index 0f2c8bd..37724db 100644
--- a/mesonbuild/backend/vs2013backend.py
+++ b/mesonbuild/backend/vs2013backend.py
@@ -24,6 +24,8 @@ class Vs2013Backend(Vs2010Backend):
super().__init__(build, interpreter)
self.name = 'vs2013'
self.vs_version = '2013'
+ self.sln_file_version = '12.00'
+ self.sln_version_comment = '2013'
if self.environment is not None:
# TODO: we assume host == build
comps = self.environment.coredata.compilers.host
diff --git a/mesonbuild/backend/vs2015backend.py b/mesonbuild/backend/vs2015backend.py
index bdc1675..4952caf 100644
--- a/mesonbuild/backend/vs2015backend.py
+++ b/mesonbuild/backend/vs2015backend.py
@@ -24,6 +24,8 @@ class Vs2015Backend(Vs2010Backend):
super().__init__(build, interpreter)
self.name = 'vs2015'
self.vs_version = '2015'
+ self.sln_file_version = '12.00'
+ self.sln_version_comment = '14'
if self.environment is not None:
# TODO: we assume host == build
comps = self.environment.coredata.compilers.host
diff --git a/mesonbuild/backend/vs2017backend.py b/mesonbuild/backend/vs2017backend.py
index 452d7a6..e9f949d 100644
--- a/mesonbuild/backend/vs2017backend.py
+++ b/mesonbuild/backend/vs2017backend.py
@@ -27,6 +27,8 @@ class Vs2017Backend(Vs2010Backend):
super().__init__(build, interpreter)
self.name = 'vs2017'
self.vs_version = '2017'
+ self.sln_file_version = '12.00'
+ self.sln_version_comment = '15'
# We assume that host == build
if self.environment is not None:
comps = self.environment.coredata.compilers.host
@@ -59,4 +61,4 @@ class Vs2017Backend(Vs2010Backend):
if 'c' in file_args:
optargs = [x for x in file_args['c'] if x.startswith('/std:c')]
if optargs:
- ET.SubElement(clconf, 'LanguageStandard_C').text = optargs[0].replace("/std:c", "stdc") \ No newline at end of file
+ ET.SubElement(clconf, 'LanguageStandard_C').text = optargs[0].replace("/std:c", "stdc")
diff --git a/mesonbuild/backend/vs2019backend.py b/mesonbuild/backend/vs2019backend.py
index a87fa8a..1efadcd 100644
--- a/mesonbuild/backend/vs2019backend.py
+++ b/mesonbuild/backend/vs2019backend.py
@@ -25,6 +25,8 @@ class Vs2019Backend(Vs2010Backend):
def __init__(self, build: T.Optional[Build], interpreter: T.Optional[Interpreter]):
super().__init__(build, interpreter)
self.name = 'vs2019'
+ self.sln_file_version = '12.00'
+ self.sln_version_comment = 'Version 16'
if self.environment is not None:
comps = self.environment.coredata.compilers.host
if comps and all(c.id == 'clang-cl' for c in comps.values()):
diff --git a/mesonbuild/backend/vs2022backend.py b/mesonbuild/backend/vs2022backend.py
index 19ad090..b0925a4 100644
--- a/mesonbuild/backend/vs2022backend.py
+++ b/mesonbuild/backend/vs2022backend.py
@@ -25,6 +25,8 @@ class Vs2022Backend(Vs2010Backend):
def __init__(self, build: T.Optional[Build], interpreter: T.Optional[Interpreter]):
super().__init__(build, interpreter)
self.name = 'vs2022'
+ self.sln_file_version = '12.00'
+ self.sln_version_comment = 'Version 17'
if self.environment is not None:
comps = self.environment.coredata.compilers.host
if comps and all(c.id == 'clang-cl' for c in comps.values()):
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 18c0911..545575c 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -695,7 +695,7 @@ class BuildTarget(Target):
self.external_deps: T.List[dependencies.Dependency] = []
self.include_dirs: T.List['IncludeDirs'] = []
self.link_language = kwargs.get('link_language')
- self.link_targets: T.List[BuildTarget] = []
+ self.link_targets: T.List[T.Union['BuildTarget', 'CustomTarget', 'CustomTargetIndex']] = []
self.link_whole_targets = []
self.link_depends = []
self.added_deps = set()
@@ -1588,15 +1588,19 @@ You probably should put it in link_with instead.''')
Warn if shared modules are linked with target: (link_with) #2865
'''
for link_target in self.link_targets:
- if isinstance(link_target, SharedModule):
+ if isinstance(link_target, SharedModule) and not link_target.backwards_compat_want_soname:
if self.environment.machines[self.for_machine].is_darwin():
raise MesonException(
- 'target links against shared modules. This is not permitted on OSX')
+ f'target {self.name} links against shared module {link_target.name}. This is not permitted on OSX')
else:
- mlog.warning('target links against shared modules. This '
- 'is not recommended as it is not supported on some '
- 'platforms')
- return
+ mlog.deprecation(f'target {self.name} links against shared module {link_target.name}, which is incorrect.'
+ '\n '
+ f'This will be an error in the future, so please use shared_library() for {link_target.name} instead.'
+ '\n '
+ f'If shared_module() was used for {link_target.name} because it has references to undefined symbols,'
+ '\n '
+ 'use shared_libary() with `override_options: [\'b_lundef=false\']` instead.')
+ link_target.backwards_compat_want_soname = True
class Generator(HoldableObject):
def __init__(self, exe: T.Union['Executable', programs.ExternalProgram],
@@ -2259,6 +2263,9 @@ class SharedModule(SharedLibrary):
raise MesonException('Shared modules must not specify the soversion kwarg.')
super().__init__(name, subdir, subproject, for_machine, sources, objects, environment, kwargs)
self.typename = 'shared module'
+ # We need to set the soname in cases where build files link the module
+ # to build targets, see: https://github.com/mesonbuild/meson/issues/9492
+ self.backwards_compat_want_soname = False
def get_default_install_dir(self, environment) -> T.Tuple[str, str]:
return environment.get_shared_module_dir(), '{moduledir_shared}'
@@ -2796,7 +2803,7 @@ class Data(HoldableObject):
self.data_type = data_type
class TestSetup:
- def __init__(self, exe_wrapper: T.Optional[T.List[str]], gdb: bool,
+ def __init__(self, exe_wrapper: T.List[str], gdb: bool,
timeout_multiplier: int, env: EnvironmentVariables,
exclude_suites: T.List[str]):
self.exe_wrapper = exe_wrapper
diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py
index f550a33..4201d82 100644
--- a/mesonbuild/compilers/d.py
+++ b/mesonbuild/compilers/d.py
@@ -64,7 +64,7 @@ ldc_optimization_args = {'0': [],
'1': ['-O1'],
'2': ['-O2'],
'3': ['-O3'],
- 's': ['-Os'],
+ 's': ['-Oz'],
} # type: T.Dict[str, T.List[str]]
dmd_optimization_args = {'0': [],
diff --git a/mesonbuild/compilers/mixins/c2000.py b/mesonbuild/compilers/mixins/c2000.py
index 65261c6..ab0278e 100644
--- a/mesonbuild/compilers/mixins/c2000.py
+++ b/mesonbuild/compilers/mixins/c2000.py
@@ -49,7 +49,7 @@ c2000_optimization_args = {
c2000_debug_args = {
False: [],
- True: []
+ True: ['-g']
} # type: T.Dict[bool, T.List[str]]
@@ -106,9 +106,9 @@ class C2000Compiler(Compiler):
result = []
for i in args:
if i.startswith('-D'):
- i = '-define=' + i[2:]
+ i = '--define=' + i[2:]
if i.startswith('-I'):
- i = '-include=' + i[2:]
+ i = '--include_path=' + i[2:]
if i.startswith('-Wl,-rpath='):
continue
elif i == '--print-search-dirs':
@@ -120,8 +120,10 @@ class C2000Compiler(Compiler):
def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], build_dir: str) -> T.List[str]:
for idx, i in enumerate(parameter_list):
- if i[:9] == '-include=':
- parameter_list[idx] = i[:9] + os.path.normpath(os.path.join(build_dir, i[9:]))
+ if i[:14] == '--include_path':
+ parameter_list[idx] = i[:14] + os.path.normpath(os.path.join(build_dir, i[14:]))
+ if i[:2] == '-I':
+ parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:]))
return parameter_list
diff --git a/mesonbuild/compilers/mixins/visualstudio.py b/mesonbuild/compilers/mixins/visualstudio.py
index 4bc721f..1ea7219 100644
--- a/mesonbuild/compilers/mixins/visualstudio.py
+++ b/mesonbuild/compilers/mixins/visualstudio.py
@@ -332,6 +332,8 @@ class VisualStudioLikeCompiler(Compiler, metaclass=abc.ABCMeta):
return '14.1' # (Visual Studio 2017)
elif version < 1930:
return '14.2' # (Visual Studio 2019)
+ elif version < 1940:
+ return '14.3' # (Visual Studio 2022)
mlog.warning(f'Could not find toolset for version {self.version!r}')
return None
diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py
index eaa018a..43d7feb 100644
--- a/mesonbuild/dependencies/misc.py
+++ b/mesonbuild/dependencies/misc.py
@@ -452,7 +452,7 @@ class IconvBuiltinDependency(BuiltinDependency):
def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]):
super().__init__(name, env, kwargs)
- if self.clib_compiler.has_function('iconv_open', '', env)[0]:
+ if self.clib_compiler.has_function('iconv_open', '#include <iconv.h>', env)[0]:
self.is_found = True
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py
index d946678..2e96273 100644
--- a/mesonbuild/interpreter/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -56,9 +56,11 @@ from .type_checking import (
CT_INPUT_KW,
CT_INSTALL_DIR_KW,
CT_OUTPUT_KW,
+ DEFAULT_OPTIONS,
DEPENDS_KW,
DEPEND_FILES_KW,
DEPFILE_KW,
+ DISABLER_KW,
ENV_KW,
INSTALL_KW,
INSTALL_MODE_KW,
@@ -90,11 +92,21 @@ if T.TYPE_CHECKING:
# Input source types passed to Targets
SourceInputs = T.Union[mesonlib.File, build.GeneratedList, build.BuildTarget, build.BothLibraries,
- build.CustomTargetIndex, build.CustomTarget, build.GeneratedList, str]
- # Input source types passed to the build.Target5 classes
+ build.CustomTargetIndex, build.CustomTarget, build.GeneratedList,
+ build.ExtractedObjects, str]
+ # Input source types passed to the build.Target classes
SourceOutputs = T.Union[mesonlib.File, build.GeneratedList,
build.BuildTarget, build.CustomTargetIndex, build.CustomTarget,
- build.GeneratedList]
+ build.ExtractedObjects, build.GeneratedList]
+
+
+def _project_version_validator(value: T.Union[T.List, str, mesonlib.File, None]) -> T.Optional[str]:
+ if isinstance(value, list):
+ if len(value) != 1:
+ return 'when passed as array must have a length of 1'
+ elif not isinstance(value[0], (str, mesonlib.File)):
+ return 'when passed as array must contain a string or File'
+ return None
def stringifyUserArguments(args, quote=False):
@@ -111,19 +123,14 @@ def stringifyUserArguments(args, quote=False):
raise InvalidArguments('Function accepts only strings, integers, bools, lists, dictionaries and lists thereof.')
class Summary:
- def __init__(self, project_name, project_version):
+ def __init__(self, project_name: str, project_version: str):
self.project_name = project_name
self.project_version = project_version
self.sections = collections.defaultdict(dict)
self.max_key_len = 0
- def add_section(self, section, values, kwargs, subproject):
- bool_yn = kwargs.get('bool_yn', False)
- if not isinstance(bool_yn, bool):
- raise InterpreterException('bool_yn keyword argument must be boolean')
- list_sep = kwargs.get('list_sep')
- if list_sep is not None and not isinstance(list_sep, str):
- raise InterpreterException('list_sep keyword argument must be string')
+ def add_section(self, section: str, values: T.Dict[str, T.Any], bool_yn: bool,
+ list_sep: T.Optional[str], subproject: str) -> None:
for k, v in values.items():
if k in self.sections[section]:
raise InterpreterException(f'Summary section {section!r} already have key {k!r}')
@@ -257,7 +264,7 @@ class Interpreter(InterpreterBase, HoldableObject):
self.environment = self.build.environment
self.coredata = self.environment.get_coredata()
self.backend = backend
- self.summary = {}
+ self.summary: T.Dict[str, 'Summary'] = {}
self.modules = {}
# Subproject directory is usually the name of the subproject, but can
# be different for dependencies provided by wrap files.
@@ -565,7 +572,7 @@ class Interpreter(InterpreterBase, HoldableObject):
@typed_kwargs(
'import',
REQUIRED_KW.evolve(since='0.59.0'),
- KwargInfo('disabler', bool, default=False, since='0.59.0'),
+ DISABLER_KW.evolve(since='0.59.0'),
)
@disablerIfNotFound
def func_import(self, node: mparser.BaseNode, args: T.Tuple[str],
@@ -1019,16 +1026,29 @@ external dependencies (including libraries) must go to "dependencies".''')
options = {k: v for k, v in self.environment.options.items() if k.is_backend()}
self.coredata.set_options(options)
- @permittedKwargs({'version', 'meson_version', 'default_options', 'license', 'subproject_dir'})
@typed_pos_args('project', str, varargs=str)
- def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str]], kwargs: 'TYPE_kwargs') -> None:
+ @typed_kwargs(
+ 'project',
+ DEFAULT_OPTIONS,
+ KwargInfo('meson_version', (str, NoneType)),
+ KwargInfo(
+ 'version',
+ (str, mesonlib.File, NoneType, list),
+ default='undefined',
+ validator=_project_version_validator,
+ convertor=lambda x: x[0] if isinstance(x, list) else x,
+ ),
+ KwargInfo('license', ContainerTypeInfo(list, str), default=['unknown'], listify=True),
+ KwargInfo('subproject_dir', str, default='subprojects'),
+ )
+ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str]], kwargs: 'kwargs.Project') -> None:
proj_name, proj_langs = args
if ':' in proj_name:
raise InvalidArguments(f"Project name {proj_name!r} must not contain ':'")
# This needs to be evaluated as early as possible, as meson uses this
# for things like deprecation testing.
- if 'meson_version' in kwargs:
+ if kwargs['meson_version']:
cv = coredata.version
pv = kwargs['meson_version']
if not mesonlib.version_compare(cv, pv):
@@ -1045,8 +1065,8 @@ external dependencies (including libraries) must go to "dependencies".''')
# values previously set from command line. That means that changing
# default_options in a project will trigger a reconfigure but won't
# have any effect.
- self.project_default_options = mesonlib.stringlistify(kwargs.get('default_options', []))
- self.project_default_options = coredata.create_options_dict(self.project_default_options, self.subproject)
+ self.project_default_options = coredata.create_options_dict(
+ kwargs['default_options'], self.subproject)
# If this is the first invocation we always need to initialize
# builtins, if this is a subproject that is new in a re-invocation we
@@ -1062,11 +1082,8 @@ external dependencies (including libraries) must go to "dependencies".''')
if not self.is_subproject():
self.build.project_name = proj_name
self.active_projectname = proj_name
- version = kwargs.get('version', 'undefined')
- if isinstance(version, list):
- if len(version) != 1:
- raise InvalidCode('Version argument is an array with more than one entry.')
- version = version[0]
+
+ version = kwargs['version']
if isinstance(version, mesonlib.File):
FeatureNew.single_use('version from file', '0.57.0', self.subproject)
self.add_build_def_file(version)
@@ -1081,33 +1098,29 @@ external dependencies (including libraries) must go to "dependencies".''')
if len(ver_data) != 1:
raise InterpreterException('Version file must contain exactly one line of text.')
self.project_version = ver_data[0]
- elif isinstance(version, str):
- self.project_version = version
else:
- raise InvalidCode('The version keyword argument must be a string or a file.')
+ self.project_version = version
+
if self.build.project_version is None:
self.build.project_version = self.project_version
- proj_license = mesonlib.stringlistify(kwargs.get('license', 'unknown'))
+ proj_license = kwargs['license']
self.build.dep_manifest[proj_name] = build.DepManifest(self.project_version, proj_license)
if self.subproject in self.build.projects:
raise InvalidCode('Second call to project().')
# spdirname is the subproject_dir for this project, relative to self.subdir.
# self.subproject_dir is the subproject_dir for the main project, relative to top source dir.
- spdirname = kwargs.get('subproject_dir')
- if spdirname:
- if not isinstance(spdirname, str):
- raise InterpreterException('Subproject_dir must be a string')
- if os.path.isabs(spdirname):
- raise InterpreterException('Subproject_dir must not be an absolute path.')
- if spdirname.startswith('.'):
- raise InterpreterException('Subproject_dir must not begin with a period.')
- if '..' in spdirname:
- raise InterpreterException('Subproject_dir must not contain a ".." segment.')
- if not self.is_subproject():
- self.subproject_dir = spdirname
- else:
- spdirname = 'subprojects'
+ spdirname = kwargs['subproject_dir']
+ if not isinstance(spdirname, str):
+ raise InterpreterException('Subproject_dir must be a string')
+ if os.path.isabs(spdirname):
+ raise InterpreterException('Subproject_dir must not be an absolute path.')
+ if spdirname.startswith('.'):
+ raise InterpreterException('Subproject_dir must not begin with a period.')
+ if '..' in spdirname:
+ raise InterpreterException('Subproject_dir must not contain a ".." segment.')
+ if not self.is_subproject():
+ self.subproject_dir = spdirname
self.build.subproject_dir = self.subproject_dir
# Load wrap files from this (sub)project.
@@ -1159,7 +1172,7 @@ external dependencies (including libraries) must go to "dependencies".''')
tv = FeatureNew.get_target_version(self.subproject)
if FeatureNew.check_version(tv, '0.54.0'):
mlog.warning('add_languages is missing native:, assuming languages are wanted for both host and build.',
- location=self.current_node)
+ location=node)
success = self.add_languages(langs, False, MachineChoice.BUILD)
success &= self.add_languages(langs, required, MachineChoice.HOST)
@@ -1177,31 +1190,33 @@ external dependencies (including libraries) must go to "dependencies".''')
mlog.log(mlog.bold('Message:'), *args)
@noArgsFlattening
- @FeatureNewKwargs('summary', '0.54.0', ['list_sep'])
- @permittedKwargs({'section', 'bool_yn', 'list_sep'})
@FeatureNew('summary', '0.53.0')
- def func_summary(self, node, args, kwargs):
- if len(args) == 1:
+ @typed_pos_args('summary', (str, dict), optargs=[object])
+ @typed_kwargs(
+ 'summary',
+ KwargInfo('section', str, default=''),
+ KwargInfo('bool_yn', bool, default=False),
+ KwargInfo('list_sep', (str, NoneType), since='0.54.0')
+ )
+ def func_summary(self, node: mparser.BaseNode, args: T.Tuple[T.Union[str, T.Dict[str, T.Any]], T.Optional[T.Any]],
+ kwargs: 'kwargs.Summary') -> None:
+ if args[1] is None:
if not isinstance(args[0], dict):
raise InterpreterException('Summary first argument must be dictionary.')
values = args[0]
- elif len(args) == 2:
+ else:
if not isinstance(args[0], str):
raise InterpreterException('Summary first argument must be string.')
values = {args[0]: args[1]}
- else:
- raise InterpreterException('Summary accepts at most 2 arguments.')
- section = kwargs.get('section', '')
- if not isinstance(section, str):
- raise InterpreterException('Summary\'s section keyword argument must be string.')
- self.summary_impl(section, values, kwargs)
+ self.summary_impl(kwargs['section'], values, kwargs)
- def summary_impl(self, section, values, kwargs):
+ def summary_impl(self, section: str, values, kwargs: 'kwargs.Summary') -> None:
if self.subproject not in self.summary:
self.summary[self.subproject] = Summary(self.active_projectname, self.project_version)
- self.summary[self.subproject].add_section(section, values, kwargs, self.subproject)
+ self.summary[self.subproject].add_section(
+ section, values, kwargs['bool_yn'], kwargs['list_sep'], self.subproject)
- def _print_summary(self):
+ def _print_summary(self) -> None:
# Add automatic 'Supbrojects' section in main project.
all_subprojects = collections.OrderedDict()
for name, subp in sorted(self.subprojects.items()):
@@ -1228,7 +1243,7 @@ external dependencies (including libraries) must go to "dependencies".''')
sorted_options = sorted(self.user_defined_options.cmd_line_options.items())
values.update({str(k): v for k, v in sorted_options})
if values:
- self.summary_impl('User defined options', values, {})
+ self.summary_impl('User defined options', values, {'bool_yn': False, 'list_sep': None})
# Print all summaries, main project last.
mlog.log('') # newline
main_summary = self.summary.pop('', None)
@@ -1395,9 +1410,13 @@ external dependencies (including libraries) must go to "dependencies".''')
# TODO update modules to always pass `for_machine`. It is bad-form to assume
# the host machine.
- def find_program_impl(self, args, for_machine: MachineChoice = MachineChoice.HOST,
- required=True, silent=True, wanted='', search_dirs=None,
- version_func=None):
+ def find_program_impl(self, args: T.List[mesonlib.FileOrString],
+ for_machine: MachineChoice = MachineChoice.HOST,
+ required: bool = True, silent: bool = True,
+ wanted: T.Union[str, T.List[str]] = '',
+ search_dirs: T.Optional[T.List[str]] = None,
+ version_func: T.Optional[T.Callable[[T.Union['ExternalProgram', 'build.Executable', 'OverrideProgram']], str]] = None
+ ) -> T.Union['ExternalProgram', 'build.Executable', 'OverrideProgram']:
args = mesonlib.listify(args)
extra_info = []
@@ -1421,7 +1440,7 @@ external dependencies (including libraries) must go to "dependencies".''')
interp = self.subprojects[progobj.subproject].held_object
assert isinstance(interp, Interpreter)
version = interp.project_version
- elif isinstance(progobj, ExternalProgram):
+ else:
version = progobj.get_version(self)
is_found, not_found, found = mesonlib.version_compare_many(version, wanted)
if not is_found:
@@ -1471,25 +1490,27 @@ external dependencies (including libraries) must go to "dependencies".''')
self.do_subproject(fallback, 'meson', sp_kwargs)
return self.program_from_overrides(args, extra_info)
- @FeatureNewKwargs('find_program', '0.53.0', ['dirs'])
- @FeatureNewKwargs('find_program', '0.52.0', ['version'])
- @FeatureNewKwargs('find_program', '0.49.0', ['disabler'])
+ @typed_pos_args('find_program', varargs=(str, mesonlib.File), min_varargs=1)
+ @typed_kwargs(
+ 'find_program',
+ DISABLER_KW.evolve(since='0.49.0'),
+ NATIVE_KW,
+ REQUIRED_KW,
+ KwargInfo('dirs', ContainerTypeInfo(list, str), default=[], listify=True, since='0.53.0'),
+ KwargInfo('version', ContainerTypeInfo(list, str), default=[], listify=True, since='0.52.0'),
+ )
@disablerIfNotFound
- @permittedKwargs({'required', 'native', 'version', 'dirs'})
- def func_find_program(self, node, args, kwargs) -> T.Union['build.Executable', ExternalProgram, 'OverrideProgram']:
- if not args:
- raise InterpreterException('No program name specified.')
-
+ def func_find_program(self, node: mparser.BaseNode, args: T.Tuple[T.List[mesonlib.FileOrString]],
+ kwargs: 'kwargs.FindProgram',
+ ) -> T.Union['build.Executable', ExternalProgram, 'OverrideProgram']:
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
if disabled:
- mlog.log('Program', mlog.bold(' '.join(args)), 'skipped: feature', mlog.bold(feature), 'disabled')
- return self.notfound_program(args)
+ mlog.log('Program', mlog.bold(' '.join(args[0])), 'skipped: feature', mlog.bold(feature), 'disabled')
+ return self.notfound_program(args[0])
search_dirs = extract_search_dirs(kwargs)
- wanted = mesonlib.stringlistify(kwargs.get('version', []))
- for_machine = self.machine_from_native_kwarg(kwargs)
- return self.find_program_impl(args, for_machine, required=required,
- silent=False, wanted=wanted,
+ return self.find_program_impl(args[0], kwargs['native'], required=required,
+ silent=False, wanted=kwargs['version'],
search_dirs=search_dirs)
def func_find_library(self, node, args, kwargs):
@@ -1731,11 +1752,11 @@ external dependencies (including libraries) must go to "dependencies".''')
kwargs['input'] = self.source_strings_to_files(extract_as_list(kwargs, 'input'))
except mesonlib.MesonException:
mlog.warning(f'''Custom target input '{kwargs['input']}' can't be converted to File object(s).
-This will become a hard error in the future.''', location=self.current_node)
+This will become a hard error in the future.''', location=node)
kwargs['env'] = self.unpack_env_kwarg(kwargs)
if 'command' in kwargs and isinstance(kwargs['command'], list) and kwargs['command']:
if isinstance(kwargs['command'][0], str):
- kwargs['command'][0] = self.func_find_program(node, kwargs['command'][0], {})
+ kwargs['command'][0] = self.find_program_impl([kwargs['command'][0]])
tg = build.CustomTarget(name, self.subdir, self.subproject, kwargs, backend=self.backend)
self.add_target(tg.name, tg)
return tg
@@ -1755,7 +1776,7 @@ This will become a hard error in the future.''', location=self.current_node)
if isinstance(i, ExternalProgram) and not i.found():
raise InterpreterException(f'Tried to use non-existing executable {i.name!r}')
if isinstance(all_args[0], str):
- all_args[0] = self.func_find_program(node, all_args[0], {})
+ all_args[0] = self.find_program_impl([all_args[0]])
name = args[0]
tg = build.RunTarget(name, all_args, kwargs['depends'], self.subdir, self.subproject, kwargs['env'])
self.add_target(name, tg)
@@ -1833,7 +1854,7 @@ This will become a hard error in the future.''', location=self.current_node)
name = name.replace(':', '_')
exe = args[1]
if isinstance(exe, mesonlib.File):
- exe = self.func_find_program(node, args[1], {})
+ exe = self.find_program_impl([exe])
env = self.unpack_env_kwarg(kwargs)
@@ -1930,10 +1951,19 @@ This will become a hard error in the future.''', location=self.current_node)
return d
- @FeatureNewKwargs('subdir', '0.44.0', ['if_found'])
- @permittedKwargs({'if_found'})
@typed_pos_args('subdir', str)
- def func_subdir(self, node: mparser.BaseNode, args: T.Tuple[str], kwargs: 'TYPE_kwargs') -> None:
+ @typed_kwargs(
+ 'subdir',
+ KwargInfo(
+ 'if_found',
+ ContainerTypeInfo(list, object),
+ validator=lambda a: 'Objects must have a found() method' if not all(hasattr(x, 'found') for x in a) else None,
+ since='0.44.0',
+ default=[],
+ listify=True,
+ ),
+ )
+ def func_subdir(self, node: mparser.BaseNode, args: T.Tuple[str], kwargs: 'kwargs.Subdir') -> None:
mesonlib.check_direntry_issues(args)
if '..' in args[0]:
raise InvalidArguments('Subdir contains ..')
@@ -1941,11 +1971,10 @@ This will become a hard error in the future.''', location=self.current_node)
raise InvalidArguments('Must not go into subprojects dir with subdir(), use subproject() instead.')
if self.subdir == '' and args[0].startswith('meson-'):
raise InvalidArguments('The "meson-" prefix is reserved and cannot be used for top-level subdir().')
- for i in mesonlib.extract_as_list(kwargs, 'if_found'):
- if not hasattr(i, 'found'):
- raise InterpreterException('Object used in if_found does not have a found method.')
+ for i in kwargs['if_found']:
if not i.found():
return
+
prev_subdir = self.subdir
subdir = os.path.join(prev_subdir, args[0])
if os.path.isabs(subdir):
@@ -2345,49 +2374,43 @@ This will become a hard error in the future.''', location=self.current_node)
i = build.IncludeDirs(self.subdir, incdir_strings, is_system)
return i
- @permittedKwargs({'exe_wrapper', 'gdb', 'timeout_multiplier', 'env', 'is_default',
- 'exclude_suites'})
@typed_pos_args('add_test_setup', str)
- def func_add_test_setup(self, node: mparser.BaseNode, args: T.Tuple[str], kwargs: 'TYPE_kwargs') -> None:
+ @typed_kwargs(
+ 'add_test_setup',
+ KwargInfo('exe_wrapper', ContainerTypeInfo(list, (str, ExternalProgram)), listify=True, default=[]),
+ KwargInfo('gdb', bool, default=False),
+ KwargInfo('timeout_multiplier', int, default=1),
+ KwargInfo('exclude_suites', ContainerTypeInfo(list, str), listify=True, default=[], since='0.57.0'),
+ KwargInfo('is_default', bool, default=False, since='0.49.0'),
+ ENV_KW,
+ )
+ def func_add_test_setup(self, node: mparser.BaseNode, args: T.Tuple[str], kwargs: 'kwargs.AddTestSetup') -> None:
setup_name = args[0]
if re.fullmatch('([_a-zA-Z][_0-9a-zA-Z]*:)?[_a-zA-Z][_0-9a-zA-Z]*', setup_name) is None:
raise InterpreterException('Setup name may only contain alphanumeric characters.')
if ":" not in setup_name:
- setup_name = (self.subproject if self.subproject else self.build.project_name) + ":" + setup_name
- try:
- inp = extract_as_list(kwargs, 'exe_wrapper')
- exe_wrapper = []
- for i in inp:
- if isinstance(i, str):
- exe_wrapper.append(i)
- elif isinstance(i, ExternalProgram):
- if not i.found():
- raise InterpreterException('Tried to use non-found executable.')
- exe_wrapper += i.get_command()
- else:
- raise InterpreterException('Exe wrapper can only contain strings or external binaries.')
- except KeyError:
- exe_wrapper = None
- gdb = kwargs.get('gdb', False)
- if not isinstance(gdb, bool):
- raise InterpreterException('Gdb option must be a boolean')
- timeout_multiplier = kwargs.get('timeout_multiplier', 1)
- if not isinstance(timeout_multiplier, int):
- raise InterpreterException('Timeout multiplier must be a number.')
+ setup_name = f'{(self.subproject if self.subproject else self.build.project_name)}:{setup_name}'
+
+ exe_wrapper: T.List[str] = []
+ for i in kwargs['exe_wrapper']:
+ if isinstance(i, str):
+ exe_wrapper.append(i)
+ else:
+ if not i.found():
+ raise InterpreterException('Tried to use non-found executable.')
+ exe_wrapper += i.get_command()
+
+ timeout_multiplier = kwargs['timeout_multiplier']
if timeout_multiplier <= 0:
FeatureNew('add_test_setup() timeout_multiplier <= 0', '0.57.0').use(self.subproject)
- is_default = kwargs.get('is_default', False)
- if not isinstance(is_default, bool):
- raise InterpreterException('is_default option must be a boolean')
- if is_default:
+
+ if kwargs['is_default']:
if self.build.test_setup_default_name is not None:
raise InterpreterException(f'{self.build.test_setup_default_name!r} is already set as default. '
'is_default can be set to true only once')
self.build.test_setup_default_name = setup_name
- exclude_suites = mesonlib.stringlistify(kwargs.get('exclude_suites', []))
- env = self.unpack_env_kwarg(kwargs)
- self.build.test_setups[setup_name] = build.TestSetup(exe_wrapper, gdb, timeout_multiplier, env,
- exclude_suites)
+ self.build.test_setups[setup_name] = build.TestSetup(exe_wrapper, kwargs['gdb'], timeout_multiplier, kwargs['env'],
+ kwargs['exclude_suites'])
@typed_pos_args('add_global_arguments', varargs=str)
@typed_kwargs('add_global_arguments', NATIVE_KW, LANGUAGE_KW)
@@ -2573,7 +2596,7 @@ Try setting b_lundef to false instead.'''.format(self.coredata.options[OptionKey
results.append(s)
elif isinstance(s, (build.GeneratedList, build.BuildTarget,
build.CustomTargetIndex, build.CustomTarget,
- build.GeneratedList)):
+ build.ExtractedObjects)):
results.append(s)
else:
raise InterpreterException(f'Source item is {s!r} instead of '
diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py
index ccaa1c7..b3dbe55 100644
--- a/mesonbuild/interpreter/interpreterobjects.py
+++ b/mesonbuild/interpreter/interpreterobjects.py
@@ -139,7 +139,8 @@ class FeatureOptionHolder(ObjectHolder[coredata.UserFeatureOption]):
assert isinstance(error_message, str)
if self.value == 'enabled':
prefix = f'Feature {self.held_object.name} cannot be enabled'
- prefix = prefix + ': ' if error_message else ''
+ if error_message:
+ prefix += ': '
raise InterpreterException(prefix + error_message)
return self.as_disabled()
diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py
index 2229984..4f8cf06 100644
--- a/mesonbuild/interpreter/kwargs.py
+++ b/mesonbuild/interpreter/kwargs.py
@@ -6,7 +6,7 @@
import typing as T
-from typing_extensions import TypedDict, Literal
+from typing_extensions import TypedDict, Literal, Protocol
from .. import build
from .. import coredata
@@ -186,3 +186,49 @@ class CustomTarget(TypedDict):
install_tag: T.List[T.Union[str, bool]]
output: T.List[str]
override_options: T.Dict[OptionKey, str]
+
+class AddTestSetup(TypedDict):
+
+ exe_wrapper: T.List[T.Union[str, ExternalProgram]]
+ gdb: bool
+ timeout_multiplier: int
+ is_default: bool
+ exclude_suites: T.List[str]
+ env: build.EnvironmentVariables
+
+
+class Project(TypedDict):
+
+ version: T.Optional[FileOrString]
+ meson_version: T.Optional[str]
+ default_options: T.List[str]
+ license: T.List[str]
+ subproject_dir: str
+
+
+class _FoundProto(Protocol):
+
+ """Protocol for subdir arguments.
+
+ This allows us to define any objec that has a found(self) -> bool method
+ """
+
+ def found(self) -> bool: ...
+
+
+class Subdir(TypedDict):
+
+ if_found: T.List[_FoundProto]
+
+
+class Summary(TypedDict):
+
+ section: str
+ bool_yn: bool
+ list_sep: T.Optional[str]
+
+
+class FindProgram(ExtractRequired, ExtractSearchDirs):
+
+ native: MachineChoice
+ version: T.List[str]
diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py
index adc342f..e973c5d 100644
--- a/mesonbuild/interpreter/mesonmain.py
+++ b/mesonbuild/interpreter/mesonmain.py
@@ -86,7 +86,7 @@ class MesonMain(MesonInterpreterObject):
largs.append(prog)
largs.extend(args)
return self.interpreter.backend.get_executable_serialisation(largs)
- found = self.interpreter.func_find_program({}, prog, {})
+ found = self.interpreter.find_program_impl([prog])
largs.append(found)
largs.extend(args)
es = self.interpreter.backend.get_executable_serialisation(largs)
diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py
index 9e443ae..5391e9f 100644
--- a/mesonbuild/interpreter/type_checking.py
+++ b/mesonbuild/interpreter/type_checking.py
@@ -129,6 +129,8 @@ REQUIRED_KW: KwargInfo[T.Union[bool, UserFeatureOption]] = KwargInfo(
# TODO: extract_required_kwarg could be converted to a convertor
)
+DISABLER_KW: KwargInfo[bool] = KwargInfo('disabler', bool, default=False)
+
def _env_validator(value: T.Union[EnvironmentVariables, T.List['TYPE_var'], T.Dict[str, 'TYPE_var'], str, None]) -> T.Optional[str]:
def _splitter(v: str) -> T.Optional[str]:
split = v.split('=', 1)
@@ -285,3 +287,12 @@ INCLUDE_DIRECTORIES: KwargInfo[T.List[T.Union[str, IncludeDirs]]] = KwargInfo(
listify=True,
default=[],
)
+
+# for cases like default_options and override_options
+DEFAULT_OPTIONS: KwargInfo[T.List[str]] = KwargInfo(
+ 'default_options',
+ ContainerTypeInfo(list, (str, IncludeDirs)),
+ listify=True,
+ default=[],
+ validator=_env_validator,
+)
diff --git a/mesonbuild/mesonlib/universal.py b/mesonbuild/mesonlib/universal.py
index 4655810..48ad652 100644
--- a/mesonbuild/mesonlib/universal.py
+++ b/mesonbuild/mesonlib/universal.py
@@ -2125,7 +2125,7 @@ class OptionKey:
return out
def __repr__(self) -> str:
- return f'OptionKey({repr(self.name)}, {repr(self.subproject)}, {repr(self.machine)}, {repr(self.lang)})'
+ return f'OptionKey({self.name!r}, {self.subproject!r}, {self.machine!r}, {self.lang!r}, {self.module!r}, {self.type!r})'
@classmethod
def from_string(cls, raw: str) -> 'OptionKey':
diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py
index 7d0da13..cb87faf 100644
--- a/mesonbuild/minstall.py
+++ b/mesonbuild/minstall.py
@@ -18,6 +18,7 @@ import argparse
import errno
import os
import pickle
+import platform
import shlex
import shutil
import subprocess
@@ -251,6 +252,9 @@ def apply_ldconfig(dm: DirMaker) -> None:
# If we don't have ldconfig, failure is ignored quietly.
return
+ if 'bsd' in platform.system().lower():
+ return
+
# Try to update ld cache, it could fail if we don't have permission.
proc, out, err = Popen_safe(['ldconfig', '-v'])
if proc.returncode == 0:
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index 5798397..f7ce1a0 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -33,7 +33,7 @@ from ..build import CustomTarget, CustomTargetIndex, GeneratedList, InvalidArgum
from ..dependencies import Dependency, PkgConfigDependency, InternalDependency
from ..interpreter.type_checking import DEPEND_FILES_KW, INSTALL_KW, NoneType, in_set_validator
from ..interpreterbase import noPosargs, noKwargs, permittedKwargs, FeatureNew, FeatureDeprecatedKwargs
-from ..interpreterbase import typed_kwargs, KwargInfo, ContainerTypeInfo
+from ..interpreterbase import typed_kwargs, KwargInfo, ContainerTypeInfo, FeatureDeprecated
from ..interpreterbase.decorators import typed_pos_args
from ..mesonlib import (
MachineChoice, MesonException, OrderedSet, Popen_safe, join_args,
diff --git a/mesonbuild/modules/qt.py b/mesonbuild/modules/qt.py
index f874c58..9360e4a 100644
--- a/mesonbuild/modules/qt.py
+++ b/mesonbuild/modules/qt.py
@@ -170,7 +170,7 @@ class QtBaseModule(ExtensionModule):
if qt.found():
# Get all tools and then make sure that they are the right version
self.compilers_detect(state, qt)
- if version_compare(qt.version, '>=5.14.0'):
+ if version_compare(qt.version, '>=5.15.0'):
self._moc_supports_depfiles = True
else:
mlog.warning('moc dependencies will not work properly until you move to Qt >= 5.15', fatal=False)
diff --git a/mesonbuild/modules/unstable_rust.py b/mesonbuild/modules/unstable_rust.py
index 998dbfd..d0d9ca5 100644
--- a/mesonbuild/modules/unstable_rust.py
+++ b/mesonbuild/modules/unstable_rust.py
@@ -17,7 +17,7 @@ import typing as T
from . import ExtensionModule, ModuleReturnValue
from .. import mlog
-from ..build import BothLibraries, BuildTarget, CustomTargetIndex, Executable, GeneratedList, IncludeDirs, CustomTarget
+from ..build import BothLibraries, BuildTarget, CustomTargetIndex, Executable, ExtractedObjects, GeneratedList, IncludeDirs, CustomTarget
from ..dependencies import Dependency, ExternalLibrary
from ..interpreter.interpreter import TEST_KWARGS
from ..interpreterbase import ContainerTypeInfo, InterpreterException, KwargInfo, FeatureNew, typed_kwargs, typed_pos_args, noPosargs
@@ -27,7 +27,7 @@ if T.TYPE_CHECKING:
from . import ModuleState
from ..interpreter import Interpreter
from ..interpreter import kwargs as _kwargs
- from ..interpreter.interpreter import SourceInputs
+ from ..interpreter.interpreter import SourceInputs, SourceOutputs
from ..programs import ExternalProgram
from typing_extensions import TypedDict
@@ -168,7 +168,7 @@ class RustModule(ExtensionModule):
KwargInfo('include_directories', ContainerTypeInfo(list, IncludeDirs), default=[], listify=True),
KwargInfo(
'input',
- ContainerTypeInfo(list, (File, GeneratedList, BuildTarget, BothLibraries, CustomTargetIndex, CustomTarget, str), allow_empty=False),
+ ContainerTypeInfo(list, (File, GeneratedList, BuildTarget, BothLibraries, ExtractedObjects, CustomTargetIndex, CustomTarget, str), allow_empty=False),
default=[],
listify=True,
required=True,
@@ -184,7 +184,7 @@ class RustModule(ExtensionModule):
header, *_deps = self.interpreter.source_strings_to_files(kwargs['input'])
# Split File and Target dependencies to add pass to CustomTarget
- depends: T.List[T.Union[GeneratedList, BuildTarget, CustomTargetIndex, CustomTarget]] = []
+ depends: T.List['SourceOutputs'] = []
depend_files: T.List[File] = []
for d in _deps:
if isinstance(d, File):
@@ -203,6 +203,8 @@ class RustModule(ExtensionModule):
name: str
if isinstance(header, File):
name = header.fname
+ elif isinstance(header, (BuildTarget, BothLibraries, ExtractedObjects)):
+ raise InterpreterException('bindgen source file must be a C header, not an object or build target')
else:
name = header.get_outputs()[0]
diff --git a/test cases/failing/106 feature require.bis/meson.build b/test cases/failing/106 feature require.bis/meson.build
new file mode 100644
index 0000000..08c099c
--- /dev/null
+++ b/test cases/failing/106 feature require.bis/meson.build
@@ -0,0 +1,2 @@
+project('no fallback', 'c')
+foo = get_option('reqfeature').require(false)
diff --git a/test cases/failing/106 feature require.bis/meson_options.txt b/test cases/failing/106 feature require.bis/meson_options.txt
new file mode 100644
index 0000000..5910a87
--- /dev/null
+++ b/test cases/failing/106 feature require.bis/meson_options.txt
@@ -0,0 +1 @@
+option('reqfeature', type : 'feature', value : 'enabled', description : 'A required feature')
diff --git a/test cases/failing/106 feature require.bis/test.json b/test cases/failing/106 feature require.bis/test.json
new file mode 100644
index 0000000..2583990
--- /dev/null
+++ b/test cases/failing/106 feature require.bis/test.json
@@ -0,0 +1,8 @@
+{
+ "stdout": [
+ {
+ "match": "re",
+ "line": ".*/meson\\.build:2:0: ERROR: Feature reqfeature cannot be enabled"
+ }
+ ]
+}
diff --git a/test cases/failing/115 nonsensical bindgen/meson.build b/test cases/failing/115 nonsensical bindgen/meson.build
new file mode 100644
index 0000000..6995f67
--- /dev/null
+++ b/test cases/failing/115 nonsensical bindgen/meson.build
@@ -0,0 +1,20 @@
+# SPDX-license-identifer: Apache-2.0
+# Copyright © 2021 Intel Corporation
+
+project('rustmod bindgen', 'c')
+
+if not add_languages('rust', required: false)
+ error('MESON_SKIP_TEST test requires rust compiler')
+endif
+
+prog_bindgen = find_program('bindgen', required : false)
+if not prog_bindgen.found()
+ error('MESON_SKIP_TEST bindgen not found')
+endif
+
+c_lib = static_library('clib', 'src/source.c')
+
+import('unstable-rust').bindgen(
+ input : c_lib,
+ output : 'header.rs',
+)
diff --git a/test cases/failing/115 nonsensical bindgen/src/header.h b/test cases/failing/115 nonsensical bindgen/src/header.h
new file mode 100644
index 0000000..750621f
--- /dev/null
+++ b/test cases/failing/115 nonsensical bindgen/src/header.h
@@ -0,0 +1,8 @@
+// SPDX-license-identifer: Apache-2.0
+// Copyright © 2021 Intel Corporation
+
+#pragma once
+
+#include <stdint.h>
+
+int32_t add(const int32_t, const int32_t);
diff --git a/test cases/failing/115 nonsensical bindgen/src/source.c b/test cases/failing/115 nonsensical bindgen/src/source.c
new file mode 100644
index 0000000..d652d28
--- /dev/null
+++ b/test cases/failing/115 nonsensical bindgen/src/source.c
@@ -0,0 +1,8 @@
+// SPDX-license-identifer: Apache-2.0
+// Copyright © 2021 Intel Corporation
+
+#include "header.h"
+
+int32_t add(const int32_t first, const int32_t second) {
+ return first + second;
+}
diff --git a/test cases/failing/115 nonsensical bindgen/test.json b/test cases/failing/115 nonsensical bindgen/test.json
new file mode 100644
index 0000000..d9249b2
--- /dev/null
+++ b/test cases/failing/115 nonsensical bindgen/test.json
@@ -0,0 +1,8 @@
+{
+ "stdout": [
+ {
+ "line": "test cases/failing/115 nonsensical bindgen/meson.build:17:24: ERROR: bindgen source file must be a C header, not an object or build target"
+ }
+ ]
+}
+
diff --git a/test cases/failing/75 link with shared module on osx/test.json b/test cases/failing/75 link with shared module on osx/test.json
index 7db17d8..81ee2ac 100644
--- a/test cases/failing/75 link with shared module on osx/test.json
+++ b/test cases/failing/75 link with shared module on osx/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/75 link with shared module on osx/meson.build:8:0: ERROR: target links against shared modules. This is not permitted on OSX"
+ "line": "test cases/failing/75 link with shared module on osx/meson.build:8:0: ERROR: target prog links against shared module mymodule. This is not permitted on OSX"
}
]
}
diff --git a/test cases/unit/1 soname/main.c b/test cases/unit/1 soname/main.c
new file mode 100644
index 0000000..f5ccbb9
--- /dev/null
+++ b/test cases/unit/1 soname/main.c
@@ -0,0 +1,5 @@
+int versioned_func (void);
+
+int main (void) {
+ return versioned_func();
+}
diff --git a/test cases/unit/1 soname/meson.build b/test cases/unit/1 soname/meson.build
index 950dadc..44b003a 100644
--- a/test cases/unit/1 soname/meson.build
+++ b/test cases/unit/1 soname/meson.build
@@ -20,3 +20,16 @@ shared_library('settosame', 'versioned.c',
install : true,
soversion : '7.8.9',
version : '7.8.9')
+
+shared_module('some_module', 'versioned.c',
+ install: true)
+
+module1 = shared_module('linked_module1', 'versioned.c',
+ install: true)
+
+module2 = shared_module('linked_module2', 'versioned.c',
+ install: true)
+module2_dep = declare_dependency(link_with: module2)
+
+executable('main1', 'main.c', link_with: module1)
+executable('main2', 'main.c', dependencies: module2_dep)
diff --git a/test cases/unit/17 prebuilt shared/meson.build b/test cases/unit/17 prebuilt shared/meson.build
index 9a4eca0..7badcb7 100644
--- a/test cases/unit/17 prebuilt shared/meson.build
+++ b/test cases/unit/17 prebuilt shared/meson.build
@@ -1,7 +1,12 @@
project('prebuilt shared library', 'c')
+search_dir = get_option('search_dir')
+if search_dir == 'auto'
+ search_dir = meson.current_source_dir()
+endif
+
cc = meson.get_compiler('c')
-shlib = cc.find_library('alexandria', dirs : meson.current_source_dir())
+shlib = cc.find_library('alexandria', dirs : search_dir)
exe = executable('patron', 'patron.c', dependencies : shlib)
test('visitation', exe)
@@ -11,3 +16,23 @@ d = declare_dependency(dependencies : shlib)
exe2 = executable('another_visitor', 'another_visitor.c',
dependencies : d)
test('another', exe2)
+
+stlib = static_library(
+ 'rejected',
+ 'rejected.c',
+ dependencies : shlib,
+)
+
+rejected = executable(
+ 'rejected',
+ 'rejected_main.c',
+ link_with : stlib,
+)
+test('rejected', rejected)
+
+rejected_whole = executable(
+ 'rejected_whole',
+ 'rejected_main.c',
+ link_whole : stlib,
+)
+test('rejected (whole archive)', rejected_whole)
diff --git a/test cases/unit/17 prebuilt shared/meson_options.txt b/test cases/unit/17 prebuilt shared/meson_options.txt
new file mode 100644
index 0000000..7876a6f
--- /dev/null
+++ b/test cases/unit/17 prebuilt shared/meson_options.txt
@@ -0,0 +1 @@
+option('search_dir', type : 'string', value : 'auto')
diff --git a/test cases/unit/17 prebuilt shared/rejected.c b/test cases/unit/17 prebuilt shared/rejected.c
new file mode 100644
index 0000000..9d7ac94
--- /dev/null
+++ b/test cases/unit/17 prebuilt shared/rejected.c
@@ -0,0 +1,8 @@
+#include "rejected.h"
+
+void say(void) {
+ printf("You are standing outside the Great Library of Alexandria.\n");
+ printf("You decide to go inside.\n\n");
+ alexandria_visit();
+ printf("The librarian tells you it's time to leave\n");
+}
diff --git a/test cases/unit/17 prebuilt shared/rejected.h b/test cases/unit/17 prebuilt shared/rejected.h
new file mode 100644
index 0000000..b9ccf31
--- /dev/null
+++ b/test cases/unit/17 prebuilt shared/rejected.h
@@ -0,0 +1,6 @@
+#include <stdio.h>
+#include <alexandria.h>
+
+#pragma once
+
+void say(void);
diff --git a/test cases/unit/17 prebuilt shared/rejected_main.c b/test cases/unit/17 prebuilt shared/rejected_main.c
new file mode 100644
index 0000000..4d35061
--- /dev/null
+++ b/test cases/unit/17 prebuilt shared/rejected_main.c
@@ -0,0 +1,6 @@
+#include "rejected.h"
+
+int main(void) {
+ say();
+ return 0;
+}
diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py
index ec0b393..30c0572 100644
--- a/unittests/allplatformstests.py
+++ b/unittests/allplatformstests.py
@@ -1499,19 +1499,45 @@ class AllPlatformTests(BasePlatformTests):
else:
shlibfile = os.path.join(tdir, 'libalexandria.' + shared_suffix)
self.build_shared_lib(cc, source, objectfile, shlibfile, impfile)
+
+ if is_windows():
+ def cleanup() -> None:
+ """Clean up all the garbage MSVC writes in the source tree."""
+
+ for fname in glob(os.path.join(tdir, 'alexandria.*')):
+ if os.path.splitext(fname)[1] not in {'.c', '.h'}:
+ os.unlink(fname)
+ self.addCleanup(cleanup)
+ else:
+ self.addCleanup(os.unlink, shlibfile)
+
# Run the test
- try:
- self.init(tdir)
+ self.init(tdir)
+ self.build()
+ self.run_tests()
+
+ def test_prebuilt_shared_lib_rpath(self) -> None:
+ (cc, _, object_suffix, shared_suffix) = self.detect_prebuild_env()
+ tdir = os.path.join(self.unit_test_dir, '17 prebuilt shared')
+ with tempfile.TemporaryDirectory() as d:
+ source = os.path.join(tdir, 'alexandria.c')
+ objectfile = os.path.join(d, 'alexandria.' + object_suffix)
+ impfile = os.path.join(d, 'alexandria.lib')
+ if cc.get_argument_syntax() == 'msvc':
+ shlibfile = os.path.join(d, 'alexandria.' + shared_suffix)
+ elif is_cygwin():
+ shlibfile = os.path.join(d, 'cygalexandria.' + shared_suffix)
+ else:
+ shlibfile = os.path.join(d, 'libalexandria.' + shared_suffix)
+ # Ensure MSVC extra files end up in the directory that gets deleted
+ # at the end
+ with chdir(d):
+ self.build_shared_lib(cc, source, objectfile, shlibfile, impfile)
+
+ # Run the test
+ self.init(tdir, extra_args=[f'-Dsearch_dir={d}'])
self.build()
self.run_tests()
- finally:
- os.unlink(shlibfile)
- if is_windows():
- # Clean up all the garbage MSVC writes in the
- # source tree.
- for fname in glob(os.path.join(tdir, 'alexandria.*')):
- if os.path.splitext(fname)[1] not in ['.c', '.h']:
- os.unlink(fname)
@skipIfNoPkgconfig
def test_pkgconfig_static(self):
@@ -1920,8 +1946,10 @@ class AllPlatformTests(BasePlatformTests):
"""
tdir = os.path.join(self.unit_test_dir, '30 shared_mod linking')
out = self.init(tdir)
- msg = ('WARNING: target links against shared modules. This is not '
- 'recommended as it is not supported on some platforms')
+ msg = ('''DEPRECATION: target prog links against shared module mymod, which is incorrect.
+ This will be an error in the future, so please use shared_library() for mymod instead.
+ If shared_module() was used for mymod because it has references to undefined symbols,
+ use shared_libary() with `override_options: ['b_lundef=false']` instead.''')
self.assertIn(msg, out)
def test_mixed_language_linker_check(self):
@@ -3540,6 +3568,12 @@ class AllPlatformTests(BasePlatformTests):
self.build()
self.run_tests()
+ def test_extract_objects_custom_target_no_warning(self):
+ testdir = os.path.join(self.common_test_dir, '22 object extraction')
+
+ out = self.init(testdir)
+ self.assertNotRegex(out, "WARNING:.*can't be converted to File object")
+
def test_multi_output_custom_target_no_warning(self):
testdir = os.path.join(self.common_test_dir, '228 custom_target source')
diff --git a/unittests/internaltests.py b/unittests/internaltests.py
index 31271b7..c4fd0a6 100644
--- a/unittests/internaltests.py
+++ b/unittests/internaltests.py
@@ -13,6 +13,7 @@
# limitations under the License.
from configparser import ConfigParser
+from mesonbuild.mesonlib.universal import OptionType
from pathlib import Path
from unittest import mock
import contextlib
@@ -1574,3 +1575,21 @@ class InternalTests(unittest.TestCase):
self.assertFalse(coredata.major_versions_differ('0.60.0', '0.60.1'))
self.assertFalse(coredata.major_versions_differ('0.59.99', '0.59.99'))
self.assertFalse(coredata.major_versions_differ('0.60.0.rc1', '0.60.0.rc2'))
+
+ def test_option_key_from_string(self) -> None:
+ cases = [
+ ('c_args', OptionKey('args', lang='c', _type=OptionType.COMPILER)),
+ ('build.cpp_args', OptionKey('args', machine=MachineChoice.BUILD, lang='cpp', _type=OptionType.COMPILER)),
+ ('prefix', OptionKey('prefix', _type=OptionType.BUILTIN)),
+ ('made_up', OptionKey('made_up', _type=OptionType.PROJECT)),
+
+ # TODO: the from_String method should be splitting the prefix off of
+ # these, as we have the type already, but it doesn't. For now have a
+ # test so that we don't change the behavior un-intentionally
+ ('b_lto', OptionKey('b_lto', _type=OptionType.BASE)),
+ ('backend_startup_project', OptionKey('backend_startup_project', _type=OptionType.BACKEND)),
+ ]
+
+ for raw, expected in cases:
+ with self.subTest(raw):
+ self.assertEqual(OptionKey.from_string(raw), expected)
diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py
index 0f99f01..8ff0f8e 100644
--- a/unittests/linuxliketests.py
+++ b/unittests/linuxliketests.py
@@ -438,6 +438,24 @@ class LinuxlikeTests(BasePlatformTests):
self.assertEqual(get_soname(bothset), 'libbothset.so.1.2.3')
self.assertEqual(len(self.glob_sofiles_without_privdir(bothset[:-3] + '*')), 3)
+ # A shared_module that is not linked to anything
+ module = os.path.join(libpath, 'libsome_module.so')
+ self.assertPathExists(module)
+ self.assertFalse(os.path.islink(module))
+ self.assertEqual(get_soname(module), None)
+
+ # A shared_module that is not linked to an executable with link_with:
+ module = os.path.join(libpath, 'liblinked_module1.so')
+ self.assertPathExists(module)
+ self.assertFalse(os.path.islink(module))
+ self.assertEqual(get_soname(module), 'liblinked_module1.so')
+
+ # A shared_module that is not linked to an executable with dependencies:
+ module = os.path.join(libpath, 'liblinked_module2.so')
+ self.assertPathExists(module)
+ self.assertFalse(os.path.islink(module))
+ self.assertEqual(get_soname(module), 'liblinked_module2.so')
+
def test_soname(self):
self._test_soname_impl(self.builddir, False)