diff options
-rw-r--r-- | mesonbuild/backend/ninjabackend.py | 6 | ||||
-rw-r--r-- | mesonbuild/compilers.py | 181 | ||||
-rw-r--r-- | mesonbuild/coredata.py | 4 | ||||
-rw-r--r-- | mesonbuild/environment.py | 105 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 11 | ||||
-rw-r--r-- | mesonbuild/mconf.py | 11 | ||||
-rw-r--r-- | mesonbuild/modules/gnome.py | 147 | ||||
-rwxr-xr-x | run_tests.py | 12 | ||||
-rwxr-xr-x | run_unittests.py | 14 | ||||
-rw-r--r-- | test cases/osx/3 has function xcode8/meson.build | 26 | ||||
-rw-r--r-- | test cases/vala/12 generated vapi/installed_files.txt | 7 | ||||
-rw-r--r-- | test cases/vala/12 generated vapi/libbar/bar.c | 12 | ||||
-rw-r--r-- | test cases/vala/12 generated vapi/libbar/bar.h | 5 | ||||
-rw-r--r-- | test cases/vala/12 generated vapi/libbar/meson.build | 33 | ||||
-rw-r--r-- | test cases/vala/12 generated vapi/libfoo/foo.c | 11 | ||||
-rw-r--r-- | test cases/vala/12 generated vapi/libfoo/foo.h | 5 | ||||
-rw-r--r-- | test cases/vala/12 generated vapi/libfoo/meson.build | 36 | ||||
-rw-r--r-- | test cases/vala/12 generated vapi/main.vala | 9 | ||||
-rw-r--r-- | test cases/vala/12 generated vapi/meson.build | 13 |
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) |