diff options
36 files changed, 376 insertions, 71 deletions
diff --git a/.appveyor.yml b/.appveyor.yml index 56a123a..196ef0f 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -79,20 +79,14 @@ install: - cmd: if %compiler%==msvc2015 ( call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %arch% ) - cmd: if %compiler%==msvc2017 ( call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\VsDevCmd.bat" -arch=%arch% ) - cmd: if %compiler%==cygwin ( set PYTHON=python3 ) else ( set PYTHON=python ) - - ps: | - If($Env:compiler -eq 'msys2-mingw') { - If($Env:arch -eq 'x86') { - $env:Path = 'C:\msys64\mingw32\bin;' + $env:Path - $env:MESON_PYTHON_PATH = 'C:\msys64\mingw32\bin' - $env:PYTHON = 'python3' - C:\msys64\usr\bin\pacman -S --noconfirm mingw32/mingw-w64-i686-python3 - } Else { - $env:Path = 'C:\msys64\mingw64\bin;' + $env:Path - $env:MESON_PYTHON_PATH = 'C:\msys64\mingw64\bin' - $env:PYTHON = 'python3' - C:\msys64\usr\bin\pacman -S --noconfirm mingw64/mingw-w64-x86_64-python3 - } - } + # MinGW setup, lines are split to prevent "The input line is too long." error. + - cmd: if %arch%==x86 ( set "PACMAN_ARCH=i686" ) else ( set "PACMAN_ARCH=x86_64" ) + - cmd: if %arch%==x86 ( set "PACMAN_BITS=32" ) else ( set "PACMAN_BITS=64" ) + - cmd: if %compiler%==msys2-mingw ( set "PATH=C:\msys64\mingw%PACMAN_BITS%\bin;%PATH%" ) + - cmd: if %compiler%==msys2-mingw ( set "MESON_PYTHON_PATH=C:\msys64\mingw%PACMAN_BITS%\bin" ) + - cmd: if %compiler%==msys2-mingw ( set "PYTHON=python3" ) + - cmd: if %compiler%==msys2-mingw ( C:\msys64\usr\bin\pacman -S --needed --noconfirm "mingw%PACMAN_BITS%/mingw-w64-%PACMAN_ARCH%-python3" ) + # Cygwin - cmd: if not %compiler%==cygwin ( set "PATH=%cd%;%MESON_PYTHON_PATH%;%PATH%;" ) - cmd: if %compiler%==cygwin ( set WRAPPER=ci\run-in-cygwin.bat ) - cmd: if %compiler%==cygwin ( %WRAPPER% which %PYTHON% ) else ( where %PYTHON% ) @@ -8,7 +8,7 @@ build system. [](https://pypi.python.org/pypi/meson) [](https://travis-ci.org/mesonbuild/meson) -[](https://ci.appveyor.com/project/jpakkane/meson) +[](https://ci.appveyor.com/project/mesonbuild/meson) [](https://codecov.io/gh/mesonbuild/meson/branch/master) #### Dependencies diff --git a/docs/markdown/Feature-autodetection.md b/docs/markdown/Feature-autodetection.md index 65318ec..f865174 100644 --- a/docs/markdown/Feature-autodetection.md +++ b/docs/markdown/Feature-autodetection.md @@ -16,4 +16,4 @@ If you do not wish to use CCache for some reason, just specify your compiler wit Coverage -- -When doing a code coverage build, Meson will check the existence of binaries `gcovr`, `lcov` and `genhtml`. If the first one is found, it will create targets called *coverage-text* and *coverage-xml*. If the latter two are found, it generates the target *coverage-html*. You can then generate coverage reports just by calling e.g. `ninja coverage-xml`. +When doing a code coverage build, Meson will check the existence of binaries `gcovr`, `lcov` and `genhtml`. If the first one is found, it will create targets called *coverage-text* and *coverage-xml*. If the latter two or a new enough `gcovr` is found, it generates the target *coverage-html*. You can then generate coverage reports just by calling e.g. `ninja coverage-xml`. diff --git a/docs/markdown/Icestorm-module.md b/docs/markdown/Icestorm-module.md index 6aa8481..bc2ad61 100644 --- a/docs/markdown/Icestorm-module.md +++ b/docs/markdown/Icestorm-module.md @@ -1,6 +1,6 @@ # Unstable IceStorm module -This module provides is available since version 0.45.0. +This module is available since version 0.45.0. **Note**:Â this module is unstable. It is only provided as a technology preview. Its API may change in arbitrary ways between releases or it @@ -8,7 +8,7 @@ might be removed from Meson altogether. ## Usage -This module provides an experimental to create FPGA bitstreams using +This module provides an experimental method to create FPGA bitstreams using the [IceStorm](http://www.clifford.at/icestorm/) suite of tools. The module exposes only one method called `project` and it is used @@ -24,4 +24,4 @@ constraint file. This produces output files called `projname.asc`, `projname.blif` and `projname.bin`. In addition it creates two run targets called `projname-time` for running timing analysis and `projname-upload` that uploads the generated bitstream to an FPGA -devide using the `iceprog` programming executable. +device using the `iceprog` programming executable. diff --git a/docs/markdown/Project-templates.md b/docs/markdown/Project-templates.md index d8459c6..5f323bd 100644 --- a/docs/markdown/Project-templates.md +++ b/docs/markdown/Project-templates.md @@ -25,6 +25,6 @@ $ ninja -C builddir ``` The generator has many different projects and settings. They can all -be listed by invoking the command `meson test --help`. +be listed by invoking the command `meson init --help`. This feature is available since Meson version 0.45.0. diff --git a/docs/markdown/Unit-tests.md b/docs/markdown/Unit-tests.md index afbeaa0..53ce9ec 100644 --- a/docs/markdown/Unit-tests.md +++ b/docs/markdown/Unit-tests.md @@ -30,7 +30,7 @@ Note how you need to specify multiple values as an array. Coverage -- -If you enable coverage measurements by giving Meson the command line flag `-Db_coverage=true`, you can generate coverage reports. Meson will autodetect what coverage generator tools you have installed and will generate the corresponding targets. These targets are `coverage-xml` and `coverage-text` which are both provided by [Gcovr](http://gcovr.com) and `coverage-html`, which requires [Lcov](https://ltp.sourceforge.io/coverage/lcov.php) and [GenHTML](https://linux.die.net/man/1/genhtml). +If you enable coverage measurements by giving Meson the command line flag `-Db_coverage=true`, you can generate coverage reports. Meson will autodetect what coverage generator tools you have installed and will generate the corresponding targets. These targets are `coverage-xml` and `coverage-text` which are both provided by [Gcovr](http://gcovr.com) and `coverage-html`, which requires [Lcov](https://ltp.sourceforge.io/coverage/lcov.php) and [GenHTML](https://linux.die.net/man/1/genhtml) or [Gcovr](http://gcovr.com) with html support. The output of these commands is written to the log directory `meson-logs` in your build directory. diff --git a/docs/markdown/snippets/del-old-names.md b/docs/markdown/snippets/del-old-names.md new file mode 100644 index 0000000..c4abc9a --- /dev/null +++ b/docs/markdown/snippets/del-old-names.md @@ -0,0 +1,7 @@ +## Old command names are now errors + +Old executable names `mesonintrospect`, `mesonconf`, `mesonrewriter` +and `mesontest` have been deprecated for a long time. Starting from +this versino they no longer do anything but instead always error +out. All functionality is available as subcommands in the main `meson` +binary. diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index a8e8164..b8ca71f 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -108,9 +108,6 @@ class Backend: self.processed_targets = {} self.build_to_src = os.path.relpath(self.environment.get_source_dir(), self.environment.get_build_dir()) - for t in self.build.targets: - priv_dirname = self.get_target_private_dir_abs(t) - os.makedirs(priv_dirname, exist_ok=True) def get_target_filename(self, t): if isinstance(t, build.CustomTarget): @@ -170,12 +167,10 @@ class Backend: return self.build_to_src def get_target_private_dir(self, target): - dirname = os.path.join(self.get_target_dir(target), target.get_basename() + target.type_suffix()) - return dirname + return os.path.join(self.get_target_dir(target), target.get_id()) def get_target_private_dir_abs(self, target): - dirname = os.path.join(self.environment.get_build_dir(), self.get_target_private_dir(target)) - return dirname + return os.path.join(self.environment.get_build_dir(), self.get_target_private_dir(target)) def get_target_generated_dir(self, target, gensrc, src): """ @@ -519,9 +514,8 @@ class Backend: # Fortran requires extra include directives. if compiler.language == 'fortran': for lt in target.link_targets: - priv_dir = os.path.join(self.get_target_dir(lt), lt.get_basename() + lt.type_suffix()) - incflag = compiler.get_include_args(priv_dir, False) - commands += incflag + priv_dir = self.get_target_private_dir(lt) + commands += compiler.get_include_args(priv_dir, False) return commands def build_target_link_arguments(self, compiler, deps): diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 0c774c1..ba249ed 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -474,8 +474,7 @@ int dummy; def process_target_dependencies(self, target, outfile): for t in target.get_dependencies(): - tname = t.get_basename() + t.type_suffix() - if tname not in self.processed_targets: + if t.get_id() not in self.processed_targets: self.generate_target(t, outfile) def custom_target_generator_inputs(self, target, outfile): @@ -628,19 +627,24 @@ int dummy; self.generate_coverage_legacy_rules(outfile) def generate_coverage_legacy_rules(self, outfile): - (gcovr_exe, lcov_exe, genhtml_exe) = environment.find_coverage_tools() + (gcovr_exe, gcovr_new_rootdir, lcov_exe, genhtml_exe) = environment.find_coverage_tools() added_rule = False if gcovr_exe: + # gcovr >= 3.1 interprets rootdir differently + if gcovr_new_rootdir: + rootdir = self.environment.get_build_dir() + else: + rootdir = self.environment.get_source_dir(), added_rule = True elem = NinjaBuildElement(self.all_outputs, 'meson-coverage-xml', 'CUSTOM_COMMAND', '') - elem.add_item('COMMAND', [gcovr_exe, '-x', '-r', self.environment.get_source_dir(), + elem.add_item('COMMAND', [gcovr_exe, '-x', '-r', rootdir, '-o', os.path.join(self.environment.get_log_dir(), 'coverage.xml')]) elem.add_item('DESC', 'Generating XML coverage report.') elem.write(outfile) # Alias that runs the target defined above self.create_target_alias('meson-coverage-xml', outfile) elem = NinjaBuildElement(self.all_outputs, 'meson-coverage-text', 'CUSTOM_COMMAND', '') - elem.add_item('COMMAND', [gcovr_exe, '-r', self.environment.get_source_dir(), + elem.add_item('COMMAND', [gcovr_exe, '-r', rootdir, '-o', os.path.join(self.environment.get_log_dir(), 'coverage.txt')]) elem.add_item('DESC', 'Generating text coverage report.') elem.write(outfile) @@ -682,6 +686,19 @@ int dummy; elem.add_item('COMMAND', command) elem.add_item('DESC', 'Generating HTML coverage report.') elem.write(outfile) + elif gcovr_exe and gcovr_new_rootdir: + added_rule = True + htmloutdir = os.path.join(self.environment.get_log_dir(), 'coveragereport') + phony_elem = NinjaBuildElement(self.all_outputs, 'meson-coverage-html', 'phony', os.path.join(htmloutdir, 'index.html')) + phony_elem.write(outfile) + # Alias that runs the target defined above + self.create_target_alias('meson-coverage-html', outfile) + elem = NinjaBuildElement(self.all_outputs, os.path.join(htmloutdir, 'index.html'), 'CUSTOM_COMMAND', '') + command = [gcovr_exe, '--html', '--html-details', '-r', self.environment.get_build_dir(), + '-o', os.path.join(htmloutdir, 'index.html')] + elem.add_item('COMMAND', command) + elem.add_item('DESC', 'Generating HTML coverage report.') + elem.write(outfile) if not added_rule: mlog.warning('coverage requested but neither gcovr nor lcov/genhtml found.') diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 7f4c2ef..3b0dc0e 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -304,6 +304,7 @@ class Vs2010Backend(backends.Backend): projlist = [] for name, target in self.build.targets.items(): outdir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(target)) + os.makedirs(outdir, exist_ok=True) fname = name + '.vcxproj' relname = os.path.join(target.subdir, fname) projfile = os.path.join(outdir, fname) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 034fef4..f8dfc96 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -348,7 +348,7 @@ def get_base_link_args(options, linker, is_shared_module): pass try: if 'b_asneeded' in linker.base_options and options['b_asneeded'].value: - args.append('-Wl,--as-needed') + args.append(linker.get_asneeded_args()) except KeyError: pass try: @@ -900,6 +900,13 @@ ICC_STANDARD = 0 ICC_OSX = 1 ICC_WIN = 2 +# GNU ld cannot be installed on macOS +# https://github.com/Homebrew/homebrew-core/issues/17794#issuecomment-328174395 +# Hence, we don't need to differentiate between OS and ld +# for the sake of adding as-needed support +GNU_LD_AS_NEEDED = '-Wl,--as-needed' +APPLE_LD_AS_NEEDED = '-Wl,-dead_strip_dylibs' + def get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module): if soversion is None: sostr = '' @@ -1002,10 +1009,18 @@ class GnuCompiler: 'b_colorout', 'b_ndebug', 'b_staticpic'] if self.gcc_type != GCC_OSX: self.base_options.append('b_lundef') - self.base_options.append('b_asneeded') + self.base_options.append('b_asneeded') # All GCC backends can do assembly self.can_compile_suffixes.add('s') + # TODO: centralise this policy more globally, instead + # of fragmenting it into GnuCompiler and ClangCompiler + def get_asneeded_args(self): + if self.gcc_type == GCC_OSX: + return APPLE_LD_AS_NEEDED + else: + return GNU_LD_AS_NEEDED + def get_colorout_args(self, colortype): if mesonlib.version_compare(self.version, '>=4.9.0'): return gnu_color_args[colortype][:] @@ -1084,10 +1099,18 @@ class ClangCompiler: 'b_ndebug', 'b_staticpic', 'b_colorout'] if self.clang_type != CLANG_OSX: self.base_options.append('b_lundef') - self.base_options.append('b_asneeded') + self.base_options.append('b_asneeded') # All Clang backends can do assembly and LLVM IR self.can_compile_suffixes.update(['ll', 's']) + # TODO: centralise this policy more globally, instead + # of fragmenting it into GnuCompiler and ClangCompiler + def get_asneeded_args(self): + if self.clang_type == CLANG_OSX: + return APPLE_LD_AS_NEEDED + else: + return GNU_LD_AS_NEEDED + def get_pic_args(self): if self.clang_type in (CLANG_WIN, CLANG_OSX): return [] # On Window and OS X, pic is always on. diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 31ca2a2..ff7c706 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -76,19 +76,32 @@ cflags_mapping = {'c': 'CFLAGS', 'd': 'DFLAGS', 'vala': 'VALAFLAGS'} +def detect_gcovr(version='3.1', log=False): + gcovr_exe = 'gcovr' + try: + p, found = Popen_safe([gcovr_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: + if log: + mlog.log('Found gcovr-{} at {}'.format(found, shlex.quote(shutil.which(gcovr_exe)))) + return gcovr_exe, mesonlib.version_compare(found, '>=' + version) + return None, None def find_coverage_tools(): - gcovr_exe = 'gcovr' + gcovr_exe, gcovr_new_rootdir = detect_gcovr() + lcov_exe = 'lcov' genhtml_exe = 'genhtml' - if not mesonlib.exe_exists([gcovr_exe, '--version']): - gcovr_exe = None 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, lcov_exe, genhtml_exe + + return gcovr_exe, gcovr_new_rootdir, lcov_exe, genhtml_exe def detect_ninja(version='1.5', log=False): for n in ['ninja', 'ninja-build']: diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 1819db4..cab8bf3 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -25,7 +25,7 @@ from .mesonlib import FileMode, Popen_safe, listify, extract_as_list from .dependencies import ExternalProgram from .dependencies import InternalDependency, Dependency, DependencyException from .interpreterbase import InterpreterBase -from .interpreterbase import check_stringlist, noPosargs, noKwargs, stringArgs, permittedKwargs +from .interpreterbase import check_stringlist, noPosargs, noKwargs, stringArgs, permittedKwargs, permittedMethodKwargs from .interpreterbase import InterpreterException, InvalidArguments, InvalidCode from .interpreterbase import InterpreterObject, MutableInterpreterObject, Disabler from .modules import ModuleReturnValue @@ -720,9 +720,11 @@ class CompilerHolder(InterpreterObject): 'symbols_have_underscore_prefix': self.symbols_have_underscore_prefix_method, }) + @permittedMethodKwargs({}) def version_method(self, args, kwargs): return self.compiler.version + @permittedMethodKwargs({}) def cmd_array_method(self, args, kwargs): return self.compiler.exelist @@ -762,6 +764,11 @@ class CompilerHolder(InterpreterObject): deps = final_deps return deps + @permittedMethodKwargs({ + 'prefix', + 'args', + 'dependencies', + }) def alignment_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('Alignment method takes exactly one positional argument.') @@ -776,6 +783,13 @@ class CompilerHolder(InterpreterObject): mlog.log('Checking for alignment of "', mlog.bold(typename), '": ', result, sep='') return result + @permittedMethodKwargs({ + 'name', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def run_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('Run method takes exactly one positional argument.') @@ -801,9 +815,11 @@ class CompilerHolder(InterpreterObject): mlog.log('Checking if "', mlog.bold(testname), '" runs: ', h, sep='') return TryRunResultHolder(result) + @permittedMethodKwargs({}) def get_id_method(self, args, kwargs): return self.compiler.get_id() + @permittedMethodKwargs({}) def symbols_have_underscore_prefix_method(self, args, kwargs): ''' Check if the compiler prefixes _ (underscore) to global C symbols @@ -811,6 +827,7 @@ class CompilerHolder(InterpreterObject): ''' return self.compiler.symbols_have_underscore_prefix(self.environment) + @permittedMethodKwargs({}) def unittest_args_method(self, args, kwargs): ''' This function is deprecated and should not be used. @@ -821,6 +838,13 @@ class CompilerHolder(InterpreterObject): build_to_src = os.path.relpath(self.environment.get_source_dir(), self.environment.get_build_dir()) return self.compiler.get_feature_args({'unittest': 'true'}, build_to_src) + @permittedMethodKwargs({ + 'prefix', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def has_member_method(self, args, kwargs): if len(args) != 2: raise InterpreterException('Has_member takes exactly two arguments.') @@ -842,6 +866,13 @@ class CompilerHolder(InterpreterObject): '" has member "', mlog.bold(membername), '": ', hadtxt, sep='') return had + @permittedMethodKwargs({ + 'prefix', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def has_members_method(self, args, kwargs): check_stringlist(args) typename = args[0] @@ -862,6 +893,13 @@ class CompilerHolder(InterpreterObject): '" has members ', members, ': ', hadtxt, sep='') return had + @permittedMethodKwargs({ + 'prefix', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def has_function_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('Has_function takes exactly one argument.') @@ -880,6 +918,13 @@ class CompilerHolder(InterpreterObject): mlog.log('Checking for function "', mlog.bold(funcname), '": ', hadtxt, sep='') return had + @permittedMethodKwargs({ + 'prefix', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def has_type_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('Has_type takes exactly one argument.') @@ -898,6 +943,16 @@ class CompilerHolder(InterpreterObject): mlog.log('Checking for type "', mlog.bold(typename), '": ', hadtxt, sep='') return had + @permittedMethodKwargs({ + 'prefix', + 'low', + 'high', + 'guess', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def compute_int_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('Compute_int takes exactly one argument.') @@ -921,6 +976,13 @@ class CompilerHolder(InterpreterObject): mlog.log('Computing int of "%s": %d' % (expression, res)) return res + @permittedMethodKwargs({ + 'prefix', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def sizeof_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('Sizeof takes exactly one argument.') @@ -935,6 +997,13 @@ class CompilerHolder(InterpreterObject): mlog.log('Checking for size of "%s": %d' % (element, esize)) return esize + @permittedMethodKwargs({ + 'prefix', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def get_define_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('get_define() takes exactly one argument.') @@ -949,6 +1018,13 @@ class CompilerHolder(InterpreterObject): mlog.log('Fetching value of define "%s": %s' % (element, value)) return value + @permittedMethodKwargs({ + 'name', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def compiles_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('compiles method takes exactly one argument.') @@ -972,6 +1048,13 @@ class CompilerHolder(InterpreterObject): mlog.log('Checking if "', mlog.bold(testname), '" compiles: ', h, sep='') return result + @permittedMethodKwargs({ + 'name', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def links_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('links method takes exactly one argument.') @@ -995,6 +1078,13 @@ class CompilerHolder(InterpreterObject): mlog.log('Checking if "', mlog.bold(testname), '" links: ', h, sep='') return result + @permittedMethodKwargs({ + 'prefix', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def has_header_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('has_header method takes exactly one argument.') @@ -1013,6 +1103,13 @@ class CompilerHolder(InterpreterObject): mlog.log('Has header "%s":' % hname, h) return haz + @permittedMethodKwargs({ + 'prefix', + 'no_builtin_args', + 'include_directories', + 'args', + 'dependencies', + }) def has_header_symbol_method(self, args, kwargs): if len(args) != 2: raise InterpreterException('has_header_symbol method takes exactly two arguments.') @@ -1032,6 +1129,10 @@ class CompilerHolder(InterpreterObject): mlog.log('Header <{0}> has symbol "{1}":'.format(hname, symbol), h) return haz + @permittedMethodKwargs({ + 'required', + 'dirs', + }) def find_library_method(self, args, kwargs): # TODO add dependencies support? if len(args) != 1: @@ -1053,6 +1154,7 @@ class CompilerHolder(InterpreterObject): self.compiler.language) return ExternalLibraryHolder(lib) + @permittedMethodKwargs({}) def has_argument_method(self, args, kwargs): args = mesonlib.stringlistify(args) if len(args) != 1: @@ -1065,6 +1167,7 @@ class CompilerHolder(InterpreterObject): mlog.log('Compiler for {} supports argument {}:'.format(self.compiler.get_display_language(), args[0]), h) return result + @permittedMethodKwargs({}) def has_multi_arguments_method(self, args, kwargs): args = mesonlib.stringlistify(args) result = self.compiler.has_multi_arguments(args, self.environment) @@ -1078,6 +1181,7 @@ class CompilerHolder(InterpreterObject): h) return result + @permittedMethodKwargs({}) def get_supported_arguments_method(self, args, kwargs): args = mesonlib.stringlistify(args) result = self.compiler.get_supported_arguments(args, self.environment) @@ -1093,6 +1197,7 @@ class CompilerHolder(InterpreterObject): h) return result + @permittedMethodKwargs({}) def first_supported_argument_method(self, args, kwargs): for i in mesonlib.stringlistify(args): if self.compiler.has_argument(i, self.environment): diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py index 170df29..9279506 100644 --- a/mesonbuild/interpreterbase.py +++ b/mesonbuild/interpreterbase.py @@ -80,6 +80,22 @@ class permittedKwargs: return wrapped +class permittedMethodKwargs: + + def __init__(self, permitted): + self.permitted = permitted + + def __call__(self, f): + @wraps(f) + def wrapped(obj, args, kwargs): + for k in kwargs: + if k not in self.permitted: + mlog.warning('''Passed invalid keyword argument "{}".'''.format(k)) + mlog.warning('This will become a hard error in the future.') + return f(obj, args, kwargs) + return wrapped + + class InterpreterException(mesonlib.MesonException): pass diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 1805e6c..23e666c 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -186,6 +186,7 @@ def list_tests(testdata): to['workdir'] = t.workdir to['timeout'] = t.timeout to['suite'] = t.suite + to['is_parallel'] = t.is_parallel result.append(to) print(json.dumps(result)) diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index bd89670..c89f657 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -63,10 +63,13 @@ class DependenciesHelper: processed_reqs.append(obj.name) elif isinstance(obj, str): processed_reqs.append(obj) + elif isinstance(obj, dependencies.Dependency) and not obj.found(): + pass else: raise mesonlib.MesonException('requires argument not a string, ' 'library with pkgconfig-generated file ' - 'or pkgconfig-dependency object.') + 'or pkgconfig-dependency object, ' + 'got {!r}'.format(obj)) return processed_reqs def add_cflags(self, cflags): diff --git a/mesonbuild/scripts/coverage.py b/mesonbuild/scripts/coverage.py index 47f4cda..2d1f8c3 100644 --- a/mesonbuild/scripts/coverage.py +++ b/mesonbuild/scripts/coverage.py @@ -17,15 +17,20 @@ from mesonbuild import environment import sys, os, subprocess, pathlib def coverage(source_root, build_root, log_dir): - (gcovr_exe, lcov_exe, genhtml_exe) = environment.find_coverage_tools() + (gcovr_exe, gcovr_new_rootdir, lcov_exe, genhtml_exe) = environment.find_coverage_tools() if gcovr_exe: + # gcovr >= 3.1 interprets rootdir differently + if gcovr_new_rootdir: + rootdir = build_root + else: + rootdir = source_root subprocess.check_call([gcovr_exe, '-x', - '-r', source_root, + '-r', rootdir, '-o', os.path.join(log_dir, 'coverage.xml'), ]) subprocess.check_call([gcovr_exe, - '-r', source_root, + '-r', rootdir, '-o', os.path.join(log_dir, 'coverage.txt'), ]) if lcov_exe and genhtml_exe: @@ -65,13 +70,21 @@ def coverage(source_root, build_root, log_dir): '--show-details', '--branch-coverage', covinfo]) + elif gcovr_exe and gcovr_new_rootdir: + htmloutdir = os.path.join(log_dir, 'coveragereport') + subprocess.check_call([gcovr_exe, + '--html', + '--html-details', + '-r', build_root, + '-o', os.path.join(htmloutdir, 'index.html'), + ]) if gcovr_exe: print('') print('XML coverage report can be found at', pathlib.Path(log_dir, 'coverage.xml').as_uri()) print('Text coverage report can be found at', pathlib.Path(log_dir, 'coverage.txt').as_uri()) - if lcov_exe and genhtml_exe: + if (lcov_exe and genhtml_exe) or (gcovr_exe and gcovr_new_rootdir): print('Html coverage report can be found at', pathlib.Path(htmloutdir, 'index.html').as_uri()) return 0 diff --git a/mesonbuild/scripts/meson_install.py b/mesonbuild/scripts/meson_install.py index f895f17..1414ace 100644 --- a/mesonbuild/scripts/meson_install.py +++ b/mesonbuild/scripts/meson_install.py @@ -97,6 +97,10 @@ def restore_selinux_contexts(): # is ignored quietly. return + if not shutil.which('restorecon'): + # If we don't have restorecon, failure is ignored quietly. + return + with subprocess.Popen(['restorecon', '-F', '-f-', '-0'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc: out, err = proc.communicate(input=b'\0'.join(os.fsencode(f) for f in selinux_updates) + b'\0') diff --git a/mesonconf.py b/mesonconf.py index d1874e0..894ec01 100755 --- a/mesonconf.py +++ b/mesonconf.py @@ -14,10 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from mesonbuild import mesonmain import sys if __name__ == '__main__': - print('Warning: This executable is deprecated. Use "meson configure" instead.', - file=sys.stderr) - sys.exit(mesonmain.run(['configure'] + sys.argv[1:])) + sys.exit('Error: This executable is no more. Use "meson configure" instead.') diff --git a/mesonintrospect.py b/mesonintrospect.py index 5cc07bf..9ef1535 100755 --- a/mesonintrospect.py +++ b/mesonintrospect.py @@ -14,10 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from mesonbuild import mesonmain import sys if __name__ == '__main__': - print('Warning: This executable is deprecated. Use "meson introspect" instead.', - file=sys.stderr) - sys.exit(mesonmain.run(['introspect'] + sys.argv[1:])) + sys.exit('Error: This executable is no more. Use "meson introspect" instead.') diff --git a/mesonrewriter.py b/mesonrewriter.py index e6f2637..ef47e57 100755 --- a/mesonrewriter.py +++ b/mesonrewriter.py @@ -23,10 +23,7 @@ # - move targets # - reindent? -from mesonbuild import mesonmain import sys if __name__ == '__main__': - print('Warning: This executable is deprecated. Use "meson rewrite" instead.', - file=sys.stderr) - sys.exit(mesonmain.run(['rewrite'] + sys.argv[1:])) + sys.exit('Error: This executable is no more. Use "meson rewrite" instead.') diff --git a/mesontest.py b/mesontest.py index c2d39d6..e973d56 100755 --- a/mesontest.py +++ b/mesontest.py @@ -16,10 +16,7 @@ # A tool to run tests in many different ways. -from mesonbuild import mesonmain import sys if __name__ == '__main__': - print('Warning: This executable is deprecated. Use "meson test" instead.', - file=sys.stderr) - sys.exit(mesonmain.run(['test'] + sys.argv[1:])) + sys.exit('Error: This executable is no more. Use "meson test" instead.') diff --git a/run_unittests.py b/run_unittests.py index d528717..3ea0412 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -68,9 +68,11 @@ def get_dynamic_section_entry(fname, entry): def get_soname(fname): return get_dynamic_section_entry(fname, 'soname') + def get_rpath(fname): return get_dynamic_section_entry(fname, r'(?:rpath|runpath)') + class InternalTests(unittest.TestCase): def test_version_number(self): @@ -444,6 +446,7 @@ class InternalTests(unittest.TestCase): if f.name != 'add_release_note_snippets_here': self.assertTrue(False, 'A file without .md suffix in snippets dir: ' + f.name) + class BasePlatformTests(unittest.TestCase): def setUp(self): super().setUp() @@ -1783,6 +1786,16 @@ int main(int argc, char **argv) { ]: self.assertRegex(out, re.escape(expected)) + def test_permitted_method_kwargs(self): + tdir = os.path.join(self.unit_test_dir, '23 non-permitted kwargs') + out = self.init(tdir) + for expected in [ + r'WARNING: Passed invalid keyword argument "prefixxx".', + r'WARNING: Passed invalid keyword argument "argsxx".', + r'WARNING: Passed invalid keyword argument "invalidxx".', + ]: + self.assertRegex(out, re.escape(expected)) + def test_templates(self): ninja = detect_ninja() if ninja is None: @@ -1860,6 +1873,15 @@ int main(int argc, char **argv) { testdir = os.path.join(self.unit_test_dir, '23 compiler run_command') self.init(testdir) + def test_identical_target_name_in_subproject_flat_layout(self): + ''' + Test that identical targets in different subprojects do not collide + if layout is flat. + ''' + testdir = os.path.join(self.common_test_dir, '182 identical target name in subproject flat layout') + self.init(testdir, extra_args=['--layout=flat']) + self.build() + class FailureTests(BasePlatformTests): ''' @@ -2458,8 +2480,8 @@ class LinuxlikeTests(BasePlatformTests): def test_unity_subproj(self): testdir = os.path.join(self.common_test_dir, '49 subproject') self.init(testdir, extra_args='--unity=subprojects') - self.assertPathExists(os.path.join(self.builddir, 'subprojects/sublib/simpletest@exe/simpletest-unity.c')) - self.assertPathExists(os.path.join(self.builddir, 'subprojects/sublib/sublib@sha/sublib-unity.c')) + self.assertPathExists(os.path.join(self.builddir, 'subprojects/sublib/sublib@@simpletest@exe/simpletest-unity.c')) + self.assertPathExists(os.path.join(self.builddir, 'subprojects/sublib/sublib@@sublib@sha/sublib-unity.c')) self.assertPathDoesNotExist(os.path.join(self.builddir, 'user@exe/user-unity.c')) self.build() @@ -2641,10 +2663,11 @@ class LinuxlikeTests(BasePlatformTests): self.assertIn("-fsanitize=address", i["command"]) def test_coverage(self): - if not shutil.which('gcovr'): + gcovr_exe, gcovr_new_rootdir = mesonbuild.environment.detect_gcovr() + if not gcovr_exe: raise unittest.SkipTest('gcovr not found') - if not shutil.which('genhtml'): - raise unittest.SkipTest('genhtml not found') + if not shutil.which('genhtml') and not gcovr_new_rootdir: + raise unittest.SkipTest('genhtml not found and gcovr is too old') if 'clang' in os.environ.get('CC', ''): # We need to use llvm-cov instead of gcovr with clang raise unittest.SkipTest('Coverage does not work with clang right now, help wanted!') @@ -2772,6 +2795,7 @@ class LinuxArmCrossCompileTests(BasePlatformTests): compdb = self.get_compdb() self.assertNotIn('-DBUILD_ENVIRONMENT_ONLY', compdb[0]['command']) + class RewriterTests(unittest.TestCase): def setUp(self): diff --git a/test cases/common/182 identical target name in subproject flat layout/foo.c b/test cases/common/182 identical target name in subproject flat layout/foo.c new file mode 100644 index 0000000..ed42789 --- /dev/null +++ b/test cases/common/182 identical target name in subproject flat layout/foo.c @@ -0,0 +1 @@ +int meson_test_main_foo(void) { return 10; } diff --git a/test cases/common/182 identical target name in subproject flat layout/main.c b/test cases/common/182 identical target name in subproject flat layout/main.c new file mode 100644 index 0000000..6f02aeb --- /dev/null +++ b/test cases/common/182 identical target name in subproject flat layout/main.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +int meson_test_main_foo(void); +int meson_test_subproj_foo(void); + +int main(void) { + if (meson_test_main_foo() != 10) { + printf("Failed meson_test_main_foo\n"); + return 1; + } + if (meson_test_subproj_foo() != 20) { + printf("Failed meson_test_subproj_foo\n"); + return 1; + } + return 0; +} diff --git a/test cases/common/182 identical target name in subproject flat layout/meson.build b/test cases/common/182 identical target name in subproject flat layout/meson.build new file mode 100644 index 0000000..d859fda --- /dev/null +++ b/test cases/common/182 identical target name in subproject flat layout/meson.build @@ -0,0 +1,11 @@ +project('subproject targets', 'c') + +# Idea behind this test is to create targets with identical name +# but different output files. We can do this by choosing different +# name_prefix of libraries. Target id does not depend on name_prefix. + +main_foo = static_library('foo', 'foo.c', name_prefix : 'main') +subproj_foo = subproject('subproj').get_variable('foo') + +exe = executable('prog', 'main.c', link_with : [main_foo, subproj_foo]) +test('main test', exe) diff --git a/test cases/common/182 identical target name in subproject flat layout/subprojects/subproj/foo.c b/test cases/common/182 identical target name in subproject flat layout/subprojects/subproj/foo.c new file mode 100644 index 0000000..f334292 --- /dev/null +++ b/test cases/common/182 identical target name in subproject flat layout/subprojects/subproj/foo.c @@ -0,0 +1 @@ +int meson_test_subproj_foo(void) { return 20; } diff --git a/test cases/common/182 identical target name in subproject flat layout/subprojects/subproj/meson.build b/test cases/common/182 identical target name in subproject flat layout/subprojects/subproj/meson.build new file mode 100644 index 0000000..c927194 --- /dev/null +++ b/test cases/common/182 identical target name in subproject flat layout/subprojects/subproj/meson.build @@ -0,0 +1,3 @@ +project('subproj', 'c') + +foo = static_library('foo', 'foo.c', name_prefix : 'subproj') diff --git a/test cases/common/183 as-needed/config.h b/test cases/common/183 as-needed/config.h new file mode 100644 index 0000000..b8fb60f --- /dev/null +++ b/test cases/common/183 as-needed/config.h @@ -0,0 +1,14 @@ +#if defined _WIN32 || defined __CYGWIN__ + #if defined BUILDING_DLL + #define DLL_PUBLIC __declspec(dllexport) + #else + #define DLL_PUBLIC __declspec(dllimport) + #endif +#else + #if defined __GNUC__ + #define DLL_PUBLIC __attribute__ ((visibility("default"))) + #else + #pragma message ("Compiler does not support symbol visibility.") + #define DLL_PUBLIC + #endif +#endif diff --git a/test cases/common/183 as-needed/libA.cpp b/test cases/common/183 as-needed/libA.cpp new file mode 100644 index 0000000..5f45bc0 --- /dev/null +++ b/test cases/common/183 as-needed/libA.cpp @@ -0,0 +1,7 @@ +#define BUILDING_DLL + +#include "libA.h" + +namespace meson_test_as_needed { + DLL_PUBLIC bool linked = false; +} diff --git a/test cases/common/183 as-needed/libA.h b/test cases/common/183 as-needed/libA.h new file mode 100644 index 0000000..8e76d22 --- /dev/null +++ b/test cases/common/183 as-needed/libA.h @@ -0,0 +1,5 @@ +#include "config.h" + +namespace meson_test_as_needed { + DLL_PUBLIC extern bool linked; +} diff --git a/test cases/common/183 as-needed/libB.cpp b/test cases/common/183 as-needed/libB.cpp new file mode 100644 index 0000000..a872394 --- /dev/null +++ b/test cases/common/183 as-needed/libB.cpp @@ -0,0 +1,19 @@ +#include "libA.h" + +#undef DLL_PUBLIC +#define BUILDING_DLL +#include "config.h" + +namespace meson_test_as_needed { + namespace { + bool set_linked() { + linked = true; + return true; + } + bool stub = set_linked(); + } + + DLL_PUBLIC int libB_unused_func() { + return 0; + } +} diff --git a/test cases/common/183 as-needed/main.cpp b/test cases/common/183 as-needed/main.cpp new file mode 100644 index 0000000..cecb2ff --- /dev/null +++ b/test cases/common/183 as-needed/main.cpp @@ -0,0 +1,7 @@ +#include <cstdlib> + +#include "libA.h" + +int main() { + return (meson_test_as_needed::linked == false ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/test cases/common/183 as-needed/meson.build b/test cases/common/183 as-needed/meson.build new file mode 100644 index 0000000..3b54aaa --- /dev/null +++ b/test cases/common/183 as-needed/meson.build @@ -0,0 +1,13 @@ +project('as-needed test', 'cpp') + +# Idea behind this test is to have -Wl,--as-needed prune +# away unneeded linkages, which would otherwise cause global +# static initialiser side-effects to set a boolean to true. + +# Credits for portable ISO C++ idea go to sarum9in + +libA = library('A', 'libA.cpp') +libB = library('B', 'libB.cpp', link_with : libA) + +main_exe = executable('C', 'main.cpp', link_with : [libA, libB]) +test('main test', main_exe) diff --git a/test cases/linuxlike/9 compiler checks with dependencies/meson.build b/test cases/linuxlike/9 compiler checks with dependencies/meson.build index bebfb84..9f1755b 100644 --- a/test cases/linuxlike/9 compiler checks with dependencies/meson.build +++ b/test cases/linuxlike/9 compiler checks with dependencies/meson.build @@ -26,7 +26,7 @@ int main(int argc, char *argv[]) { return ptr == 0; } ''' - assert (cc.has_function('deflate', prefix : '#include<zlib.h>', dependencies : zlib, name : 'Test for function in zlib'), 'has_function test failed.') + assert (cc.has_function('deflate', prefix : '#include<zlib.h>', dependencies : zlib), 'has_function test failed.') assert (cc.links(linkcode, dependencies : zlib, name : 'Test link against zlib'), 'Linking test failed against zlib.') endif diff --git a/test cases/unit/23 non-permitted kwargs/meson.build b/test cases/unit/23 non-permitted kwargs/meson.build new file mode 100644 index 0000000..9f7dc1f --- /dev/null +++ b/test cases/unit/23 non-permitted kwargs/meson.build @@ -0,0 +1,5 @@ +project('non-permitted kwargs', 'c') +cc = meson.get_compiler('c') +cc.has_header_symbol('stdio.h', 'printf', prefixxx: '#define XXX') +cc.links('int main(){}', argsxx: '') +cc.get_id(invalidxx: '') |