Merge pull request #1006 from centricular/cpp-has-header-symbol

A bunch of fixes for compiler checks with C++
pull/1017/head
Jussi Pakkanen 8 years ago committed by GitHub
commit cf7b50364f
  1. 8
      .travis.yml
  2. 58
      mesonbuild/compilers.py
  3. 32
      test cases/common/111 has header symbol/meson.build
  4. 29
      test cases/common/33 try compile/meson.build
  5. 24
      test cases/common/35 sizeof/meson.build
  6. 6
      test cases/common/35 sizeof/prog.c.in
  7. 18
      test cases/common/37 has header/meson.build
  8. 80
      test cases/common/39 tryrun/meson.build
  9. 72
      test cases/common/43 has function/meson.build
  10. 30
      test cases/common/44 has member/meson.build
  11. 48
      test cases/common/45 alignment/meson.build
  12. 18
      test cases/common/83 has type/meson.build

@ -18,6 +18,12 @@ language:
services:
- docker
matrix:
exclude:
# On OS X gcc is just a wrapper around clang, so don't waste time testing that
- os: osx
compiler: gcc
before_install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install ninja python3; fi
@ -30,5 +36,5 @@ script:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then echo FROM jpakkane/mesonci:yakkety > Dockerfile; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then echo ADD . /root >> Dockerfile; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker build -t withgit .; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker run withgit /bin/sh -c "cd /root && TRAVIS=true ./run_tests.py -- $MESON_ARGS"; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker run withgit /bin/sh -c "cd /root && TRAVIS=true CC=$CC CXX=$CXX ./run_tests.py -- $MESON_ARGS"; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then SDKROOT=$(xcodebuild -version -sdk macosx Path) ./run_tests.py --backend=ninja -- $MESON_ARGS ; fi

@ -513,6 +513,13 @@ class CCompiler(Compiler):
def get_no_optimization_args(self):
return ['-O0']
def get_compiler_check_args(self):
'''
Get arguments useful for compiler checks such as being permissive in
the code quality and not doing any optimization.
'''
return self.get_no_optimization_args()
def get_output_args(self, target):
return ['-o', target]
@ -644,9 +651,14 @@ int someSymbolHereJustForFun;
extra_args = []
templ = '''{2}
#include <{0}>
int main () {{ {1}; }}'''
# Pass -O0 to ensure that the symbol isn't optimized away
args = extra_args + self.get_no_optimization_args()
int main () {{
/* If it's not defined as a macro, try to use as a symbol */
#ifndef {1}
{1};
#endif
return 0;
}}'''
args = extra_args + self.get_compiler_check_args()
return self.compiles(templ.format(hname, symbol, prefix), env, args, dependencies)
@contextlib.contextmanager
@ -785,7 +797,7 @@ int main(int argc, char **argv) {{
%s
int temparray[%d-sizeof(%s)];
'''
args = extra_args + self.get_no_optimization_args()
args = extra_args + self.get_compiler_check_args()
if not self.compiles(element_exists_templ.format(prefix, element), env, args, dependencies):
return -1
for i in range(1, 1024):
@ -834,7 +846,7 @@ struct tmp {
int testarray[%d-offsetof(struct tmp, target)];
'''
args = extra_args + self.get_no_optimization_args()
args = extra_args + self.get_compiler_check_args()
if not self.compiles(type_exists_templ.format(typename), env, args, dependencies):
return -1
for i in range(1, 1024):
@ -919,7 +931,7 @@ int main(int argc, char **argv) {
head = '#include <limits.h>\n{0}\n'
# We don't know what the function takes or returns, so try to use it as
# a function pointer
main = '\nint main() {{ int a = (int) &{1}; }}'
main = '\nint main() {{ void *a = (void*) &{1}; }}'
return head, main
def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None):
@ -968,9 +980,8 @@ int main(int argc, char **argv) {
head, main = self._no_prototype_templ()
templ = head + stubs_fail + main
# Add -O0 to ensure that the symbol isn't optimized away by the compiler
args = extra_args + self.get_no_optimization_args()
if self.links(templ.format(prefix, funcname), env, extra_args, dependencies):
args = extra_args + self.get_compiler_check_args()
if self.links(templ.format(prefix, funcname), env, args, dependencies):
return True
# Some functions like alloca() are defined as compiler built-ins which
# are inlined by the compiler, so test for that instead. Built-ins are
@ -1049,6 +1060,20 @@ class CPPCompiler(CCompiler):
code = 'class breakCCompiler;int main(int argc, char **argv) { return 0; }\n'
return self.sanity_check_impl(work_dir, environment, 'sanitycheckcpp.cc', code)
def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None, dependencies=None):
# Check if it's a C-like symbol
if super().has_header_symbol(hname, symbol, prefix, env, extra_args, dependencies):
return True
# Check if it's a class or a template
if extra_args is None:
extra_args = []
templ = '''{2}
#include <{0}>
using {1};
int main () {{ return 0; }}'''
args = extra_args + self.get_compiler_check_args()
return self.compiles(templ.format(hname, symbol, prefix), env, args, dependencies)
class ObjCCompiler(CCompiler):
def __init__(self, exelist, version, is_cross, exe_wrap):
self.language = 'objc'
@ -1868,9 +1893,10 @@ class VisualStudioCCompiler(CCompiler):
pdbarr += ['pdb']
return ['/DEBUG', '/PDB:' + '.'.join(pdbarr)]
class VisualStudioCPPCompiler(VisualStudioCCompiler):
class VisualStudioCPPCompiler(VisualStudioCCompiler, CPPCompiler):
def __init__(self, exelist, version, is_cross, exe_wrap):
self.language = 'cpp'
CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap)
VisualStudioCCompiler.__init__(self, exelist, version, is_cross, exe_wrap)
self.base_options = ['b_pch'] # FIXME add lto, pgo and the like
@ -2045,6 +2071,12 @@ class GnuCPPCompiler(GnuCompiler, CPPCompiler):
return options['cpp_winlibs'].value
return []
def get_compiler_check_args(self):
# -fpermissive allows non-conforming code to compile which is necessary
# for many C++ checks. Particularly, the has_header_symbol check is
# too strict without this and always fails.
return self.get_no_optimization_args() + ['-fpermissive']
class GnuObjCCompiler(GnuCompiler,ObjCCompiler):
def __init__(self, exelist, version, is_cross, exe_wrapper=None, defines=None):
@ -2067,6 +2099,12 @@ class GnuObjCPPCompiler(GnuCompiler, ObjCPPCompiler):
'2': ['-Wall', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor'],
'3' : ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor']}
def get_compiler_check_args(self):
# -fpermissive allows non-conforming code to compile which is necessary
# for many ObjC++ checks. Particularly, the has_header_symbol check is
# too strict without this and always fails.
return self.get_no_optimization_args() + ['-fpermissive']
class ClangCompiler():
def __init__(self, clang_type):
self.id = 'clang'

@ -1,18 +1,32 @@
project('has header symbol', 'c')
project('has header symbol', 'c', 'cpp')
cc = meson.get_compiler('c')
cpp = meson.get_compiler('cpp')
assert (cc.has_header_symbol('stdio.h', 'int'), 'base types should always be available')
assert (cc.has_header_symbol('stdio.h', 'printf'), 'printf function not found')
assert (cc.has_header_symbol('stdio.h', 'FILE'), 'FILE structure not found')
assert (cc.has_header_symbol('limits.h', 'INT_MAX'), 'INT_MAX define not found')
assert (not cc.has_header_symbol('limits.h', 'guint64'), 'guint64 is not defined in limits.h')
assert (not cc.has_header_symbol('stdlib.h', 'FILE'), 'FILE structure is defined in stdio.h, not stdlib.h')
assert (not cc.has_header_symbol('stdlol.h', 'printf'), 'stdlol.h shouldn\'t exist')
assert (not cc.has_header_symbol('stdlol.h', 'int'), 'shouldn\'t be able to find "int" with invalid header')
foreach comp : [cc, cpp]
assert (comp.has_header_symbol('stdio.h', 'int'), 'base types should always be available')
assert (comp.has_header_symbol('stdio.h', 'printf'), 'printf function not found')
assert (comp.has_header_symbol('stdio.h', 'FILE'), 'FILE structure not found')
assert (comp.has_header_symbol('limits.h', 'INT_MAX'), 'INT_MAX define not found')
assert (not comp.has_header_symbol('limits.h', 'guint64'), 'guint64 is not defined in limits.h')
assert (not comp.has_header_symbol('stdlib.h', 'FILE'), 'FILE structure is defined in stdio.h, not stdlib.h')
assert (not comp.has_header_symbol('stdlol.h', 'printf'), 'stdlol.h shouldn\'t exist')
assert (not comp.has_header_symbol('stdlol.h', 'int'), 'shouldn\'t be able to find "int" with invalid header')
endforeach
# This is likely only available on Glibc, so just test for it
if cc.has_function('ppoll')
assert (not cc.has_header_symbol('poll.h', 'ppoll'), 'ppoll should not be accessible without _GNU_SOURCE')
assert (cc.has_header_symbol('poll.h', 'ppoll', prefix : '#define _GNU_SOURCE'), 'ppoll should be accessible with _GNU_SOURCE')
endif
assert (cpp.has_header_symbol('iostream', 'std::iostream'), 'iostream not found in iostream.h')
assert (cpp.has_header_symbol('vector', 'std::vector'), 'vector not found in vector.h')
assert (not cpp.has_header_symbol('limits.h', 'std::iostream'), 'iostream should not be defined in limits.h')
boost = dependency('boost', required : false)
if boost.found()
assert (cpp.has_header_symbol('boost/math/quaternion.hpp', 'boost::math::quaternion', dependencies : boost), 'quaternion not found')
else
assert (not cpp.has_header_symbol('boost/math/quaternion.hpp', 'boost::math::quaternion', dependencies : boost), 'quaternion found?!')
endif

@ -1,4 +1,4 @@
project('try compile', 'c')
project('try compile', 'c', 'cpp')
code = '''#include<stdio.h>
void func() { printf("Something.\n"); }
@ -8,19 +8,20 @@ breakcode = '''#include<nonexisting.h>
void func() { printf("This won't work.\n"); }
'''
compiler = meson.get_compiler('c')
if compiler.compiles(code, name : 'should succeed') == false
error('Compiler is fail.')
endif
foreach compiler : [meson.get_compiler('c'), meson.get_compiler('cpp')]
if compiler.compiles(code, name : 'should succeed') == false
error('Compiler ' + compiler.get_id() + ' is fail.')
endif
if compiler.compiles(files('valid.c'), name : 'should succeed') == false
error('Compiler is fail.')
endif
if compiler.compiles(files('valid.c'), name : 'should succeed') == false
error('Compiler ' + compiler.get_id() + ' is fail.')
endif
if compiler.compiles(breakcode, name : 'should fail')
error('Compiler returned true on broken code.')
endif
if compiler.compiles(breakcode, name : 'should fail')
error('Compiler ' + compiler.get_id() + ' returned true on broken code.')
endif
if compiler.compiles(files('invalid.c'), name : 'should fail')
error('Compiler returned true on broken code.')
endif
if compiler.compiles(files('invalid.c'), name : 'should fail')
error('Compiler ' + compiler.get_id() + ' returned true on broken code.')
endif
endforeach

@ -1,13 +1,33 @@
project('sizeof', 'c')
project('sizeof', 'c', 'cpp')
# Test with C
cc = meson.get_compiler('c')
intsize = cc.sizeof('int')
wcharsize = cc.sizeof('wchar_t', prefix : '#include<wchar.h>')
cd = configuration_data()
cd.set('INTSIZE', intsize)
cd.set('WCHARSIZE', wcharsize)
cd.set('CONFIG', 'config.h')
configure_file(input : 'config.h.in', output : 'config.h', configuration : cd)
s = configure_file(input : 'prog.c.in', output : 'prog.c', configuration : cd)
e = executable('prog', 'prog.c')
e = executable('prog', s)
test('sizeof test', e)
# Test with C++
cpp = meson.get_compiler('cpp')
intsize = cpp.sizeof('int')
wcharsize = cpp.sizeof('wchar_t', prefix : '#include<wchar.h>')
cdpp = configuration_data()
cdpp.set('INTSIZE', intsize)
cdpp.set('WCHARSIZE', wcharsize)
cdpp.set('CONFIG', 'config.hpp')
configure_file(input : 'config.h.in', output : 'config.hpp', configuration : cdpp)
spp = configure_file(input : 'prog.c.in', output : 'prog.cc', configuration : cdpp)
epp = executable('progpp', spp)
test('sizeof test c++', epp)

@ -1,6 +1,6 @@
#include"config.h"
#include<stdio.h>
#include<wchar.h>
#include "@CONFIG@"
#include <stdio.h>
#include <wchar.h>
int main(int argc, char **argv) {
if(INTSIZE != sizeof(int)) {

@ -1,11 +1,11 @@
project('has header', 'c')
project('has header', 'c', 'cpp')
cc = meson.get_compiler('c')
foreach comp : [meson.get_compiler('c'), meson.get_compiler('cpp')]
if comp.has_header('stdio.h') == false
error('Stdio missing.')
endif
if cc.has_header('stdio.h') == false
error('Stdio missing.')
endif
if cc.has_header('ouagadougou.h')
error('Found non-existant header.')
endif
if comp.has_header('ouagadougou.h')
error('Found non-existant header.')
endif
endforeach

@ -1,14 +1,14 @@
project('tryrun', 'c')
project('tryrun', 'c', 'cpp')
# Complex to exercise all code paths.
if meson.is_cross_build()
if meson.has_exe_wrapper()
cc = meson.get_compiler('c', native : false)
compilers = [meson.get_compiler('c', native : false), meson.get_compiler('cpp', native : false)]
else
cc = meson.get_compiler('c', native : true)
compilers = [meson.get_compiler('c', native : true), meson.get_compiler('cpp', native : true)]
endif
else
cc = meson.get_compiler('c')
compilers = [meson.get_compiler('c'), meson.get_compiler('cpp')]
endif
ok_code = '''#include<stdio.h>
@ -32,45 +32,47 @@ INPUTS = [
['File', files('ok.c'), files('error.c'), files('no_compile.c')],
]
foreach input : INPUTS
type = input[0]
ok = cc.run(input[1], name : type + ' should succeed')
err = cc.run(input[2], name : type + ' should fail')
noc = cc.run(input[3], name : type + ' does not compile')
foreach cc : compilers
foreach input : INPUTS
type = input[0]
ok = cc.run(input[1], name : type + ' should succeed')
err = cc.run(input[2], name : type + ' should fail')
noc = cc.run(input[3], name : type + ' does not compile')
if noc.compiled()
error(type + ' compilation fail test failed.')
else
message(type + ' fail detected properly.')
endif
if noc.compiled()
error(type + ' compilation fail test failed.')
else
message(type + ' fail detected properly.')
endif
if ok.compiled()
message(type + ' compilation worked.')
else
error(type + ' compilation did not work.')
endif
if ok.compiled()
message(type + ' compilation worked.')
else
error(type + ' compilation did not work.')
endif
if ok.returncode() == 0
message(type + ' return code ok.')
else
error(type + ' return code fail')
endif
if ok.returncode() == 0
message(type + ' return code ok.')
else
error(type + ' return code fail')
endif
if err.returncode() == 1
message(type + ' bad return code ok.')
else
error(type + ' bad return code fail.')
endif
if err.returncode() == 1
message(type + ' bad return code ok.')
else
error(type + ' bad return code fail.')
endif
if ok.stdout().strip() == 'stdout'
message(type + ' stdout ok.')
else
message(type + ' bad stdout.')
endif
if ok.stdout().strip() == 'stdout'
message(type + ' stdout ok.')
else
message(type + ' bad stdout.')
endif
if ok.stderr().strip() == 'stderr'
message(type + ' stderr ok.')
else
message(type + ' bad stderr.')
endif
if ok.stderr().strip() == 'stderr'
message(type + ' stderr ok.')
else
message(type + ' bad stderr.')
endif
endforeach
endforeach

@ -1,41 +1,43 @@
project('has function', 'c')
project('has function', 'c', 'cpp')
cc = meson.get_compiler('c')
compilers = [meson.get_compiler('c'), meson.get_compiler('cpp')]
if not cc.has_function('printf', prefix : '#include<stdio.h>')
error('"printf" function not found (should always exist).')
endif
foreach cc : compilers
if not cc.has_function('printf', prefix : '#include<stdio.h>')
error('"printf" function not found (should always exist).')
endif
# Should also be able to detect it without specifying the header
# We check for a different function here to make sure the result is
# not taken from a cache (ie. the check above)
# On MSVC fprintf is defined as an inline function in the header, so it cannot
# be found without the include.
if cc.get_id() != 'msvc'
assert(cc.has_function('fprintf'), '"fprintf" function not found without include (on !msvc).')
else
assert(cc.has_function('fprintf', prefix : '#include <stdio.h>'), '"fprintf" function not found with include (on msvc).')
endif
# Should also be able to detect it without specifying the header
# We check for a different function here to make sure the result is
# not taken from a cache (ie. the check above)
# On MSVC fprintf is defined as an inline function in the header, so it cannot
# be found without the include.
if cc.get_id() != 'msvc'
assert(cc.has_function('fprintf'), '"fprintf" function not found without include (on !msvc).')
else
assert(cc.has_function('fprintf', prefix : '#include <stdio.h>'), '"fprintf" function not found with include (on msvc).')
endif
if cc.has_function('hfkerhisadf', prefix : '#include<stdio.h>')
error('Found non-existent function "hfkerhisadf".')
endif
if cc.has_function('hfkerhisadf', prefix : '#include<stdio.h>')
error('Found non-existent function "hfkerhisadf".')
endif
# With glibc on Linux lchmod is a stub that will always return an error,
# we want to detect that and declare that the function is not available.
# We can't check for the C library used here of course, but if it's not
# implemented in glibc it's probably not implemented in any other 'slimmer'
# C library variants either, so the check should be safe either way hopefully.
if host_machine.system() == 'linux' and cc.get_id() == 'gcc'
assert (cc.has_function('poll', prefix : '#include <poll.h>'), 'couldn\'t detect "poll" when defined by a header')
assert (not cc.has_function('lchmod', prefix : '''#include <sys/stat.h>
#include <unistd.h>'''), '"lchmod" check should have failed')
endif
# With glibc on Linux lchmod is a stub that will always return an error,
# we want to detect that and declare that the function is not available.
# We can't check for the C library used here of course, but if it's not
# implemented in glibc it's probably not implemented in any other 'slimmer'
# C library variants either, so the check should be safe either way hopefully.
if host_machine.system() == 'linux' and cc.get_id() == 'gcc'
assert (cc.has_function('poll', prefix : '#include <poll.h>'), 'couldn\'t detect "poll" when defined by a header')
assert (not cc.has_function('lchmod', prefix : '''#include <sys/stat.h>
#include <unistd.h>'''), '"lchmod" check should have failed')
endif
# For some functions one needs to define _GNU_SOURCE before including the
# right headers to get them picked up. Make sure we can detect these functions
# as well without any prefix
if cc.has_header_symbol('sys/socket.h', 'recvmmsg', prefix : '#define _GNU_SOURCE')
# We assume that if recvmmsg exists sendmmsg does too
assert (cc.has_function('sendmmsg'), 'Failed to detect function "sendmmsg" (should always exist).')
endif
# For some functions one needs to define _GNU_SOURCE before including the
# right headers to get them picked up. Make sure we can detect these functions
# as well without any prefix
if cc.has_header_symbol('sys/socket.h', 'recvmmsg', prefix : '#define _GNU_SOURCE')
# We assume that if recvmmsg exists sendmmsg does too
assert (cc.has_function('sendmmsg'), 'Failed to detect function "sendmmsg" (should always exist).')
endif
endforeach

@ -1,19 +1,21 @@
project('has member', 'c')
project('has member', 'c', 'cpp')
cc = meson.get_compiler('c')
compilers = [meson.get_compiler('c'), meson.get_compiler('cpp')]
if not cc.has_member('struct tm', 'tm_sec', prefix : '#include<time.h>')
error('Did not detect member of "struct tm" that exists: "tm_sec"')
endif
foreach cc : compilers
if not cc.has_member('struct tm', 'tm_sec', prefix : '#include<time.h>')
error('Did not detect member of "struct tm" that exists: "tm_sec"')
endif
if cc.has_member('struct tm', 'tm_nonexistent', prefix : '#include<time.h>')
error('Not existing member "tm_nonexistent" found.')
endif
if cc.has_member('struct tm', 'tm_nonexistent', prefix : '#include<time.h>')
error('Not existing member "tm_nonexistent" found.')
endif
if not cc.has_members('struct tm', 'tm_sec', 'tm_min', prefix : '#include<time.h>')
error('Did not detect members of "struct tm" that exist: "tm_sec" "tm_min"')
endif
if not cc.has_members('struct tm', 'tm_sec', 'tm_min', prefix : '#include<time.h>')
error('Did not detect members of "struct tm" that exist: "tm_sec" "tm_min"')
endif
if cc.has_members('struct tm', 'tm_sec', 'tm_nonexistent2', prefix : '#include<time.h>')
error('Not existing member "tm_nonexistent2" found.')
endif
if cc.has_members('struct tm', 'tm_sec', 'tm_nonexistent2', prefix : '#include<time.h>')
error('Not existing member "tm_nonexistent2" found.')
endif
endforeach

@ -1,29 +1,31 @@
project('alignment', 'c')
project('alignment', 'c', 'cpp')
cc = meson.get_compiler('c')
compilers = [meson.get_compiler('c'), meson.get_compiler('cpp')]
# These tests should return the same value on all
# platforms. If (and when) they don't, fix 'em up.
if cc.alignment('char') != 1
error('Alignment of char misdetected.')
endif
foreach cc : compilers
# These tests should return the same value on all
# platforms. If (and when) they don't, fix 'em up.
if cc.alignment('char') != 1
error('Alignment of char misdetected.')
endif
ptr_size = cc.sizeof('void*')
dbl_alignment = cc.alignment('double')
ptr_size = cc.sizeof('void*')
dbl_alignment = cc.alignment('double')
# These tests are not thorough. Doing this properly
# would take a lot of work because it is strongly
# platform and compiler dependent. So just check
# that they produce something fairly sane.
# These tests are not thorough. Doing this properly
# would take a lot of work because it is strongly
# platform and compiler dependent. So just check
# that they produce something fairly sane.
if ptr_size == 8 or ptr_size == 4
message('Size of ptr ok.')
else
error('Size of ptr misdetected.')
endif
if ptr_size == 8 or ptr_size == 4
message('Size of ptr ok.')
else
error('Size of ptr misdetected.')
endif
if dbl_alignment == 8 or dbl_alignment == 4
message('Alignment of double ok.')
else
error('Alignment of double misdetected.')
endif
if dbl_alignment == 8 or dbl_alignment == 4
message('Alignment of double ok.')
else
error('Alignment of double misdetected.')
endif
endforeach

@ -1,11 +1,13 @@
project('has type', 'c')
project('has type', 'c', 'cpp')
cc = meson.get_compiler('c')
compilers = [meson.get_compiler('c'), meson.get_compiler('cpp')]
if not cc.has_type('time_t', prefix : '#include<time.h>')
error('Did not detect type that exists.')
endif
foreach cc : compilers
if not cc.has_type('time_t', prefix : '#include<time.h>')
error('Did not detect type that exists.')
endif
if cc.has_type('no_time_t', prefix : '#include<time.h>')
error('Not existing type found.')
endif
if cc.has_type('no_time_t', prefix : '#include<time.h>')
error('Not existing type found.')
endif
endforeach

Loading…
Cancel
Save