aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mesonbuild/backend/ninjabackend.py6
-rw-r--r--mesonbuild/compilers.py181
-rw-r--r--mesonbuild/coredata.py4
-rw-r--r--mesonbuild/environment.py105
-rw-r--r--mesonbuild/interpreter.py11
-rw-r--r--mesonbuild/mconf.py11
-rw-r--r--mesonbuild/modules/gnome.py147
-rwxr-xr-xrun_tests.py12
-rwxr-xr-xrun_unittests.py14
-rw-r--r--test cases/osx/3 has function xcode8/meson.build26
-rw-r--r--test cases/vala/12 generated vapi/installed_files.txt7
-rw-r--r--test cases/vala/12 generated vapi/libbar/bar.c12
-rw-r--r--test cases/vala/12 generated vapi/libbar/bar.h5
-rw-r--r--test cases/vala/12 generated vapi/libbar/meson.build33
-rw-r--r--test cases/vala/12 generated vapi/libfoo/foo.c11
-rw-r--r--test cases/vala/12 generated vapi/libfoo/foo.h5
-rw-r--r--test cases/vala/12 generated vapi/libfoo/meson.build36
-rw-r--r--test cases/vala/12 generated vapi/main.vala9
-rw-r--r--test cases/vala/12 generated vapi/meson.build13
19 files changed, 486 insertions, 162 deletions
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index dd0143c..add7dcc 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -212,10 +212,10 @@ int dummy;
builddir = self.environment.get_build_dir()
try:
jsondb = subprocess.check_output([ninja_exe, '-t', 'compdb', 'c_COMPILER', 'cpp_COMPILER'], cwd=builddir)
+ with open(os.path.join(builddir, 'compile_commands.json'), 'wb') as f:
+ f.write(jsondb)
except Exception:
- raise MesonException('Could not create compilation database.')
- with open(os.path.join(builddir, 'compile_commands.json'), 'wb') as f:
- f.write(jsondb)
+ mlog.log(mlog.red('Warning:', 'Could not create compilation database.'))
# Get all generated headers. Any source file might need them so
# we need to add an order dependency to them.
diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py
index d7cd1f7..94e8a54 100644
--- a/mesonbuild/compilers.py
+++ b/mesonbuild/compilers.py
@@ -17,7 +17,7 @@ import subprocess, os.path
import tempfile
from .import mesonlib
from . import mlog
-from .mesonlib import MesonException
+from .mesonlib import MesonException, version_compare
from . import coredata
"""This file contains the data files of all compilers Meson knows
@@ -707,12 +707,16 @@ int main () {{ {1}; }}'''
args = self.unix_link_flags_to_native(cargs + extra_args)
# Read c_args/cpp_args/etc from the cross-info file (if needed)
args += self.get_cross_extra_flags(env, compile=True, link=False)
+ # Add CFLAGS/CXXFLAGS/OBJCFLAGS/OBJCXXFLAGS from the env
+ # We assume that the user has ensured these are compiler-specific
+ args += env.coredata.external_args[self.language]
# We only want to compile; not link
args += self.get_compile_only_args()
with self.compile(code, args) as p:
return p.returncode == 0
- def links(self, code, env, extra_args=None, dependencies=None):
+ def _links_wrapper(self, code, env, extra_args, dependencies):
+ "Shares common code between self.links and self.run"
if extra_args is None:
extra_args = []
elif isinstance(extra_args, str):
@@ -729,27 +733,19 @@ int main () {{ {1}; }}'''
args += self.get_linker_debug_crt_args()
# Read c_args/c_link_args/cpp_args/cpp_link_args/etc from the cross-info file (if needed)
args += self.get_cross_extra_flags(env, compile=True, link=True)
- with self.compile(code, args) as p:
+ # Add LDFLAGS from the env. We assume that the user has ensured these
+ # are compiler-specific
+ args += env.coredata.external_link_args[self.language]
+ return self.compile(code, args)
+
+ def links(self, code, env, extra_args=None, dependencies=None):
+ with self._links_wrapper(code, env, extra_args, dependencies) as p:
return p.returncode == 0
def run(self, code, env, extra_args=None, dependencies=None):
- if extra_args is None:
- extra_args = []
- if dependencies is None:
- dependencies = []
- elif not isinstance(dependencies, list):
- dependencies = [dependencies]
if self.is_cross and self.exe_wrapper is None:
raise CrossNoRunException('Can not run test applications in this cross environment.')
- cargs = [a for d in dependencies for a in d.get_compile_args()]
- link_args = [a for d in dependencies for a in d.get_link_args()]
- # Convert flags to the native type of the selected compiler
- args = self.unix_link_flags_to_native(cargs + link_args + extra_args)
- # Select a CRT if needed since we're linking
- args += self.get_linker_debug_crt_args()
- # Read c_link_args/cpp_link_args/etc from the cross-info file
- args += self.get_cross_extra_flags(env, compile=True, link=True)
- with self.compile(code, args) as p:
+ with self._links_wrapper(code, env, extra_args, dependencies) as p:
if p.returncode != 0:
mlog.debug('Could not compile test file %s: %d\n' % (
p.input_name,
@@ -878,55 +874,65 @@ int main(int argc, char **argv) {
raise EnvironmentException('Could not determine alignment of %s. Sorry. You might want to file a bug.' % typename)
return align
- def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None):
+ @staticmethod
+ def _no_prototype_templ():
"""
- First, this function looks for the symbol in the default libraries
- provided by the compiler (stdlib + a few others usually). If that
- fails, it checks if any of the headers specified in the prefix provide
- an implementation of the function, and if that fails, it checks if it's
- implemented as a compiler-builtin.
+ Try to find the function without a prototype from a header by defining
+ our own dummy prototype and trying to link with the C library (and
+ whatever else the compiler links in by default). This is very similar
+ to the check performed by Autoconf for AC_CHECK_FUNCS.
"""
- if extra_args is None:
- extra_args = []
- # Define the symbol to something else in case it is defined by the
- # includes or defines listed by the user `{0}` or by the compiler.
- # Then, undef the symbol to get rid of it completely.
- templ = '''
+ # Define the symbol to something else since it is defined by the
+ # includes or defines listed by the user (prefix -> {0}) or by the
+ # compiler. Then, undef the symbol to get rid of it completely.
+ head = '''
#define {1} meson_disable_define_of_{1}
#include <limits.h>
{0}
#undef {1}
'''
-
# Override any GCC internal prototype and declare our own definition for
# the symbol. Use char because that's unlikely to be an actual return
# value for a function which ensures that we override the definition.
- templ += '''
+ head += '''
#ifdef __cplusplus
extern "C"
#endif
char {1} ();
'''
-
- # glibc defines functions that are not available on Linux as stubs that
- # fail with ENOSYS (such as e.g. lchmod). In this case we want to fail
- # instead of detecting the stub as a valid symbol.
- # We always include limits.h above to ensure that these are defined for
- # stub functions.
- stubs_fail = '''
- #if defined __stub_{1} || defined __stub___{1}
- fail fail fail this function is not going to work
- #endif
- '''
- templ += stubs_fail
-
- # And finally the actual function call
- templ += '''
- int
- main ()
+ # The actual function call
+ main = '''
+ int main ()
{{
return {1} ();
}}'''
+ return head, main
+
+ @staticmethod
+ def _have_prototype_templ():
+ """
+ Returns a head-er and main() call that uses the headers listed by the
+ user for the function prototype while checking if a function exists.
+ """
+ # Add the 'prefix', aka defines, includes, etc that the user provides
+ 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}; }}'
+ return head, main
+
+ def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None):
+ """
+ First, this function looks for the symbol in the default libraries
+ provided by the compiler (stdlib + a few others usually). If that
+ fails, it checks if any of the headers specified in the prefix provide
+ an implementation of the function, and if that fails, it checks if it's
+ implemented as a compiler-builtin.
+ """
+ if extra_args is None:
+ extra_args = []
+
+ # Short-circuit if the check is already provided by the cross-info file
varname = 'has function ' + funcname
varname = varname.replace(' ', '_')
if self.is_cross:
@@ -935,16 +941,35 @@ int main(int argc, char **argv) {
if isinstance(val, bool):
return val
raise EnvironmentException('Cross variable {0} is not a boolean.'.format(varname))
- if self.links(templ.format(prefix, funcname), env, extra_args, dependencies):
- return True
+
+ # glibc defines functions that are not available on Linux as stubs that
+ # fail with ENOSYS (such as e.g. lchmod). In this case we want to fail
+ # instead of detecting the stub as a valid symbol.
+ # We already included limits.h earlier to ensure that these are defined
+ # for stub functions.
+ stubs_fail = '''
+ #if defined __stub_{1} || defined __stub___{1}
+ fail fail fail this function is not going to work
+ #endif
+ '''
+
+ # If we have any includes in the prefix supplied by the user, assume
+ # that the user wants us to use the symbol prototype defined in those
+ # includes. If not, then try to do the Autoconf-style check with
+ # a dummy prototype definition of our own.
+ # This is needed when the linker determines symbol availability from an
+ # SDK based on the prototype in the header provided by the SDK.
+ # Ignoring this prototype would result in the symbol always being
+ # marked as available.
+ if '#include' in prefix:
+ head, main = self._have_prototype_templ()
+ else:
+ 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()
- # Sometimes the implementation is provided by the header, or the header
- # redefines the symbol to be something else. In that case, we want to
- # still detect the function. We still want to fail if __stub_foo or
- # _stub_foo are defined, of course.
- header_templ = '#include <limits.h>\n{0}\n' + stubs_fail + '\nint main() {{ {1}; }}'
- if self.links(header_templ.format(prefix, funcname), env, args, dependencies):
+ if self.links(templ.format(prefix, funcname), env, extra_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
@@ -2073,6 +2098,20 @@ class ClangCompiler():
raise MesonException('Unreachable code when converting clang type to gcc type.')
return get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion)
+ def has_argument(self, arg, env):
+ return super().has_argument(['-Werror=unknown-warning-option', arg], env)
+
+ def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None):
+ if extra_args is None:
+ extra_args = []
+ # Starting with XCode 8, we need to pass this to force linker
+ # visibility to obey OS X and iOS minimum version targets with
+ # -mmacosx-version-min, -miphoneos-version-min, etc.
+ # https://github.com/Homebrew/homebrew-core/issues/3727
+ if self.clang_type == CLANG_OSX and version_compare(self.version, '>=8.0'):
+ extra_args.append('-Wl,-no_weak_imports')
+ return super().has_function(funcname, prefix, env, extra_args, dependencies)
+
class ClangCCompiler(ClangCompiler, CCompiler):
def __init__(self, exelist, version, clang_type, is_cross, exe_wrapper=None):
CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper)
@@ -2099,11 +2138,8 @@ class ClangCCompiler(ClangCompiler, CCompiler):
def get_option_link_args(self, options):
return []
- def has_argument(self, arg, env):
- return super().has_argument(['-Werror=unknown-warning-option', arg], env)
-
-class ClangCPPCompiler(ClangCompiler, CPPCompiler):
+class ClangCPPCompiler(ClangCompiler, CPPCompiler):
def __init__(self, exelist, version, cltype, is_cross, exe_wrapper=None):
CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrapper)
ClangCompiler.__init__(self, cltype)
@@ -2127,28 +2163,17 @@ class ClangCPPCompiler(ClangCompiler, CPPCompiler):
def get_option_link_args(self, options):
return []
- def has_argument(self, arg, env):
- return super().has_argument(['-Werror=unknown-warning-option', arg], env)
-
-class ClangObjCCompiler(GnuObjCCompiler):
+class ClangObjCCompiler(ClangCompiler, GnuObjCCompiler):
def __init__(self, exelist, version, cltype, is_cross, exe_wrapper=None):
- super().__init__(exelist, version, is_cross, exe_wrapper)
- self.id = 'clang'
+ GnuObjCCompiler.__init__(self, exelist, version, is_cross, exe_wrapper)
+ ClangCompiler.__init__(self, cltype)
self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage']
- self.clang_type = cltype
- if self.clang_type != CLANG_OSX:
- self.base_options.append('b_lundef')
- self.base_options.append('b_asneeded')
-class ClangObjCPPCompiler(GnuObjCPPCompiler):
+class ClangObjCPPCompiler(ClangCompiler, GnuObjCPPCompiler):
def __init__(self, exelist, version, cltype, is_cross, exe_wrapper=None):
- super().__init__(exelist, version, is_cross, exe_wrapper)
- self.id = 'clang'
- self.clang_type = cltype
+ GnuObjCPPCompiler.__init__(self, exelist, version, is_cross, exe_wrapper)
+ ClangCompiler.__init__(self, cltype)
self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage']
- if self.clang_type != CLANG_OSX:
- self.base_options.append('b_lundef')
- self.base_options.append('b_asneeded')
class FortranCompiler(Compiler):
def __init__(self, exelist, version, is_cross, exe_wrapper=None):
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index eb6f06d..c8ee13f 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -216,6 +216,10 @@ builtin_options = {
# uses that. Instead they always set it manually to /etc. This default
# value is thus pointless and not really used but we set it to this
# for consistency with other systems.
+ #
+ # Projects installing to sysconfdir probably want to set the following in project():
+ #
+ # default_options : ['sysconfdir=/etc']
'sysconfdir' : [ UserStringOption, 'Sysconf data directory.', 'etc' ],
'werror' : [ UserBooleanOption, 'Treat warnings as errors.', False ],
'warning_level' : [ UserComboOption, 'Compiler warning level to use.', [ '1', '2', '3' ], '1'],
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index 86c23ae..f7045f4 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -173,11 +173,28 @@ def for_darwin(is_cross, env):
return False
+def search_version(text):
+ # Usually of the type 4.1.4 but compiler output may contain
+ # stuff like this:
+ # (Sourcery CodeBench Lite 2014.05-29) 4.8.3 20140320 (prerelease)
+ # Limiting major version number to two digits seems to work
+ # thus far. When we get to GCC 100, this will break, but
+ # if we are still relevant when that happens, it can be
+ # considered an achievement in itself.
+ #
+ # This regex is reaching magic levels. If it ever needs
+ # to be updated, do not complexify but convert to something
+ # saner instead.
+ version_regex = '(?<!(\d|\.))(\d{1,2}(\.\d+)+(-[a-zA-Z0-9]+)?)'
+ match = re.search(version_regex, text)
+ if match:
+ return match.group(0)
+ return 'unknown version'
+
class Environment():
private_dir = 'meson-private'
log_dir = 'meson-logs'
coredata_file = os.path.join(private_dir, 'coredata.dat')
- version_regex = '\d+(\.\d+)+(-[a-zA-Z0-9]+)?'
def __init__(self, source_dir, build_dir, main_script_launcher, options, original_cmd_line_args):
self.source_dir = source_dir
@@ -361,11 +378,7 @@ class Environment():
(out, err) = p.communicate()
out = out.decode(errors='ignore')
err = err.decode(errors='ignore')
- vmatch = re.search(Environment.version_regex, out)
- if vmatch:
- version = vmatch.group(0)
- else:
- version = 'unknown version'
+ version = search_version(out)
if 'Free Software Foundation' in out:
defines = self.get_gnu_compiler_defines([compiler])
if not defines:
@@ -382,7 +395,7 @@ class Environment():
if 'Microsoft' in out or 'Microsoft' in err:
# Visual Studio prints version number to stderr but
# everything else to stdout. Why? Lord only knows.
- version = re.search(Environment.version_regex, err).group()
+ version = search_version(err)
return VisualStudioCCompiler([compiler], version, is_cross, exe_wrap)
errmsg = 'Unknown compiler(s): "' + ', '.join(compilers) + '"'
if popen_exceptions:
@@ -422,10 +435,7 @@ class Environment():
out = out.decode(errors='ignore')
err = err.decode(errors='ignore')
- version = 'unknown version'
- vmatch = re.search(Environment.version_regex, out)
- if vmatch:
- version = vmatch.group(0)
+ version = search_version(out)
if 'GNU Fortran' in out:
defines = self.get_gnu_compiler_defines([compiler])
@@ -439,10 +449,7 @@ class Environment():
return G95FortranCompiler([compiler], version, is_cross, exe_wrap)
if 'Sun Fortran' in err:
- version = 'unknown version'
- vmatch = re.search(Environment.version_regex, err)
- if vmatch:
- version = vmatch.group(0)
+ version = search_version(err)
return SunFortranCompiler([compiler], version, is_cross, exe_wrap)
if 'ifort (IFORT)' in out:
@@ -510,11 +517,7 @@ class Environment():
(out, err) = p.communicate()
out = out.decode(errors='ignore')
err = err.decode(errors='ignore')
- vmatch = re.search(Environment.version_regex, out)
- if vmatch:
- version = vmatch.group(0)
- else:
- version = 'unknown version'
+ version = search_version(out)
if 'Free Software Foundation' in out:
defines = self.get_gnu_compiler_defines([compiler])
if not defines:
@@ -529,7 +532,7 @@ class Environment():
cltype = CLANG_STANDARD
return ClangCPPCompiler(ccache + [compiler], version, cltype, is_cross, exe_wrap)
if 'Microsoft' in out or 'Microsoft' in err:
- version = re.search(Environment.version_regex, err).group()
+ version = search_version(err)
return VisualStudioCPPCompiler([compiler], version, is_cross, exe_wrap)
errmsg = 'Unknown compiler(s): "' + ', '.join(compilers) + '"'
if popen_exceptions:
@@ -557,11 +560,7 @@ class Environment():
(out, err) = p.communicate()
out = out.decode(errors='ignore')
err = err.decode(errors='ignore')
- vmatch = re.search(Environment.version_regex, out)
- if vmatch:
- version = vmatch.group(0)
- else:
- version = 'unknown version'
+ version = search_version(out)
if 'Free Software Foundation' in out:
defines = self.get_gnu_compiler_defines(exelist)
return GnuObjCCompiler(exelist, version, is_cross, exe_wrap, defines)
@@ -588,11 +587,7 @@ class Environment():
(out, err) = p.communicate()
out = out.decode(errors='ignore')
err = err.decode(errors='ignore')
- vmatch = re.search(Environment.version_regex, out)
- if vmatch:
- version = vmatch.group(0)
- else:
- version = 'unknown version'
+ version = search_version(out)
if 'Free Software Foundation' in out:
defines = self.get_gnu_compiler_defines(exelist)
return GnuObjCPPCompiler(exelist, version, is_cross, exe_wrap, defines)
@@ -609,11 +604,7 @@ class Environment():
(out, err) = p.communicate()
out = out.decode(errors='ignore')
err = err.decode(errors='ignore')
- vmatch = re.search(Environment.version_regex, err)
- if vmatch:
- version = vmatch.group(0)
- else:
- version = 'unknown version'
+ version = search_version(err)
if 'javac' in err:
return JavaCompiler(exelist, version)
raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
@@ -627,11 +618,7 @@ class Environment():
(out, err) = p.communicate()
out = out.decode(errors='ignore')
err = err.decode(errors='ignore')
- vmatch = re.search(Environment.version_regex, out)
- if vmatch:
- version = vmatch.group(0)
- else:
- version = 'unknown version'
+ version = search_version(out)
if 'Mono' in out:
return MonoCompiler(exelist, version)
raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
@@ -644,11 +631,7 @@ class Environment():
raise EnvironmentException('Could not execute Vala compiler "%s"' % ' '.join(exelist))
(out, _) = p.communicate()
out = out.decode(errors='ignore')
- vmatch = re.search(Environment.version_regex, out)
- if vmatch:
- version = vmatch.group(0)
- else:
- version = 'unknown version'
+ version = search_version(out)
if 'Vala' in out:
return ValaCompiler(exelist, version)
raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
@@ -661,11 +644,7 @@ class Environment():
raise EnvironmentException('Could not execute Rust compiler "%s"' % ' '.join(exelist))
(out, _) = p.communicate()
out = out.decode(errors='ignore')
- vmatch = re.search(Environment.version_regex, out)
- if vmatch:
- version = vmatch.group(0)
- else:
- version = 'unknown version'
+ version = search_version(out)
if 'rustc' in out:
return RustCompiler(exelist, version)
raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
@@ -699,11 +678,7 @@ class Environment():
raise EnvironmentException('Could not execute D compiler "%s"' % ' '.join(exelist))
(out, _) = p.communicate()
out = out.decode(errors='ignore')
- vmatch = re.search(Environment.version_regex, out)
- if vmatch:
- version = vmatch.group(0)
- else:
- version = 'unknown version'
+ version = search_version(out)
if 'LLVM D compiler' in out:
return LLVMDCompiler(exelist, version, is_cross)
elif 'gdc' in out:
@@ -720,11 +695,7 @@ class Environment():
raise EnvironmentException('Could not execute Swift compiler "%s"' % ' '.join(exelist))
(_, err) = p.communicate()
err = err.decode(errors='ignore')
- vmatch = re.search(Environment.version_regex, err)
- if vmatch:
- version = vmatch.group(0)
- else:
- version = 'unknown version'
+ version = search_version(err)
if 'Swift' in err:
return SwiftCompiler(exelist, version)
raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
@@ -833,9 +804,9 @@ class Environment():
return self.coredata.get_builtin_option('datadir')
-def get_args_from_envvars(lang, compiler_is_linker):
+def get_args_from_envvars(compiler):
"""
- @lang: Language to fetch environment flags for
+ @compiler: Compiler to fetch environment flags for
Returns a tuple of (compile_flags, link_flags) for the specified language
from the inherited environment
@@ -844,14 +815,18 @@ def get_args_from_envvars(lang, compiler_is_linker):
if val:
mlog.log('Appending {} from environment: {!r}'.format(var, val))
+ lang = compiler.get_language()
+ compiler_is_linker = False
+ if hasattr(compiler, 'get_linker_exelist'):
+ compiler_is_linker = (compiler.get_exelist() == compiler.get_linker_exelist())
+
if lang not in ('c', 'cpp', 'objc', 'objcpp', 'fortran', 'd'):
return ([], [])
# Compile flags
cflags_mapping = {'c': 'CFLAGS', 'cpp': 'CXXFLAGS',
'objc': 'OBJCFLAGS', 'objcpp': 'OBJCXXFLAGS',
- 'fortran': 'FFLAGS',
- 'd': 'DFLAGS'}
+ 'fortran': 'FFLAGS', 'd': 'DFLAGS'}
compile_flags = os.environ.get(cflags_mapping[lang], '')
log_var(cflags_mapping[lang], compile_flags)
compile_flags = compile_flags.split()
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index dd2a227..5203528 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -990,6 +990,7 @@ class ModuleHolder(InterpreterObject):
state.project_version = self.interpreter.build.dep_manifest[self.interpreter.active_projectname]
state.compilers = self.interpreter.build.compilers
state.targets = self.interpreter.build.targets
+ state.data = self.interpreter.build.data
state.headers = self.interpreter.build.get_headers()
state.man = self.interpreter.build.get_man()
state.global_args = self.interpreter.build.global_args
@@ -1269,6 +1270,11 @@ class Interpreter():
self.build.install_scripts.append(v)
elif isinstance(v, build.Data):
self.build.data.append(v)
+ elif isinstance(v, dependencies.InternalDependency):
+ # FIXME: This is special cased and not ideal:
+ # The first source is our new VapiTarget, the rest are deps
+ self.module_method_callback(v.sources[0])
+ outvalues.append(InternalDependencyHolder(v))
else:
print(v)
raise InterpreterException('Module returned a value of unknown type.')
@@ -1792,11 +1798,8 @@ class Interpreter():
else:
raise
mlog.log('Native %s compiler: ' % lang, mlog.bold(' '.join(comp.get_exelist())), ' (%s %s)' % (comp.id, comp.version), sep='')
- compiler_is_linker = False
- if hasattr(comp, 'get_linker_exelist'):
- compiler_is_linker = (comp.get_exelist() == comp.get_linker_exelist())
if not comp.get_language() in self.coredata.external_args:
- (ext_compile_args, ext_link_args) = environment.get_args_from_envvars(comp.get_language(), compiler_is_linker)
+ (ext_compile_args, ext_link_args) = environment.get_args_from_envvars(comp)
self.coredata.external_args[comp.get_language()] = ext_compile_args
self.coredata.external_link_args[comp.get_language()] = ext_link_args
self.build.add_compiler(comp)
diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py
index 25f2c6b..4ef8d92 100644
--- a/mesonbuild/mconf.py
+++ b/mesonbuild/mconf.py
@@ -165,7 +165,16 @@ class Conf:
print('')
print('Directories:')
parr = []
- for key in [ 'prefix', 'libdir', 'libexecdir', 'bindir', 'includedir', 'datadir', 'mandir', 'localedir' ]:
+ for key in ['prefix',
+ 'libdir',
+ 'libexecdir',
+ 'bindir',
+ 'includedir',
+ 'datadir',
+ 'mandir',
+ 'localedir',
+ 'sysconfdir',
+ ]:
parr.append([key, coredata.get_builtin_option_description(key),
self.coredata.get_builtin_option(key), coredata.get_builtin_option_choices(key)])
self.print_aligned(parr)
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index 89d41c5..b7d1438 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -801,6 +801,149 @@ class GnomeModule:
return [body, header]
+ @staticmethod
+ def _vapi_args_to_command(prefix, variable, kwargs, accept_vapi=False):
+ arg_list = kwargs.get(variable)
+ if not arg_list:
+ return []
+ ret = []
+ if not isinstance(arg_list, list):
+ arg_list = [arg_list]
+ for arg in arg_list:
+ if not isinstance(arg, str):
+ types = 'strings' + ' or InternalDependencys' if accept_vapi else ''
+ raise MesonException('All {} must be {}'.format(variable, types))
+ ret.append(prefix + arg)
+ return ret
+
+ def _extract_vapi_packages(self, state, kwargs):
+ '''
+ Packages are special because we need to:
+ - Get a list of packages for the .deps file
+ - Get a list of depends for any VapiTargets
+ - Get package name from VapiTargets
+ - Add include dirs for any VapiTargets
+ '''
+ arg_list = kwargs.get('packages')
+ if not arg_list:
+ return [], [], [], []
+ if not isinstance(arg_list, list):
+ arg_list = [arg_list]
+
+ vapi_depends = []
+ vapi_packages = []
+ vapi_includes = []
+ ret = []
+ remaining_args = []
+ for arg in arg_list:
+ if hasattr(arg, 'held_object'):
+ arg = arg.held_object
+ if isinstance(arg, dependencies.InternalDependency):
+ targets = [t for t in arg.sources if isinstance(t, VapiTarget)]
+ for target in targets:
+ srcdir = os.path.join(state.environment.get_source_dir(),
+ target.get_subdir())
+ outdir = os.path.join(state.environment.get_build_dir(),
+ target.get_subdir())
+ outfile = target.output[0][:-5] # Strip .vapi
+ ret.append('--vapidir=' + outdir)
+ ret.append('--girdir=' + outdir)
+ ret.append('--pkg=' + outfile)
+ vapi_depends.append(target)
+ vapi_packages.append(outfile)
+ vapi_includes.append(srcdir)
+ else:
+ vapi_packages.append(arg)
+ remaining_args.append(arg)
+
+ kwargs['packages'] = remaining_args
+ vapi_args = ret + self._vapi_args_to_command('--pkg=', 'packages', kwargs, accept_vapi=True)
+ return vapi_args, vapi_depends, vapi_packages, vapi_includes
+
+ def _generate_deps(self, state, library, packages, indir):
+ outdir = state.environment.scratch_dir
+ fname = os.path.join(outdir, library + '.deps')
+ with open(fname, 'w') as ofile:
+ for package in packages:
+ ofile.write(package + '\n')
+ return build.Data(False, outdir, [fname], indir)
+
+ def _get_vapi_link_with(self, target):
+ link_with = []
+ for dep in target.get_target_dependencies():
+ if isinstance(dep, build.SharedLibrary):
+ link_with.append(dep)
+ elif isinstance(dep, GirTarget):
+ link_with += self._get_vapi_link_with(dep)
+ return link_with
+
+ def generate_vapi(self, state, args, kwargs):
+ if len(args) != 1:
+ raise MesonException('The library name is required')
+
+ if not isinstance(args[0], str):
+ raise MesonException('The first argument must be the name of the library')
+
+ library = args[0]
+ build_dir = os.path.join(state.environment.get_build_dir(), state.subdir)
+ source_dir = os.path.join(state.environment.get_source_dir(), state.subdir)
+ pkg_cmd, vapi_depends, vapi_packages, vapi_includes = self._extract_vapi_packages(state, kwargs)
+ cmd = ['vapigen', '--quiet', '--library=' + library, '--directory=' + build_dir]
+ cmd += self._vapi_args_to_command('--vapidir=', 'vapi_dirs', kwargs)
+ cmd += self._vapi_args_to_command('--metadatadir=', 'metadata_dirs', kwargs)
+ cmd += self._vapi_args_to_command('--girdir=', 'gir_dirs', kwargs)
+ cmd += pkg_cmd
+ cmd += ['--metadatadir=' + source_dir]
+
+ inputs = kwargs.get('sources')
+ if not inputs:
+ raise MesonException('sources are required to generate the vapi file')
+
+ if not isinstance(inputs, list):
+ inputs = [inputs]
+
+ link_with = []
+ for i in inputs:
+ if isinstance(i, str):
+ cmd.append(os.path.join(source_dir, i))
+ elif hasattr(i, 'held_object') \
+ and isinstance(i.held_object, GirTarget):
+ link_with += self._get_vapi_link_with(i.held_object)
+ subdir = os.path.join(state.environment.get_build_dir(),
+ i.held_object.get_subdir())
+ gir_file = os.path.join(subdir, i.held_object.output[0])
+ cmd.append(gir_file)
+ else:
+ raise MesonException('Input must be a str or GirTarget')
+
+ vapi_output = library + '.vapi'
+ custom_kwargs = {
+ 'command': cmd,
+ 'input': inputs,
+ 'output': vapi_output,
+ 'depends': vapi_depends,
+ }
+ install_dir = kwargs.get('install_dir',
+ os.path.join(state.environment.coredata.get_builtin_option('datadir'),
+ 'vala', 'vapi'))
+ if kwargs.get('install'):
+ custom_kwargs['install'] = kwargs['install']
+ custom_kwargs['install_dir'] = install_dir
+
+ # We shouldn't need this locally but we install it
+ deps_target = self._generate_deps(state, library, vapi_packages, install_dir)
+ state.data.append(deps_target)
+ vapi_target = VapiTarget(vapi_output, state.subdir, custom_kwargs)
+
+ # So to try our best to get this to just work we need:
+ # - link with with the correct library
+ # - include the vapi and dependent vapi files in sources
+ # - add relevant directories to include dirs
+ includes = [build.IncludeDirs(state.subdir, ['.'] + vapi_includes, False)]
+ sources = [vapi_target] + vapi_depends
+ return dependencies.InternalDependency(
+ None, includes, [], [], link_with, sources, []
+ )
def initialize():
return GnomeModule()
@@ -812,3 +955,7 @@ class GirTarget(build.CustomTarget):
class TypelibTarget(build.CustomTarget):
def __init__(self, name, subdir, kwargs):
super().__init__(name, subdir, kwargs)
+
+class VapiTarget(build.CustomTarget):
+ def __init__(self, name, subdir, kwargs):
+ super().__init__(name, subdir, kwargs)
diff --git a/run_tests.py b/run_tests.py
index ec8970a..752354e 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -21,15 +21,7 @@ from mesonbuild import mesonlib
if __name__ == '__main__':
returncode = 0
if mesonlib.is_linux():
- myenv = os.environ.copy()
- myenv['CC'] = 'gcc'
- myenv['CXX'] = 'g++'
- print('Running unittests with GCC.\n')
- returncode += subprocess.call([sys.executable, 'run_unittests.py', '-v'], env=myenv)
- if shutil.which('clang'):
- myenv['CC'] = 'clang'
- myenv['CXX'] = 'clang++'
- print('\nRunning unittests with clang.\n')
- returncode += subprocess.call([sys.executable, 'run_unittests.py', '-v'], env=myenv)
+ print('Running unittests.\n')
+ returncode += subprocess.call([sys.executable, 'run_unittests.py', '-v'])
returncode += subprocess.call([sys.executable, 'run_project_tests.py'] + sys.argv[1:])
sys.exit(returncode)
diff --git a/run_unittests.py b/run_unittests.py
index 0e3b7d5..cf30276 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -17,6 +17,7 @@ import unittest, os, sys, shutil, time
import subprocess
import re, json
import tempfile
+import mesonbuild.environment
from mesonbuild.environment import detect_ninja
from mesonbuild.dependencies import PkgConfigDependency
@@ -32,10 +33,21 @@ def get_soname(fname):
class FakeEnvironment(object):
def __init__(self):
self.cross_info = None
-
+
def is_cross_build(self):
return False
+class InternalTests(unittest.TestCase):
+
+ def test_version_number(self):
+ searchfunc = mesonbuild.environment.search_version
+ self.assertEqual(searchfunc('foobar 1.2.3'), '1.2.3')
+ self.assertEqual(searchfunc('1.2.3'), '1.2.3')
+ self.assertEqual(searchfunc('foobar 2016.10.28 1.2.3'), '1.2.3')
+ self.assertEqual(searchfunc('2016.10.28 1.2.3'), '1.2.3')
+ self.assertEqual(searchfunc('foobar 2016.10.128'), 'unknown version')
+ self.assertEqual(searchfunc('2016.10.128'), 'unknown version')
+
class LinuxlikeTests(unittest.TestCase):
def setUp(self):
super().setUp()
diff --git a/test cases/osx/3 has function xcode8/meson.build b/test cases/osx/3 has function xcode8/meson.build
new file mode 100644
index 0000000..300d352
--- /dev/null
+++ b/test cases/osx/3 has function xcode8/meson.build
@@ -0,0 +1,26 @@
+project('has function xcode8', 'c')
+
+cc = meson.get_compiler('c')
+
+# XCode 8 location for the macOS 10.12 SDK
+sdk_args = ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk']
+args_10_11 = ['-mmacosx-version-min=10.11'] + sdk_args
+args_10_12 = ['-mmacosx-version-min=10.12'] + sdk_args
+
+# Test requires XCode 8 which has the MacOSX 10.12 SDK
+if cc.version().version_compare('>=8.0')
+ if cc.has_function('clock_gettime', args : args_10_11, prefix : '#include <time.h>')
+ error('Should not have found clock_gettime via <time.h> when targetting Mac OS X 10.11')
+ endif
+ if not cc.has_function('clock_gettime', args : args_10_12, prefix : '#include <time.h>')
+ error('Did NOT find clock_gettime via <time.h> when targetting Mac OS X 10.12')
+ endif
+ if not cc.has_function('clock_gettime', args : args_10_11)
+ error('Did NOT find clock_gettime w/o a prototype when targetting Mac OS X 10.11')
+ endif
+ if not cc.has_function('clock_gettime', args : args_10_12)
+ error('Did NOT find clock_gettime w/o a prototype when targetting Mac OS X 10.12')
+ endif
+else
+ message('Test needs XCode 8, skipping...')
+endif
diff --git a/test cases/vala/12 generated vapi/installed_files.txt b/test cases/vala/12 generated vapi/installed_files.txt
new file mode 100644
index 0000000..5993d01
--- /dev/null
+++ b/test cases/vala/12 generated vapi/installed_files.txt
@@ -0,0 +1,7 @@
+usr/bin/vapigen-test
+usr/lib/libfoo.so
+usr/lib/libbar.so
+usr/share/vala/vapi/foo-1.0.vapi
+usr/share/vala/vapi/foo-1.0.deps
+usr/share/vala/vapi/bar-1.0.vapi
+usr/share/vala/vapi/bar-1.0.deps
diff --git a/test cases/vala/12 generated vapi/libbar/bar.c b/test cases/vala/12 generated vapi/libbar/bar.c
new file mode 100644
index 0000000..f0f5cb8
--- /dev/null
+++ b/test cases/vala/12 generated vapi/libbar/bar.c
@@ -0,0 +1,12 @@
+#include "bar.h"
+#include "foo.h"
+
+/**
+ * bar_return_success:
+ *
+ * Returns 0
+ */
+int bar_return_success(void)
+{
+ return foo_return_success();
+}
diff --git a/test cases/vala/12 generated vapi/libbar/bar.h b/test cases/vala/12 generated vapi/libbar/bar.h
new file mode 100644
index 0000000..165b104
--- /dev/null
+++ b/test cases/vala/12 generated vapi/libbar/bar.h
@@ -0,0 +1,5 @@
+#include <glib-object.h>
+
+#pragma once
+
+int bar_return_success(void);
diff --git a/test cases/vala/12 generated vapi/libbar/meson.build b/test cases/vala/12 generated vapi/libbar/meson.build
new file mode 100644
index 0000000..6482504
--- /dev/null
+++ b/test cases/vala/12 generated vapi/libbar/meson.build
@@ -0,0 +1,33 @@
+libbar_sources = [
+ 'bar.c',
+ 'bar.h',
+]
+
+libbar_deps = [
+ dependency('gobject-2.0'),
+ libfoo_dep,
+]
+
+libbar = shared_library('bar', libbar_sources,
+ dependencies: libbar_deps,
+ install: true,
+)
+
+libbar_api_ver = '1.0'
+
+libbar_gir = gnome.generate_gir(libbar,
+ sources: libbar_sources,
+ namespace: 'Bar',
+ nsversion: libbar_api_ver,
+ packages: 'gobject-2.0',
+ symbol_prefix: 'bar',
+ extra_args: [
+ '--c-include=bar.h',
+ ],
+)
+
+libbar_vapi = gnome.generate_vapi('bar-' + libbar_api_ver,
+ sources: libbar_gir[0],
+ packages: libfoo_vapi,
+ install: true,
+)
diff --git a/test cases/vala/12 generated vapi/libfoo/foo.c b/test cases/vala/12 generated vapi/libfoo/foo.c
new file mode 100644
index 0000000..0413ac5
--- /dev/null
+++ b/test cases/vala/12 generated vapi/libfoo/foo.c
@@ -0,0 +1,11 @@
+#include "foo.h"
+
+/**
+ * foo_return_success:
+ *
+ * Returns 0
+ */
+int foo_return_success(void)
+{
+ return 0;
+}
diff --git a/test cases/vala/12 generated vapi/libfoo/foo.h b/test cases/vala/12 generated vapi/libfoo/foo.h
new file mode 100644
index 0000000..f09256d
--- /dev/null
+++ b/test cases/vala/12 generated vapi/libfoo/foo.h
@@ -0,0 +1,5 @@
+#include <glib-object.h>
+
+#pragma once
+
+int foo_return_success(void);
diff --git a/test cases/vala/12 generated vapi/libfoo/meson.build b/test cases/vala/12 generated vapi/libfoo/meson.build
new file mode 100644
index 0000000..482c8fe
--- /dev/null
+++ b/test cases/vala/12 generated vapi/libfoo/meson.build
@@ -0,0 +1,36 @@
+libfoo_sources = [
+ 'foo.c',
+ 'foo.h',
+]
+
+libfoo_deps = [
+ dependency('gobject-2.0')
+]
+
+libfoo = shared_library('foo', libfoo_sources,
+ dependencies: libfoo_deps,
+ install: true,
+)
+
+libfoo_api_ver = '1.0'
+
+libfoo_gir = gnome.generate_gir(libfoo,
+ sources: libfoo_sources,
+ namespace: 'Foo',
+ nsversion: libfoo_api_ver,
+ packages: 'gobject-2.0',
+ symbol_prefix: 'foo',
+ extra_args: [
+ '--c-include=foo.h',
+ ],
+)
+
+libfoo_vapi = gnome.generate_vapi('foo-' + libfoo_api_ver,
+ sources: libfoo_gir[0],
+ install: true,
+)
+
+libfoo_dep = declare_dependency(
+ link_with: libfoo,
+ include_directories: include_directories('.'),
+)
diff --git a/test cases/vala/12 generated vapi/main.vala b/test cases/vala/12 generated vapi/main.vala
new file mode 100644
index 0000000..303ab33
--- /dev/null
+++ b/test cases/vala/12 generated vapi/main.vala
@@ -0,0 +1,9 @@
+using Foo;
+using Bar;
+
+class Main : GLib.Object {
+ public static int main(string[] args) {
+ var ignore = Foo.return_success();
+ return Bar.return_success();
+ }
+}
diff --git a/test cases/vala/12 generated vapi/meson.build b/test cases/vala/12 generated vapi/meson.build
new file mode 100644
index 0000000..82f0c44
--- /dev/null
+++ b/test cases/vala/12 generated vapi/meson.build
@@ -0,0 +1,13 @@
+project('vapi-test', ['c', 'vala'])
+
+gnome = import('gnome')
+subdir('libfoo')
+subdir('libbar')
+
+vapiexe = executable('vapigen-test',
+ 'main.vala',
+ dependencies: [dependency('gobject-2.0'), libbar_vapi],
+ install: true,
+)
+
+test('vapigen-test', vapiexe)