aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml22
-rw-r--r--README.md2
-rw-r--r--docs/markdown/Feature-autodetection.md2
-rw-r--r--docs/markdown/Icestorm-module.md6
-rw-r--r--docs/markdown/Project-templates.md2
-rw-r--r--docs/markdown/Unit-tests.md2
-rw-r--r--docs/markdown/snippets/del-old-names.md7
-rw-r--r--mesonbuild/backend/backends.py14
-rw-r--r--mesonbuild/backend/ninjabackend.py27
-rw-r--r--mesonbuild/backend/vs2010backend.py1
-rw-r--r--mesonbuild/compilers/compilers.py29
-rw-r--r--mesonbuild/environment.py21
-rw-r--r--mesonbuild/interpreter.py107
-rw-r--r--mesonbuild/interpreterbase.py16
-rw-r--r--mesonbuild/mintro.py1
-rw-r--r--mesonbuild/modules/pkgconfig.py5
-rw-r--r--mesonbuild/scripts/coverage.py21
-rw-r--r--mesonbuild/scripts/meson_install.py4
-rwxr-xr-xmesonconf.py5
-rwxr-xr-xmesonintrospect.py5
-rwxr-xr-xmesonrewriter.py5
-rwxr-xr-xmesontest.py5
-rwxr-xr-xrun_unittests.py34
-rw-r--r--test cases/common/182 identical target name in subproject flat layout/foo.c1
-rw-r--r--test cases/common/182 identical target name in subproject flat layout/main.c16
-rw-r--r--test cases/common/182 identical target name in subproject flat layout/meson.build11
-rw-r--r--test cases/common/182 identical target name in subproject flat layout/subprojects/subproj/foo.c1
-rw-r--r--test cases/common/182 identical target name in subproject flat layout/subprojects/subproj/meson.build3
-rw-r--r--test cases/common/183 as-needed/config.h14
-rw-r--r--test cases/common/183 as-needed/libA.cpp7
-rw-r--r--test cases/common/183 as-needed/libA.h5
-rw-r--r--test cases/common/183 as-needed/libB.cpp19
-rw-r--r--test cases/common/183 as-needed/main.cpp7
-rw-r--r--test cases/common/183 as-needed/meson.build13
-rw-r--r--test cases/linuxlike/9 compiler checks with dependencies/meson.build2
-rw-r--r--test cases/unit/23 non-permitted kwargs/meson.build5
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% )
diff --git a/README.md b/README.md
index 6185f8e..969e251 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ build system.
[![PyPI](https://img.shields.io/pypi/v/meson.svg)](https://pypi.python.org/pypi/meson)
[![Travis](https://travis-ci.org/mesonbuild/meson.svg?branch=master)](https://travis-ci.org/mesonbuild/meson)
-[![Appveyor](https://ci.appveyor.com/api/projects/status/l5c8v71ninew2i3p?svg=true)](https://ci.appveyor.com/project/jpakkane/meson)
+[![Appveyor](https://ci.appveyor.com/api/projects/status/7jfaotriu8d8ncov?svg=true)](https://ci.appveyor.com/project/mesonbuild/meson)
[![Codecov](https://codecov.io/gh/mesonbuild/meson/coverage.svg?branch=master)](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: '')