diff --git a/.github/workflows/msys2.yml b/.github/workflows/msys2.yml index 8f1823d70633..2bdcdd30e6f4 100644 --- a/.github/workflows/msys2.yml +++ b/.github/workflows/msys2.yml @@ -51,9 +51,6 @@ jobs: TOOLCHAIN: clang env: MESON_CI_JOBNAME: msys2-${{ matrix.NAME }} - # XXX: For some reason enabling jit debugging "fixes" random python crashes - # see https://github.com/msys2/MINGW-packages/issues/11864 - MSYS: "winjitdebug" defaults: run: diff --git a/README.md b/README.md index 868728d4181e..f3a2657b7d05 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,11 @@ build system. - [Python](https://python.org) (version 3.7 or newer) - [Ninja](https://ninja-build.org) (version 1.8.2 or newer) +Latest Meson version supporting previous Python versions: +- Python 3.6: **0.61.5** +- Python 3.5: **0.56.2** +- Python 3.4: **0.45.1** + #### Installing from source Meson is available on [PyPi](https://pypi.python.org/pypi/meson), so diff --git a/ci/ciimage/build.py b/ci/ciimage/build.py index 1e1f23811c3d..f9c0ad6a78a1 100755 --- a/ci/ciimage/build.py +++ b/ci/ciimage/build.py @@ -137,13 +137,14 @@ def copy_meson(self) -> None: shutil.copytree( self.meson_root, self.temp_dir / 'meson', + symlinks=True, ignore=shutil.ignore_patterns( '.git', '*_cache', '__pycache__', # 'work area', self.temp_dir.name, - ) + ), ) def do_test(self, tty: bool = False) -> None: @@ -175,7 +176,7 @@ def do_test(self, tty: bool = False) -> None: else: test_cmd = [ self.docker, 'run', '--rm', '-t', 'meson_test_image', - '/bin/bash', '-c', 'source /ci/env_vars.sh; cd meson; ./run_tests.py $CI_ARGS' + '/bin/bash', '-xc', 'source /ci/env_vars.sh; cd meson; ./run_tests.py $CI_ARGS' ] if subprocess.run(test_cmd).returncode != 0 and not tty: diff --git a/ci/ciimage/common.sh b/ci/ciimage/common.sh index 6209ef9a1c08..67d59978f5a7 100644 --- a/ci/ciimage/common.sh +++ b/ci/ciimage/common.sh @@ -39,11 +39,11 @@ dub_fetch() { } install_minimal_python_packages() { - rm -f /usr/lib/python3.*/EXTERNALLY-MANAGED + rm -f /usr/lib*/python3.*/EXTERNALLY-MANAGED python3 -m pip install "${base_python_pkgs[@]}" $* } install_python_packages() { - rm -f /usr/lib/python3.*/EXTERNALLY-MANAGED + rm -f /usr/lib*/python3.*/EXTERNALLY-MANAGED python3 -m pip install "${base_python_pkgs[@]}" "${python_pkgs[@]}" $* } diff --git a/ci/ciimage/opensuse/install.sh b/ci/ciimage/opensuse/install.sh index 41cb96192e8f..b0097172a8a9 100755 --- a/ci/ciimage/opensuse/install.sh +++ b/ci/ciimage/opensuse/install.sh @@ -9,7 +9,7 @@ pkgs=( ninja make git autoconf automake patch libjpeg-devel elfutils gcc gcc-c++ gcc-fortran gcc-objc gcc-obj-c++ vala rust bison flex curl lcov mono-core gtkmm3-devel gtest gmock protobuf-devel wxGTK3-3_2-devel gobject-introspection-devel - itstool gtk3-devel java-15-openjdk-devel gtk-doc llvm-devel clang-devel libSDL2-devel graphviz-devel zlib-devel zlib-devel-static + itstool gtk3-devel java-17-openjdk-devel gtk-doc llvm-devel clang-devel libSDL2-devel graphviz-devel zlib-devel zlib-devel-static #hdf5-devel netcdf-devel libscalapack2-openmpi3-devel libscalapack2-gnu-openmpi3-hpc-devel openmpi3-devel doxygen vulkan-devel vulkan-validationlayers openssh mercurial gtk-sharp3-complete gtk-sharp2-complete libpcap-devel libgpgme-devel libqt5-qtbase-devel libqt5-qttools-devel libqt5-linguist libqt5-qtbase-private-headers-devel diff --git a/cross/armclang-linux.txt b/cross/armclang-linux.txt index 863b355c345f..36927b882b5b 100644 --- a/cross/armclang-linux.txt +++ b/cross/armclang-linux.txt @@ -22,7 +22,7 @@ c = ['/opt/arm/developmentstudio-2019.0/sw/ARMCompiler6.12/bin/armclang', '--tar #cpp = '/usr/bin/arm-linux-gnueabihf-g++' ar = '/opt/arm/developmentstudio-2019.0/sw/ARMCompiler6.12/bin/armar' #strip = '/usr/arm-linux-gnueabihf/bin/strip' -#pkgconfig = '/usr/bin/arm-linux-gnueabihf-pkg-config' +#pkg-config = '/usr/bin/arm-linux-gnueabihf-pkg-config' [host_machine] system = 'baremetal' diff --git a/cross/linux-mingw-w64-32bit.txt b/cross/linux-mingw-w64-32bit.txt index caf1da1a045f..91ad9c5029a4 100644 --- a/cross/linux-mingw-w64-32bit.txt +++ b/cross/linux-mingw-w64-32bit.txt @@ -4,7 +4,7 @@ cpp = '/usr/bin/i686-w64-mingw32-g++' objc = '/usr/bin/i686-w64-mingw32-gcc' ar = '/usr/bin/i686-w64-mingw32-ar' strip = '/usr/bin/i686-w64-mingw32-strip' -pkgconfig = '/usr/bin/i686-w64-mingw32-pkg-config' +pkg-config = '/usr/bin/i686-w64-mingw32-pkg-config' windres = '/usr/bin/i686-w64-mingw32-windres' exe_wrapper = 'wine' ld = '/usr/bin/i686-w64-mingw32-ld' diff --git a/cross/linux-mingw-w64-64bit.txt b/cross/linux-mingw-w64-64bit.txt index f49fb35bfbd3..08fa70410430 100644 --- a/cross/linux-mingw-w64-64bit.txt +++ b/cross/linux-mingw-w64-64bit.txt @@ -4,9 +4,9 @@ cpp = '/usr/bin/x86_64-w64-mingw32-g++' objc = '/usr/bin/x86_64-w64-mingw32-gcc' ar = '/usr/bin/x86_64-w64-mingw32-ar' strip = '/usr/bin/x86_64-w64-mingw32-strip' -pkgconfig = '/usr/bin/x86_64-w64-mingw32-pkg-config' +pkg-config = '/usr/bin/x86_64-w64-mingw32-pkg-config' windres = '/usr/bin/x86_64-w64-mingw32-windres' -exe_wrapper = 'wine64' +exe_wrapper = 'wine' cmake = '/usr/bin/cmake' [properties] diff --git a/cross/none.txt b/cross/none.txt index 1fbe4713ac09..9eadf974c321 100644 --- a/cross/none.txt +++ b/cross/none.txt @@ -15,5 +15,5 @@ fc = ['false'] objc = ['false'] objcpp = ['false'] ar = ['false'] -pkgconfig = ['false'] +pkg-config = ['false'] cmake = ['false'] diff --git a/cross/ubuntu-armhf.txt b/cross/ubuntu-armhf.txt index 408c2ceaecba..6409e396b577 100644 --- a/cross/ubuntu-armhf.txt +++ b/cross/ubuntu-armhf.txt @@ -6,7 +6,7 @@ cpp = ['/usr/bin/arm-linux-gnueabihf-g++'] rust = ['rustc', '--target', 'arm-unknown-linux-gnueabihf', '-C', 'linker=/usr/bin/arm-linux-gnueabihf-gcc-7'] ar = '/usr/arm-linux-gnueabihf/bin/ar' strip = '/usr/arm-linux-gnueabihf/bin/strip' -pkgconfig = '/usr/bin/arm-linux-gnueabihf-pkg-config' +pkg-config = '/usr/bin/arm-linux-gnueabihf-pkg-config' ld = '/usr/bin/arm-linux/gnueabihf-ld' [built-in options] diff --git a/data/test.schema.json b/data/test.schema.json index a809388b4798..fa5a53e5f77d 100644 --- a/data/test.schema.json +++ b/data/test.schema.json @@ -26,6 +26,7 @@ "exe", "shared_lib", "python_lib", + "python_bytecode", "pdb", "implib", "py_implib", diff --git a/docs/markdown/Machine-files.md b/docs/markdown/Machine-files.md index 9e4b0c2a5f4e..c30076919f5e 100644 --- a/docs/markdown/Machine-files.md +++ b/docs/markdown/Machine-files.md @@ -90,7 +90,7 @@ arch = 'aarch64-linux-gnu' c = arch + '-gcc' cpp = arch + '-g++' strip = arch + '-strip' -pkgconfig = arch + '-pkg-config' +pkg-config = arch + '-pkg-config' ... ``` @@ -165,7 +165,7 @@ c_ld = 'gold' cpp_ld = 'gold' ar = '/usr/i586-mingw32msvc/bin/ar' strip = '/usr/i586-mingw32msvc/bin/strip' -pkgconfig = '/usr/bin/i586-mingw32msvc-pkg-config' +pkg-config = '/usr/bin/i586-mingw32msvc-pkg-config' ``` An incomplete list of internally used programs that can be overridden @@ -179,7 +179,7 @@ here is: - libwmf-config - llvm-config - pcap-config -- pkgconfig +- pkg-config - sdl2-config - wx-config (or wx-3.0-config or wx-config-gtk) diff --git a/docs/markdown/Reference-tables.md b/docs/markdown/Reference-tables.md index 377b36286523..26cbdf037aa1 100644 --- a/docs/markdown/Reference-tables.md +++ b/docs/markdown/Reference-tables.md @@ -127,11 +127,13 @@ set in the cross file. | sh4 | SuperH SH-4 | | sparc | 32 bit SPARC | | sparc64 | SPARC v9 processor | +| sw_64 | 64 bit sunway processor | | wasm32 | 32 bit Webassembly | | wasm64 | 64 bit Webassembly | | x86 | 32 bit x86 processor | | x86_64 | 64 bit x86 processor | + Any cpu family not listed in the above list is not guaranteed to remain stable in future releases. diff --git a/man/meson.1 b/man/meson.1 index c2c49cfbda40..f3b6de733cef 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -1,4 +1,4 @@ -.TH MESON "1" "July 2023" "meson 1.2.0" "User Commands" +.TH MESON "1" "October 2023" "meson 1.2.3" "User Commands" .SH NAME meson - a high productivity build system .SH DESCRIPTION diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py index 68e2b6e5254f..c963b273f324 100644 --- a/mesonbuild/ast/interpreter.py +++ b/mesonbuild/ast/interpreter.py @@ -29,6 +29,7 @@ InvalidArguments, BreakRequest, ContinueRequest, + Disabler, default_resolve_key, ) @@ -53,6 +54,7 @@ NotNode, PlusAssignmentNode, TernaryNode, + TestCaseClauseNode, ) if T.TYPE_CHECKING: @@ -445,3 +447,6 @@ def flatten_kwargs(self, kwargs: T.Dict[str, TYPE_nvar], include_unknown_args: b elif isinstance(val, (str, bool, int, float)) or include_unknown_args: flattened_kwargs[key] = val return flattened_kwargs + + def evaluate_testcase(self, node: TestCaseClauseNode) -> Disabler | None: + return Disabler(subproject=self.subproject) diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index d66e73f3e320..d5203822ea33 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -362,3 +362,22 @@ def analyze(self) -> None: self.sanity_check_ast() self.parse_project() self.run() + + def extract_subproject_dir(self) -> T.Optional[str]: + '''Fast path to extract subproject_dir kwarg. + This is faster than self.parse_project() which also initialize options + and also calls parse_project() on every subproject. + ''' + if not self.ast.lines: + return + project = self.ast.lines[0] + # first line is always project() + if not isinstance(project, FunctionNode): + return + for kw, val in project.args.kwargs.items(): + assert isinstance(kw, IdNode), 'for mypy' + if kw.value == 'subproject_dir': + # mypy does not understand "and isinstance" + if isinstance(val, StringNode): + return val.value + return None diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 1786fef893f3..bc86355847c1 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -237,8 +237,10 @@ def _quoter(x, qf = quote_func): return ninja_quote(qf(str(x))) def write(self, outfile: T.TextIO) -> None: + rspfile_args = self.args if self.rspfile_quote_style is RSPFileSyntax.MSVC: rspfile_quote_func = cmd_quote + rspfile_args = [NinjaCommandArg('$in_newline', arg.quoting) if arg.s == '$in' else arg for arg in rspfile_args] else: rspfile_quote_func = gcc_rsp_quote @@ -253,7 +255,7 @@ def rule_iter(): if rsp == '_RSP': outfile.write(' command = {} @$out.rsp\n'.format(' '.join([self._quoter(x) for x in self.command]))) outfile.write(' rspfile = $out.rsp\n') - outfile.write(' rspfile_content = {}\n'.format(' '.join([self._quoter(x, rspfile_quote_func) for x in self.args]))) + outfile.write(' rspfile_content = {}\n'.format(' '.join([self._quoter(x, rspfile_quote_func) for x in rspfile_args]))) else: outfile.write(' command = {}\n'.format(' '.join([self._quoter(x) for x in self.command + self.args]))) if self.deps: @@ -636,7 +638,7 @@ def generate(self, capture: bool = False, vslite_ctx: dict = None) -> T.Optional key = OptionKey('b_coverage') if (key in self.environment.coredata.options and self.environment.coredata.options[key].value): - gcovr_exe, gcovr_version, lcov_exe, genhtml_exe, _ = environment.find_coverage_tools() + gcovr_exe, gcovr_version, lcov_exe, lcov_version, genhtml_exe, _ = environment.find_coverage_tools() if gcovr_exe or (lcov_exe and genhtml_exe): self.add_build_comment(NinjaComment('Coverage rules')) self.generate_coverage_rules(gcovr_exe, gcovr_version) @@ -658,9 +660,9 @@ def generate(self, capture: bool = False, vslite_ctx: dict = None) -> T.Optional os.replace(tempfilename, outfilename) mlog.cmd_ci_include(outfilename) # For CI debugging # Refresh Ninja's caches. https://github.com/ninja-build/ninja/pull/1685 - if mesonlib.version_compare(self.ninja_version, '>=1.10.0') and os.path.exists('.ninja_deps'): - subprocess.call(self.ninja_command + ['-t', 'restat']) - subprocess.call(self.ninja_command + ['-t', 'cleandead']) + if mesonlib.version_compare(self.ninja_version, '>=1.10.0') and os.path.exists(os.path.join(self.environment.build_dir, '.ninja_log')): + subprocess.call(self.ninja_command + ['-t', 'restat'], cwd=self.environment.build_dir) + subprocess.call(self.ninja_command + ['-t', 'cleandead'], cwd=self.environment.build_dir) self.generate_compdb() self.generate_rust_project_json() @@ -1687,8 +1689,6 @@ def generate_vala_compile(self, target: build.BuildTarget) -> \ # Without this, it will write it inside c_out_dir args += ['--vapi', os.path.join('..', target.vala_vapi)] valac_outputs.append(vapiname) - target.outputs += [target.vala_header, target.vala_vapi] - target.install_tag += ['devel', 'devel'] # Install header and vapi to default locations if user requests this if len(target.install_dir) > 1 and target.install_dir[1] is True: target.install_dir[1] = self.environment.get_includedir() @@ -1699,8 +1699,6 @@ def generate_vala_compile(self, target: build.BuildTarget) -> \ girname = os.path.join(self.get_target_dir(target), target.vala_gir) args += ['--gir', os.path.join('..', target.vala_gir)] valac_outputs.append(girname) - target.outputs.append(target.vala_gir) - target.install_tag.append('devel') # Install GIR to default location if requested by user if len(target.install_dir) > 3 and target.install_dir[3] is True: target.install_dir[3] = os.path.join(self.environment.get_datadir(), 'gir-1.0') @@ -2097,13 +2095,14 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: if a in rustc.native_static_libs: # Exclude link args that rustc already add by default continue - if a.endswith(('.dll', '.so', '.dylib')): + if a.endswith(('.dll', '.so', '.dylib', '.a', '.lib')): dir_, lib = os.path.split(a) linkdirs.add(dir_) lib, ext = os.path.splitext(lib) if lib.startswith('lib'): lib = lib[3:] - args.extend(['-l', f'dylib={lib}']) + _type = 'static' if a.endswith(('.a', '.lib')) else 'dylib' + args.extend(['-l', f'{_type}={lib}']) elif a.startswith('-L'): args.append(a) elif a.startswith('-l'): @@ -2343,7 +2342,7 @@ def generate_static_link_rules(self): if static_linker is None: continue rule = 'STATIC_LINKER{}'.format(self.get_rule_suffix(for_machine)) - cmdlist = [] + cmdlist: T.List[T.Union[str, NinjaCommandArg]] = [] args = ['$in'] # FIXME: Must normalize file names with pathlib.Path before writing # them out to fix this properly on Windows. See: @@ -2357,6 +2356,22 @@ def generate_static_link_rules(self): cmdlist += static_linker.get_exelist() cmdlist += ['$LINK_ARGS'] cmdlist += NinjaCommandArg.list(static_linker.get_output_args('$out'), Quoting.none) + # The default ar on MacOS (at least through version 12), does not + # add extern'd variables to the symbol table by default, and + # requires that apple's ranlib be called with a special flag + # instead after linking + if static_linker.id == 'applear': + # This is a bit of a hack, but we assume that that we won't need + # an rspfile on MacOS, otherwise the arguments are passed to + # ranlib, not to ar + cmdlist.extend(args) + args = [] + # Ensure that we use the user-specified ranlib if any, and + # fallback to just picking up some ranlib otherwise + ranlib = self.environment.lookup_binary_entry(for_machine, 'ranlib') + if ranlib is None: + ranlib = ['ranlib'] + cmdlist.extend(['&&'] + ranlib + ['-c', '$out']) description = 'Linking static target $out' if num_pools > 0: pool = 'pool = link_pool' diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 0a4160fb1c6b..3c62fe8b9ee3 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -719,7 +719,7 @@ def __init__( environment: environment.Environment, compilers: T.Dict[str, 'Compiler'], kwargs): - super().__init__(name, subdir, subproject, True, for_machine, environment) + super().__init__(name, subdir, subproject, True, for_machine, environment, install=kwargs.get('install', False)) self.all_compilers = compilers self.compilers = OrderedDict() # type: OrderedDict[str, Compiler] self.objects: T.List[ObjectTypes] = [] @@ -737,7 +737,6 @@ def __init__( # The list of all files outputted by this target. Useful in cases such # as Vala which generates .vapi and .h besides the compiled output. self.outputs = [self.filename] - self.need_install = False self.pch: T.Dict[str, T.List[str]] = {} self.extra_args: T.Dict[str, T.List['FileOrString']] = {} self.sources: T.List[File] = [] @@ -789,6 +788,12 @@ def post_init(self) -> None: # relocation-model=pic is rustc's default and Meson does not # currently have a way to disable PIC. self.pic = True + if 'vala' in self.compilers and self.is_linkable_target(): + self.outputs += [self.vala_header, self.vala_vapi] + self.install_tag += ['devel', 'devel'] + if self.vala_gir: + self.outputs.append(self.vala_gir) + self.install_tag.append('devel') def __repr__(self): repr_str = "<{0} {1}: {2}>" @@ -803,7 +808,7 @@ def is_unity(self) -> bool: return unity_opt == 'on' or (unity_opt == 'subprojects' and self.subproject != '') def validate_install(self): - if self.for_machine is MachineChoice.BUILD and self.need_install: + if self.for_machine is MachineChoice.BUILD and self.install: if self.environment.is_cross_build(): raise InvalidArguments('Tried to install a target for the build machine in a cross build.') else: @@ -1089,8 +1094,6 @@ def get_custom_install_mode(self) -> T.Optional['FileMode']: def process_kwargs(self, kwargs): self.process_kwargs_base(kwargs) self.original_kwargs = kwargs - kwargs.get('modules', []) - self.need_install = kwargs.get('install', self.need_install) for lang in all_languages: lang_args = extract_as_list(kwargs, f'{lang}_args') @@ -1325,7 +1328,7 @@ def get_generated_sources(self) -> T.List['GeneratedTypes']: return self.generated def should_install(self) -> bool: - return self.need_install + return self.install def has_pch(self) -> bool: return bool(self.pch) @@ -1400,17 +1403,6 @@ def is_internal(self) -> bool: def link(self, targets): for t in targets: - if isinstance(self, StaticLibrary) and self.need_install: - if isinstance(t, (CustomTarget, CustomTargetIndex)): - if not t.should_install(): - mlog.warning(f'Try to link an installed static library target {self.name} with a' - 'custom target that is not installed, this might cause problems' - 'when you try to use this static library') - elif t.is_internal(): - # When we're a static library and we link_with to an - # internal/convenience library, promote to link_whole. - self.link_whole([t]) - continue if not isinstance(t, (Target, CustomTargetIndex)): if isinstance(t, dependencies.ExternalLibrary): raise MesonException(textwrap.dedent('''\ @@ -1423,6 +1415,11 @@ def link(self, targets): raise InvalidArguments(f'{t!r} is not a target.') if not t.is_linkable_target(): raise InvalidArguments(f"Link target '{t!s}' is not linkable.") + if isinstance(self, StaticLibrary) and self.install and t.is_internal(): + # When we're a static library and we link_with to an + # internal/convenience library, promote to link_whole. + self.link_whole([t], promoted=True) + continue if isinstance(self, SharedLibrary) and isinstance(t, StaticLibrary) and not t.pic: msg = f"Can't link non-PIC static library {t.name!r} into shared library {self.name!r}. " msg += "Use the 'pic' option to static_library to build with PIC." @@ -1435,7 +1432,7 @@ def link(self, targets): mlog.warning(msg + ' This will fail in cross build.') self.link_targets.append(t) - def link_whole(self, targets): + def link_whole(self, targets, promoted: bool = False): for t in targets: if isinstance(t, (CustomTarget, CustomTargetIndex)): if not t.is_linkable_target(): @@ -1455,40 +1452,49 @@ def link_whole(self, targets): else: mlog.warning(msg + ' This will fail in cross build.') if isinstance(self, StaticLibrary) and not self.uses_rust(): - if isinstance(t, (CustomTarget, CustomTargetIndex)) or t.uses_rust(): - # There are cases we cannot do this, however. In Rust, for - # example, this can't be done with Rust ABI libraries, though - # it could be done with C ABI libraries, though there are - # several meson issues that need to be fixed: - # https://github.com/mesonbuild/meson/issues/10722 - # https://github.com/mesonbuild/meson/issues/10723 - # https://github.com/mesonbuild/meson/issues/10724 - # FIXME: We could extract the .a archive to get object files - raise InvalidArguments('Cannot link_whole a custom or Rust target into a static library') # When we're a static library and we link_whole: to another static # library, we need to add that target's objects to ourselves. + self.check_can_extract_objects(t, origin=self, promoted=promoted) self.objects += [t.extract_all_objects()] # If we install this static library we also need to include objects # from all uninstalled static libraries it depends on. - if self.need_install: - for lib in t.get_internal_static_libraries(): + if self.install: + for lib in t.get_internal_static_libraries(origin=self): self.objects += [lib.extract_all_objects()] self.link_whole_targets.append(t) @lru_cache(maxsize=None) - def get_internal_static_libraries(self) -> OrderedSet[Target]: + def get_internal_static_libraries(self, origin: StaticLibrary) -> OrderedSet[Target]: result: OrderedSet[Target] = OrderedSet() - self.get_internal_static_libraries_recurse(result) + self.get_internal_static_libraries_recurse(result, origin) return result - def get_internal_static_libraries_recurse(self, result: OrderedSet[Target]) -> None: + def get_internal_static_libraries_recurse(self, result: OrderedSet[Target], origin: StaticLibrary) -> None: for t in self.link_targets: if t.is_internal() and t not in result: + self.check_can_extract_objects(t, origin, promoted=True) result.add(t) - t.get_internal_static_libraries_recurse(result) + t.get_internal_static_libraries_recurse(result, origin) for t in self.link_whole_targets: if t.is_internal(): - t.get_internal_static_libraries_recurse(result) + t.get_internal_static_libraries_recurse(result, origin) + + def check_can_extract_objects(self, t: T.Union[Target, CustomTargetIndex], origin: StaticLibrary, promoted: bool = False) -> None: + if isinstance(t, (CustomTarget, CustomTargetIndex)) or t.uses_rust(): + # To extract objects from a custom target we would have to extract + # the archive, WIP implementation can be found in + # https://github.com/mesonbuild/meson/pull/9218. + # For Rust C ABI we could in theory have access to objects, but there + # are several meson issues that need to be fixed: + # https://github.com/mesonbuild/meson/issues/10722 + # https://github.com/mesonbuild/meson/issues/10723 + # https://github.com/mesonbuild/meson/issues/10724 + m = (f'Cannot link_whole a custom or Rust target {t.name!r} into a static library {origin.name!r}. ' + 'Instead, pass individual object files with the "objects:" keyword argument if possible.') + if promoted: + m += (f' Meson had to promote link to link_whole because {origin.name!r} is installed but not {t.name!r},' + f' and thus has to include objects from {t.name!r} to be usable.') + raise InvalidArguments(m) def add_pch(self, language: str, pchlist: T.List[str]) -> None: if not pchlist: @@ -1962,7 +1968,7 @@ def post_init(self) -> None: self.filename = self.name if self.suffix: self.filename += '.' + self.suffix - self.outputs = [self.filename] + self.outputs[0] = self.filename # The import library this target will generate self.import_filename = None @@ -2111,7 +2117,7 @@ def post_init(self) -> None: else: self.suffix = 'a' self.filename = self.prefix + self.name + '.' + self.suffix - self.outputs = [self.filename] + self.outputs[0] = self.filename def get_link_deps_mapping(self, prefix: str) -> T.Mapping[str, str]: return {} @@ -2135,7 +2141,7 @@ def is_linkable_target(self): return True def is_internal(self) -> bool: - return not self.need_install + return not self.install class SharedLibrary(BuildTarget): known_kwargs = known_shlib_kwargs @@ -2402,7 +2408,7 @@ def process_kwargs(self, kwargs): elif isinstance(path, File): # When passing a generated file. self.vs_module_defs = path - elif hasattr(path, 'get_filename'): + elif isinstance(path, CustomTarget): # When passing output of a Custom Target self.vs_module_defs = File.from_built_file(path.subdir, path.get_filename()) else: diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 7e8c327f0334..1c57ab4ef88a 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -154,6 +154,8 @@ def _find_best_cpp_std(self, cpp_std: str) -> str: 'gnu++17': 'gnu++1z', 'c++20': 'c++2a', 'gnu++20': 'gnu++2a', + 'c++23': 'c++2b', + 'gnu++23': 'gnu++2b', } # Currently, remapping is only supported for Clang, Elbrus and GCC @@ -222,6 +224,9 @@ def language_stdlib_only_link_flags(self, env: Environment) -> T.List[str]: class ClangCPPCompiler(_StdCPPLibMixin, ClangCompiler, CPPCompiler): + + _CPP23_VERSION = '>=12.0.0' + def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, linker: T.Optional['DynamicLinker'] = None, @@ -248,11 +253,15 @@ def get_options(self) -> 'MutableKeyedOptionDictType': ), key.evolve('rtti'): coredata.UserBooleanOption('Enable RTTI', True), }) - opts[key.evolve('std')].choices = [ + cppstd_choices = [ 'none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'c++1z', 'c++2a', 'c++20', 'gnu++11', 'gnu++14', 'gnu++17', 'gnu++1z', 'gnu++2a', 'gnu++20', ] + if version_compare(self.version, self._CPP23_VERSION): + cppstd_choices.append('c++23') + cppstd_choices.append('gnu++23') + opts[key.evolve('std')].choices = cppstd_choices if self.info.is_windows() or self.info.is_cygwin(): opts.update({ key.evolve('winlibs'): coredata.UserArrayOption( @@ -294,7 +303,8 @@ class ArmLtdClangCPPCompiler(ClangCPPCompiler): class AppleClangCPPCompiler(ClangCPPCompiler): - pass + + _CPP23_VERSION = '>=13.0.0' class EmscriptenCPPCompiler(EmscriptenMixin, ClangCPPCompiler): @@ -411,7 +421,7 @@ def get_options(self) -> 'MutableKeyedOptionDictType': 'c++2a', 'c++20', 'gnu++03', 'gnu++11', 'gnu++14', 'gnu++17', 'gnu++1z', 'gnu++2a', 'gnu++20', ] - if version_compare(self.version, '>=12.2.0'): + if version_compare(self.version, '>=11.0.0'): cppstd_choices.append('c++23') cppstd_choices.append('gnu++23') opts[key].choices = cppstd_choices diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index 210ec4d4065a..58d6f4e64e58 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -1034,7 +1034,7 @@ def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> Rust extra_args['machine'] = cc.linker.machine else: exelist = cc.linker.exelist + cc.linker.get_always_args() - if 'ccache' in exelist[0]: + if os.path.basename(exelist[0]) in {'ccache', 'sccache'}: del exelist[0] c = exelist.pop(0) compiler.extend(cls.use_linker_args(c, '')) diff --git a/mesonbuild/compilers/java.py b/mesonbuild/compilers/java.py index 42cf0a159ce0..9f508d637313 100644 --- a/mesonbuild/compilers/java.py +++ b/mesonbuild/compilers/java.py @@ -100,7 +100,7 @@ def sanity_check(self, work_dir: str, environment: 'Environment') -> None: raise EnvironmentException(f'Java compiler {self.name_string()} cannot compile programs.') runner = shutil.which(self.javarunner) if runner: - cmdlist = [runner, obj] + cmdlist = [runner, '-cp', '.', obj] pe = subprocess.Popen(cmdlist, cwd=work_dir) pe.wait() if pe.returncode != 0: diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index da361856ca72..e4cae47f61f9 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -1066,19 +1066,22 @@ def get_library_naming(self, env: 'Environment', libtype: LibType, strict: bool @staticmethod def _sort_shlibs_openbsd(libs: T.List[str]) -> T.List[str]: - filtered: T.List[str] = [] + def tuple_key(x: str) -> T.Tuple[int, ...]: + ver = x.rsplit('.so.', maxsplit=1)[1] + return tuple(int(i) for i in ver.split('.')) + + filtered = [] # type: T.List[str] for lib in libs: # Validate file as a shared library of type libfoo.so.X.Y ret = lib.rsplit('.so.', maxsplit=1) if len(ret) != 2: continue try: - float(ret[1]) + tuple(int(i) for i in ret[1].split('.')) except ValueError: continue filtered.append(lib) - float_cmp = lambda x: float(x.rsplit('.so.', maxsplit=1)[1]) - return sorted(filtered, key=float_cmp, reverse=True) + return sorted(filtered, key=tuple_key, reverse=True) @classmethod def _get_trials_from_pattern(cls, pattern: str, directory: str, libname: str) -> T.List[Path]: diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index a6178f04dc26..890de163d164 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -89,9 +89,10 @@ def get_genvs_default_buildtype_list() -> list[str]: class MesonVersionMismatchException(MesonException): '''Build directory generated with Meson version is incompatible with current version''' - def __init__(self, old_version: str, current_version: str) -> None: + def __init__(self, old_version: str, current_version: str, extra_msg: str = '') -> None: super().__init__(f'Build directory has been generated with Meson version {old_version}, ' - f'which is incompatible with the current version {current_version}.') + f'which is incompatible with the current version {current_version}.' + + extra_msg) self.old_version = old_version self.current_version = current_version @@ -807,7 +808,7 @@ def update_project_options(self, options: 'MutableKeyedOptionDictType') -> None: continue oldval = self.options[key] - if type(oldval) != type(value): + if type(oldval) is not type(value): self.options[key] = value elif oldval.choices != value.choices: # If the choices have changed, use the new value, but attempt @@ -992,9 +993,11 @@ def _parse_section(self, s: str) -> T.Dict[str, T.Union[str, bool, int, T.List[s value = value.replace('\\', '\\\\') try: ast = mparser.Parser(value, 'machinefile').parse() + if not ast.lines: + raise EnvironmentException('value cannot be empty') res = self._evaluate_statement(ast.lines[0]) - except MesonException: - raise EnvironmentException(f'Malformed value in machine file variable {entry!r}.') + except MesonException as e: + raise EnvironmentException(f'Malformed value in machine file variable {entry!r}: {str(e)}.') except KeyError as e: raise EnvironmentException(f'Undefined constant {e.args[0]!r} in machine file variable {entry!r}.') section[entry] = res @@ -1091,9 +1094,9 @@ def major_versions_differ(v1: str, v2: str) -> bool: # Major version differ, or one is development version but not the other. return v1_major != v2_major or ('99' in {v1_minor, v2_minor} and v1_minor != v2_minor) -def load(build_dir: str) -> CoreData: +def load(build_dir: str, suggest_reconfigure: bool = True) -> CoreData: filename = os.path.join(build_dir, 'meson-private', 'coredata.dat') - return pickle_load(filename, 'Coredata', CoreData) + return pickle_load(filename, 'Coredata', CoreData, suggest_reconfigure) def save(obj: CoreData, build_dir: str) -> str: diff --git a/mesonbuild/dependencies/cmake.py b/mesonbuild/dependencies/cmake.py index 8827c9abdcd8..0e42ad3a70e2 100644 --- a/mesonbuild/dependencies/cmake.py +++ b/mesonbuild/dependencies/cmake.py @@ -225,7 +225,7 @@ def process_paths(l: T.List[str]) -> T.Set[str]: module_paths = [x for x in module_paths if os.path.isdir(x)] archs = temp_parser.get_cmake_var('MESON_ARCH_LIST') - common_paths = ['lib', 'lib32', 'lib64', 'libx32', 'share'] + common_paths = ['lib', 'lib32', 'lib64', 'libx32', 'share', ''] for i in archs: common_paths += [os.path.join('lib', i)] @@ -652,3 +652,19 @@ def get_variable(self, *, cmake: T.Optional[str] = None, pkgconfig: T.Optional[s if default_value is not None: return default_value raise DependencyException(f'Could not get cmake variable and no default provided for {self!r}') + + +class CMakeDependencyFactory: + + def __init__(self, name: T.Optional[str] = None, modules: T.Optional[T.List[str]] = None): + self.name = name + self.modules = modules + + def __call__(self, name: str, env: Environment, kwargs: T.Dict[str, T.Any], language: T.Optional[str] = None, force_use_global_compilers: bool = False) -> CMakeDependency: + if self.modules: + kwargs['modules'] = self.modules + return CMakeDependency(self.name or name, env, kwargs, language, force_use_global_compilers) + + @staticmethod + def log_tried() -> str: + return CMakeDependency.log_tried() diff --git a/mesonbuild/dependencies/cuda.py b/mesonbuild/dependencies/cuda.py index af0ae4b1f569..f1c890ba807b 100644 --- a/mesonbuild/dependencies/cuda.py +++ b/mesonbuild/dependencies/cuda.py @@ -45,8 +45,18 @@ def __init__(self, environment: 'Environment', kwargs: T.Dict[str, T.Any]) -> No super().__init__('cuda', environment, kwargs, language=language) self.lib_modules: T.Dict[str, T.List[str]] = {} self.requested_modules = self.get_requested(kwargs) - if 'cudart' not in self.requested_modules: - self.requested_modules = ['cudart'] + self.requested_modules + if not any(runtime in self.requested_modules for runtime in ['cudart', 'cudart_static']): + # By default, we prefer to link the static CUDA runtime, since this is what nvcc also does by default: + # https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html#cudart-none-shared-static-cudart + req_modules = ['cudart'] + if kwargs.get('static', True): + req_modules = ['cudart_static'] + machine = self.env.machines[self.for_machine] + if machine.is_linux(): + # extracted by running + # nvcc -v foo.o + req_modules += ['rt', 'pthread', 'dl'] + self.requested_modules = req_modules + self.requested_modules (self.cuda_path, self.version, self.is_found) = self._detect_cuda_path_and_version() if not self.is_found: diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py index ec7015103b7a..09f55b705d42 100644 --- a/mesonbuild/dependencies/dev.py +++ b/mesonbuild/dependencies/dev.py @@ -536,7 +536,7 @@ def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T. else: libs = ['z'] for lib in libs: - l = self.clib_compiler.find_library(lib, environment, []) + l = self.clib_compiler.find_library(lib, environment, [], self.libtype) h = self.clib_compiler.has_header('zlib.h', '', environment, dependencies=[self]) if l and h[0]: self.is_found = True diff --git a/mesonbuild/dependencies/dub.py b/mesonbuild/dependencies/dub.py index 37a9ea11e5eb..c206ce9e73bf 100644 --- a/mesonbuild/dependencies/dub.py +++ b/mesonbuild/dependencies/dub.py @@ -53,7 +53,7 @@ def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T. self.is_found = False return - (self.dubbin, dubver) = DubDependency.class_dubbin + (self.dubbin, dubver) = DubDependency.class_dubbin # pylint: disable=unpacking-non-sequence assert isinstance(self.dubbin, ExternalProgram) diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index d77566961a3c..b2df20886566 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -23,7 +23,7 @@ from .. import mlog from .base import DependencyException, DependencyMethods from .base import BuiltinDependency, SystemDependency -from .cmake import CMakeDependency +from .cmake import CMakeDependency, CMakeDependencyFactory from .configtool import ConfigToolDependency from .detect import packages from .factory import DependencyFactory, factory_methods @@ -85,6 +85,8 @@ def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]): class OpenMPDependency(SystemDependency): # Map date of specification release (which is the macro value) to a version. VERSIONS = { + '202111': '5.2', + '202011': '5.1', '201811': '5.0', '201611': '5.0-revision1', # This is supported by ICC 19.x '201511': '4.5', @@ -599,19 +601,19 @@ def shaderc_factory(env: 'Environment', 'openssl', [DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM, DependencyMethods.CMAKE], system_class=OpensslSystemDependency, - cmake_class=lambda name, env, kwargs: CMakeDependency('OpenSSL', env, dict(kwargs, modules=['OpenSSL::Crypto', 'OpenSSL::SSL'])), + cmake_class=CMakeDependencyFactory('OpenSSL', modules=['OpenSSL::Crypto', 'OpenSSL::SSL']), ) packages['libcrypto'] = libcrypto_factory = DependencyFactory( 'libcrypto', [DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM, DependencyMethods.CMAKE], system_class=OpensslSystemDependency, - cmake_class=lambda name, env, kwargs: CMakeDependency('OpenSSL', env, dict(kwargs, modules=['OpenSSL::Crypto'])), + cmake_class=CMakeDependencyFactory('OpenSSL', modules=['OpenSSL::Crypto']), ) packages['libssl'] = libssl_factory = DependencyFactory( 'libssl', [DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM, DependencyMethods.CMAKE], system_class=OpensslSystemDependency, - cmake_class=lambda name, env, kwargs: CMakeDependency('OpenSSL', env, dict(kwargs, modules=['OpenSSL::SSL'])), + cmake_class=CMakeDependencyFactory('OpenSSL', modules=['OpenSSL::SSL']), ) diff --git a/mesonbuild/dependencies/python.py b/mesonbuild/dependencies/python.py index 186a6830ddbf..244cd31fe0e2 100644 --- a/mesonbuild/dependencies/python.py +++ b/mesonbuild/dependencies/python.py @@ -119,7 +119,9 @@ def sanity(self) -> bool: with context_mgr as f: cmd = self.get_command() + [str(f)] - p, stdout, stderr = mesonlib.Popen_safe(cmd) + env = os.environ.copy() + env['SETUPTOOLS_USE_DISTUTILS'] = 'stdlib' + p, stdout, stderr = mesonlib.Popen_safe(cmd, env=env) try: info = json.loads(stdout) diff --git a/mesonbuild/dependencies/qt.py b/mesonbuild/dependencies/qt.py index 1a86bd28a8ea..ba9b42032e48 100644 --- a/mesonbuild/dependencies/qt.py +++ b/mesonbuild/dependencies/qt.py @@ -53,7 +53,7 @@ def _qt_get_private_includes(mod_inc_dir: str, module: str, mod_version: str) -> private_dir = os.path.join(mod_inc_dir, mod_version) # fallback, let's try to find a directory with the latest version - if not os.path.exists(private_dir): + if os.path.isdir(mod_inc_dir) and not os.path.exists(private_dir): dirs = [filename for filename in os.listdir(mod_inc_dir) if os.path.isdir(os.path.join(mod_inc_dir, filename))] diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py index 7e0c56703121..cd464fda1208 100644 --- a/mesonbuild/envconfig.py +++ b/mesonbuild/envconfig.py @@ -68,6 +68,7 @@ 'sh4', 'sparc', 'sparc64', + 'sw_64', 'wasm32', 'wasm64', 'x86', @@ -86,6 +87,7 @@ 'riscv64', 's390x', 'sparc64', + 'sw_64', 'wasm64', 'x86_64', ] diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index d22fc2904710..52bd0a66951e 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -96,6 +96,20 @@ def detect_gcovr(min_version: str = '3.3', log: bool = False): return gcovr_exe, found return None, None +def detect_lcov(log: bool = False): + lcov_exe = 'lcov' + try: + p, found = Popen_safe([lcov_exe, '--version'])[0:2] + except (FileNotFoundError, PermissionError): + # Doesn't exist in PATH or isn't executable + return None, None + found = search_version(found) + if p.returncode == 0 and found: + if log: + mlog.log('Found lcov-{} at {}'.format(found, quote_arg(shutil.which(lcov_exe)))) + return lcov_exe, found + return None, None + def detect_llvm_cov(): tools = get_llvm_tool_names('llvm-cov') for tool in tools: @@ -103,20 +117,18 @@ def detect_llvm_cov(): return tool return None -def find_coverage_tools() -> T.Tuple[T.Optional[str], T.Optional[str], T.Optional[str], T.Optional[str], T.Optional[str]]: +def find_coverage_tools() -> T.Tuple[T.Optional[str], T.Optional[str], T.Optional[str], T.Optional[str], T.Optional[str], T.Optional[str]]: gcovr_exe, gcovr_version = detect_gcovr() llvm_cov_exe = detect_llvm_cov() - lcov_exe = 'lcov' + lcov_exe, lcov_version = detect_lcov() genhtml_exe = 'genhtml' - if not mesonlib.exe_exists([lcov_exe, '--version']): - lcov_exe = None if not mesonlib.exe_exists([genhtml_exe, '--version']): genhtml_exe = None - return gcovr_exe, gcovr_version, lcov_exe, genhtml_exe, llvm_cov_exe + return gcovr_exe, gcovr_version, lcov_exe, lcov_version, genhtml_exe, llvm_cov_exe def detect_ninja(version: str = '1.8.2', log: bool = False) -> T.List[str]: r = detect_ninja_command_and_version(version, log) @@ -157,6 +169,7 @@ def get_llvm_tool_names(tool: str) -> T.List[str]: # unless it becomes a stable release. suffixes = [ '', # base (no suffix) + '-17', '17', '-16', '16', '-15', '15', '-14', '14', @@ -410,6 +423,11 @@ def detect_cpu(compilers: CompilersDict) -> str: def detect_kernel(system: str) -> T.Optional[str]: if system == 'sunos': + # Solaris 5.10 uname doesn't support the -o switch, and illumos started + # with version 5.11 so shortcut the logic to report 'solaris' in such + # cases where the version is 5.10 or below. + if mesonlib.version_compare(platform.uname().release, '<=5.10'): + return 'solaris' # This needs to be /usr/bin/uname because gnu-uname could be installed and # won't provide the necessary information p, out, _ = Popen_safe(['/usr/bin/uname', '-o']) @@ -490,7 +508,7 @@ def __init__(self, source_dir: T.Optional[str], build_dir: T.Optional[str], opti os.makedirs(self.log_dir, exist_ok=True) os.makedirs(self.info_dir, exist_ok=True) try: - self.coredata: coredata.CoreData = coredata.load(self.get_build_dir()) + self.coredata: coredata.CoreData = coredata.load(self.get_build_dir(), suggest_reconfigure=False) self.first_invocation = False except FileNotFoundError: self.create_new_coredata(options) @@ -508,7 +526,7 @@ def __init__(self, source_dir: T.Optional[str], build_dir: T.Optional[str], opti coredata.read_cmd_line_file(self.build_dir, options) self.create_new_coredata(options) else: - raise e + raise MesonException(f'{str(e)} Try regenerating using "meson setup --wipe".') else: # Just create a fresh coredata in this case self.scratch_dir = '' diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 235129289751..bdf6bed3d3d7 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -37,6 +37,7 @@ from ..interpreterbase import ObjectHolder, ContextManagerObject from ..modules import ExtensionModule, ModuleObject, MutableModuleObject, NewExtensionModule, NotFoundExtensionModule from ..backend.backends import ExecutableSerialisation +from ..optinterpreter import optname_regex from . import interpreterobjects as OBJ from . import compiler as compilerOBJ @@ -1101,6 +1102,10 @@ def func_get_option(self, nodes: mparser.BaseNode, args: T.Tuple[str], raise InterpreterException('Having a colon in option name is forbidden, ' 'projects are not allowed to directly access ' 'options of other subprojects.') + + if optname_regex.search(optname.split('.', maxsplit=1)[-1]) is not None: + raise InterpreterException(f'Invalid option name {optname!r}') + opt = self.get_option_internal(optname) if isinstance(opt, coredata.UserFeatureOption): opt.name = optname @@ -3284,7 +3289,6 @@ def build_target_decorator_caller(self, node, args, kwargs): kwargs['include_directories'] = self.extract_incdirs(kwargs) target = targetclass(name, self.subdir, self.subproject, for_machine, srcs, struct, objs, self.environment, self.compilers[for_machine], kwargs) - target.project_version = self.project_version self.add_target(name, target) self.project_args_frozen = True diff --git a/mesonbuild/interpreterbase/baseobjects.py b/mesonbuild/interpreterbase/baseobjects.py index d5b8c947624c..4966978a0c2b 100644 --- a/mesonbuild/interpreterbase/baseobjects.py +++ b/mesonbuild/interpreterbase/baseobjects.py @@ -119,12 +119,12 @@ def op_equals(self, other: TYPE_var) -> bool: # We use `type(...) == type(...)` here to enforce an *exact* match for comparison. We # don't want comparisons to be possible where `isinstance(derived_obj, type(base_obj))` # would pass because this comparison must never be true: `derived_obj == base_obj` - if type(self) != type(other): + if type(self) is not type(other): self._throw_comp_exception(other, '==') return self == other def op_not_equals(self, other: TYPE_var) -> bool: - if type(self) != type(other): + if type(self) is not type(other): self._throw_comp_exception(other, '!=') return self != other @@ -157,12 +157,12 @@ def display_name(self) -> str: # Override default comparison operators for the held object def op_equals(self, other: TYPE_var) -> bool: # See the comment from InterpreterObject why we are using `type()` here. - if type(self.held_object) != type(other): + if type(self.held_object) is not type(other): self._throw_comp_exception(other, '==') return self.held_object == other def op_not_equals(self, other: TYPE_var) -> bool: - if type(self.held_object) != type(other): + if type(self.held_object) is not type(other): self._throw_comp_exception(other, '!=') return self.held_object != other diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index 8f413c857901..7b3202c45db1 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -146,6 +146,8 @@ class VisualStudioLinker(VisualStudioLikeLinker, StaticLinker): """Microsoft's lib static linker.""" + id = 'lib' + def __init__(self, exelist: T.List[str], machine: str): StaticLinker.__init__(self, exelist) VisualStudioLikeLinker.__init__(self, machine) @@ -155,6 +157,8 @@ class IntelVisualStudioLinker(VisualStudioLikeLinker, StaticLinker): """Intel's xilib static linker.""" + id = 'xilib' + def __init__(self, exelist: T.List[str], machine: str): StaticLinker.__init__(self, exelist) VisualStudioLikeLinker.__init__(self, machine) diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index daac9edb6dab..697f4f3e52b1 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -112,7 +112,7 @@ def find_tool(self, name: str, depname: str, varname: str, required: bool = True if dep.found() and dep.type_name == 'pkgconfig': value = dep.get_variable(pkgconfig=varname) if value: - return ExternalProgram(name, [value]) + return ExternalProgram(value) # Normal program lookup return self.find_program(name, required=required, wanted=wanted) @@ -167,6 +167,8 @@ def process_include_dirs(self, dirs: T.Iterable[T.Union[str, IncludeDirs]]) -> T else: yield self._interpreter.build_incdir_object([d]) + def add_language(self, lang: str, for_machine: MachineChoice) -> None: + self._interpreter.add_languages([lang], True, for_machine) class ModuleObject(HoldableObject): """Base class for all objects returned by modules diff --git a/mesonbuild/modules/fs.py b/mesonbuild/modules/fs.py index 7d969958838d..53174d2316aa 100644 --- a/mesonbuild/modules/fs.py +++ b/mesonbuild/modules/fs.py @@ -261,8 +261,10 @@ def read(self, state: 'ModuleState', args: T.Tuple['FileOrString'], kwargs: 'Rea try: with open(path, encoding=encoding) as f: data = f.read() + except FileNotFoundError: + raise MesonException(f'File {args[0]} does not exist.') except UnicodeDecodeError: - raise MesonException(f'decoding failed for {path}') + raise MesonException(f'decoding failed for {args[0]}') # Reconfigure when this file changes as it can contain data used by any # part of the build configuration (e.g. `project(..., version: # fs.read_file('VERSION')` or `configure_file(...)` diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 7a9acea0009f..2ce039f099b4 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -203,6 +203,8 @@ class MkEnums(_MkEnumsCommon): vtail: T.Optional[str] depends: T.List[T.Union[BuildTarget, CustomTarget, CustomTargetIndex]] + ToolType = T.Union[Executable, ExternalProgram, OverrideProgram] + # Differs from the CustomTarget version in that it straight defaults to True _BUILD_BY_DEFAULT: KwargInfo[bool] = KwargInfo( @@ -317,6 +319,22 @@ def _print_gdbus_warning() -> None: mlog.bold('https://github.com/mesonbuild/meson/issues/1387'), once=True, fatal=False) + @staticmethod + def _find_tool(state: 'ModuleState', tool: str) -> 'ToolType': + tool_map = { + 'gio-querymodules': 'gio-2.0', + 'glib-compile-schemas': 'gio-2.0', + 'glib-compile-resources': 'gio-2.0', + 'gdbus-codegen': 'gio-2.0', + 'glib-genmarshal': 'glib-2.0', + 'glib-mkenums': 'glib-2.0', + 'g-ir-scanner': 'gobject-introspection-1.0', + 'g-ir-compiler': 'gobject-introspection-1.0', + } + depname = tool_map[tool] + varname = tool.replace('-', '_') + return state.find_tool(tool, depname, varname) + @typed_kwargs( 'gnome.post_install', KwargInfo('glib_compile_schemas', bool, default=False), @@ -332,7 +350,7 @@ def post_install(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: ' datadir_abs = os.path.join(state.environment.get_prefix(), state.environment.get_datadir()) if kwargs['glib_compile_schemas'] and not self.install_glib_compile_schemas: self.install_glib_compile_schemas = True - prog = state.find_tool('glib-compile-schemas', 'gio-2.0', 'glib_compile_schemas') + prog = self._find_tool(state, 'glib-compile-schemas') schemasdir = os.path.join(datadir_abs, 'glib-2.0', 'schemas') script = state.backend.get_executable_serialisation([prog, schemasdir]) script.skip_if_destdir = True @@ -340,7 +358,7 @@ def post_install(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: ' for d in kwargs['gio_querymodules']: if d not in self.install_gio_querymodules: self.install_gio_querymodules.append(d) - prog = state.find_tool('gio-querymodules', 'gio-2.0', 'gio_querymodules') + prog = self._find_tool(state, 'gio-querymodules') moduledir = os.path.join(state.environment.get_prefix(), d) script = state.backend.get_executable_serialisation([prog, moduledir]) script.skip_if_destdir = True @@ -390,8 +408,8 @@ def compile_resources(self, state: 'ModuleState', args: T.Tuple[str, 'FileOrStri self.__print_gresources_warning(state) glib_version = self._get_native_glib_version(state) - glib_compile_resources = state.find_program('glib-compile-resources') - cmd: T.List[T.Union[ExternalProgram, Executable, OverrideProgram, str]] = [glib_compile_resources, '@INPUT@'] + glib_compile_resources = self._find_tool(state, 'glib-compile-resources') + cmd: T.List[T.Union['ToolType', str]] = [glib_compile_resources, '@INPUT@'] source_dirs = kwargs['source_dir'] dependencies = kwargs['dependencies'] @@ -481,7 +499,7 @@ def compile_resources(self, state: 'ModuleState', args: T.Tuple[str, 'FileOrStri raise MesonException('GResource header is installed yet export is not enabled') depfile: T.Optional[str] = None - target_cmd: T.List[T.Union[ExternalProgram, Executable, OverrideProgram, str]] + target_cmd: T.List[T.Union['ToolType', str]] if not mesonlib.version_compare(glib_version, gresource_dep_needed_version): # This will eventually go out of sync if dependencies are added target_cmd = cmd @@ -780,8 +798,8 @@ def _get_gir_dep(self, state: 'ModuleState') -> T.Tuple[Dependency, T.Union[buil T.Union[build.Executable, 'ExternalProgram', 'OverrideProgram']]: if not self.gir_dep: self.gir_dep = state.dependency('gobject-introspection-1.0') - self.giscanner = state.find_tool('g-ir-scanner', 'gobject-introspection-1.0', 'g_ir_scanner') - self.gicompiler = state.find_tool('g-ir-compiler', 'gobject-introspection-1.0', 'g_ir_compiler') + self.giscanner = self._find_tool(state, 'g-ir-scanner') + self.gicompiler = self._find_tool(state, 'g-ir-compiler') return self.gir_dep, self.giscanner, self.gicompiler @functools.lru_cache(maxsize=None) @@ -1113,6 +1131,9 @@ def _get_scanner_ldflags(ldflags: T.Iterable[str]) -> T.Iterable[str]: ) def generate_gir(self, state: 'ModuleState', args: T.Tuple[T.List[T.Union[build.Executable, build.SharedLibrary, build.StaticLibrary]]], kwargs: 'GenerateGir') -> ModuleReturnValue: + # Ensure we have a C compiler even in C++ projects. + state.add_language('c', MachineChoice.HOST) + girtargets = [self._unwrap_gir_target(arg, state) for arg in args[0]] if len(girtargets) > 1 and any(isinstance(el, build.Executable) for el in girtargets): raise MesonException('generate_gir only accepts a single argument when one of the arguments is an executable') @@ -1227,7 +1248,7 @@ def compile_schemas(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs srcdir = os.path.join(state.build_to_src, state.subdir) outdir = state.subdir - cmd: T.List[T.Union[ExternalProgram, Executable, OverrideProgram, str]] = [state.find_program('glib-compile-schemas'), '--targetdir', outdir, srcdir] + cmd: T.List[T.Union['ToolType', str]] = [self._find_tool(state, 'glib-compile-schemas'), '--targetdir', outdir, srcdir] if state.subdir == '': targetname = 'gsettings-compile' else: @@ -1427,6 +1448,9 @@ def gtkdoc(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'GtkDoc') -> namespace = kwargs['namespace'] + # Ensure we have a C compiler even in C++ projects. + state.add_language('c', MachineChoice.HOST) + def abs_filenames(files: T.Iterable['FileOrString']) -> T.Iterator[str]: for f in files: if isinstance(f, mesonlib.File): @@ -1592,7 +1616,7 @@ def gdbus_codegen(self, state: 'ModuleState', args: T.Tuple[str, T.Optional[T.Un kwargs: 'GdbusCodegen') -> ModuleReturnValue: namebase = args[0] xml_files: T.List[T.Union['FileOrString', build.GeneratedTypes]] = [args[1]] if args[1] else [] - cmd: T.List[T.Union[ExternalProgram, Executable, OverrideProgram, str]] = [state.find_program('gdbus-codegen')] + cmd: T.List[T.Union['ToolType', str]] = [self._find_tool(state, 'gdbus-codegen')] cmd.extend(kwargs['extra_args']) # Autocleanup supported? @@ -1899,8 +1923,8 @@ def mkenums_simple(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'MkEn return ModuleReturnValue([c_file, h_file], [c_file, h_file]) - @staticmethod def _make_mkenum_impl( + self, state: 'ModuleState', sources: T.Sequence[T.Union[str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList]], output: str, @@ -1910,7 +1934,7 @@ def _make_mkenum_impl( install_dir: T.Optional[T.Sequence[T.Union[str, bool]]] = None, depends: T.Optional[T.Sequence[T.Union[CustomTarget, CustomTargetIndex, BuildTarget]]] = None ) -> build.CustomTarget: - real_cmd: T.List[T.Union[ExternalProgram, Executable, OverrideProgram, str]] = [state.find_program(['glib-mkenums', 'mkenums'])] + real_cmd: T.List[T.Union[str, 'ToolType']] = [self._find_tool(state, 'glib-mkenums')] real_cmd.extend(cmd) _install_dir = install_dir or state.environment.coredata.get_option(mesonlib.OptionKey('includedir')) assert isinstance(_install_dir, str), 'for mypy' @@ -1954,7 +1978,7 @@ def genmarshal(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'GenMarsh new_genmarshal = mesonlib.version_compare(self._get_native_glib_version(state), '>= 2.53.3') - cmd: T.List[T.Union[ExternalProgram, Executable, OverrideProgram, str]] = [state.find_program('glib-genmarshal')] + cmd: T.List[T.Union['ToolType', str]] = [self._find_tool(state, 'glib-genmarshal')] if kwargs['prefix']: cmd.extend(['--prefix', kwargs['prefix']]) if kwargs['extra_args']: diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index 921fb666167b..63025a502a3a 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -391,7 +391,8 @@ def __init__(self) -> None: }) def postconf_hook(self, b: build.Build) -> None: - b.devenv.append(self.devenv) + if self.devenv is not None: + b.devenv.append(self.devenv) def _get_lname(self, l: T.Union[build.SharedLibrary, build.StaticLibrary, build.CustomTarget, build.CustomTargetIndex], msg: str, pcfile: str) -> str: diff --git a/mesonbuild/modules/qt.py b/mesonbuild/modules/qt.py index 85558885343a..ad438db65e0f 100644 --- a/mesonbuild/modules/qt.py +++ b/mesonbuild/modules/qt.py @@ -23,7 +23,7 @@ from .. import build from .. import coredata from .. import mlog -from ..dependencies import find_external_dependency, Dependency, ExternalLibrary +from ..dependencies import find_external_dependency, Dependency, ExternalLibrary, InternalDependency from ..mesonlib import MesonException, File, version_compare, Popen_safe from ..interpreter import extract_required_kwarg from ..interpreter.type_checking import INSTALL_DIR_KW, INSTALL_KW, NoneType @@ -455,7 +455,10 @@ def _compile_moc_impl(self, state: 'ModuleState', kwargs: 'MocCompilerKwArgs') - inc = state.get_include_args(include_dirs=kwargs['include_directories']) compile_args: T.List[str] = [] for dep in kwargs['dependencies']: - compile_args.extend([a for a in dep.get_all_compile_args() if a.startswith(('-I', '-D'))]) + compile_args.extend(a for a in dep.get_all_compile_args() if a.startswith(('-I', '-D'))) + if isinstance(dep, InternalDependency): + for incl in dep.include_directories: + compile_args.extend(f'-I{i}' for i in incl.to_string_list(self.interpreter.source_root, self.interpreter.environment.build_dir)) output: T.List[build.GeneratedList] = [] @@ -472,7 +475,7 @@ def _compile_moc_impl(self, state: 'ModuleState', kwargs: 'MocCompilerKwArgs') - if kwargs['sources']: moc_gen = build.Generator( self.tools['moc'], arguments, ['@BASENAME@.moc'], - depfile='@BASENAME.moc.d@', + depfile='@BASENAME@.moc.d', name=f'Qt{self.qt_version} moc source') output.append(moc_gen.process_files(kwargs['sources'], state)) diff --git a/mesonbuild/msubprojects.py b/mesonbuild/msubprojects.py index 3ecfba18fe6b..f20290debf27 100755 --- a/mesonbuild/msubprojects.py +++ b/mesonbuild/msubprojects.py @@ -14,7 +14,7 @@ import zipfile from . import mlog -from .ast import IntrospectionInterpreter, AstIDGenerator +from .ast import IntrospectionInterpreter from .mesonlib import quiet_git, GitException, Popen_safe, MesonException, windows_proof_rmtree from .wrap.wrap import (Resolver, WrapException, ALL_TYPES, PackageDefinition, parse_patch_url, update_wrap_file, get_releases) @@ -153,6 +153,11 @@ def update_wrapdb(self) -> bool: try: wrapdb_version = self.wrap.get('wrapdb_version') branch, revision = wrapdb_version.split('-', 1) + except ValueError: + if not options.force: + self.log(' ->', mlog.red('Malformed wrapdb_version field, use --force to update anyway')) + return False + branch = revision = None except WrapException: # Fallback to parsing the patch URL to determine current version. # This won't work for projects that have upstream Meson support. @@ -161,7 +166,7 @@ def update_wrapdb(self) -> bool: branch, revision = parse_patch_url(patch_url) except WrapException: if not options.force: - self.log(' ->', mlog.red('Could not determine current version, use --force to update any way')) + self.log(' ->', mlog.red('Could not determine current version, use --force to update anyway')) return False branch = revision = None @@ -229,7 +234,9 @@ def git_rebase(self, revision: str) -> bool: try: self.git_output(['-c', 'rebase.autoStash=true', 'rebase', 'FETCH_HEAD']) except GitException as e: - self.log(' -> Could not rebase', mlog.bold(self.repo_dir), 'onto', mlog.bold(revision)) + self.git_output(['-c', 'rebase.autoStash=true', 'rebase', '--abort']) + self.log(' -> Could not rebase', mlog.bold(self.repo_dir), 'onto', mlog.bold(revision), + '-- aborted') self.log(mlog.red(e.output)) self.log(mlog.red(str(e))) return False @@ -283,6 +290,19 @@ def git_checkout_and_rebase(self, revision: str) -> bool: success = self.git_rebase(revision) return success + def git_branch_has_upstream(self, urls: set) -> bool: + cmd = ['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{upstream}'] + ret, upstream = quiet_git(cmd, self.repo_dir) + if not ret: + return False + try: + remote = upstream.split('/', maxsplit=1)[0] + except IndexError: + return False + cmd = ['remote', 'get-url', remote] + ret, remote_url = quiet_git(cmd, self.repo_dir) + return remote_url.strip() in urls + def update_git(self) -> bool: options = T.cast('UpdateArguments', self.options) if not os.path.exists(os.path.join(self.repo_dir, '.git')): @@ -374,12 +394,16 @@ def update_git(self) -> bool: success = self.git_rebase(revision) else: # We are in another branch, either the user created their own branch and - # we should rebase it, or revision changed in the wrap file and we need - # to checkout the new branch. + # we should rebase it, or revision changed in the wrap file (we + # know this when the current branch has an upstream) and we need to + # checkout the new branch. if options.reset: success = self.git_checkout_and_reset(revision) else: - success = self.git_rebase(revision) + if self.git_branch_has_upstream({url, push_url}): + success = self.git_checkout_and_rebase(revision) + else: + success = self.git_rebase(revision) if success: self.update_git_done() return success @@ -691,11 +715,9 @@ def run(options: 'Arguments') -> int: mlog.error('Directory', mlog.bold(source_dir), 'does not seem to be a Meson source directory.') return 1 with mlog.no_logging(): - intr = IntrospectionInterpreter(source_dir, '', 'none', visitors = [AstIDGenerator()]) + intr = IntrospectionInterpreter(source_dir, '', 'none') intr.load_root_meson_file() - intr.sanity_check_ast() - intr.parse_project() - subproject_dir = intr.subproject_dir + subproject_dir = intr.extract_subproject_dir() or 'subprojects' if not os.path.isdir(os.path.join(source_dir, subproject_dir)): mlog.log('Directory', mlog.bold(source_dir), 'does not seem to have subprojects.') return 0 diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index eb56c42be555..24cf08233299 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -867,10 +867,10 @@ def log(self, harness: 'TestHarness', test: 'TestRun') -> None: et.SubElement(testcase, 'system-out').text = subtest.explanation if test.stdo: out = et.SubElement(suite, 'system-out') - out.text = test.stdo.rstrip() + out.text = replace_unencodable_xml_chars(test.stdo.rstrip()) if test.stde: err = et.SubElement(suite, 'system-err') - err.text = test.stde.rstrip() + err.text = replace_unencodable_xml_chars(test.stde.rstrip()) else: if test.project not in self.suites: suite = self.suites[test.project] = et.Element( @@ -893,10 +893,10 @@ def log(self, harness: 'TestHarness', test: 'TestRun') -> None: suite.attrib['failures'] = str(int(suite.attrib['failures']) + 1) if test.stdo: out = et.SubElement(testcase, 'system-out') - out.text = test.stdo.rstrip() + out.text = replace_unencodable_xml_chars(test.stdo.rstrip()) if test.stde: err = et.SubElement(testcase, 'system-err') - err.text = test.stde.rstrip() + err.text = replace_unencodable_xml_chars(test.stde.rstrip()) async def finish(self, harness: 'TestHarness') -> None: """Calculate total test counts and write out the xml result.""" @@ -1180,9 +1180,9 @@ def decode(stream: T.Union[None, bytes]) -> str: if stream is None: return '' try: - return replace_unencodable_xml_chars(stream.decode('utf-8')) + return stream.decode('utf-8') except UnicodeDecodeError: - return replace_unencodable_xml_chars(stream.decode('iso-8859-1', errors='ignore')) + return stream.decode('iso-8859-1', errors='ignore') async def read_decode(reader: asyncio.StreamReader, queue: T.Optional['asyncio.Queue[T.Optional[str]]'], @@ -1933,7 +1933,7 @@ def tests_from_args(self, tests: T.List[TestSerialisation]) -> T.Generator[TestS # succeed on an invalid pattern. raise MesonException(f'{arg} test name does not match any test') - def get_tests(self, errorfile: T.Optional[T.IO] = sys.stdout) -> T.List[TestSerialisation]: + def get_tests(self, errorfile: T.Optional[T.IO] = None) -> T.List[TestSerialisation]: if not self.tests: print('No tests defined.', file=errorfile) return [] diff --git a/mesonbuild/scripts/coverage.py b/mesonbuild/scripts/coverage.py index cb865d08d258..4c0f81e8a026 100644 --- a/mesonbuild/scripts/coverage.py +++ b/mesonbuild/scripts/coverage.py @@ -22,7 +22,7 @@ def coverage(outputs: T.List[str], source_root: str, subproject_root: str, build outfiles = [] exitcode = 0 - (gcovr_exe, gcovr_version, lcov_exe, genhtml_exe, llvm_cov_exe) = environment.find_coverage_tools() + (gcovr_exe, gcovr_version, lcov_exe, lcov_version, genhtml_exe, llvm_cov_exe) = environment.find_coverage_tools() # load config files for tools if available in the source tree # - lcov requires manually specifying a per-project config @@ -35,6 +35,11 @@ def coverage(outputs: T.List[str], source_root: str, subproject_root: str, build else: lcov_config = [] + if lcov_exe and mesonlib.version_compare(lcov_version, '>=2.0'): + lcov_exe_rc_branch_coverage = ['--rc', 'branch_coverage=1'] + else: + lcov_exe_rc_branch_coverage = ['--rc', 'lcov_branch_coverage=1'] + gcovr_config = ['-e', re.escape(subproject_root)] # gcovr >= 4.2 requires a different syntax for out of source builds @@ -90,6 +95,9 @@ def coverage(outputs: T.List[str], source_root: str, subproject_root: str, build initial_tracefile = covinfo + '.initial' run_tracefile = covinfo + '.run' raw_tracefile = covinfo + '.raw' + lcov_subpoject_exclude = [] + if os.path.exists(subproject_root): + lcov_subpoject_exclude.append(os.path.join(subproject_root, '*')) if use_llvm_cov: # Create a shim to allow using llvm-cov as a gcov tool. if mesonlib.is_windows(): @@ -117,26 +125,26 @@ def coverage(outputs: T.List[str], source_root: str, subproject_root: str, build '--capture', '--output-file', run_tracefile, '--no-checksum', - '--rc', 'lcov_branch_coverage=1'] + + *lcov_exe_rc_branch_coverage] + lcov_config + gcov_tool_args) # Join initial and test results. subprocess.check_call([lcov_exe, '-a', initial_tracefile, '-a', run_tracefile, - '--rc', 'lcov_branch_coverage=1', + *lcov_exe_rc_branch_coverage, '-o', raw_tracefile] + lcov_config) # Remove all directories outside the source_root from the covinfo subprocess.check_call([lcov_exe, '--extract', raw_tracefile, os.path.join(source_root, '*'), - '--rc', 'lcov_branch_coverage=1', + *lcov_exe_rc_branch_coverage, '--output-file', covinfo] + lcov_config) # Remove all directories inside subproject dir subprocess.check_call([lcov_exe, '--remove', covinfo, - os.path.join(subproject_root, '*'), - '--rc', 'lcov_branch_coverage=1', + *lcov_subpoject_exclude, + *lcov_exe_rc_branch_coverage, '--output-file', covinfo] + lcov_config) subprocess.check_call([genhtml_exe, '--prefix', build_root, diff --git a/mesonbuild/scripts/env2mfile.py b/mesonbuild/scripts/env2mfile.py index 27a7dd969841..df93730424de 100755 --- a/mesonbuild/scripts/env2mfile.py +++ b/mesonbuild/scripts/env2mfile.py @@ -213,7 +213,7 @@ def detect_cross_debianlike(options: T.Any) -> MachineInfo: except ValueError: pass try: - infos.binaries['pkgconfig'] = locate_path("%s-pkg-config" % host_arch) + infos.binaries['pkg-config'] = locate_path("%s-pkg-config" % host_arch) except ValueError: pass # pkg-config is optional try: diff --git a/mesonbuild/scripts/python_info.py b/mesonbuild/scripts/python_info.py index 9c3a0791ac90..e9a78f84141a 100755 --- a/mesonbuild/scripts/python_info.py +++ b/mesonbuild/scripts/python_info.py @@ -13,7 +13,6 @@ del sys.path[0] import json, os, sysconfig -import distutils.command.install def get_distutils_paths(scheme=None, prefix=None): import distutils.dist @@ -37,24 +36,51 @@ def get_distutils_paths(scheme=None, prefix=None): # default scheme to a custom one pointing to /usr/local and replacing # site-packages with dist-packages. # See https://github.com/mesonbuild/meson/issues/8739. -# XXX: We should be using sysconfig, but Debian only patches distutils. +# +# We should be using sysconfig, but before 3.10.3, Debian only patches distutils. +# So we may end up falling back. -if 'deb_system' in distutils.command.install.INSTALL_SCHEMES: - paths = get_distutils_paths(scheme='deb_system') - install_paths = get_distutils_paths(scheme='deb_system', prefix='') -else: - paths = sysconfig.get_paths() +def get_install_paths(): + if sys.version_info >= (3, 10): + scheme = sysconfig.get_default_scheme() + else: + scheme = sysconfig._get_default_scheme() + + if sys.version_info >= (3, 10, 3): + if 'deb_system' in sysconfig.get_scheme_names(): + scheme = 'deb_system' + else: + import distutils.command.install + if 'deb_system' in distutils.command.install.INSTALL_SCHEMES: + paths = get_distutils_paths(scheme='deb_system') + install_paths = get_distutils_paths(scheme='deb_system', prefix='') + return paths, install_paths + + paths = sysconfig.get_paths(scheme=scheme) empty_vars = {'base': '', 'platbase': '', 'installed_base': ''} - install_paths = sysconfig.get_paths(vars=empty_vars) + install_paths = sysconfig.get_paths(scheme=scheme, vars=empty_vars) + return paths, install_paths + +paths, install_paths = get_install_paths() def links_against_libpython(): - from distutils.core import Distribution, Extension - cmd = Distribution().get_command_obj('build_ext') - cmd.ensure_finalized() - return bool(cmd.get_libraries(Extension('dummy', []))) + # on versions supporting python-embed.pc, this is the non-embed lib + # + # PyPy is not yet up to 3.12 and work is still pending to export the + # relevant information (it doesn't automatically provide arbitrary + # Makefile vars) + if sys.version_info >= (3, 8) and not is_pypy: + variables = sysconfig.get_config_vars() + return bool(variables.get('LIBPYTHON', 'yes')) + else: + from distutils.core import Distribution, Extension + cmd = Distribution().get_command_obj('build_ext') + cmd.ensure_finalized() + return bool(cmd.get_libraries(Extension('dummy', []))) variables = sysconfig.get_config_vars() variables.update({'base_prefix': getattr(sys, 'base_prefix', sys.prefix)}) +is_pypy = '__pypy__' in sys.builtin_module_names if sys.version_info < (3, 0): suffix = variables.get('SO') @@ -72,7 +98,7 @@ def links_against_libpython(): 'install_paths': install_paths, 'version': sysconfig.get_python_version(), 'platform': sysconfig.get_platform(), - 'is_pypy': '__pypy__' in sys.builtin_module_names, + 'is_pypy': is_pypy, 'is_venv': sys.prefix != variables['base_prefix'], 'link_libpython': links_against_libpython(), 'suffix': suffix, diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index d78fadd438c6..169491250eab 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -2384,22 +2384,22 @@ def is_base(self) -> bool: return self.type is OptionType.BASE -def pickle_load(filename: str, object_name: str, object_type: T.Type[_PL]) -> _PL: - load_fail_msg = f'{object_name} file {filename!r} is corrupted. Try with a fresh build tree.' +def pickle_load(filename: str, object_name: str, object_type: T.Type[_PL], suggest_reconfigure: bool = True) -> _PL: + load_fail_msg = f'{object_name} file {filename!r} is corrupted.' + extra_msg = ' Consider reconfiguring the directory with "meson setup --reconfigure".' if suggest_reconfigure else '' try: with open(filename, 'rb') as f: obj = pickle.load(f) except (pickle.UnpicklingError, EOFError): - raise MesonException(load_fail_msg) + raise MesonException(load_fail_msg + extra_msg) except (TypeError, ModuleNotFoundError, AttributeError): - build_dir = os.path.dirname(os.path.dirname(filename)) raise MesonException( f"{object_name} file {filename!r} references functions or classes that don't " "exist. This probably means that it was generated with an old " - "version of meson. Try running from the source directory " - f'meson setup {build_dir} --wipe') + "version of meson." + extra_msg) + if not isinstance(obj, object_type): - raise MesonException(load_fail_msg) + raise MesonException(load_fail_msg + extra_msg) # Because these Protocols are not available at runtime (and cannot be made # available at runtime until we drop support for Python < 3.8), we have to @@ -2413,7 +2413,7 @@ def pickle_load(filename: str, object_name: str, object_type: T.Type[_PL]) -> _P from ..coredata import version as coredata_version from ..coredata import major_versions_differ, MesonVersionMismatchException if major_versions_differ(version, coredata_version): - raise MesonVersionMismatchException(version, coredata_version) + raise MesonVersionMismatchException(version, coredata_version, extra_msg) return obj diff --git a/mesonbuild/utils/vsenv.py b/mesonbuild/utils/vsenv.py index 3c2687884de2..e9a5c1072e02 100644 --- a/mesonbuild/utils/vsenv.py +++ b/mesonbuild/utils/vsenv.py @@ -6,6 +6,7 @@ import pathlib import shutil import tempfile +import locale from .. import mlog from .universal import MesonException, is_windows, windows_detect_native_arch @@ -92,7 +93,8 @@ def _setup_vsenv(force: bool) -> bool: bat_file.write(bat_contents) bat_file.flush() bat_file.close() - bat_output = subprocess.check_output(bat_file.name, universal_newlines=True) + bat_output = subprocess.check_output(bat_file.name, universal_newlines=True, + encoding=locale.getpreferredencoding(False)) os.unlink(bat_file.name) bat_lines = bat_output.split('\n') bat_separator_seen = False diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index 2b0a0ba9eb84..faa167e64033 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -547,7 +547,10 @@ def get_file(self) -> None: if 'lead_directory_missing' in self.wrap.values: os.mkdir(self.dirname) extract_dir = self.dirname - shutil.unpack_archive(path, extract_dir) + try: + shutil.unpack_archive(path, extract_dir) + except OSError as e: + raise WrapException(f'failed to unpack archive with error: {str(e)}') from e def get_git(self) -> None: if not GIT: @@ -797,15 +800,17 @@ def apply_diff_files(self) -> None: if not path.exists(): raise WrapException(f'Diff file "{path}" does not exist') relpath = os.path.relpath(str(path), self.dirname) - if PATCH: - # Always pass a POSIX path to patch, because on Windows it's MSYS - cmd = [PATCH, '-f', '-p1', '-i', str(Path(relpath).as_posix())] - elif GIT: - # If the `patch` command is not available, fall back to `git - # apply`. The `--work-tree` is necessary in case we're inside a + if GIT: + # Git is more likely to be available on Windows and more likely + # to apply correctly assuming patches are often generated by git. + # See https://github.com/mesonbuild/meson/issues/12092. + # The `--work-tree` is necessary in case we're inside a # Git repository: by default, Git will try to apply the patch to # the repository root. cmd = [GIT, '--work-tree', '.', 'apply', '-p1', relpath] + elif PATCH: + # Always pass a POSIX path to patch, because on Windows it's MSYS + cmd = [PATCH, '-f', '-p1', '-i', str(Path(relpath).as_posix())] else: raise WrapException('Missing "patch" or "git" commands to apply diff files') diff --git a/packaging/createmsi.py b/packaging/createmsi.py index 4febc393dda6..fef2b38759d0 100755 --- a/packaging/createmsi.py +++ b/packaging/createmsi.py @@ -31,7 +31,7 @@ from mesonbuild import coredata # Elementtree does not support CDATA. So hack it. -WINVER_CHECK = ' 602)]]>' +WINVER_CHECK = 'Installed OR (VersionNT64 > 602)>' def gen_guid(): ''' @@ -102,7 +102,7 @@ def __init__(self): 'Title': 'Meson', 'Description': 'Meson executables', 'Level': '1', - 'Absent': 'disallow', + 'AllowAbsent': 'no', }, self.staging_dirs[1]: { 'Id': 'NinjaProgram', @@ -160,49 +160,44 @@ def generate_files(self): ''' Generate package files for MSI installer package ''' - self.root = ET.Element('Wix', {'xmlns': 'http://schemas.microsoft.com/wix/2006/wi'}) - product = ET.SubElement(self.root, 'Product', { + self.root = ET.Element('Wix', { + 'xmlns': 'http://wixtoolset.org/schemas/v4/wxs', + 'xmlns:ui': 'http://wixtoolset.org/schemas/v4/wxs/ui' + }) + + package = ET.SubElement(self.root, 'Package', { 'Name': self.product_name, 'Manufacturer': 'The Meson Development Team', - 'Id': self.guid, 'UpgradeCode': self.update_guid, 'Language': '1033', 'Codepage': '1252', 'Version': self.version, }) - package = ET.SubElement(product, 'Package', { - 'Id': '*', + ET.SubElement(package, 'SummaryInformation', { 'Keywords': 'Installer', 'Description': f'Meson {self.version} installer', - 'Comments': 'Meson is a high performance build system', 'Manufacturer': 'The Meson Development Team', - 'InstallerVersion': '500', - 'Languages': '1033', - 'Compressed': 'yes', - 'SummaryCodepage': '1252', }) - condition = ET.SubElement(product, 'Condition', {'Message': 'This application is only supported on Windows 10 or higher.'}) + ET.SubElement(package, + 'Launch', + {'Message': 'This application is only supported on Windows 10 or higher.', + 'Condition': 'X'*len(WINVER_CHECK)}) - condition.text = 'X'*len(WINVER_CHECK) - ET.SubElement(product, 'MajorUpgrade', - {'DowngradeErrorMessage': 'A newer version of Meson is already installed.'}) + ET.SubElement(package, 'MajorUpgrade', + {'DowngradeErrorMessage': + 'A newer version of Meson is already installed.'}) - package.set('Platform', 'x64') - ET.SubElement(product, 'Media', { + ET.SubElement(package, 'Media', { 'Id': '1', 'Cabinet': 'meson.cab', 'EmbedCab': 'yes', }) - targetdir = ET.SubElement(product, 'Directory', { - 'Id': 'TARGETDIR', - 'Name': 'SourceDir', + targetdir = ET.SubElement(package, 'StandardDirectory', { + 'Id': 'ProgramFiles64Folder', }) - progfiledir = ET.SubElement(targetdir, 'Directory', { - 'Id': self.progfile_dir, - }) - installdir = ET.SubElement(progfiledir, 'Directory', { + installdir = ET.SubElement(targetdir, 'Directory', { 'Id': 'INSTALLDIR', 'Name': 'Meson', }) @@ -213,16 +208,12 @@ def generate_files(self): 'Language': '0', }) - ET.SubElement(product, 'Property', { - 'Id': 'WIXUI_INSTALLDIR', - 'Value': 'INSTALLDIR', - }) - ET.SubElement(product, 'UIRef', { + ET.SubElement(package, 'ui:WixUI', { 'Id': 'WixUI_FeatureTree', }) for s_d in self.staging_dirs: assert os.path.isdir(s_d) - top_feature = ET.SubElement(product, 'Feature', { + top_feature = ET.SubElement(package, 'Feature', { 'Id': 'Complete', 'Title': 'Meson ' + self.version, 'Description': 'The complete package', @@ -277,10 +268,10 @@ def create_xml(self, nodes, current_dir, parent_xml_node, staging_dir): component_id = f'ApplicationFiles{self.component_num}' comp_xml_node = ET.SubElement(parent_xml_node, 'Component', { 'Id': component_id, + 'Bitness': 'always64', 'Guid': gen_guid(), }) self.feature_components[staging_dir].append(component_id) - comp_xml_node.set('Win64', 'yes') if self.component_num == 0: ET.SubElement(comp_xml_node, 'Environment', { 'Id': 'Environment', @@ -300,7 +291,7 @@ def create_xml(self, nodes, current_dir, parent_xml_node, staging_dir): }) for dirname in cur_node.dirs: - dir_id = os.path.join(current_dir, dirname).replace('\\', '_').replace('/', '_') + dir_id = os.path.join(current_dir, dirname).replace('\\', '_').replace('/', '_').replace('-', '_') dir_node = ET.SubElement(parent_xml_node, 'Directory', { 'Id': dir_id, 'Name': dirname, @@ -311,23 +302,40 @@ def build_package(self): ''' Generate the Meson build MSI package. ''' - wixdir = 'c:\\Program Files\\Wix Toolset v3.11\\bin' - if not os.path.isdir(wixdir): - wixdir = 'c:\\Program Files (x86)\\Wix Toolset v3.11\\bin' - if not os.path.isdir(wixdir): - print("ERROR: This script requires WIX") - sys.exit(1) - subprocess.check_call([os.path.join(wixdir, 'candle'), self.main_xml]) - subprocess.check_call([os.path.join(wixdir, 'light'), - '-ext', 'WixUIExtension', - '-cultures:en-us', - '-dWixUILicenseRtf=packaging\\License.rtf', - '-out', self.final_output, - self.main_o]) + subprocess.check_call(['wix', + 'build', + '-bindvariable', 'WixUILicenseRtf=packaging\\License.rtf', + '-ext', 'WixToolset.UI.wixext', + '-culture', 'en-us', + '-arch', 'x64', + '-o', + self.final_output, + self.main_xml, + ]) + + +def install_wix(): + subprocess.check_call(['dotnet', + 'nuget', + 'add', + 'source', + 'https://api.nuget.org/v3/index.json']) + subprocess.check_call(['dotnet', + 'tool', + 'install', + '--global', + 'wix']) + subprocess.check_call(['wix', + 'extension', + 'add', + 'WixToolset.UI.wixext', + ]) if __name__ == '__main__': if not os.path.exists('meson.py'): sys.exit(print('Run me in the top level source dir.')) + if not shutil.which('wix'): + install_wix() subprocess.check_call(['pip', 'install', '--upgrade', 'pyinstaller']) p = PackageGenerator() diff --git a/packaging/hook-mesonbuild.py b/packaging/hook-mesonbuild.py index 86e74639f03b..d6b06cd26213 100644 --- a/packaging/hook-mesonbuild.py +++ b/packaging/hook-mesonbuild.py @@ -25,7 +25,11 @@ def get_all_modules_from_dir(dirname): datas += collect_data_files('mesonbuild.cmake.data') datas += collect_data_files('mesonbuild.dependencies.data') +# lazy-loaded +hiddenimports += get_all_modules_from_dir('mesonbuild/dependencies') +# imported by meson.build files hiddenimports += get_all_modules_from_dir('mesonbuild/modules') +# executed when named on CLI hiddenimports += get_all_modules_from_dir('mesonbuild/scripts') # Python packagers want to be minimal and only copy the things diff --git a/run_project_tests.py b/run_project_tests.py index facf1e98f6af..33ee5caaac9d 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -40,6 +40,7 @@ import typing as T import xml.etree.ElementTree as ET import collections +import importlib.util from mesonbuild import build from mesonbuild import environment @@ -167,7 +168,7 @@ def get_path(self, compiler: str, env: environment.Environment) -> T.Optional[Pa return None # Handle the different types - if self.typ in {'py_implib', 'python_lib', 'python_file'}: + if self.typ in {'py_implib', 'python_lib', 'python_file', 'python_bytecode'}: val = p.as_posix() val = val.replace('@PYTHON_PLATLIB@', python.platlib) val = val.replace('@PYTHON_PURELIB@', python.purelib) @@ -184,6 +185,8 @@ def get_path(self, compiler: str, env: environment.Environment) -> T.Optional[Pa return p.with_suffix('.dll.a') else: return None + if self.typ == 'python_bytecode': + return p.parent / importlib.util.cache_from_source(p.name) elif self.typ in {'file', 'dir'}: return p elif self.typ == 'shared_lib': @@ -288,6 +291,7 @@ def __lt__(self, other: object) -> bool: return (s_id, self.path, self.name or '') < (o_id, other.path, other.name or '') return NotImplemented +failing_testcases: T.List[str] = [] failing_logs: T.List[str] = [] print_debug = 'MESON_PRINT_TEST_OUTPUT' in os.environ under_ci = 'CI' in os.environ @@ -432,9 +436,9 @@ def _run_ci_include(args: T.List[str]) -> str: return 'At least one parameter required' try: data = Path(args[0]).read_text(errors='ignore', encoding='utf-8') - return f'{header}\n{data}\n{footer}' + return f'{header}\n{data}\n{footer}\n' except Exception: - return 'Failed to open {}'.format(args[0]) + return 'Failed to open {}\n'.format(args[0]) ci_commands = { 'ci_include': _run_ci_include @@ -448,7 +452,7 @@ def run_ci_commands(raw_log: str) -> T.List[str]: cmd = shlex.split(l[11:]) if not cmd or cmd[0] not in ci_commands: continue - res += ['CI COMMAND {}:\n{}\n'.format(cmd[0], ci_commands[cmd[0]](cmd[1:]))] + res += ['CI COMMAND {}:\n{}'.format(cmd[0], ci_commands[cmd[0]](cmd[1:]))] return res class OutputMatch: @@ -552,11 +556,11 @@ def run_test_inprocess(testdir: str) -> T.Tuple[int, str, str, str]: sys.stderr = mystderr = StringIO() old_cwd = os.getcwd() os.chdir(testdir) - test_log_fname = Path('meson-logs', 'testlog.txt') + test_log_fname = os.path.join('meson-logs', 'testlog.txt') try: returncode_test = mtest.run_with_args(['--no-rebuild']) - if test_log_fname.exists(): - test_log = test_log_fname.open(encoding='utf-8', errors='ignore').read() + if os.path.exists(test_log_fname): + test_log = _run_ci_include([test_log_fname]) else: test_log = '' returncode_benchmark = mtest.run_with_args(['--no-rebuild', '--benchmark', '--logbase', 'benchmarklog']) @@ -666,11 +670,10 @@ def _run_test(test: TestDef, returncode, stdo, stde = res cmd = '(inprocess) $ ' if inprocess else '$ ' cmd += mesonlib.join_args(gen_args) - try: - logfile = Path(test_build_dir, 'meson-logs', 'meson-log.txt') - with logfile.open(errors='ignore', encoding='utf-8') as fid: - mesonlog = '\n'.join((cmd, fid.read())) - except Exception: + logfile = os.path.join(test_build_dir, 'meson-logs', 'meson-log.txt') + if os.path.exists(logfile): + mesonlog = '\n'.join((cmd, _run_ci_include([logfile]))) + else: mesonlog = no_meson_log_msg cicmds = run_ci_commands(mesonlog) testresult = TestResult(cicmds) @@ -698,8 +701,8 @@ def _run_test(test: TestDef, # Build with subprocess def build_step() -> None: build_start = time.time() - pc, o, e = Popen_safe(compile_commands + dir_args, cwd=test_build_dir) - testresult.add_step(BuildStep.build, o, e, '', time.time() - build_start) + pc, o, _ = Popen_safe(compile_commands + dir_args, cwd=test_build_dir, stderr=subprocess.STDOUT) + testresult.add_step(BuildStep.build, o, '', '', time.time() - build_start) if should_fail == 'build': if pc.returncode != 0: raise testresult @@ -1369,7 +1372,11 @@ def tqdm_print(*args: mlog.TV_Loggable, sep: str = ' ') -> None: left_w = max(3, left_w) right_w = cols - left_w - name_len - 2 right_w = max(3, right_w) + failing_testcases.append(name_str) failing_logs.append(f'\n\x1b[31m{"="*left_w}\x1b[0m {name_str} \x1b[31m{"="*right_w}\x1b[0m\n') + _during = bold('Failed during:') + _reason = bold('Reason:') + failing_logs.append(f'{_during} {result.step.name}\n{_reason} {result.msg}\n') if result.step == BuildStep.configure and result.mlog != no_meson_log_msg: # For configure failures, instead of printing stdout, # print the meson log if available since it's a superset @@ -1624,10 +1631,6 @@ def clear_transitive_files() -> None: (passing_tests, failing_tests, skipped_tests) = res except StopException: pass - print() - print('Total passed tests: ', green(str(passing_tests))) - print('Total failed tests: ', red(str(failing_tests))) - print('Total skipped tests:', yellow(str(skipped_tests))) if failing_tests > 0: print('\nMesonlogs of failing tests\n') for l in failing_logs: @@ -1635,6 +1638,14 @@ def clear_transitive_files() -> None: print(l, '\n') except UnicodeError: print(l.encode('ascii', errors='replace').decode(), '\n') + print() + print('Total passed tests: ', green(str(passing_tests))) + print('Total failed tests: ', red(str(failing_tests))) + print('Total skipped tests:', yellow(str(skipped_tests))) + if failing_tests > 0: + print('\nAll failures:') + for c in failing_testcases: + print(f' -> {c}') for name, dirs, _ in all_tests: dir_names = list({x.path.name for x in dirs}) for k, g in itertools.groupby(dir_names, key=lambda x: x.split()[0]): diff --git a/run_unittests.py b/run_unittests.py index 23e40b475659..da823ff57e2a 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -137,7 +137,7 @@ def main(): # Let there be colors! if 'CI' in os.environ: pytest_args += ['--color=yes'] - pytest_args += ['./run_unittests.py'] + pytest_args += ['unittests'] pytest_args += convert_args(sys.argv[1:]) # Always disable pytest-cov because we use a custom setup try: diff --git a/setup.cfg b/setup.cfg index a23af07c6cd3..2f2962eedcba 100644 --- a/setup.cfg +++ b/setup.cfg @@ -30,6 +30,7 @@ classifiers = Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 Topic :: Software Development :: Build Tools long_description = Meson is a cross-platform build system designed to be both as fast and as user friendly as possible. It supports many languages and compilers, including GCC, Clang, PGI, Intel, and Visual Studio. Its build definitions are written in a simple non-Turing complete DSL. @@ -62,4 +63,4 @@ include = mesonbuild, mesonbuild.* [tool:pytest] python_classes = python_files = - run_unittests.py + unittests/*tests.py diff --git a/test cases/common/13 pch/meson.build b/test cases/common/13 pch/meson.build index 4bb3e1f275ba..d83291963284 100644 --- a/test cases/common/13 pch/meson.build +++ b/test cases/common/13 pch/meson.build @@ -13,7 +13,11 @@ subdir('cpp') subdir('generated') subdir('userDefined') subdir('withIncludeDirectories') -subdir('withIncludeFile') +if meson.backend() == 'xcode' + warning('Xcode backend does not support forced includes. Skipping "withIncludeFile" which requires this.') +else + subdir('withIncludeFile') +endif if meson.backend() == 'xcode' warning('Xcode backend only supports one precompiled header per target. Skipping "mixed" which has various precompiled headers.') diff --git a/test cases/common/220 fs module/meson.build b/test cases/common/220 fs module/meson.build index a1e9c44fc468..b860fc8a32c8 100644 --- a/test cases/common/220 fs module/meson.build +++ b/test cases/common/220 fs module/meson.build @@ -142,3 +142,7 @@ assert(fs.stem('foo/bar/baz.dll.a') == 'baz.dll', 'failed to get stem with compo subdir('subdir') subproject('subbie') + +testcase expect_error('File notfound does not exist.') + fs.read('notfound') +endtestcase diff --git a/test cases/common/40 options/meson.build b/test cases/common/40 options/meson.build index a10ff28f0c6e..de4a7d50db14 100644 --- a/test cases/common/40 options/meson.build +++ b/test cases/common/40 options/meson.build @@ -46,3 +46,11 @@ assert(get_option('wrap_mode') == 'default', 'Wrap mode option is broken.') assert(get_option('boolean_string') == false) assert(get_option('boolean_string2') == true) assert(get_option('integer_string') == 42) + +testcase expect_error('Invalid option name \'..invalid\'') + get_option('..invalid') +endtestcase + +testcase expect_error('Invalid option name \'this.is.also.invalid\'') + get_option('this.is.also.invalid') +endtestcase diff --git a/test cases/common/44 pkgconfig-gen/meson.build b/test cases/common/44 pkgconfig-gen/meson.build index adf3e2765e81..fd6371ef4b92 100644 --- a/test cases/common/44 pkgconfig-gen/meson.build +++ b/test cases/common/44 pkgconfig-gen/meson.build @@ -8,7 +8,7 @@ if not cc.find_library('z', required: false).found() endif # First check we have pkg-config >= 0.29 -pkgconfig = find_program('pkg-config', required: false) +pkgconfig = find_program('pkg-config', native: true, required: false) if not pkgconfig.found() error('MESON_SKIP_TEST: pkg-config not found') endif diff --git a/test cases/frameworks/15 llvm/meson.build b/test cases/frameworks/15 llvm/meson.build index aac037fe6cd7..9c39e45b6a43 100644 --- a/test cases/frameworks/15 llvm/meson.build +++ b/test cases/frameworks/15 llvm/meson.build @@ -8,6 +8,11 @@ if not d.found() error('MESON_SKIP_TEST llvm not found.') endif +if method != 'config-tool' and d.version().startswith('17.0') and host_machine.system() == 'windows' + # https://github.com/llvm/llvm-project/commit/e7fc7540daa9333f0be4f380fc9c619236d17f57#r130257253 + error('MESON_SKIP_TEST broken llvm cmake files on MSYS2') +endif + modules_to_find = [ 'bitwriter', 'asmprinter', 'executionengine', 'mcjit', 'target', 'nativecodegen', 'amdgpu', 'engine' diff --git a/test cases/frameworks/15 llvm/test.json b/test cases/frameworks/15 llvm/test.json index 66ecd43552f6..f9d730514220 100644 --- a/test cases/frameworks/15 llvm/test.json +++ b/test cases/frameworks/15 llvm/test.json @@ -2,9 +2,9 @@ "matrix": { "options": { "method": [ - { "val": "config-tool", "skip_on_jobname": ["msys2-gcc"]}, - { "val": "cmake", "skip_on_jobname": ["msys2-gcc"] }, - { "val": "combination", "skip_on_jobname": ["msys2-gcc"]} + { "val": "config-tool", "skip_on_jobname": ["msys2-gcc"] }, + { "val": "cmake", "skip_on_jobname": ["msys2"] }, + { "val": "combination", "skip_on_jobname": ["msys2"] } ], "link-static": [ { "val": true, "skip_on_jobname": ["opensuse"] }, diff --git a/test cases/frameworks/27 gpgme/test.json b/test cases/frameworks/27 gpgme/test.json index aa7d932dd607..59eb0e24d871 100644 --- a/test cases/frameworks/27 gpgme/test.json +++ b/test cases/frameworks/27 gpgme/test.json @@ -1,3 +1,3 @@ { - "skip_on_jobname": ["azure", "cygwin", "macos", "msys2", "linux-arch"] + "skip_on_jobname": ["azure", "cygwin", "macos", "msys2", "linux-arch", "ubuntu"] } diff --git a/test cases/frameworks/36 gtkdoc cpp/foo-docs.xml b/test cases/frameworks/36 gtkdoc cpp/foo-docs.xml new file mode 100644 index 000000000000..85c673c57b4b --- /dev/null +++ b/test cases/frameworks/36 gtkdoc cpp/foo-docs.xml @@ -0,0 +1,16 @@ + + + +]> + + + Foo Reference Manual + + + + GLib Core Application Support + + + diff --git a/test cases/frameworks/36 gtkdoc cpp/foo.cpp b/test cases/frameworks/36 gtkdoc cpp/foo.cpp new file mode 100644 index 000000000000..15fa269806a2 --- /dev/null +++ b/test cases/frameworks/36 gtkdoc cpp/foo.cpp @@ -0,0 +1,5 @@ +#include "foo.h" + +int foo_do_something(void) { + return 42; +} diff --git a/test cases/frameworks/36 gtkdoc cpp/foo.h b/test cases/frameworks/36 gtkdoc cpp/foo.h new file mode 100644 index 000000000000..cac03d3c1a70 --- /dev/null +++ b/test cases/frameworks/36 gtkdoc cpp/foo.h @@ -0,0 +1 @@ +int foo_do_something(void); diff --git a/test cases/frameworks/36 gtkdoc cpp/meson.build b/test cases/frameworks/36 gtkdoc cpp/meson.build new file mode 100644 index 000000000000..747eae546741 --- /dev/null +++ b/test cases/frameworks/36 gtkdoc cpp/meson.build @@ -0,0 +1,13 @@ +project('gnome module without C', 'cpp') + +gtkdoc = find_program('gtkdoc-scan', required: false) +if not gtkdoc.found() + error('MESON_SKIP_TEST gtkdoc not found.') +endif + +gnome = import('gnome') + +lib = library('foo++', 'foo.cpp') +gnome.gtkdoc('foo', + src_dir: '.', + main_xml : 'foo-docs.xml',) diff --git a/test cases/frameworks/36 gtkdoc cpp/test.json b/test cases/frameworks/36 gtkdoc cpp/test.json new file mode 100644 index 000000000000..b2d9bc88f6a0 --- /dev/null +++ b/test cases/frameworks/36 gtkdoc cpp/test.json @@ -0,0 +1,17 @@ +{ + "installed": [ + {"type": "file", "file": "usr/share/gtk-doc/html/foo/up-insensitive.png"}, + {"type": "file", "file": "usr/share/gtk-doc/html/foo/home.png"}, + {"type": "file", "file": "usr/share/gtk-doc/html/foo/foo.html"}, + {"type": "file", "file": "usr/share/gtk-doc/html/foo/foo-foo.html"}, + {"type": "file", "file": "usr/share/gtk-doc/html/foo/style.css"}, + {"type": "file", "file": "usr/share/gtk-doc/html/foo/index.html"}, + {"type": "file", "file": "usr/share/gtk-doc/html/foo/foo.devhelp2"}, + {"type": "file", "file": "usr/share/gtk-doc/html/foo/left.png"}, + {"type": "file", "file": "usr/share/gtk-doc/html/foo/left-insensitive.png"}, + {"type": "file", "file": "usr/share/gtk-doc/html/foo/right-insensitive.png"}, + {"type": "file", "file": "usr/share/gtk-doc/html/foo/up.png"}, + {"type": "file", "file": "usr/share/gtk-doc/html/foo/right.png"} + ], + "skip_on_jobname": ["azure", "msys2"] +} diff --git a/test cases/frameworks/37 gir cpp/foo.cpp b/test cases/frameworks/37 gir cpp/foo.cpp new file mode 100644 index 000000000000..15fa269806a2 --- /dev/null +++ b/test cases/frameworks/37 gir cpp/foo.cpp @@ -0,0 +1,5 @@ +#include "foo.h" + +int foo_do_something(void) { + return 42; +} diff --git a/test cases/frameworks/37 gir cpp/foo.h b/test cases/frameworks/37 gir cpp/foo.h new file mode 100644 index 000000000000..cac03d3c1a70 --- /dev/null +++ b/test cases/frameworks/37 gir cpp/foo.h @@ -0,0 +1 @@ +int foo_do_something(void); diff --git a/test cases/frameworks/37 gir cpp/meson.build b/test cases/frameworks/37 gir cpp/meson.build new file mode 100644 index 000000000000..0d228b0fe610 --- /dev/null +++ b/test cases/frameworks/37 gir cpp/meson.build @@ -0,0 +1,22 @@ +project('gnome module without C', 'cpp') + +gi = dependency('gobject-introspection-1.0', required: false) +if not gi.found() + error('MESON_SKIP_TEST gobject-introspection not found.') +endif + +if host_machine.system() == 'cygwin' + # FIXME: g-ir-scanner seems broken on cygwin: + # ERROR: can't resolve libraries to shared libraries: foo++ + error('MESON_SKIP_TEST g-ir-scanner is broken on cygwin.') +endif + +gnome = import('gnome') + +lib = library('foo++', 'foo.cpp') +gnome.generate_gir( + lib, + sources: ['foo.cpp', 'foo.h'], + namespace: 'foo', + nsversion: meson.project_version(), +) diff --git a/test cases/frameworks/37 gir cpp/test.json b/test cases/frameworks/37 gir cpp/test.json new file mode 100644 index 000000000000..3641d75ca9e8 --- /dev/null +++ b/test cases/frameworks/37 gir cpp/test.json @@ -0,0 +1,3 @@ +{ + "skip_on_jobname": ["azure", "macos", "msys2", "cygwin"] +} diff --git a/test cases/frameworks/4 qt/manualinclude.cpp b/test cases/frameworks/4 qt/manualinclude.cpp index 60b94e510e12..ef151a44aad6 100644 --- a/test cases/frameworks/4 qt/manualinclude.cpp +++ b/test cases/frameworks/4 qt/manualinclude.cpp @@ -1,4 +1,5 @@ #include"manualinclude.h" +#include #include #include diff --git a/test cases/frameworks/4 qt/meson.build b/test cases/frameworks/4 qt/meson.build index 825cd5766fa6..04a5288bf1c4 100644 --- a/test cases/frameworks/4 qt/meson.build +++ b/test cases/frameworks/4 qt/meson.build @@ -2,6 +2,9 @@ project('qt4, qt5, and qt6 build test', 'cpp', # Qt6 requires C++ 17 support default_options : ['cpp_std=c++17']) +# Visit the subdir before entering the loop +subdir('mocdep') + qt5_modules = ['Widgets'] qt6_modules = ['Widgets'] foreach qt : ['qt4', 'qt5', 'qt6'] @@ -114,17 +117,21 @@ foreach qt : ['qt4', 'qt5', 'qt6'] moc_extra_arguments : ['-DMOC_EXTRA_FLAG'], # This is just a random macro to test `extra_arguments` moc_sources : 'manualinclude.cpp', moc_headers : 'manualinclude.h', - method : get_option('method')) + method : get_option('method'), + dependencies: mocdep, + ) manpreprocessed = qtmodule.compile_moc( extra_args : ['-DMOC_EXTRA_FLAG'], # This is just a random macro to test `extra_arguments` sources : 'manualinclude.cpp', headers : 'manualinclude.h', - method : get_option('method')) + method : get_option('method'), + dependencies: mocdep, + ) qtmaninclude = executable(qt + 'maninclude', sources : ['manualinclude.cpp', manpreprocessed], - dependencies : qtcore) + dependencies : [qtcore, mocdep]) test(qt + 'maninclude', qtmaninclude) diff --git a/test cases/frameworks/4 qt/mocdep/meson.build b/test cases/frameworks/4 qt/mocdep/meson.build new file mode 100644 index 000000000000..174b42679090 --- /dev/null +++ b/test cases/frameworks/4 qt/mocdep/meson.build @@ -0,0 +1,3 @@ +mocdep = declare_dependency( + include_directories: include_directories('.'), +) diff --git a/test cases/frameworks/4 qt/mocdep/mocdep.h b/test cases/frameworks/4 qt/mocdep/mocdep.h new file mode 100644 index 000000000000..357a52c3dd2b --- /dev/null +++ b/test cases/frameworks/4 qt/mocdep/mocdep.h @@ -0,0 +1 @@ +#define MOC_DEP 1 diff --git a/test cases/frameworks/5 protocol buffers/meson.build b/test cases/frameworks/5 protocol buffers/meson.build index 046847a89297..9a4154b652f3 100644 --- a/test cases/frameworks/5 protocol buffers/meson.build +++ b/test cases/frameworks/5 protocol buffers/meson.build @@ -1,4 +1,4 @@ -project('protocol buffer test', 'cpp', default_options: ['cpp_std=c++11']) +project('protocol buffer test', 'cpp', default_options: ['cpp_std=c++14']) protoc = find_program('protoc', required : false) dep = dependency('protobuf', required : false) diff --git a/test cases/linuxlike/13 cmake dependency/meson.build b/test cases/linuxlike/13 cmake dependency/meson.build index 193ad184b637..f612e1d52d6d 100644 --- a/test cases/linuxlike/13 cmake dependency/meson.build +++ b/test cases/linuxlike/13 cmake dependency/meson.build @@ -2,13 +2,20 @@ # due to use of setup_env.json project('external CMake dependency', ['c', 'cpp']) -if not find_program('cmake', required: false).found() +cmake = find_program('cmake', required: false) +if not cmake.found() error('MESON_SKIP_TEST cmake binary not available.') endif # Zlib is probably on all dev machines. dep = dependency('ZLIB', version : '>=1.2', method : 'cmake') + +if '#define' in dep.version() and cmake.version().version_compare('< 3.27.4') + # ZLIB 1.3 version is broken with those cmake versions + error('MESON_SKIP_TEST known bug in cmake (https://gitlab.kitware.com/cmake/cmake/-/issues/25200)') +endif + exe = executable('zlibprog', 'prog-checkver.c', dependencies : dep, c_args : '-DFOUND_ZLIB="' + dep.version() + '"') diff --git a/test cases/linuxlike/3 linker script/meson.build b/test cases/linuxlike/3 linker script/meson.build index 5901bf7cf716..660858792cb2 100644 --- a/test cases/linuxlike/3 linker script/meson.build +++ b/test cases/linuxlike/3 linker script/meson.build @@ -22,7 +22,7 @@ m = configure_file( output : 'bob-conf.map', configuration : conf, ) -vflag = '-Wl,--version-script,@0@'.format(m) +vflag = '-Wl,--version-script,@0@'.format(meson.current_build_dir() / 'bob-conf.map') l = shared_library('bob-conf', 'bob.c', link_args : vflag, link_depends : m) e = executable('prog-conf', 'prog.c', link_with : l) @@ -43,14 +43,6 @@ l = shared_library('bob-ct', ['bob.c', m], link_args : vflag, link_depends : m) e = executable('prog-ct', 'prog.c', link_with : l) test('core', e) -# File -mapfile = files('bob.map') -vflag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile[0]) - -l = shared_library('bob-files', 'bob.c', link_args : vflag, link_depends : mapfile) -e = executable('prog-files', 'prog.c', link_with : l) -test('core', e) - subdir('sub') # With map file in subdir diff --git a/test cases/nasm/2 asm language/meson.build b/test cases/nasm/2 asm language/meson.build index 0eecd99a648b..d025d43ea9b7 100644 --- a/test cases/nasm/2 asm language/meson.build +++ b/test cases/nasm/2 asm language/meson.build @@ -7,6 +7,8 @@ endif if host_machine.system() == 'windows' error('MESON_SKIP_TEST: this test asm is not made for Windows') +elif host_machine.system() == 'sunos' + error('MESON_SKIP_TEST: this test asm is not made for Solaris or illumos') endif if meson.backend().startswith('vs') diff --git a/test cases/osx/9 global variable ar/libfile.c b/test cases/osx/9 global variable ar/libfile.c new file mode 100644 index 000000000000..b258d7b8250f --- /dev/null +++ b/test cases/osx/9 global variable ar/libfile.c @@ -0,0 +1,9 @@ +// Source: https://lists.gnu.org/archive/html/libtool/2002-07/msg00025.html + +#include + +extern int l2; +void l1(void) +{ + printf("l1 %d\n", l2); +} diff --git a/test cases/osx/9 global variable ar/libfile2.c b/test cases/osx/9 global variable ar/libfile2.c new file mode 100644 index 000000000000..1499c4dc7f8a --- /dev/null +++ b/test cases/osx/9 global variable ar/libfile2.c @@ -0,0 +1,7 @@ +// Source: https://lists.gnu.org/archive/html/libtool/2002-07/msg00025.html + +int l2; +void l2_func(void) +{ + l2 = 77; +} diff --git a/test cases/osx/9 global variable ar/meson.build b/test cases/osx/9 global variable ar/meson.build new file mode 100644 index 000000000000..313dd1b37653 --- /dev/null +++ b/test cases/osx/9 global variable ar/meson.build @@ -0,0 +1,6 @@ +# Source: https://lists.gnu.org/archive/html/libtool/2002-07/msg00025.html + +project('global variable test', 'c') + +lib = static_library('mylib', 'libfile.c', 'libfile2.c') +test('global variable', executable('prog', 'prog.c', link_with: lib)) diff --git a/test cases/osx/9 global variable ar/nativefile.ini b/test cases/osx/9 global variable ar/nativefile.ini new file mode 100644 index 000000000000..4fb5e7f7db1b --- /dev/null +++ b/test cases/osx/9 global variable ar/nativefile.ini @@ -0,0 +1,2 @@ +[binaries] +ar = 'ar' diff --git a/test cases/osx/9 global variable ar/prog.c b/test cases/osx/9 global variable ar/prog.c new file mode 100644 index 000000000000..4665016f6149 --- /dev/null +++ b/test cases/osx/9 global variable ar/prog.c @@ -0,0 +1,7 @@ +// Source: https://lists.gnu.org/archive/html/libtool/2002-07/msg00025.html + +extern void l1(void); +int main(void) +{ + l1(); +} diff --git a/test cases/python/4 custom target depends extmodule/blaster.py b/test cases/python/4 custom target depends extmodule/blaster.py index 61b11f922977..65b6493df890 100644 --- a/test cases/python/4 custom target depends extmodule/blaster.py +++ b/test cases/python/4 custom target depends extmodule/blaster.py @@ -10,6 +10,9 @@ if list(filedir.glob('ext/*tachyon*')): sys.path.insert(0, (filedir / 'ext').as_posix()) +if hasattr(os, 'add_dll_directory'): + os.add_dll_directory(filedir / 'ext' / 'lib') + import tachyon parser = argparse.ArgumentParser() diff --git a/test cases/python/5 modules kwarg/meson.build b/test cases/python/5 modules kwarg/meson.build index 9751adaaba2b..41a9a4faee9a 100644 --- a/test cases/python/5 modules kwarg/meson.build +++ b/test cases/python/5 modules kwarg/meson.build @@ -1,7 +1,7 @@ project('python kwarg') py = import('python') -prog_python = py.find_installation('python3', modules : ['distutils']) +prog_python = py.find_installation('python3', modules : ['os', 'sys', 're']) assert(prog_python.found() == true, 'python not found when should be') prog_python = py.find_installation('python3', modules : ['thisbetternotexistmod'], required : false) assert(prog_python.found() == false, 'python not found but reported as found') diff --git a/test cases/python3/4 custom target depends extmodule/blaster.py b/test cases/python3/4 custom target depends extmodule/blaster.py index d2c93ad038fa..9cce64505e5d 100644 --- a/test cases/python3/4 custom target depends extmodule/blaster.py +++ b/test cases/python3/4 custom target depends extmodule/blaster.py @@ -10,6 +10,9 @@ if list(filedir.glob('ext/*tachyon.*')): sys.path.insert(0, (filedir / 'ext').as_posix()) +if hasattr(os, 'add_dll_directory'): + os.add_dll_directory(filedir / 'ext' / 'lib') + import tachyon parser = argparse.ArgumentParser() diff --git a/test cases/rust/5 polyglot static/meson.build b/test cases/rust/5 polyglot static/meson.build index 5d1f02368c20..54f383cd3872 100644 --- a/test cases/rust/5 polyglot static/meson.build +++ b/test cases/rust/5 polyglot static/meson.build @@ -7,7 +7,7 @@ r = static_library('stuff', 'stuff.rs', rust_crate_type : 'staticlib') # as it would do with C libraries, but then cannot extract objects from stuff and # thus should error out. # FIXME: We should support this use-case in the future. -testcase expect_error('Cannot link_whole a custom or Rust target into a static library') +testcase expect_error('Cannot link_whole a custom or Rust target \'stuff\' into a static library \'clib\'. Instead, pass individual object files with the "objects:" keyword argument if possible. Meson had to promote link to link_whole because \'clib\' is installed but not \'stuff\', and thus has to include objects from \'stuff\' to be usable.') l = static_library('clib', 'clib.c', link_with : r, install : true) endtestcase diff --git a/test cases/unit/110 replace unencodable xml chars/meson.build b/test cases/unit/110 replace unencodable xml chars/meson.build index 2e6b1b7930ad..73485b07325b 100644 --- a/test cases/unit/110 replace unencodable xml chars/meson.build +++ b/test cases/unit/110 replace unencodable xml chars/meson.build @@ -1,4 +1,4 @@ project('replace unencodable xml chars') test_script = find_program('script.py') -test('main', test_script) +test('main', test_script, verbose: true) diff --git a/test cases/unit/113 complex link cases/meson.build b/test cases/unit/113 complex link cases/meson.build index 04e6281775d5..3b4b898df748 100644 --- a/test cases/unit/113 complex link cases/meson.build +++ b/test cases/unit/113 complex link cases/meson.build @@ -1,5 +1,7 @@ project('complex link cases', 'c') +cc = meson.get_compiler('c') + # In all tests, e1 uses s3 which uses s2 which uses s1. # Executable links with s3 and s1 but not s2 because it is included in s3. @@ -58,3 +60,52 @@ e = executable('t8-e1', 'main.c', link_with: [s1, s2], dependencies: declare_dependency(link_with: s3), ) + +if cc.get_argument_syntax() == 'gcc' + # s1 is an internal static library, using custom target. + s1_o = custom_target( + input: 's1.c', + output: 's1.c.o', + command: [cc.cmd_array(), '-c', '-o', '@OUTPUT@', '@INPUT@'] + ) + s1 = custom_target( + output: 'libt9-s1.a', + command: ['ar', 'rcs', '@OUTPUT@', s1_o], + ) + + # Executable needs to link with s1, s2 and s3. + s2 = static_library('t9-s2', 's2.c', link_with: s1) + s3 = static_library('t9-s3', 's3.c', link_with: s2) + e = executable('t9-e1', 'main.c', link_with: s3) + + # s2 cannot be installed because s1 is not being installed and Meson cannot + # extract object files from the custom target. + testcase expect_error('Cannot link_whole a custom or Rust target \'libt9-s1.a\' into a static library \'t10-s2\'. Instead, pass individual object files with the "objects:" keyword argument if possible. Meson had to promote link to link_whole because \'t10-s2\' is installed but not \'libt9-s1.a\', and thus has to include objects from \'libt9-s1.a\' to be usable.') + s2 = static_library('t10-s2', 's2.c', link_with: s1, install: true) + endtestcase + + # s3 cannot be installed because s1 is not being installed and Meson cannot + # extract object files from the custom target. + testcase expect_error('Cannot link_whole a custom or Rust target \'libt9-s1.a\' into a static library \'t11-s3\'. Instead, pass individual object files with the "objects:" keyword argument if possible. Meson had to promote link to link_whole because \'t11-s3\' is installed but not \'libt9-s1.a\', and thus has to include objects from \'libt9-s1.a\' to be usable.') + s2 = static_library('t11-s2', 's2.c', link_with: s1) + s3 = static_library('t11-s3', 's3.c', link_with: s2, install: true) + endtestcase + + # s1 is an installed static library, using custom target. + s1 = custom_target( + output: 'libt12-s1.a', + command: ['ar', 'rcs', '@OUTPUT@', s1_o], + install: true, + install_dir: get_option('libdir'), + ) + + # Executable needs to link with s1, s2 and s3. + s2 = static_library('t12-s2', 's2.c', link_with: s1, install: true) + s3 = static_library('t12-s3', 's3.c', link_with: s2) + e = executable('t12-e1', 'main.c', link_with: s3) + + # Executable links with s3 and s1 but not s2 because it is included in s3. + s2 = static_library('t13-s2', 's2.c', link_with: s1) + s3 = static_library('t13-s3', 's3.c', link_with: s2, install: true) + e = executable('t13-e1', 'main.c', link_with: s3) +endif diff --git a/test cases/unit/117 openssl cmake bug/meson.build b/test cases/unit/117 openssl cmake bug/meson.build new file mode 100644 index 000000000000..d08a8ef429a2 --- /dev/null +++ b/test cases/unit/117 openssl cmake bug/meson.build @@ -0,0 +1,5 @@ +project('bug', 'cpp') + +# When cmake is not available, +# this triggers the bug described in #12098 +openssl_dep = dependency('openssl') diff --git a/test cases/unit/117 openssl cmake bug/nativefile.ini b/test cases/unit/117 openssl cmake bug/nativefile.ini new file mode 100644 index 000000000000..dd6b0ff299c9 --- /dev/null +++ b/test cases/unit/117 openssl cmake bug/nativefile.ini @@ -0,0 +1,7 @@ +[binaries] + +cmake = '/path/to/nothing' + +[built-in options] + +pkg_config_path = '' \ No newline at end of file diff --git a/test cases/unit/33 cross file overrides always args/ubuntu-armhf-overrides.txt b/test cases/unit/33 cross file overrides always args/ubuntu-armhf-overrides.txt index a00a7d1366c7..d687b29817a7 100644 --- a/test cases/unit/33 cross file overrides always args/ubuntu-armhf-overrides.txt +++ b/test cases/unit/33 cross file overrides always args/ubuntu-armhf-overrides.txt @@ -6,7 +6,7 @@ cpp = '/usr/bin/arm-linux-gnueabihf-g++' rust = ['rustc', '--target', 'arm-unknown-linux-gnueabihf', '-C', 'linker=/usr/bin/arm-linux-gnueabihf-gcc-7'] ar = '/usr/arm-linux-gnueabihf/bin/ar' strip = '/usr/arm-linux-gnueabihf/bin/strip' -pkgconfig = '/usr/bin/arm-linux-gnueabihf-pkg-config' +pkg-config = '/usr/bin/arm-linux-gnueabihf-pkg-config' [properties] root = '/usr/arm-linux-gnueabihf' diff --git a/test cases/unit/36 exe_wrapper behaviour/broken-cross.txt b/test cases/unit/36 exe_wrapper behaviour/broken-cross.txt index a5a39318d2c8..3615f92f763d 100644 --- a/test cases/unit/36 exe_wrapper behaviour/broken-cross.txt +++ b/test cases/unit/36 exe_wrapper behaviour/broken-cross.txt @@ -3,7 +3,7 @@ c = '/usr/bin/x86_64-w64-mingw32-gcc' cpp = '/usr/bin/x86_64-w64-mingw32-g++' ar = '/usr/bin/x86_64-w64-mingw32-ar' strip = '/usr/bin/x86_64-w64-mingw32-strip' -pkgconfig = '/usr/bin/x86_64-w64-mingw32-pkg-config' +pkg-config = '/usr/bin/x86_64-w64-mingw32-pkg-config' windres = '/usr/bin/x86_64-w64-mingw32-windres' exe_wrapper = 'broken' diff --git a/test cases/unit/90 devenv/meson.build b/test cases/unit/90 devenv/meson.build index 72d8fdc33ca8..316b20c62a39 100644 --- a/test cases/unit/90 devenv/meson.build +++ b/test cases/unit/90 devenv/meson.build @@ -20,3 +20,6 @@ env = environment({'TEST_C': ['/prefix']}, method: 'prepend') meson.add_devenv(env) env = environment({'TEST_C': ['/suffix']}, method: 'append') meson.add_devenv(env) + +# Reproducer for https://github.com/mesonbuild/meson/issues/12032 +pkgconf = import('pkgconfig') diff --git a/test cases/vala/7 shared library/lib/meson.build b/test cases/vala/7 shared library/lib/meson.build index edeeb96d19bb..bbd3862f1611 100644 --- a/test cases/vala/7 shared library/lib/meson.build +++ b/test cases/vala/7 shared library/lib/meson.build @@ -33,3 +33,8 @@ shared_library('installed_vala_onlyvapi', 'mylib.vala', dependencies : valadeps, install : true, install_dir : [false, false, join_paths(get_option('datadir'), 'vala', 'vapi')]) + +# Regression test: Vala libraries were broken when also installing python modules. +# https://gitlab.gnome.org/GNOME/gitg/-/issues/412 +python = import('python').find_installation() +python.install_sources('source.py') diff --git a/test cases/vala/7 shared library/lib/source.py b/test cases/vala/7 shared library/lib/source.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/test cases/vala/7 shared library/test.json b/test cases/vala/7 shared library/test.json index eee3c3dca845..08bd707347c3 100644 --- a/test cases/vala/7 shared library/test.json +++ b/test cases/vala/7 shared library/test.json @@ -9,6 +9,8 @@ {"type": "file", "file": "usr/include/installed_vala_onlyh.h"}, {"type": "file", "file": "usr/share/vala/vapi/installed_vala_all.vapi"}, {"type": "file", "file": "usr/share/vala-1.0/vapi/installed_vala_all_nolib.vapi"}, - {"type": "file", "file": "usr/share/vala/vapi/installed_vala_onlyvapi.vapi"} + {"type": "file", "file": "usr/share/vala/vapi/installed_vala_onlyvapi.vapi"}, + {"type": "python_file", "file": "usr/@PYTHON_PURELIB@/source.py"}, + {"type": "python_bytecode", "file": "usr/@PYTHON_PURELIB@/source.py"} ] } diff --git a/unittests/__init__.py b/unittests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index b4bf3714a4e8..82bfca9b3d51 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -431,9 +431,8 @@ def test_replace_unencodable_xml_chars(self): valid_string = base_string_valid + repr(invalid_string)[1:-1] + base_string_valid invalid_string = base_string_invalid + invalid_string + base_string_invalid - broken_xml_stream = invalid_string.encode() - decoded_broken_stream = mtest.decode(broken_xml_stream) - self.assertEqual(decoded_broken_stream, valid_string) + fixed_string = mtest.replace_unencodable_xml_chars(invalid_string) + self.assertEqual(fixed_string, valid_string) def test_replace_unencodable_xml_chars_unit(self): ''' @@ -445,9 +444,16 @@ def test_replace_unencodable_xml_chars_unit(self): raise SkipTest('xmllint not installed') testdir = os.path.join(self.unit_test_dir, '110 replace unencodable xml chars') self.init(testdir) - self.run_tests() + tests_command_output = self.run_tests() junit_xml_logs = Path(self.logdir, 'testlog.junit.xml') subprocess.run(['xmllint', junit_xml_logs], check=True) + # Ensure command output and JSON / text logs are not mangled. + raw_output_sample = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0b' + assert raw_output_sample in tests_command_output + text_log = Path(self.logdir, 'testlog.txt').read_text() + assert raw_output_sample in text_log + json_log = json.loads(Path(self.logdir, 'testlog.json').read_bytes()) + assert raw_output_sample in json_log['stdout'] def test_run_target_files_path(self): ''' @@ -2721,7 +2727,7 @@ def test_native_dep_pkgconfig(self): with tempfile.NamedTemporaryFile(mode='w', delete=False, encoding='utf-8') as crossfile: crossfile.write(textwrap.dedent( '''[binaries] - pkgconfig = '{}' + pkg-config = '{}' [properties] @@ -2748,7 +2754,7 @@ def test_pkg_config_libdir(self): with tempfile.NamedTemporaryFile(mode='w', delete=False, encoding='utf-8') as crossfile: crossfile.write(textwrap.dedent( '''[binaries] - pkgconfig = 'pkg-config' + pkg-config = 'pkg-config' [properties] pkg_config_libdir = ['{}'] diff --git a/unittests/internaltests.py b/unittests/internaltests.py index b4d090dd29c3..e57dd6d83549 100644 --- a/unittests/internaltests.py +++ b/unittests/internaltests.py @@ -549,11 +549,14 @@ def _test_all_naming(self, cc, env, patterns, platform): if platform != 'openbsd': return with tempfile.TemporaryDirectory() as tmpdir: - for i in ['libfoo.so.6.0', 'libfoo.so.5.0', 'libfoo.so.54.0', 'libfoo.so.66a.0b', 'libfoo.so.70.0.so.1']: + for i in ['libfoo.so.6.0', 'libfoo.so.5.0', 'libfoo.so.54.0', 'libfoo.so.66a.0b', 'libfoo.so.70.0.so.1', + 'libbar.so.7.10', 'libbar.so.7.9', 'libbar.so.7.9.3']: libpath = Path(tmpdir) / i libpath.write_text('', encoding='utf-8') found = cc._find_library_real('foo', env, [tmpdir], '', LibType.PREFER_SHARED, lib_prefix_warning=True) self.assertEqual(os.path.basename(found[0]), 'libfoo.so.54.0') + found = cc._find_library_real('bar', env, [tmpdir], '', LibType.PREFER_SHARED, lib_prefix_warning=True) + self.assertEqual(os.path.basename(found[0]), 'libbar.so.7.10') def test_find_library_patterns(self): ''' diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py index 373887401f0d..1899cb48ccc6 100644 --- a/unittests/linuxliketests.py +++ b/unittests/linuxliketests.py @@ -517,6 +517,12 @@ def _test_stds_impl(self, testdir: str, compiler: 'Compiler') -> None: has_cpp20 = (compiler.get_id() not in {'clang', 'gcc'} or compiler.get_id() == 'clang' and _clang_at_least(compiler, '>=10.0.0', None) or compiler.get_id() == 'gcc' and version_compare(compiler.version, '>=10.0.0')) + has_cpp2b = (compiler.get_id() not in {'clang', 'gcc'} or + compiler.get_id() == 'clang' and _clang_at_least(compiler, '>=12.0.0', None) or + compiler.get_id() == 'gcc' and version_compare(compiler.version, '>=11.0.0')) + has_cpp23 = (compiler.get_id() not in {'clang', 'gcc'} or + compiler.get_id() == 'clang' and _clang_at_least(compiler, '>=17.0.0', None) or + compiler.get_id() == 'gcc' and version_compare(compiler.version, '>=11.0.0')) has_c18 = (compiler.get_id() not in {'clang', 'gcc'} or compiler.get_id() == 'clang' and _clang_at_least(compiler, '>=8.0.0', '>=11.0') or compiler.get_id() == 'gcc' and version_compare(compiler.version, '>=8.0.0')) @@ -533,6 +539,10 @@ def _test_stds_impl(self, testdir: str, compiler: 'Compiler') -> None: continue elif '++20' in v and not has_cpp20: continue + elif '++2b' in v and not has_cpp2b: + continue + elif '++23' in v and not has_cpp23: + continue # now C elif '17' in v and not has_cpp2a_c17: continue @@ -1847,3 +1857,6 @@ def test_complex_link_cases(self): self.assertIn('build t6-e1: c_LINKER t6-e1.p/main.c.o | libt6-s2.a libt6-s3.a\n', content) self.assertIn('build t7-e1: c_LINKER t7-e1.p/main.c.o | libt7-s3.a\n', content) self.assertIn('build t8-e1: c_LINKER t8-e1.p/main.c.o | libt8-s1.a libt8-s2.a libt8-s3.a\n', content) + self.assertIn('build t9-e1: c_LINKER t9-e1.p/main.c.o | libt9-s1.a libt9-s2.a libt9-s3.a\n', content) + self.assertIn('build t12-e1: c_LINKER t12-e1.p/main.c.o | libt12-s1.a libt12-s2.a libt12-s3.a\n', content) + self.assertIn('build t13-e1: c_LINKER t13-e1.p/main.c.o | libt12-s1.a libt13-s3.a\n', content) diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py index fd012939c408..cd2d31993eb5 100644 --- a/unittests/platformagnostictests.py +++ b/unittests/platformagnostictests.py @@ -254,3 +254,10 @@ def test_setup_loaded_modules(self): self.assertEqual(data['modules'], expected) self.assertEqual(data['count'], 68) + + def test_cmake_openssl_not_found_bug(self): + """Issue #12098""" + testdir = os.path.join(self.unit_test_dir, '117 openssl cmake bug') + self.meson_native_files.append(os.path.join(testdir, 'nativefile.ini')) + out = self.init(testdir, allow_fail=True) + self.assertNotIn('Unhandled python exception', out) diff --git a/unittests/windowstests.py b/unittests/windowstests.py index 37e92996d97a..c201953276cc 100644 --- a/unittests/windowstests.py +++ b/unittests/windowstests.py @@ -462,6 +462,9 @@ def test_non_utf8_fails(self): @unittest.skipIf(is_cygwin(), "Needs visual studio") def test_vsenv_option(self): + if mesonbuild.environment.detect_msys2_arch(): + # https://github.com/msys2-contrib/cpython-mingw/issues/141 + raise SkipTest('mingw python fails with /bin being removed from PATH') if self.backend is not Backend.ninja: raise SkipTest('Only ninja backend is valid for test') env = os.environ.copy()