diff options
57 files changed, 1158 insertions, 247 deletions
@@ -9,7 +9,7 @@ build system. [](https://pypi.python.org/pypi/meson) [](https://travis-ci.org/mesonbuild/meson) [](https://ci.appveyor.com/project/mesonbuild/meson) -[)](https://dev.azure.com/jussi0947/jussi/_build/latest?definitionId=2) +[](https://dev.azure.com/jussi0947/jussi/_build/latest?definitionId=1) [](https://codecov.io/gh/mesonbuild/meson/branch/master) [](https://lgtm.com/projects/g/mesonbuild/meson/context:python) [](https://lgtm.com/projects/g/mesonbuild/meson/alerts) diff --git a/docs/.editorconfig b/docs/.editorconfig new file mode 100644 index 0000000..b5276f1 --- /dev/null +++ b/docs/.editorconfig @@ -0,0 +1,2 @@ +[sitemap.txt] +indent_style = tab diff --git a/docs/markdown/Native-environments.md b/docs/markdown/Native-environments.md new file mode 100644 index 0000000..af7edd2 --- /dev/null +++ b/docs/markdown/Native-environments.md @@ -0,0 +1,76 @@ +--- +short-description: Setting up native compilation +... + +# Persistent native environments + +New in 0.49.0 + +Meson has [cross files for describing cross compilation environments](Cross-compilation.md), +for describing native environments it has equivalent "native files". + +Natives describe the *build machine*, and can be used to override properties of +non-cross builds, as well as properties that are marked as "native" in a cross +build. + +There are a couple of reasons you might want to use a native file to keep a +persistent environment: + +* To build with a non-default native tool chain (such as clang instead of gcc) +* To use a non-default version of another binary, such as yacc, or llvm-config + + +## Changing native file settings + +All of the rules about cross files and changed settings apply to native files +as well, see [here](Cross-compilation.md#Changing-cross-file-settings) + + +## Defining the environment + +### Binaries + +Currently the only use of native files is to override native binaries. This +includes the compilers and binaries collected with `find_program`, and those +used by dependencies that use a config-tool instead of pkgconfig for detection, +like `llvm-config` + +```ini +[binaries] +c = '/usr/local/bin/clang' +cpp = '/usr/local/bin/clang++' +rust = '/usr/local/bin/rust' +llvm-conifg = '/usr/local/llvm-svn/bin/llvm-config' +``` + +## Loading multiple native files + +Unlike cross file, native files allow layering. More than one native file can be +loaded, with values from a previous file being overridden by the next. The +intention of this is not overriding, but to allow composing native files. + +For example, if there is a project using C and C++, python 3.4-3.7, and LLVM +5-7, and it needs to build with clang 5, 6, and 7, and gcc 5.x, 6.x, and 7.x; +expressing all of these configurations in monolithic configurations would +result in 81 different native files. By layering them, it can be expressed by +just 12 native files. + + +## Native file locations + +Like cross files, native files may be installed to user or system wide +locations, defined as: + - $XDG_DATA_DIRS/meson/native + (/usr/local/share/meson/native:/usr/share/meson/native if $XDG_DATA_DIRS is + undefined) + - $XDG_DATA_HOME/meson/native ($HOME/.local/share/meson/native if + $XDG_DATA_HOME is undefined) + +The order of locations tried is as follows: + - A file relative to the local dir + - The user local location + - The system wide locations in order + +These files are not intended to be shipped by distributions, unless they are +specifically for distribution packaging, they are mainly intended for +developers. diff --git a/docs/markdown/Pkg-config-files.md b/docs/markdown/Pkg-config-files.md index 0427b0e..305a6d8 100644 --- a/docs/markdown/Pkg-config-files.md +++ b/docs/markdown/Pkg-config-files.md @@ -18,4 +18,4 @@ pkg.generate(libraries : libs, This causes a file called `simple.pc` to be created and placed into the install directory during the install phase. -More infromation on the pkg-config module and the parameters can be found on the [pkgconfig-module](http://mesonbuild.com/Pkgconfig-module.html) page. +More infromation on the pkg-config module and the parameters can be found on the [pkgconfig-module](Pkgconfig-module.md) page. diff --git a/docs/markdown/Pkgconfig-module.md b/docs/markdown/Pkgconfig-module.md index b727f1a..9a34e14 100644 --- a/docs/markdown/Pkgconfig-module.md +++ b/docs/markdown/Pkgconfig-module.md @@ -14,7 +14,7 @@ can, of course, replace the name `pkg` with anything else. The generated file's properties are specified with the following keyword arguments. -- `description` a string describing the library +- `description` a string describing the library, used to set the `Description:` field - `extra_cflags` a list of extra compiler flags to be added to the `Cflags` field after the header search path - `filebase` the base name to use for the pkg-config file; as an @@ -33,7 +33,7 @@ keyword arguments. - `libraries_private` list of built libraries or strings to put in the `Libs.private` field. Since 0.45.0 it can also contain dependency objects, their `link_args` will be added to `Libs.private`. -- `name` the name of this library +- `name` the name of this library, used to set the `Name:` field - `subdirs` which subdirs of `include` should be added to the header search path, for example if you install headers into `${PREFIX}/include/foobar-1`, the correct value for this argument @@ -48,7 +48,8 @@ keyword arguments. reference other pkgconfig variables, e.g. `datadir=${prefix}/share`. The names `prefix`, `libdir` and `installdir` are reserved and may not be used. -- `version` a string describing the version of this library +- `version` a string describing the version of this library, used to set the + `Version:` field. Defaults to the project version if unspecified. - `d_module_versions` a list of module version flags used when compiling D sources referred to by this pkg-config file diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 72e9609..7df0717 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -105,6 +105,9 @@ the following: - `exe_wrapper` a list containing the wrapper command or script followed by the arguments to it - `gdb` if `true`, the tests are also run under `gdb` - `timeout_multiplier` a number to multiply the test timeout with +- `is_default` a bool to set whether this is the default test setup. + If `true`, the setup will be used whenever `meson test` is run + without the `--setup` option. Since 0.49.0 To use the test setup, run `meson test --setup=*name*` inside the build dir. diff --git a/docs/markdown/index.md b/docs/markdown/index.md index cffd488..e57cd69 100644 --- a/docs/markdown/index.md +++ b/docs/markdown/index.md @@ -33,13 +33,13 @@ developers. The first one is the mailing list, which is hosted at The second way is via IRC. The channel to use is `#mesonbuild` at [Freenode](https://freenode.net/). -### [Projects using Meson](http://mesonbuild.com/Users.html) +### [Projects using Meson](Users.md) Many projects out there are using Meson and their communities are also a great resource for learning about what (and what not too!) do when trying to convert to using Meson. -[A short list of Meson users can be found here](http://mesonbuild.com/Users.html) +[A short list of Meson users can be found here](Users.md) but there are many more. We would love to hear about your success stories too and how things could be improved too! diff --git a/docs/markdown/snippets/native_files.md b/docs/markdown/snippets/native_files.md new file mode 100644 index 0000000..7bc3644 --- /dev/null +++ b/docs/markdown/snippets/native_files.md @@ -0,0 +1,15 @@ +## Native config files + +Native files are the counterpart to cross files, and allow specifying +information about the build machine, both when cross compiling and when not. + +Currently the native files only allow specifying the names of binaries, similar +to the cross file, for example: + +```ini +[binaries] +llvm-config = "/opt/llvm-custom/bin/llvm-config" +``` + +Will override the llvm-config used for *native* binaries. Targets for the host +machine will continue to use the cross file. diff --git a/docs/markdown/snippets/test_setup_is_default.md b/docs/markdown/snippets/test_setup_is_default.md new file mode 100644 index 0000000..2274dc9 --- /dev/null +++ b/docs/markdown/snippets/test_setup_is_default.md @@ -0,0 +1,14 @@ +## New keyword argument `is_default` to `add_test_setup()` + +The keyword argument `is_default` may be used to set whether the test +setup should be used by default whenever `meson test` is run without +the `--setup` option. + +```meson +add_test_setup('default', is_default: true, env: 'G_SLICE=debug-blocks') +add_test_setup('valgrind', env: 'G_SLICE=always-malloc', ...) +test('mytest', exe) +``` + +For the example above, running `meson test` and `meson test +--setup=default` is now equivalent. diff --git a/docs/sitemap.txt b/docs/sitemap.txt index bfed027..f79eb05 100644 --- a/docs/sitemap.txt +++ b/docs/sitemap.txt @@ -9,6 +9,7 @@ index.md Using-with-Visual-Studio.md Meson-sample.md Syntax.md + Native-environments.md Build-targets.md Include-directories.md Installing.md diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 9e76c5b..5606c41 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -877,6 +877,10 @@ int dummy; def generate_single_java_compile(self, src, target, compiler, outfile): deps = [os.path.join(self.get_target_dir(l), l.get_filename()) for l in target.link_targets] + generated_sources = self.get_target_generated_sources(target) + for rel_src, gensrc in generated_sources.items(): + if rel_src.endswith('.java'): + deps.append(rel_src) args = [] args += compiler.get_buildtype_args(self.get_option_for_target('buildtype', target)) args += self.build.get_global_args(compiler, target.is_cross) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 2e86ca9..05bcd3a 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -26,7 +26,7 @@ from .. import mlog from .. import compilers from ..compilers import CompilerArgs from ..mesonlib import MesonException, File, python_command -from ..environment import Environment +from ..environment import Environment, build_filename def autodetect_vs_version(build): vs_version = os.getenv('VisualStudioVersion', None) @@ -417,7 +417,7 @@ class Vs2010Backend(backends.Backend): pref = ET.SubElement(ig, 'ProjectReference', Include=include) ET.SubElement(pref, 'Project').text = '{%s}' % projid - def create_basic_crap(self, target): + def create_basic_crap(self, target, guid): project_name = target.name root = ET.Element('Project', {'DefaultTargets': "Build", 'ToolsVersion': '4.0', @@ -431,7 +431,7 @@ class Vs2010Backend(backends.Backend): pl.text = self.platform globalgroup = ET.SubElement(root, 'PropertyGroup', Label='Globals') guidelem = ET.SubElement(globalgroup, 'ProjectGuid') - guidelem.text = '{%s}' % self.environment.coredata.test_guid + guidelem.text = '{%s}' % guid kw = ET.SubElement(globalgroup, 'Keyword') kw.text = self.platform + 'Proj' p = ET.SubElement(globalgroup, 'Platform') @@ -460,7 +460,7 @@ class Vs2010Backend(backends.Backend): return root def gen_run_target_vcxproj(self, target, ofname, guid): - root = self.create_basic_crap(target) + root = self.create_basic_crap(target, guid) action = ET.SubElement(root, 'ItemDefinitionGroup') customstep = ET.SubElement(action, 'PostBuildEvent') cmd_raw = [target.command] + target.args @@ -486,7 +486,7 @@ class Vs2010Backend(backends.Backend): self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname) def gen_custom_target_vcxproj(self, target, ofname, guid): - root = self.create_basic_crap(target) + root = self.create_basic_crap(target, guid) action = ET.SubElement(root, 'ItemDefinitionGroup') customstep = ET.SubElement(action, 'CustomBuildStep') # We need to always use absolute paths because our invocation is always @@ -1098,6 +1098,9 @@ class Vs2010Backend(backends.Backend): else: raise MesonException('Unsupported Visual Studio target machine: ' + targetmachine) + meson_file_group = ET.SubElement(root, 'ItemGroup') + ET.SubElement(meson_file_group, 'None', Include=os.path.join(proj_to_src_dir, build_filename)) + extra_files = target.extra_files if len(headers) + len(gen_hdrs) + len(extra_files) > 0: inc_hdrs = ET.SubElement(root, 'ItemGroup') @@ -1173,7 +1176,7 @@ class Vs2010Backend(backends.Backend): pl.text = self.platform globalgroup = ET.SubElement(root, 'PropertyGroup', Label='Globals') guidelem = ET.SubElement(globalgroup, 'ProjectGuid') - guidelem.text = '{%s}' % self.environment.coredata.test_guid + guidelem.text = '{%s}' % self.environment.coredata.regen_guid kw = ET.SubElement(globalgroup, 'Keyword') kw.text = self.platform + 'Proj' p = ET.SubElement(globalgroup, 'Platform') diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 814b6bb..c0f4564 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -135,6 +135,7 @@ class Build: self.dep_manifest = {} self.cross_stdlibs = {} self.test_setups = {} + self.test_setup_default_name = None self.find_overrides = {} self.searched_programs = set() # The list of all programs that have been searched for. @@ -1549,13 +1550,9 @@ class SharedLibrary(BuildTarget): prefix = '' suffix = '' self.filename_tpl = self.basic_filename_tpl - # If the user already provided the prefix and suffix to us, we don't - # need to do any filename suffix/prefix detection. # NOTE: manual prefix/suffix override is currently only tested for C/C++ - if self.prefix is not None and self.suffix is not None: - pass # C# and Mono - elif 'cs' in self.compilers: + if 'cs' in self.compilers: prefix = '' suffix = 'dll' self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}' @@ -1564,8 +1561,8 @@ class SharedLibrary(BuildTarget): # For all other targets/platforms import_filename stays None elif for_windows(is_cross, env): suffix = 'dll' - self.vs_import_filename = '{0}.lib'.format(self.name) - self.gcc_import_filename = 'lib{0}.dll.a'.format(self.name) + self.vs_import_filename = '{0}{1}.lib'.format(self.prefix if self.prefix is not None else '', self.name) + self.gcc_import_filename = '{0}{1}.dll.a'.format(self.prefix if self.prefix is not None else 'lib', self.name) if self.get_using_msvc(): # Shared library is of the form foo.dll prefix = '' @@ -1584,7 +1581,7 @@ class SharedLibrary(BuildTarget): self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}' elif for_cygwin(is_cross, env): suffix = 'dll' - self.gcc_import_filename = 'lib{0}.dll.a'.format(self.name) + self.gcc_import_filename = '{0}{1}.dll.a'.format(self.prefix if self.prefix is not None else 'lib', self.name) # Shared library is of the form cygfoo.dll # (ld --dll-search-prefix=cyg is the default) prefix = 'cyg' diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index b57001a..1b198b6 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -209,7 +209,9 @@ class CCompiler(Compiler): def _get_search_dirs(self, env): extra_args = ['--print-search-dirs'] stdo = None - with self._build_wrapper('', env, extra_args, None, 'compile', True) as p: + with self._build_wrapper('', env, extra_args=extra_args, + dependencies=None, mode='compile', + want_output=True) as p: stdo = p.stdo return stdo @@ -217,9 +219,22 @@ class CCompiler(Compiler): def _split_fetch_real_dirs(pathstr, sep=':'): paths = [] for p in pathstr.split(sep): - p = Path(p) - if p.exists() and p.resolve().as_posix() not in paths: - paths.append(p.resolve().as_posix()) + # GCC returns paths like this: + # /usr/lib/gcc/x86_64-linux-gnu/8/../../../../x86_64-linux-gnu/lib + # It would make sense to normalize them to get rid of the .. parts + # Sadly when you are on a merged /usr fs it also kills these: + # /lib/x86_64-linux-gnu + # since /lib is a symlink to /usr/lib. This would mean + # paths under /lib would be considered not a "system path", + # which is wrong and breaks things. Store everything, just to be sure. + pobj = Path(p) + unresolved = pobj.as_posix() + resolved = Path(p).resolve().as_posix() + if pobj.exists(): + if unresolved not in paths: + paths.append(unresolved) + if resolved not in paths: + paths.append(resolved) return tuple(paths) def get_compiler_dirs(self, env, name): @@ -361,13 +376,14 @@ class CCompiler(Compiler): code = 'int main(int argc, char **argv) { int class=0; return class; }\n' return self.sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code) - def check_header(self, hname, prefix, env, extra_args=None, dependencies=None): + def check_header(self, hname, prefix, env, *, extra_args=None, dependencies=None): fargs = {'prefix': prefix, 'header': hname} code = '''{prefix} #include <{header}>''' - return self.compiles(code.format(**fargs), env, extra_args, dependencies) + return self.compiles(code.format(**fargs), env, extra_args=extra_args, + dependencies=dependencies) - def has_header(self, hname, prefix, env, extra_args=None, dependencies=None): + def has_header(self, hname, prefix, env, *, extra_args=None, dependencies=None): fargs = {'prefix': prefix, 'header': hname} code = '''{prefix} #ifdef __has_include @@ -377,10 +393,10 @@ class CCompiler(Compiler): #else #include <{header}> #endif''' - return self.compiles(code.format(**fargs), env, extra_args, - dependencies, 'preprocess') + return self.compiles(code.format(**fargs), env, extra_args=extra_args, + dependencies=dependencies, mode='preprocess') - def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None, dependencies=None): + def has_header_symbol(self, hname, symbol, prefix, env, *, extra_args=None, dependencies=None): fargs = {'prefix': prefix, 'header': hname, 'symbol': symbol} t = '''{prefix} #include <{header}> @@ -390,7 +406,8 @@ class CCompiler(Compiler): {symbol}; #endif }}''' - return self.compiles(t.format(**fargs), env, extra_args, dependencies) + return self.compiles(t.format(**fargs), env, extra_args=extra_args, + dependencies=dependencies) def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'): if callable(extra_args): @@ -437,7 +454,7 @@ class CCompiler(Compiler): args += extra_args return args - def compiles(self, code, env, extra_args=None, dependencies=None, mode='compile'): + def compiles(self, code, env, *, extra_args=None, dependencies=None, mode='compile'): with self._build_wrapper(code, env, extra_args, dependencies, mode) as p: return p.returncode == 0 @@ -445,10 +462,11 @@ class CCompiler(Compiler): args = self._get_compiler_check_args(env, extra_args, dependencies, mode) return self.compile(code, args, mode, want_output=want_output) - def links(self, code, env, extra_args=None, dependencies=None): - return self.compiles(code, env, extra_args, dependencies, mode='link') + def links(self, code, env, *, extra_args=None, dependencies=None): + return self.compiles(code, env, extra_args=extra_args, + dependencies=dependencies, mode='link') - def run(self, code, env, extra_args=None, dependencies=None): + def run(self, code, env, *, extra_args=None, dependencies=None): if self.is_cross and self.exe_wrapper is None: raise CrossNoRunException('Can not run test applications in this cross environment.') with self._build_wrapper(code, env, extra_args, dependencies, mode='link', want_output=True) as p: @@ -478,7 +496,8 @@ class CCompiler(Compiler): t = '''#include <stdio.h> {prefix} int main() {{ static int a[1-2*!({expression})]; a[0]=0; return 0; }}''' - return self.compiles(t.format(**fargs), env, extra_args, dependencies) + return self.compiles(t.format(**fargs), env, extra_args=extra_args, + dependencies=dependencies) def cross_compute_int(self, expression, low, high, guess, prefix, env, extra_args, dependencies): # Try user's guess first @@ -528,7 +547,7 @@ class CCompiler(Compiler): return low - def compute_int(self, expression, low, high, guess, prefix, env, extra_args=None, dependencies=None): + def compute_int(self, expression, low, high, guess, prefix, env, *, extra_args=None, dependencies=None): if extra_args is None: extra_args = [] if self.is_cross: @@ -540,14 +559,15 @@ class CCompiler(Compiler): printf("%ld\\n", (long)({expression})); return 0; }};''' - res = self.run(t.format(**fargs), env, extra_args, dependencies) + res = self.run(t.format(**fargs), env, extra_args=extra_args, + dependencies=dependencies) if not res.compiled: return -1 if res.returncode != 0: raise EnvironmentException('Could not run compute_int test binary.') return int(res.stdout) - def cross_sizeof(self, typename, prefix, env, extra_args=None, dependencies=None): + def cross_sizeof(self, typename, prefix, env, *, extra_args=None, dependencies=None): if extra_args is None: extra_args = [] fargs = {'prefix': prefix, 'type': typename} @@ -556,30 +576,33 @@ class CCompiler(Compiler): int main(int argc, char **argv) {{ {type} something; }}''' - if not self.compiles(t.format(**fargs), env, extra_args, dependencies): + if not self.compiles(t.format(**fargs), env, extra_args=extra_args, + dependencies=dependencies): return -1 return self.cross_compute_int('sizeof(%s)' % typename, None, None, None, prefix, env, extra_args, dependencies) - def sizeof(self, typename, prefix, env, extra_args=None, dependencies=None): + def sizeof(self, typename, prefix, env, *, extra_args=None, dependencies=None): if extra_args is None: extra_args = [] fargs = {'prefix': prefix, 'type': typename} if self.is_cross: - return self.cross_sizeof(typename, prefix, env, extra_args, dependencies) + return self.cross_sizeof(typename, prefix, env, extra_args=extra_args, + dependencies=dependencies) t = '''#include<stdio.h> {prefix} int main(int argc, char **argv) {{ printf("%ld\\n", (long)(sizeof({type}))); return 0; }};''' - res = self.run(t.format(**fargs), env, extra_args, dependencies) + res = self.run(t.format(**fargs), env, extra_args=extra_args, + dependencies=dependencies) if not res.compiled: return -1 if res.returncode != 0: raise EnvironmentException('Could not run sizeof test binary.') return int(res.stdout) - def cross_alignment(self, typename, prefix, env, extra_args=None, dependencies=None): + def cross_alignment(self, typename, prefix, env, *, extra_args=None, dependencies=None): if extra_args is None: extra_args = [] fargs = {'prefix': prefix, 'type': typename} @@ -588,7 +611,8 @@ class CCompiler(Compiler): int main(int argc, char **argv) {{ {type} something; }}''' - if not self.compiles(t.format(**fargs), env, extra_args, dependencies): + if not self.compiles(t.format(**fargs), env, extra_args=extra_args, + dependencies=dependencies): return -1 t = '''#include <stddef.h> {prefix} @@ -598,11 +622,12 @@ class CCompiler(Compiler): }};''' return self.cross_compute_int('offsetof(struct tmp, target)', None, None, None, t.format(**fargs), env, extra_args, dependencies) - def alignment(self, typename, prefix, env, extra_args=None, dependencies=None): + def alignment(self, typename, prefix, env, *, extra_args=None, dependencies=None): if extra_args is None: extra_args = [] if self.is_cross: - return self.cross_alignment(typename, prefix, env, extra_args, dependencies) + return self.cross_alignment(typename, prefix, env, extra_args=extra_args, + dependencies=dependencies) fargs = {'prefix': prefix, 'type': typename} t = '''#include <stdio.h> #include <stddef.h> @@ -615,7 +640,8 @@ class CCompiler(Compiler): printf("%d", (int)offsetof(struct tmp, target)); return 0; }}''' - res = self.run(t.format(**fargs), env, extra_args, dependencies) + res = self.run(t.format(**fargs), env, extra_args=extra_args, + dependencies=dependencies) if not res.compiled: raise EnvironmentException('Could not compile alignment test.') if res.returncode != 0: @@ -659,7 +685,7 @@ class CCompiler(Compiler): int main(int argc, char *argv[]) {{ printf ("{fmt}", {cast} {f}()); }}'''.format(**fargs) - res = self.run(code, env, extra_args, dependencies) + res = self.run(code, env, extra_args=extra_args, dependencies=dependencies) if not res.compiled: m = 'Could not get return value of {}()' raise EnvironmentException(m.format(fname)) @@ -728,7 +754,7 @@ class CCompiler(Compiler): }}''' return head, main - def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None): + 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 @@ -776,7 +802,8 @@ class CCompiler(Compiler): head, main = self._no_prototype_templ() templ = head + stubs_fail + main - if self.links(templ.format(**fargs), env, extra_args, dependencies): + if self.links(templ.format(**fargs), env, extra_args=extra_args, + dependencies=dependencies): return True # MSVC does not have compiler __builtin_-s. @@ -809,9 +836,10 @@ class CCompiler(Compiler): #endif #endif }}''' - return self.links(t.format(**fargs), env, extra_args, dependencies) + return self.links(t.format(**fargs), env, extra_args=extra_args, + dependencies=dependencies) - def has_members(self, typename, membernames, prefix, env, extra_args=None, dependencies=None): + def has_members(self, typename, membernames, prefix, env, *, extra_args=None, dependencies=None): if extra_args is None: extra_args = [] fargs = {'prefix': prefix, 'type': typename, 'name': 'foo'} @@ -825,7 +853,8 @@ class CCompiler(Compiler): {type} {name}; {members} }};''' - return self.compiles(t.format(**fargs), env, extra_args, dependencies) + return self.compiles(t.format(**fargs), env, extra_args=extra_args, + dependencies=dependencies) def has_type(self, typename, prefix, env, extra_args, dependencies=None): fargs = {'prefix': prefix, 'type': typename} @@ -833,7 +862,8 @@ class CCompiler(Compiler): void bar() {{ sizeof({type}); }};''' - return self.compiles(t.format(**fargs), env, extra_args, dependencies) + return self.compiles(t.format(**fargs), env, extra_args=extra_args, + dependencies=dependencies) def symbols_have_underscore_prefix(self, env): ''' @@ -1221,11 +1251,13 @@ class ElbrusCCompiler(GnuCCompiler, ElbrusCompiler): # Elbrus C compiler does not have lchmod, but there is only linker warning, not compiler error. # So we should explicitly fail at this case. - def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None): + def has_function(self, funcname, prefix, env, *, extra_args=None, dependencies=None): if funcname == 'lchmod': return False else: - return super().has_function(funcname, prefix, env, extra_args, dependencies) + return super().has_function(funcname, prefix, env, + extra_args=extra_args, + dependencies=dependencies) class IntelCCompiler(IntelCompiler, CCompiler): diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 495663e..85a8480 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -868,10 +868,10 @@ class Compiler: def compute_int(self, expression, low, high, guess, prefix, env, extra_args, dependencies): raise EnvironmentException('%s does not support compute_int ' % self.get_id()) - def has_members(self, typename, membernames, prefix, env, extra_args=None, dependencies=None): + def has_members(self, typename, membernames, prefix, env, *, extra_args=None, dependencies=None): raise EnvironmentException('%s does not support has_member(s) ' % self.get_id()) - def has_type(self, typename, prefix, env, extra_args, dependencies=None): + def has_type(self, typename, prefix, env, extra_args, *, dependencies=None): raise EnvironmentException('%s does not support has_type ' % self.get_id()) def symbols_have_underscore_prefix(self, env): @@ -1614,7 +1614,7 @@ class ClangCompiler(GnuLikeCompiler): myargs + args, env) - def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None): + 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 @@ -1623,7 +1623,8 @@ class ClangCompiler(GnuLikeCompiler): # https://github.com/Homebrew/homebrew-core/issues/3727 if self.compiler_type.is_osx_compiler and version_compare(self.version, '>=8.0'): extra_args.append('-Wl,-no_weak_imports') - return super().has_function(funcname, prefix, env, extra_args, dependencies) + return super().has_function(funcname, prefix, env, extra_args=extra_args, + dependencies=dependencies) def openmp_flags(self): if version_compare(self.version, '>=3.8.0'): diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index e6f5803..7d2000e 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -62,9 +62,11 @@ class CPPCompiler(CCompiler): # too strict without this and always fails. return super().get_compiler_check_args() + ['-fpermissive'] - def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None, dependencies=None): + 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): + if super().has_header_symbol(hname, symbol, prefix, env, + extra_args=extra_args, + dependencies=dependencies): return True # Check if it's a class or a template if extra_args is None: @@ -74,7 +76,8 @@ class CPPCompiler(CCompiler): #include <{header}> using {symbol}; int main () {{ return 0; }}''' - return self.compiles(t.format(**fargs), env, extra_args, dependencies) + return self.compiles(t.format(**fargs), env, extra_args=extra_args, + dependencies=dependencies) def _test_cpp_std_arg(self, cpp_std_value): # Test whether the compiler understands a -std=XY argument @@ -246,11 +249,13 @@ class ElbrusCPPCompiler(GnuCPPCompiler, ElbrusCompiler): # Elbrus C++ compiler does not have lchmod, but there is only linker warning, not compiler error. # So we should explicitly fail at this case. - def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None): + def has_function(self, funcname, prefix, env, *, extra_args=None, dependencies=None): if funcname == 'lchmod': return False else: - return super().has_function(funcname, prefix, env, extra_args, dependencies) + return super().has_function(funcname, prefix, env, + extra_args=extra_args, + dependencies=dependencies) class IntelCPPCompiler(IntelCompiler, CPPCompiler): diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index 0a59e7f..2cf0fbd 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -303,7 +303,7 @@ class DCompiler(Compiler): args += extra_args return args - def compiles(self, code, env, extra_args=None, dependencies=None, mode='compile'): + def compiles(self, code, env, *, extra_args=None, dependencies=None, mode='compile'): args = self._get_compiler_check_args(env, extra_args, dependencies, mode) with self.compile(code, args, mode) as p: diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index b58c4e0..23c4892 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -210,16 +210,18 @@ end program prog def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'): return CCompiler._get_compiler_check_args(self, env, extra_args, dependencies, mode='compile') - def compiles(self, code, env, extra_args=None, dependencies=None, mode='compile'): - return CCompiler.compiles(self, code, env, extra_args, dependencies, mode) + def compiles(self, code, env, *, extra_args=None, dependencies=None, mode='compile'): + return CCompiler.compiles(self, code, env, extra_args=extra_args, + dependencies=dependencies, mode=mode) def _build_wrapper(self, code, env, extra_args, dependencies=None, mode='compile', want_output=False): return CCompiler._build_wrapper(self, code, env, extra_args, dependencies, mode, want_output) - def links(self, code, env, extra_args=None, dependencies=None): - return CCompiler.links(self, code, env, extra_args, dependencies) + def links(self, code, env, *, extra_args=None, dependencies=None): + return CCompiler.links(self, code, env, extra_args=extra_args, + dependencies=dependencies) - def run(self, code, env, extra_args=None, dependencies=None): + def run(self, code, env, *, extra_args=None, dependencies=None): return CCompiler.run(self, code, env, extra_args, dependencies) def _get_patterns(self, *args, **kwargs): diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index cdfed7c..ae37576 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -183,6 +183,7 @@ class UserArrayOption(UserOption): ', '.join(bad), ', '.join(self.choices))) return newvalue + class UserFeatureOption(UserComboOption): static_choices = ['enabled', 'disabled', 'auto'] @@ -198,6 +199,72 @@ class UserFeatureOption(UserComboOption): def is_auto(self): return self.value == 'auto' + +def load_configs(filenames): + """Load native files.""" + def gen(): + for f in filenames: + f = os.path.expanduser(os.path.expandvars(f)) + if os.path.exists(f): + yield f + continue + elif sys.platform != 'win32': + f = os.path.basename(f) + paths = [ + os.environ.get('XDG_DATA_HOME', os.path.expanduser('~/.local/share')), + ] + os.environ.get('XDG_DATA_DIRS', '/usr/local/share:/usr/share').split(':') + for path in paths: + path_to_try = os.path.join(path, 'meson', 'native', f) + if os.path.isfile(path_to_try): + yield path_to_try + break + else: + raise MesonException('Cannot find specified native file: ' + f) + continue + + raise MesonException('Cannot find specified native file: ' + f) + + config = configparser.SafeConfigParser() + config.read(gen()) + return config + + +def _get_section(config, section): + if config.has_section(section): + final = {} + for k, v in config.items(section): + # Windows paths... + v = v.replace('\\', '\\\\') + try: + final[k] = ast.literal_eval(v) + except SyntaxError: + raise MesonException( + 'Malformed value in native file variable: {}'.format(v)) + return final + return {} + + +class ConfigData: + + """Contains configuration information provided by the user for the build.""" + + def __init__(self, config=None): + if config: + self.binaries = _get_section(config, 'binaries') + # global is a keyword and globals is a builtin, rather than mangle it, + # use a similar word + self.universal = _get_section(config, 'globals') + self.subprojects = {s: _get_section(config, s) for s in config.sections() + if s not in {'binaries', 'globals'}} + else: + self.binaries = {} + self.universal = {} + self.subprojects = {} + + def get_binaries(self, name): + return self.binaries.get(name, None) + + # This class contains all data that must persist over multiple # invocations of Meson. It is roughly the same thing as # cmakecache. @@ -229,6 +296,15 @@ class CoreData: self.deps = OrderedDict() # Only to print a warning if it changes between Meson invocations. self.pkgconf_envvar = os.environ.get('PKG_CONFIG_PATH', '') + self.config_files = self.__load_config_files(options.native_file) + + @staticmethod + def __load_config_files(filenames): + if not filenames: + return [] + filenames = [os.path.abspath(os.path.expanduser(os.path.expanduser(f))) + for f in filenames] + return filenames @staticmethod def __load_cross_file(filename): @@ -460,12 +536,18 @@ class CoreData: sub = 'In subproject {}: '.format(subproject) if subproject else '' mlog.warning('{}Unknown options: "{}"'.format(sub, unknown_options)) +class CmdLineFileParser(configparser.ConfigParser): + def __init__(self): + # We don't want ':' as key delimiter, otherwise it would break when + # storing subproject options like "subproject:option=value" + super().__init__(delimiters=['=']) + def get_cmd_line_file(build_dir): return os.path.join(build_dir, 'meson-private', 'cmd_line.txt') def read_cmd_line_file(build_dir, options): filename = get_cmd_line_file(build_dir) - config = configparser.ConfigParser() + config = CmdLineFileParser() config.read(filename) # Do a copy because config is not really a dict. options.cmd_line_options @@ -480,7 +562,7 @@ def read_cmd_line_file(build_dir, options): def write_cmd_line_file(build_dir, options): filename = get_cmd_line_file(build_dir) - config = configparser.ConfigParser() + config = CmdLineFileParser() properties = {} if options.cross_file is not None: @@ -493,7 +575,7 @@ def write_cmd_line_file(build_dir, options): def update_cmd_line_file(build_dir, options): filename = get_cmd_line_file(build_dir) - config = configparser.ConfigParser() + config = CmdLineFileParser() config.read(filename) config['options'].update(options.cmd_line_options) with open(filename, 'w') as f: diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index e67f4c0..b1d79bb 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -399,6 +399,8 @@ class ConfigToolDependency(ExternalDependency): 'Falling back to searching PATH. This may find a ' 'native version of {0}!'.format(self.tool_name)) tools = self.tools + elif self.tool_name in self.env.config_info.binaries: + tools = [self.env.config_info.binaries[self.tool_name]] else: tools = self.tools @@ -500,7 +502,8 @@ class PkgConfigDependency(ExternalDependency): if self.required: raise DependencyException('Pkg-config binary missing from cross file') else: - potential_pkgbin = ExternalProgram.from_cross_info(environment.cross_info, 'pkgconfig') + potential_pkgbin = ExternalProgram.from_bin_list( + environment.cross_info.config['binaries'], 'pkgconfig') if potential_pkgbin.found(): self.pkgbin = potential_pkgbin else: @@ -1076,10 +1079,10 @@ class ExternalProgram: return ' '.join(self.command) @staticmethod - def from_cross_info(cross_info, name): - if name not in cross_info.config['binaries']: + def from_bin_list(bins, name): + if name not in bins: return NonExistingExternalProgram() - command = cross_info.config['binaries'][name] + command = bins[name] if not isinstance(command, (list, str)): raise MesonException('Invalid type {!r} for binary {!r} in cross file' ''.format(command, name)) @@ -1238,8 +1241,8 @@ class ExternalProgram: class NonExistingExternalProgram(ExternalProgram): "A program that will never exist" - def __init__(self): - self.name = 'nonexistingprogram' + def __init__(self, name='nonexistingprogram'): + self.name = name self.command = [None] self.path = None diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index cc012ac..f220a8e 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -234,6 +234,7 @@ class MPIDependency(ExternalDependency): class OpenMPDependency(ExternalDependency): # Map date of specification release (which is the macro value) to a version. VERSIONS = { + '201811': '5.0', '201511': '4.5', '201307': '4.0', '201107': '3.1', diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py index e3b371d..b589889 100644 --- a/mesonbuild/dependencies/ui.py +++ b/mesonbuild/dependencies/ui.py @@ -30,7 +30,7 @@ from ..mesonlib import ( from ..environment import detect_cpu from .base import DependencyException, DependencyMethods -from .base import ExternalDependency, ExternalProgram +from .base import ExternalDependency, ExternalProgram, NonExistingExternalProgram from .base import ExtraFrameworkDependency, PkgConfigDependency from .base import ConfigToolDependency @@ -230,21 +230,46 @@ class QtBaseDependency(ExternalDependency): self.from_text = mlog.format_list(methods) self.version = None - def compilers_detect(self): + def compilers_detect(self, interp_obj): "Detect Qt (4 or 5) moc, uic, rcc in the specified bindir or in PATH" - if self.bindir or for_windows(self.env.is_cross_build(), self.env): - moc = ExternalProgram(os.path.join(self.bindir, 'moc'), silent=True) - uic = ExternalProgram(os.path.join(self.bindir, 'uic'), silent=True) - rcc = ExternalProgram(os.path.join(self.bindir, 'rcc'), silent=True) - lrelease = ExternalProgram(os.path.join(self.bindir, 'lrelease'), silent=True) - else: - # We don't accept unsuffixed 'moc', 'uic', and 'rcc' because they - # are sometimes older, or newer versions. - moc = ExternalProgram('moc-' + self.name, silent=True) - uic = ExternalProgram('uic-' + self.name, silent=True) - rcc = ExternalProgram('rcc-' + self.name, silent=True) - lrelease = ExternalProgram('lrelease-' + self.name, silent=True) - return moc, uic, rcc, lrelease + # It is important that this list does not change order as the order of + # the returned ExternalPrograms will change as well + bins = ['moc', 'uic', 'rcc', 'lrelease'] + found = {b: NonExistingExternalProgram(name='{}-{}'.format(b, self.name)) + for b in bins} + + def gen_bins(): + for b in bins: + yield '{}-{}'.format(b, self.name), b, False + yield b, b, self.required + + for b, name, required in gen_bins(): + if found[name].found(): + continue + + # prefer the <tool>-qt<version> of the tool to the plain one, as we + # don't know what the unsuffixed one points to without calling it. + p = interp_obj.find_program_impl([b], silent=True, required=required).held_object + if not p.found(): + continue + + if b.startswith('lrelease'): + arg = ['-version'] + elif mesonlib.version_compare(self.version, '>= 5'): + arg = ['--version'] + else: + arg = ['-v'] + + # Ensure that the version of qt and each tool are the same + _, out, err = mesonlib.Popen_safe(p.get_command() + arg) + if b.startswith('lrelease') or not self.version.startswith('4'): + care = out + else: + care = err + if mesonlib.version_compare(self.version, '== {}'.format(care.split(' ')[-1])): + found[name] = p + + return tuple([found[b] for b in bins]) def _pkgconfig_detect(self, mods, kwargs): # We set the value of required to False so that we can try the @@ -302,8 +327,15 @@ class QtBaseDependency(ExternalDependency): def _find_qmake(self, qmake): # Even when cross-compiling, if a cross-info qmake is not specified, we # fallback to using the qmake in PATH because that's what we used to do - if self.env.is_cross_build() and 'qmake' in self.env.cross_info.config['binaries']: - return ExternalProgram.from_cross_info(self.env.cross_info, 'qmake') + if self.env.is_cross_build(): + if 'qmake' in self.env.cross_info.config['binaries']: + return ExternalProgram.from_bin_list(self.env.cross_info.config['binaries'], 'qmake') + elif self.env.config_info: + # Prefer suffixed to unsuffixed version + p = ExternalProgram.from_bin_list(self.env.config_info.binaries, 'qmake-' + self.name) + if p.found(): + return p + return ExternalProgram.from_bin_list(self.env.config_info.binaries, 'qmake') return ExternalProgram(qmake, silent=True) def _qmake_detect(self, mods, kwargs): diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index f45f91b..01a7c51 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -120,7 +120,7 @@ def find_coverage_tools(): return gcovr_exe, gcovr_new_rootdir, lcov_exe, genhtml_exe def detect_ninja(version='1.5', log=False): - for n in ['ninja', 'ninja-build']: + for n in ['ninja', 'ninja-build', 'samu']: try: p, found = Popen_safe([n, '--version'])[0:2] except (FileNotFoundError, PermissionError): @@ -340,7 +340,8 @@ class Environment: self.cross_info = CrossBuildInfo(self.coredata.cross_file) if 'exe_wrapper' in self.cross_info.config['binaries']: from .dependencies import ExternalProgram - self.exe_wrapper = ExternalProgram.from_cross_info(self.cross_info, 'exe_wrapper') + self.exe_wrapper = ExternalProgram.from_bin_list( + self.cross_info.config['binaries'], 'exe_wrapper') if 'host_machine' in self.cross_info.config: self.machines.host = MachineInfo.from_literal( self.cross_info.config['host_machine']) @@ -351,6 +352,12 @@ class Environment: self.cross_info = None self.machines.default_missing() + if self.coredata.config_files: + self.config_info = coredata.ConfigData( + coredata.load_configs(self.coredata.config_files)) + else: + self.config_info = coredata.ConfigData() + self.cmd_line_options = options.cmd_line_options.copy() # List of potential compilers. @@ -505,7 +512,10 @@ class Environment: The list of compilers is detected in the exact same way for C, C++, ObjC, ObjC++, Fortran, CS so consolidate it here. ''' + is_cross = False + exe_wrap = None evar = BinaryTable.evarMap[lang] + if self.is_cross_build() and want_cross: if lang not in self.cross_info.config['binaries']: raise EnvironmentException('{!r} compiler binary not defined in cross file'.format(lang)) @@ -521,13 +531,13 @@ class Environment: shlex.split(os.environ[evar])) # Return value has to be a list of compiler 'choices' compilers = [compilers] - is_cross = False - exe_wrap = None + elif lang in self.config_info.binaries: + compilers, ccache = BinaryTable.parse_entry( + mesonlib.stringlistify(self.config_info.binaries[lang])) + compilers = [compilers] else: compilers = getattr(self, 'default_' + lang) ccache = BinaryTable.detect_ccache() - is_cross = False - exe_wrap = None return compilers, ccache, is_cross, exe_wrap def _handle_exceptions(self, exceptions, binaries, bintype='compiler'): @@ -798,13 +808,17 @@ class Environment: self._handle_exceptions(popen_exceptions, compilers) def detect_java_compiler(self): - exelist = ['javac'] + if 'java' in self.config_info.binaries: + exelist = mesonlib.stringlistify(self.config_info.binaries['java']) + else: + exelist = ['javac'] + try: p, out, err = Popen_safe(exelist + ['-version']) except OSError: raise EnvironmentException('Could not execute Java compiler "%s"' % ' '.join(exelist)) - version = search_version(err) if 'javac' in out or 'javac' in err: + version = search_version(err if 'javac' in err else out) return JavaCompiler(exelist, version) raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') @@ -831,6 +845,8 @@ class Environment: def detect_vala_compiler(self): if 'VALAC' in os.environ: exelist = shlex.split(os.environ['VALAC']) + elif 'vala' in self.config_info.binaries: + exelist = mesonlib.stringlistify(self.config_info.binaries['vala']) else: exelist = ['valac'] try: @@ -875,6 +891,8 @@ class Environment: elif self.is_cross_build() and want_cross: exelist = mesonlib.stringlistify(self.cross_info.config['binaries']['d']) is_cross = True + elif 'd' in self.config_info.binaries: + exelist = mesonlib.stringlistify(self.config_info.binaries['d']) elif shutil.which("ldc2"): exelist = ['ldc2'] elif shutil.which("ldc"): @@ -912,7 +930,10 @@ class Environment: raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') def detect_swift_compiler(self): - exelist = ['swiftc'] + if 'swift' in self.config_info.binaries: + exelist = mesonlib.stringlistify(self.config_info.binaries['swift']) + else: + exelist = ['swiftc'] try: p, _, err = Popen_safe(exelist + ['-v']) except OSError: diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 86b761e..e820afb 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -1018,7 +1018,9 @@ class CompilerHolder(InterpreterObject): raise InterpreterException('Prefix argument of sizeof must be a string.') extra_args = mesonlib.stringlistify(kwargs.get('args', [])) deps, msg = self.determine_dependencies(kwargs) - result = self.compiler.alignment(typename, prefix, self.environment, extra_args, deps) + result = self.compiler.alignment(typename, prefix, self.environment, + extra_args=extra_args, + dependencies=deps) mlog.log('Checking for alignment of', mlog.bold(typename, True), msg, result) return result @@ -1043,7 +1045,8 @@ class CompilerHolder(InterpreterObject): raise InterpreterException('Testname argument must be a string.') extra_args = functools.partial(self.determine_args, kwargs) deps, msg = self.determine_dependencies(kwargs, endl=None) - result = self.compiler.run(code, self.environment, extra_args, deps) + result = self.compiler.run(code, self.environment, extra_args=extra_args, + dependencies=deps) if len(testname) > 0: if not result.compiled: h = mlog.red('DID NOT COMPILE') @@ -1099,7 +1102,9 @@ class CompilerHolder(InterpreterObject): extra_args = functools.partial(self.determine_args, kwargs) deps, msg = self.determine_dependencies(kwargs) had = self.compiler.has_members(typename, [membername], prefix, - self.environment, extra_args, deps) + self.environment, + extra_args=extra_args, + dependencies=deps) if had: hadtxt = mlog.green('YES') else: @@ -1127,7 +1132,9 @@ class CompilerHolder(InterpreterObject): extra_args = functools.partial(self.determine_args, kwargs) deps, msg = self.determine_dependencies(kwargs) had = self.compiler.has_members(typename, membernames, prefix, - self.environment, extra_args, deps) + self.environment, + extra_args=extra_args, + dependencies=deps) if had: hadtxt = mlog.green('YES') else: @@ -1154,7 +1161,9 @@ class CompilerHolder(InterpreterObject): raise InterpreterException('Prefix argument of has_function must be a string.') extra_args = self.determine_args(kwargs) deps, msg = self.determine_dependencies(kwargs) - had = self.compiler.has_function(funcname, prefix, self.environment, extra_args, deps) + had = self.compiler.has_function(funcname, prefix, self.environment, + extra_args=extra_args, + dependencies=deps) if had: hadtxt = mlog.green('YES') else: @@ -1179,7 +1188,8 @@ class CompilerHolder(InterpreterObject): raise InterpreterException('Prefix argument of has_type must be a string.') extra_args = functools.partial(self.determine_args, kwargs) deps, msg = self.determine_dependencies(kwargs) - had = self.compiler.has_type(typename, prefix, self.environment, extra_args, deps) + had = self.compiler.has_type(typename, prefix, self.environment, + extra_args=extra_args, dependencies=deps) if had: hadtxt = mlog.green('YES') else: @@ -1217,7 +1227,9 @@ class CompilerHolder(InterpreterObject): raise InterpreterException('Guess argument of compute_int must be an int.') extra_args = functools.partial(self.determine_args, kwargs) deps, msg = self.determine_dependencies(kwargs) - res = self.compiler.compute_int(expression, low, high, guess, prefix, self.environment, extra_args, deps) + res = self.compiler.compute_int(expression, low, high, guess, prefix, + self.environment, extra_args=extra_args, + dependencies=deps) mlog.log('Computing int of', mlog.bold(expression, True), msg, res) return res @@ -1238,7 +1250,8 @@ class CompilerHolder(InterpreterObject): raise InterpreterException('Prefix argument of sizeof must be a string.') extra_args = functools.partial(self.determine_args, kwargs) deps, msg = self.determine_dependencies(kwargs) - esize = self.compiler.sizeof(element, prefix, self.environment, extra_args, deps) + esize = self.compiler.sizeof(element, prefix, self.environment, + extra_args=extra_args, dependencies=deps) mlog.log('Checking for size of', mlog.bold(element, True), msg, esize) return esize @@ -1260,7 +1273,9 @@ class CompilerHolder(InterpreterObject): raise InterpreterException('Prefix argument of get_define() must be a string.') extra_args = functools.partial(self.determine_args, kwargs) deps, msg = self.determine_dependencies(kwargs) - value = self.compiler.get_define(element, prefix, self.environment, extra_args, deps) + value = self.compiler.get_define(element, prefix, self.environment, + extra_args=extra_args, + dependencies=deps) mlog.log('Fetching value of define', mlog.bold(element, True), msg, value) return value @@ -1285,7 +1300,9 @@ class CompilerHolder(InterpreterObject): raise InterpreterException('Testname argument must be a string.') extra_args = functools.partial(self.determine_args, kwargs) deps, msg = self.determine_dependencies(kwargs, endl=None) - result = self.compiler.compiles(code, self.environment, extra_args, deps) + result = self.compiler.compiles(code, self.environment, + extra_args=extra_args, + dependencies=deps) if len(testname) > 0: if result: h = mlog.green('YES') @@ -1315,7 +1332,9 @@ class CompilerHolder(InterpreterObject): raise InterpreterException('Testname argument must be a string.') extra_args = functools.partial(self.determine_args, kwargs) deps, msg = self.determine_dependencies(kwargs, endl=None) - result = self.compiler.links(code, self.environment, extra_args, deps) + result = self.compiler.links(code, self.environment, + extra_args=extra_args, + dependencies=deps) if len(testname) > 0: if result: h = mlog.green('YES') @@ -1342,7 +1361,9 @@ class CompilerHolder(InterpreterObject): raise InterpreterException('Prefix argument of has_header must be a string.') extra_args = functools.partial(self.determine_args, kwargs) deps, msg = self.determine_dependencies(kwargs) - haz = self.compiler.check_header(hname, prefix, self.environment, extra_args, deps) + haz = self.compiler.check_header(hname, prefix, self.environment, + extra_args=extra_args, + dependencies=deps) if haz: h = mlog.green('YES') else: @@ -1367,7 +1388,8 @@ class CompilerHolder(InterpreterObject): raise InterpreterException('Prefix argument of has_header must be a string.') extra_args = functools.partial(self.determine_args, kwargs) deps, msg = self.determine_dependencies(kwargs) - haz = self.compiler.has_header(hname, prefix, self.environment, extra_args, deps) + haz = self.compiler.has_header(hname, prefix, self.environment, + extra_args=extra_args, dependencies=deps) if haz: h = mlog.green('YES') else: @@ -1393,7 +1415,9 @@ class CompilerHolder(InterpreterObject): raise InterpreterException('Prefix argument of has_header_symbol must be a string.') extra_args = functools.partial(self.determine_args, kwargs) deps, msg = self.determine_dependencies(kwargs) - haz = self.compiler.has_header_symbol(hname, symbol, prefix, self.environment, extra_args, deps) + haz = self.compiler.has_header_symbol(hname, symbol, prefix, self.environment, + extra_args=extra_args, + dependencies=deps) if haz: h = mlog.green('YES') else: @@ -2724,8 +2748,7 @@ external dependencies (including libraries) must go to "dependencies".''') self.coredata. base_options[optname] = oobj self.emit_base_options_warnings(enabled_opts) - def program_from_cross_file(self, prognames, silent=False): - cross_info = self.environment.cross_info + def _program_from_file(self, prognames, bins, silent): for p in prognames: if hasattr(p, 'held_object'): p = p.held_object @@ -2733,11 +2756,19 @@ external dependencies (including libraries) must go to "dependencies".''') continue # Always points to a local (i.e. self generated) file. if not isinstance(p, str): raise InterpreterException('Executable name must be a string') - prog = ExternalProgram.from_cross_info(cross_info, p) + prog = ExternalProgram.from_bin_list(bins, p) if prog.found(): return ExternalProgramHolder(prog) return None + def program_from_cross_file(self, prognames, silent=False): + bins = self.environment.cross_info.config['binaries'] + return self._program_from_file(prognames, bins, silent) + + def program_from_config_file(self, prognames, silent=False): + bins = self.environment.config_info.binaries + return self._program_from_file(prognames, bins, silent) + def program_from_system(self, args, silent=False): # Search for scripts relative to current subdir. # Do not cache found programs because find_program('foobar') @@ -2792,10 +2823,14 @@ external dependencies (including libraries) must go to "dependencies".''') def find_program_impl(self, args, native=False, required=True, silent=True): if not isinstance(args, list): args = [args] + progobj = self.program_from_overrides(args, silent=silent) - if progobj is None and self.build.environment.is_cross_build(): - if not native: + if progobj is None: + if self.build.environment.is_cross_build() and not native: progobj = self.program_from_cross_file(args, silent=silent) + else: + progobj = self.program_from_config_file(args, silent=silent) + if progobj is None: progobj = self.program_from_system(args, silent=silent) if required and (progobj is None or not progobj.found()): @@ -3733,6 +3768,14 @@ different subdirectory. timeout_multiplier = kwargs.get('timeout_multiplier', 1) if not isinstance(timeout_multiplier, int): raise InterpreterException('Timeout multiplier must be a number.') + is_default = kwargs.get('is_default', False) + if not isinstance(is_default, bool): + raise InterpreterException('is_default option must be a boolean') + if is_default: + if self.build.test_setup_default_name is not None: + raise InterpreterException('\'%s\' is already set as default. ' + 'is_default can be set to true only once' % self.build.test_setup_default_name) + self.build.test_setup_default_name = setup_name env = self.unpack_env_kwarg(kwargs) self.build.test_setups[setup_name] = build.TestSetup(exe_wrapper=exe_wrapper, gdb=gdb, diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index 954220b..3b2bf07 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -480,7 +480,9 @@ class PythonModule(ExtensionModule): if len(args) > 1: raise InvalidArguments('find_installation takes zero or one positional argument.') - if args: + if 'python' in state.environment.config_info.binaries: + name_or_path = state.environment.config_info.binaries['python'] + elif args: name_or_path = args[0] if not isinstance(name_or_path, str): raise InvalidArguments('find_installation argument must be a string.') diff --git a/mesonbuild/modules/python3.py b/mesonbuild/modules/python3.py index 5bda5ab..f664632 100644 --- a/mesonbuild/modules/python3.py +++ b/mesonbuild/modules/python3.py @@ -48,7 +48,10 @@ class Python3Module(ExtensionModule): @noKwargs def find_python(self, state, args, kwargs): - py3 = dependencies.ExternalProgram('python3', mesonlib.python_command, silent=True) + options = [state.environment.config_info.binaries.get('python3')] + if not options[0]: # because this would be [None] + options = ['python3', mesonlib.python_command] + py3 = dependencies.ExternalProgram(*options, silent=True) return ModuleReturnValue(py3, [py3]) @noKwargs diff --git a/mesonbuild/modules/qt.py b/mesonbuild/modules/qt.py index 7a2c338..367b15b 100644 --- a/mesonbuild/modules/qt.py +++ b/mesonbuild/modules/qt.py @@ -18,7 +18,7 @@ from .. import build from ..mesonlib import MesonException, Popen_safe, extract_as_list, File from ..dependencies import Dependency, Qt4Dependency, Qt5Dependency import xml.etree.ElementTree as ET -from . import ModuleReturnValue, get_include_args +from . import ModuleReturnValue, get_include_args, ExtensionModule from ..interpreterbase import permittedKwargs, FeatureNewKwargs _QT_DEPS_LUT = { @@ -27,10 +27,11 @@ _QT_DEPS_LUT = { } -class QtBaseModule: +class QtBaseModule(ExtensionModule): tools_detected = False - def __init__(self, qt_version=5): + def __init__(self, interpreter, qt_version=5): + ExtensionModule.__init__(self, interpreter) self.qt_version = qt_version def _detect_tools(self, env, method): @@ -43,7 +44,7 @@ class QtBaseModule: kwargs = {'required': 'true', 'modules': 'Core', 'silent': 'true', 'method': method} qt = _QT_DEPS_LUT[self.qt_version](env, kwargs) # Get all tools and then make sure that they are the right version - self.moc, self.uic, self.rcc, self.lrelease = qt.compilers_detect() + self.moc, self.uic, self.rcc, self.lrelease = qt.compilers_detect(self.interpreter) # Moc, uic and rcc write their version strings to stderr. # Moc and rcc return a non-zero result when doing so. # What kind of an idiot thought that was a good idea? diff --git a/mesonbuild/modules/qt4.py b/mesonbuild/modules/qt4.py index 29992d5..112e3e4 100644 --- a/mesonbuild/modules/qt4.py +++ b/mesonbuild/modules/qt4.py @@ -14,14 +14,13 @@ from .. import mlog from .qt import QtBaseModule -from . import ExtensionModule -class Qt4Module(ExtensionModule, QtBaseModule): +class Qt4Module(QtBaseModule): def __init__(self, interpreter): - QtBaseModule.__init__(self, qt_version=4) - ExtensionModule.__init__(self, interpreter) + QtBaseModule.__init__(self, interpreter, qt_version=4) + def initialize(*args, **kwargs): mlog.warning('rcc dependencies will not work properly until this upstream issue is fixed:', diff --git a/mesonbuild/modules/qt5.py b/mesonbuild/modules/qt5.py index 19623ac..96a7964 100644 --- a/mesonbuild/modules/qt5.py +++ b/mesonbuild/modules/qt5.py @@ -14,14 +14,13 @@ from .. import mlog from .qt import QtBaseModule -from . import ExtensionModule -class Qt5Module(ExtensionModule, QtBaseModule): +class Qt5Module(QtBaseModule): def __init__(self, interpreter): - QtBaseModule.__init__(self, qt_version=5) - ExtensionModule.__init__(self, interpreter) + QtBaseModule.__init__(self, interpreter, qt_version=5) + def initialize(*args, **kwargs): mlog.warning('rcc dependencies will not work reliably until this upstream issue is fixed:', diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py index 4d0f244..d185d89 100644 --- a/mesonbuild/modules/windows.py +++ b/mesonbuild/modules/windows.py @@ -49,8 +49,8 @@ class WindowsModule(ExtensionModule): if state.environment.is_cross_build(): # If cross compiling see if windres has been specified in the # cross file before trying to find it another way. - cross_info = state.environment.cross_info - rescomp = ExternalProgram.from_cross_info(cross_info, 'windres') + bins = state.environment.cross_info.config['binaries'] + rescomp = ExternalProgram.from_bin_list(bins, 'windres') if not rescomp or not rescomp.found(): if 'WINDRES' in os.environ: @@ -59,6 +59,13 @@ class WindowsModule(ExtensionModule): rescomp = ExternalProgram('windres', command=os.environ.get('WINDRES'), silent=True) if not rescomp or not rescomp.found(): + # Take windres from the config file after the environment, which is + # in keeping with the expectations on unix-like OSes that + # environment variables trump config files. + bins = state.environment.config_info.binaries + rescomp = ExternalProgram.from_bin_list(bins, 'windres') + + if not rescomp or not rescomp.found(): comp = self.detect_compiler(state.compilers) if comp.id == 'msvc' or comp.id == 'clang-cl': rescomp = ExternalProgram('rc', silent=True) diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index ce03f43..f9a5e1c 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -29,6 +29,10 @@ def add_arguments(parser): coredata.register_builtin_arguments(parser) parser.add_argument('--cross-file', default=None, help='File describing cross compilation environment.') + parser.add_argument('--native-file', + default=[], + action='append', + help='File containing overrides for native compilation environment.') parser.add_argument('-v', '--version', action='version', version=coredata.version) parser.add_argument('--profile-self', action='store_true', dest='profile', diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index cee4c5a..a5a3626 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -144,6 +144,8 @@ class TestResult(enum.Enum): TIMEOUT = 'TIMEOUT' SKIP = 'SKIP' FAIL = 'FAIL' + EXPECTEDFAIL = 'EXPECTEDFAIL' + UNEXPECTEDPASS = 'UNEXPECTEDPASS' class TestRun: @@ -389,10 +391,10 @@ class SingleTestRunner: res = TestResult.TIMEOUT elif p.returncode == GNU_SKIP_RETURNCODE: res = TestResult.SKIP - elif self.test.should_fail == bool(p.returncode): - res = TestResult.OK + elif self.test.should_fail: + res = TestResult.EXPECTEDFAIL if bool(p.returncode) else TestResult.UNEXPECTEDPASS else: - res = TestResult.FAIL + res = TestResult.FAIL if bool(p.returncode) else TestResult.OK return TestRun(res, p.returncode, self.test.should_fail, duration, stdo, stde, cmd, self.test.env) @@ -401,6 +403,8 @@ class TestHarness: self.options = options self.collected_logs = [] self.fail_count = 0 + self.expectedfail_count = 0 + self.unexpectedpass_count = 0 self.success_count = 0 self.skip_count = 0 self.timeout_count = 0 @@ -446,6 +450,8 @@ class TestHarness: def get_test_runner(self, test): options = deepcopy(self.options) + if not options.setup: + options.setup = self.build_data.test_setup_default_name if options.setup: env = self.merge_suite_options(options, test) else: @@ -465,6 +471,10 @@ class TestHarness: self.success_count += 1 elif result.res is TestResult.FAIL: self.fail_count += 1 + elif result.res is TestResult.EXPECTEDFAIL: + self.expectedfail_count += 1 + elif result.res is TestResult.UNEXPECTEDPASS: + self.unexpectedpass_count += 1 else: sys.exit('Unknown test result encountered: {}'.format(result.res)) @@ -480,9 +490,10 @@ class TestHarness: result_str = '%s %s %s%s%s%5.2f s %s' % \ (num, name, padding1, result.res.value, padding2, result.duration, status) - if not self.options.quiet or result.res is not TestResult.OK: - if result.res is not TestResult.OK and mlog.colorize_console: - if result.res in (TestResult.FAIL, TestResult.TIMEOUT): + ok_statuses = (TestResult.OK, TestResult.EXPECTEDFAIL) + if not self.options.quiet or result.res not in ok_statuses: + if result.res not in ok_statuses and mlog.colorize_console: + if result.res in (TestResult.FAIL, TestResult.TIMEOUT, TestResult.UNEXPECTEDPASS): decorator = mlog.red elif result.res is TestResult.SKIP: decorator = mlog.yellow @@ -503,11 +514,14 @@ class TestHarness: def print_summary(self): msg = ''' -OK: %4d -FAIL: %4d -SKIP: %4d -TIMEOUT: %4d -''' % (self.success_count, self.fail_count, self.skip_count, self.timeout_count) +Ok: %4d +Expected Fail: %4d +Fail: %4d +Unexpected Pass: %4d +Skipped: %4d +Timeout: %4d +''' % (self.success_count, self.expectedfail_count, self.fail_count, + self.unexpectedpass_count, self.skip_count, self.timeout_count) print(msg) if self.logfile: self.logfile.write(msg) diff --git a/run_meson_command_tests.py b/run_meson_command_tests.py index 6dc7964..e7eab72 100755 --- a/run_meson_command_tests.py +++ b/run_meson_command_tests.py @@ -63,15 +63,14 @@ class CommandTests(unittest.TestCase): def _run(self, command, workdir=None): ''' - Run a command while printing the stdout and stderr to stdout, - and also return a copy of it + Run a command while printing the stdout, and also return a copy of it ''' # If this call hangs CI will just abort. It is very hard to distinguish # between CI issue and test bug in that case. Set timeout and fail loud # instead. p = subprocess.run(command, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, env=os.environ.copy(), - universal_newlines=True, cwd=workdir, timeout=60 * 5) + env=os.environ.copy(), universal_newlines=True, + cwd=workdir, timeout=60 * 5) print(p.stdout) if p.returncode != 0: raise subprocess.CalledProcessError(p.returncode, command) diff --git a/run_project_tests.py b/run_project_tests.py index c73567e..0d64f47 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -118,10 +118,25 @@ def get_relative_files_list_from_dir(fromdir): return paths def platform_fix_name(fname, compiler, env): + # canonicalize compiler + if compiler == 'clang-cl': + canonical_compiler = 'msvc' + else: + canonical_compiler = compiler + if '?lib' in fname: - if mesonlib.for_cygwin(env.is_cross_build(), env): + if mesonlib.for_windows(env.is_cross_build(), env) and canonical_compiler == 'msvc': + fname = re.sub(r'lib/\?lib(.*)\.', r'bin/\1.', fname) + fname = re.sub(r'/\?lib/', r'/bin/', fname) + elif mesonlib.for_windows(env.is_cross_build(), env): + fname = re.sub(r'lib/\?lib(.*)\.', r'bin/lib\1.', fname) + fname = re.sub(r'\?lib(.*)\.dll$', r'lib\1.dll', fname) + fname = re.sub(r'/\?lib/', r'/bin/', fname) + elif mesonlib.for_cygwin(env.is_cross_build(), env): fname = re.sub(r'lib/\?lib(.*)\.so$', r'bin/cyg\1.dll', fname) + fname = re.sub(r'lib/\?lib(.*)\.', r'bin/cyg\1.', fname) fname = re.sub(r'\?lib(.*)\.dll$', r'cyg\1.dll', fname) + fname = re.sub(r'/\?lib/', r'/bin/', fname) else: fname = re.sub(r'\?lib', 'lib', fname) @@ -132,17 +147,47 @@ def platform_fix_name(fname, compiler, env): if fname.startswith('?msvc:'): fname = fname[6:] - if compiler != 'msvc': + if canonical_compiler != 'msvc': return None if fname.startswith('?gcc:'): fname = fname[5:] - if compiler == 'msvc': + if canonical_compiler == 'msvc': return None if fname.startswith('?cygwin:'): fname = fname[8:] - if compiler == 'msvc' or not mesonlib.for_cygwin(env.is_cross_build(), env): + if not mesonlib.for_cygwin(env.is_cross_build(), env): + return None + + if fname.endswith('?so'): + if mesonlib.for_windows(env.is_cross_build(), env) and canonical_compiler == 'msvc': + fname = re.sub(r'lib/([^/]*)\?so$', r'bin/\1.dll', fname) + fname = re.sub(r'/(?:lib|)([^/]*?)\?so$', r'/\1.dll', fname) + return fname + elif mesonlib.for_windows(env.is_cross_build(), env): + fname = re.sub(r'lib/([^/]*)\?so$', r'bin/\1.dll', fname) + fname = re.sub(r'/([^/]*?)\?so$', r'/\1.dll', fname) + return fname + elif mesonlib.for_cygwin(env.is_cross_build(), env): + fname = re.sub(r'lib/([^/]*)\?so$', r'bin/\1.dll', fname) + fname = re.sub(r'/lib([^/]*?)\?so$', r'/cyg\1.dll', fname) + fname = re.sub(r'/([^/]*?)\?so$', r'/\1.dll', fname) + return fname + elif mesonlib.for_darwin(env.is_cross_build(), env): + return fname[:-3] + '.dylib' + else: + return fname[:-3] + '.so' + + if fname.endswith('?implib') or fname.endswith('?implibempty'): + if mesonlib.for_windows(env.is_cross_build(), env) and canonical_compiler == 'msvc': + # only MSVC doesn't generate empty implibs + if fname.endswith('?implibempty') and compiler == 'msvc': + return None + return re.sub(r'/(?:lib|)([^/]*?)\?implib(?:empty|)$', r'/\1.lib', fname) + elif mesonlib.for_windows(env.is_cross_build(), env) or mesonlib.for_cygwin(env.is_cross_build(), env): + return re.sub(r'\?implib(?:empty|)$', r'.dll.a', fname) + else: return None return fname @@ -696,10 +741,6 @@ def detect_system_compiler(): raise RuntimeError("Could not find C compiler.") system_compiler = comp.get_id() - # canonicalize for platform_fix_name() - if system_compiler == 'clang-cl': - system_compiler = 'msvc' - if __name__ == '__main__': parser = argparse.ArgumentParser(description="Run the test suite of Meson.") parser.add_argument('extra_args', nargs='*', diff --git a/run_tests.py b/run_tests.py index 3445e30..ebee602 100755 --- a/run_tests.py +++ b/run_tests.py @@ -73,6 +73,7 @@ def get_fake_options(prefix): opts.wrap_mode = None opts.prefix = prefix opts.cmd_line_options = {} + opts.native_file = [] return opts def get_fake_env(sdir, bdir, prefix): diff --git a/run_unittests.py b/run_unittests.py index d63a961..bc11732 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -26,6 +26,7 @@ import sys import unittest import platform import pickle +import functools from itertools import chain from unittest import mock from configparser import ConfigParser @@ -41,7 +42,7 @@ import mesonbuild.modules.gnome from mesonbuild.interpreter import Interpreter, ObjectHolder from mesonbuild.mesonlib import ( is_windows, is_osx, is_cygwin, is_dragonflybsd, is_openbsd, is_haiku, - windows_proof_rmtree, python_command, version_compare, + is_linux, windows_proof_rmtree, python_command, version_compare, BuildDirLock, Version ) from mesonbuild.environment import detect_ninja @@ -108,12 +109,41 @@ def skipIfNoPkgconfig(f): Note: Yes, we provide pkg-config even while running Windows CI ''' + @functools.wraps(f) def wrapped(*args, **kwargs): if not is_ci() and shutil.which('pkg-config') is None: raise unittest.SkipTest('pkg-config not found') return f(*args, **kwargs) return wrapped +def skip_if_not_language(lang): + def wrapper(func): + @functools.wraps(func) + def wrapped(*args, **kwargs): + try: + env = get_fake_env('', '', '') + f = getattr(env, 'detect_{}_compiler'.format(lang)) + if lang in ['cs', 'vala', 'java', 'swift']: + f() + else: + f(False) + except EnvironmentException: + raise unittest.SkipTest('No {} compiler found.'.format(lang)) + return func(*args, **kwargs) + return wrapped + return wrapper + +def skip_if_env_value(value): + def wrapper(func): + @functools.wraps(func) + def wrapped(*args, **kwargs): + if value in os.environ: + raise unittest.SkipTest( + 'Environment variable "{}" set, skipping.'.format(value)) + return func(*args, **kwargs) + return wrapped + return wrapper + class PatchModule: ''' Fancy monkey-patching! Whee! Can't use mock.patch because it only @@ -921,11 +951,11 @@ class BasePlatformTests(unittest.TestCase): # Misc stuff self.orig_env = os.environ.copy() if self.backend is Backend.ninja: - self.no_rebuild_stdout = 'ninja: no work to do.' + self.no_rebuild_stdout = ['ninja: no work to do.', 'samu: nothing to do'] else: # VS doesn't have a stable output when no changes are done # XCode backend is untested with unit tests, help welcome! - self.no_rebuild_stdout = 'UNKNOWN BACKEND {!r}'.format(self.backend.name) + self.no_rebuild_stdout = ['UNKNOWN BACKEND {!r}'.format(self.backend.name)] self.builddirs = [] self.new_builddir() @@ -1076,8 +1106,11 @@ class BasePlatformTests(unittest.TestCase): def get_compdb(self): if self.backend is not Backend.ninja: raise unittest.SkipTest('Compiler db not available with {} backend'.format(self.backend.name)) - with open(os.path.join(self.builddir, 'compile_commands.json')) as ifile: - contents = json.load(ifile) + try: + with open(os.path.join(self.builddir, 'compile_commands.json')) as ifile: + contents = json.load(ifile) + except FileNotFoundError: + raise unittest.SkipTest('Compiler db not found') # If Ninja is using .rsp files, generate them, read their contents, and # replace it as the command for all compile commands in the parsed json. if len(contents) > 0 and contents[0]['command'].endswith('.rsp'): @@ -1131,7 +1164,7 @@ class BasePlatformTests(unittest.TestCase): def assertBuildIsNoop(self): ret = self.build() if self.backend is Backend.ninja: - self.assertEqual(ret.split('\n')[-2], self.no_rebuild_stdout) + self.assertIn(ret.split('\n')[-2], self.no_rebuild_stdout) elif self.backend is Backend.vs: # Ensure that some target said that no rebuild was done self.assertIn('CustomBuild:\n All outputs are up-to-date.', ret) @@ -1461,6 +1494,38 @@ class AllPlatformTests(BasePlatformTests): self.assertRaises(subprocess.CalledProcessError, self._run, self.mtest_command + ['--setup=main:onlyinbar']) + def test_testsetup_default(self): + testdir = os.path.join(self.unit_test_dir, '47 testsetup default') + self.init(testdir) + self.build() + + # Run tests without --setup will cause the default setup to be used + self.run_tests() + with open(os.path.join(self.logdir, 'testlog.txt')) as f: + default_log = f.read() + + # Run tests with explicitly using the same setup that is set as default + self._run(self.mtest_command + ['--setup=mydefault']) + with open(os.path.join(self.logdir, 'testlog-mydefault.txt')) as f: + mydefault_log = f.read() + + # Run tests with another setup + self._run(self.mtest_command + ['--setup=other']) + with open(os.path.join(self.logdir, 'testlog-other.txt')) as f: + other_log = f.read() + + self.assertTrue('ENV_A is 1' in default_log) + self.assertTrue('ENV_B is 2' in default_log) + self.assertTrue('ENV_C is 2' in default_log) + + self.assertTrue('ENV_A is 1' in mydefault_log) + self.assertTrue('ENV_B is 2' in mydefault_log) + self.assertTrue('ENV_C is 2' in mydefault_log) + + self.assertTrue('ENV_A is 1' in other_log) + self.assertTrue('ENV_B is 3' in other_log) + self.assertTrue('ENV_C is 2' in other_log) + def assertFailedTestCount(self, failure_count, command): try: self._run(command) @@ -4411,6 +4476,273 @@ class RewriterTests(unittest.TestCase): self.assertEqual(s2, self.read_contents('sub2/meson.build')) +class NativeFileTests(BasePlatformTests): + + def setUp(self): + super().setUp() + self.testcase = os.path.join(self.unit_test_dir, '46 native file binary') + self.current_config = 0 + self.current_wrapper = 0 + + def helper_create_native_file(self, values): + """Create a config file as a temporary file. + + values should be a nested dictionary structure of {section: {key: + value}} + """ + filename = os.path.join(self.builddir, 'generated{}.config'.format(self.current_config)) + self.current_config += 1 + with open(filename, 'wt') as f: + for section, entries in values.items(): + f.write('[{}]\n'.format(section)) + for k, v in entries.items(): + f.write("{}='{}'\n".format(k, v)) + return filename + + def helper_create_binary_wrapper(self, binary, **kwargs): + """Creates a wrapper around a binary that overrides specific values.""" + filename = os.path.join(self.builddir, 'binary_wrapper{}.py'.format(self.current_wrapper)) + self.current_wrapper += 1 + if is_haiku(): + chbang = '#!/bin/env python3' + else: + chbang = '#!/usr/bin/env python3' + + with open(filename, 'wt') as f: + f.write(textwrap.dedent('''\ + {} + import argparse + import subprocess + import sys + + def main(): + parser = argparse.ArgumentParser() + '''.format(chbang))) + for name in kwargs: + f.write(' parser.add_argument("-{0}", "--{0}", action="store_true")\n'.format(name)) + f.write(' args, extra_args = parser.parse_known_args()\n') + for name, value in kwargs.items(): + f.write(' if args.{}:\n'.format(name)) + f.write(' print("{}", file=sys.{})\n'.format(value, kwargs.get('outfile', 'stdout'))) + f.write(' sys.exit(0)\n') + f.write(textwrap.dedent(''' + ret = subprocess.run( + ["{}"] + extra_args, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding='utf-8') + print(ret.stdout) + print(ret.stderr, file=sys.stderr) + sys.exit(ret.returncode) + + if __name__ == '__main__': + main() + '''.format(binary))) + + if not is_windows(): + os.chmod(filename, 0o755) + return filename + + # On windows we need yet another level of indirection, as cmd cannot + # invoke python files itself, so instead we generate a .bat file, which + # invokes our python wrapper + batfile = os.path.join(self.builddir, 'binary_wrapper{}.bat'.format(self.current_wrapper)) + with open(batfile, 'wt') as f: + f.write('py -3 {} %*'.format(filename)) + return batfile + + def helper_for_compiler(self, lang, cb): + """Helper for generating tests for overriding compilers for langaugages + with more than one implementation, such as C, C++, ObjC, ObjC++, and D. + """ + env = get_fake_env('', '', '') + getter = getattr(env, 'detect_{}_compiler'.format(lang)) + if lang not in ['cs']: + getter = functools.partial(getter, False) + cc = getter() + binary, newid = cb(cc) + env.config_info.binaries = {lang: binary} + compiler = getter() + self.assertEqual(compiler.id, newid) + + def test_multiple_native_files_override(self): + wrapper = self.helper_create_binary_wrapper('bash', version='foo') + config = self.helper_create_native_file({'binaries': {'bash': wrapper}}) + wrapper = self.helper_create_binary_wrapper('bash', version='12345') + config2 = self.helper_create_native_file({'binaries': {'bash': wrapper}}) + self.init(self.testcase, extra_args=[ + '--native-file', config, '--native-file', config2, + '-Dcase=find_program']) + + def test_multiple_native_files(self): + wrapper = self.helper_create_binary_wrapper('bash', version='12345') + config = self.helper_create_native_file({'binaries': {'bash': wrapper}}) + wrapper = self.helper_create_binary_wrapper('python') + config2 = self.helper_create_native_file({'binaries': {'python': wrapper}}) + self.init(self.testcase, extra_args=[ + '--native-file', config, '--native-file', config2, + '-Dcase=find_program']) + + def _simple_test(self, case, binary): + wrapper = self.helper_create_binary_wrapper(binary, version='12345') + config = self.helper_create_native_file({'binaries': {binary: wrapper}}) + self.init(self.testcase, extra_args=['--native-file', config, '-Dcase={}'.format(case)]) + + def test_find_program(self): + self._simple_test('find_program', 'bash') + + def test_config_tool_dep(self): + # Do the skip at this level to avoid screwing up the cache + if not shutil.which('llvm-config'): + raise unittest.SkipTest('No llvm-installed, cannot test') + self._simple_test('config_dep', 'llvm-config') + + def test_python3_module(self): + self._simple_test('python3', 'python3') + + def test_python_module(self): + if is_windows(): + # Bat adds extra crap to stdout, so the version check logic in the + # python module breaks. This is fine on other OSes because they + # don't need the extra indirection. + raise unittest.SkipTest('bat indirection breaks internal sanity checks.') + self._simple_test('python', 'python') + + @unittest.skipIf(is_windows(), 'Setting up multiple compilers on windows is hard') + @skip_if_env_value('CC') + def test_c_compiler(self): + def cb(comp): + if comp.id == 'gcc': + if not shutil.which('clang'): + raise unittest.SkipTest('Only one compiler found, cannot test.') + return 'clang', 'clang' + if not shutil.which('gcc'): + raise unittest.SkipTest('Only one compiler found, cannot test.') + return 'gcc', 'gcc' + self.helper_for_compiler('c', cb) + + @unittest.skipIf(is_windows(), 'Setting up multiple compilers on windows is hard') + @skip_if_env_value('CXX') + def test_cpp_compiler(self): + def cb(comp): + if comp.id == 'gcc': + if not shutil.which('clang++'): + raise unittest.SkipTest('Only one compiler found, cannot test.') + return 'clang++', 'clang' + if not shutil.which('g++'): + raise unittest.SkipTest('Only one compiler found, cannot test.') + return 'g++', 'gcc' + self.helper_for_compiler('cpp', cb) + + @skip_if_not_language('objc') + @skip_if_env_value('OBJC') + def test_objc_compiler(self): + def cb(comp): + if comp.id == 'gcc': + if not shutil.which('clang'): + raise unittest.SkipTest('Only one compiler found, cannot test.') + return 'clang', 'clang' + if not shutil.which('gcc'): + raise unittest.SkipTest('Only one compiler found, cannot test.') + return 'gcc', 'gcc' + self.helper_for_compiler('objc', cb) + + @skip_if_not_language('objcpp') + @skip_if_env_value('OBJCXX') + def test_objcpp_compiler(self): + def cb(comp): + if comp.id == 'gcc': + if not shutil.which('clang++'): + raise unittest.SkipTest('Only one compiler found, cannot test.') + return 'clang++', 'clang' + if not shutil.which('g++'): + raise unittest.SkipTest('Only one compiler found, cannot test.') + return 'g++', 'gcc' + self.helper_for_compiler('objcpp', cb) + + @skip_if_not_language('d') + @skip_if_env_value('DC') + def test_d_compiler(self): + def cb(comp): + if comp.id == 'dmd': + if shutil.which('ldc'): + return 'ldc', 'ldc' + elif shutil.which('gdc'): + return 'gdc', 'gdc' + else: + raise unittest.SkipTest('No alternative dlang compiler found.') + return 'dmd', 'dmd' + self.helper_for_compiler('d', cb) + + @skip_if_not_language('cs') + @skip_if_env_value('CSC') + def test_cs_compiler(self): + def cb(comp): + if comp.id == 'csc': + if not shutil.which('mcs'): + raise unittest.SkipTest('No alternate C# implementation.') + return 'mcs', 'mcs' + if not shutil.which('csc'): + raise unittest.SkipTest('No alternate C# implementation.') + return 'csc', 'csc' + self.helper_for_compiler('cs', cb) + + @skip_if_not_language('fortran') + @skip_if_env_value('FC') + def test_fortran_compiler(self): + def cb(comp): + if comp.id == 'gcc': + if shutil.which('ifort'): + return 'ifort', 'intel' + # XXX: there are several other fortran compilers meson + # supports, but I don't have any of them to test with + raise unittest.SkipTest('No alternate Fortran implementation.') + if not shutil.which('gfortran'): + raise unittest.SkipTest('No alternate C# implementation.') + return 'gfortran', 'gcc' + self.helper_for_compiler('fortran', cb) + + def _single_implementation_compiler(self, lang, binary, version_str, version): + """Helper for languages with a single (supported) implementation. + + Builds a wrapper around the compiler to override the version. + """ + wrapper = self.helper_create_binary_wrapper(binary, version=version_str) + env = get_fake_env('', '', '') + getter = getattr(env, 'detect_{}_compiler'.format(lang)) + if lang in ['rust']: + getter = functools.partial(getter, False) + env.config_info.binaries = {lang: wrapper} + compiler = getter() + self.assertEqual(compiler.version, version) + + @skip_if_not_language('vala') + @skip_if_env_value('VALAC') + def test_vala_compiler(self): + self._single_implementation_compiler( + 'vala', 'valac', 'Vala 1.2345', '1.2345') + + @skip_if_not_language('rust') + @skip_if_env_value('RUSTC') + def test_rust_compiler(self): + self._single_implementation_compiler( + 'rust', 'rustc', 'rustc 1.2345', '1.2345') + + @skip_if_not_language('java') + def test_java_compiler(self): + self._single_implementation_compiler( + 'java', 'javac', 'javac 9.99.77', '9.99.77') + + @skip_if_not_language('swift') + def test_swift_compiler(self): + wrapper = self.helper_create_binary_wrapper( + 'swiftc', version='Swift 1.2345', outfile='stderr') + env = get_fake_env('', '', '') + env.config_info.binaries = {'swift': wrapper} + compiler = env.detect_swift_compiler() + self.assertEqual(compiler.version, '1.2345') + + def unset_envs(): # For unit tests we must fully control all command lines # so that there are no unexpected changes coming from the @@ -4428,7 +4760,8 @@ def should_run_cross_mingw_tests(): def main(): unset_envs() - cases = ['InternalTests', 'DataTests', 'AllPlatformTests', 'FailureTests', 'PythonTests'] + cases = ['InternalTests', 'DataTests', 'AllPlatformTests', 'FailureTests', + 'PythonTests', 'NativeFileTests'] if not is_windows(): cases += ['LinuxlikeTests'] if should_run_cross_arm_tests(): diff --git a/test cases/common/122 shared module/installed_files.txt b/test cases/common/122 shared module/installed_files.txt index 4542a55..d46527c 100644 --- a/test cases/common/122 shared module/installed_files.txt +++ b/test cases/common/122 shared module/installed_files.txt @@ -1,2 +1,3 @@ -usr/lib/libnosyms.so -?msvc:usr/lib/libnosyms.pdb +usr/lib/modules/libnosyms?so +usr/lib/modules/libnosyms?implibempty +?msvc:usr/lib/modules/nosyms.pdb diff --git a/test cases/common/122 shared module/meson.build b/test cases/common/122 shared module/meson.build index 9f9ad63..3d52300 100644 --- a/test cases/common/122 shared module/meson.build +++ b/test cases/common/122 shared module/meson.build @@ -13,8 +13,6 @@ e = executable('prog', 'prog.c', test('import test', e, args : m) # Shared module that does not export any symbols -shared_module('nosyms', 'nosyms.c', install : true, - # Because we don't have cross-platform library support in - # installed_files.txt - name_suffix : 'so', - name_prefix : 'lib') +shared_module('nosyms', 'nosyms.c', + install : true, + install_dir : join_paths(get_option('libdir'), 'modules')) diff --git a/test cases/common/152 simd/simd_sse2.c b/test cases/common/152 simd/simd_sse2.c index 0274533..271022e 100644 --- a/test cases/common/152 simd/simd_sse2.c +++ b/test cases/common/152 simd/simd_sse2.c @@ -21,7 +21,7 @@ int sse2_available() { #endif void increment_sse2(float arr[4]) { - double darr[4]; + ALIGN_16 double darr[4]; __m128d val1 = _mm_set_pd(arr[0], arr[1]); __m128d val2 = _mm_set_pd(arr[2], arr[3]); __m128d one = _mm_set_pd(1.0, 1.0); diff --git a/test cases/common/152 simd/simd_sse3.c b/test cases/common/152 simd/simd_sse3.c index e97d102..89c2f8b 100644 --- a/test cases/common/152 simd/simd_sse3.c +++ b/test cases/common/152 simd/simd_sse3.c @@ -22,7 +22,7 @@ int sse3_available() { #endif void increment_sse3(float arr[4]) { - double darr[4]; + ALIGN_16 double darr[4]; __m128d val1 = _mm_set_pd(arr[0], arr[1]); __m128d val2 = _mm_set_pd(arr[2], arr[3]); __m128d one = _mm_set_pd(1.0, 1.0); diff --git a/test cases/common/152 simd/simd_sse41.c b/test cases/common/152 simd/simd_sse41.c index 0308c7e..859fb43 100644 --- a/test cases/common/152 simd/simd_sse41.c +++ b/test cases/common/152 simd/simd_sse41.c @@ -24,7 +24,7 @@ int sse41_available() { #endif void increment_sse41(float arr[4]) { - double darr[4]; + ALIGN_16 double darr[4]; __m128d val1 = _mm_set_pd(arr[0], arr[1]); __m128d val2 = _mm_set_pd(arr[2], arr[3]); __m128d one = _mm_set_pd(1.0, 1.0); diff --git a/test cases/common/152 simd/simd_sse42.c b/test cases/common/152 simd/simd_sse42.c index 137ffc4..edd6e5b 100644 --- a/test cases/common/152 simd/simd_sse42.c +++ b/test cases/common/152 simd/simd_sse42.c @@ -27,7 +27,7 @@ int sse42_available() { #endif void increment_sse42(float arr[4]) { - double darr[4]; + ALIGN_16 double darr[4]; __m128d val1 = _mm_set_pd(arr[0], arr[1]); __m128d val2 = _mm_set_pd(arr[2], arr[3]); __m128d one = _mm_set_pd(1.0, 1.0); diff --git a/test cases/common/152 simd/simd_ssse3.c b/test cases/common/152 simd/simd_ssse3.c index ab4dff4..0156f77 100644 --- a/test cases/common/152 simd/simd_ssse3.c +++ b/test cases/common/152 simd/simd_ssse3.c @@ -30,7 +30,7 @@ int ssse3_available() { #endif void increment_ssse3(float arr[4]) { - double darr[4]; + ALIGN_16 double darr[4]; __m128d val1 = _mm_set_pd(arr[0], arr[1]); __m128d val2 = _mm_set_pd(arr[2], arr[3]); __m128d one = _mm_set_pd(1.0, 1.0); diff --git a/test cases/common/152 simd/simdchecker.c b/test cases/common/152 simd/simdchecker.c index 222fbf3..cd6fe4f 100644 --- a/test cases/common/152 simd/simdchecker.c +++ b/test cases/common/152 simd/simdchecker.c @@ -1,93 +1,143 @@ #include<simdfuncs.h> #include<stdio.h> +#include<string.h> -/* - * A function that checks at runtime which simd accelerations are - * available and calls the best one. Falls - * back to plain C implementation if SIMD is not available. - */ +typedef void (*simd_func)(float*); + +int check_simd_implementation(float *four, + const float *four_initial, + const char *simd_type, + const float *expected, + simd_func fptr, + const int blocksize) { + int rv = 0; + memcpy(four, four_initial, blocksize*sizeof(float)); + printf("Using %s.\n", simd_type); + fptr(four); + for(int i=0; i<blocksize; i++) { + if(four[i] != expected[i]) { + printf("Increment function failed, got %f expected %f.\n", four[i], expected[i]); + rv = 1; + } + } + return rv; +} int main(int argc, char **argv) { - float four[4] = {2.0, 3.0, 4.0, 5.0}; + static const float four_initial[4] = {2.0, 3.0, 4.0, 5.0}; + ALIGN_16 float four[4]; const float expected[4] = {3.0, 4.0, 5.0, 6.0}; - void (*fptr)(float[4]) = NULL; - const char *type; - int i; + int r=0; + const int blocksize = 4; -/* Add here. The first matched one is used so put "better" instruction - * sets at the top. +/* + * Test all implementations that the current CPU supports. */ #if HAVE_NEON - if(fptr == NULL && neon_available()) { - fptr = increment_neon; - type = "NEON"; + if(neon_available()) { + r += check_simd_implementation(four, + four_initial, + "NEON", + expected, + increment_neon, + blocksize); } #endif #if HAVE_AVX2 - if(fptr == NULL && avx2_available()) { - fptr = increment_avx2; - type = "AVX2"; + if(avx2_available()) { + r += check_simd_implementation(four, + four_initial, + "AVX2", + expected, + increment_avx2, + blocksize); } #endif #if HAVE_AVX - if(fptr == NULL && avx_available()) { - fptr = increment_avx; - type = "AVX"; + if(avx_available()) { + r += check_simd_implementation(four, + four_initial, + "AVC", + expected, + increment_avx, + blocksize); } #endif #if HAVE_SSE42 - if(fptr == NULL && sse42_available()) { - fptr = increment_sse42; - type = "SSE42"; + if(sse42_available()) { + r += check_simd_implementation(four, + four_initial, + "SSR42", + expected, + increment_sse42, + blocksize); } #endif #if HAVE_SSE41 - if(fptr == NULL && sse41_available()) { - fptr = increment_sse41; - type = "SSE41"; + if(sse41_available()) { + r += check_simd_implementation(four, + four_initial, + "SSE41", + expected, + increment_sse41, + blocksize); } #endif #if HAVE_SSSE3 - if(fptr == NULL && ssse3_available()) { - fptr = increment_ssse3; - type = "SSSE3"; + if(ssse3_available()) { + r += check_simd_implementation(four, + four_initial, + "SSSE3", + expected, + increment_ssse3, + blocksize); } #endif #if HAVE_SSE3 - if(fptr == NULL && sse3_available()) { - fptr = increment_sse3; - type = "SSE3"; + if(sse3_available()) { + r += check_simd_implementation(four, + four_initial, + "SSE3", + expected, + increment_sse3, + blocksize); } #endif #if HAVE_SSE2 - if(fptr == NULL && sse2_available()) { - fptr = increment_sse2; - type = "SSE2"; + if(sse2_available()) { + r += check_simd_implementation(four, + four_initial, + "SSE2", + expected, + increment_sse2, + blocksize); } #endif #if HAVE_SSE - if(fptr == NULL && sse_available()) { - fptr = increment_sse; - type = "SSE"; + if(sse_available()) { + r += check_simd_implementation(four, + four_initial, + "SSE", + expected, + increment_sse, + blocksize); } #endif #if HAVE_MMX - if(fptr == NULL && mmx_available()) { - fptr = increment_mmx; - type = "MMX"; + if(mmx_available()) { + r += check_simd_implementation(four, + four_initial, + "MMX", + expected, + increment_mmx, + blocksize); } #endif - if(fptr == NULL) { - fptr = increment_fallback; - type = "fallback"; - } - printf("Using %s.\n", type); - fptr(four); - for(i=0; i<4; i++) { - if(four[i] != expected[i]) { - printf("Increment function failed, got %f expected %f.\n", four[i], expected[i]); - return 1; - } - } - return 0; + r += check_simd_implementation(four, + four_initial, + "fallback", + expected, + increment_fallback, + blocksize); + return r; } diff --git a/test cases/common/152 simd/simdfuncs.h b/test cases/common/152 simd/simdfuncs.h index dfb0560..c5e1658 100644 --- a/test cases/common/152 simd/simdfuncs.h +++ b/test cases/common/152 simd/simdfuncs.h @@ -2,6 +2,14 @@ #include<simdconfig.h> +#ifdef _MSC_VER +#define ALIGN_16 __declspec(align(16)) +#else +#include<stdalign.h> +#define ALIGN_16 alignas(16) +#endif + + /* Yes, I do know that arr[4] decays into a pointer * as a function argument. Don't do this in real code * but for this test it is ok. diff --git a/test cases/common/207 install name_prefix name_suffix/installed_files.txt b/test cases/common/207 install name_prefix name_suffix/installed_files.txt new file mode 100644 index 0000000..240a8be --- /dev/null +++ b/test cases/common/207 install name_prefix name_suffix/installed_files.txt @@ -0,0 +1,15 @@ +?msvc:usr/bin/baz.pdb +?msvc:usr/bin/bowcorge.pdb +?msvc:usr/bin/foo.pdb +?msvc:usr/lib/baz.pdb +?msvc:usr/lib/bowcorge.pdb +?msvc:usr/lib/foo.pdb +usr/?lib/bowcorge.stern +usr/lib/?libbaz.cheese +usr/lib/bar.a +usr/lib/bowcorge?implib +usr/lib/bowgrault.stern +usr/lib/foo?implib +usr/lib/foo?so +usr/lib/libbaz?implib +usr/lib/libqux.cheese diff --git a/test cases/common/207 install name_prefix name_suffix/libfile.c b/test cases/common/207 install name_prefix name_suffix/libfile.c new file mode 100644 index 0000000..44f7667 --- /dev/null +++ b/test cases/common/207 install name_prefix name_suffix/libfile.c @@ -0,0 +1,14 @@ +#if defined _WIN32 || defined __CYGWIN__ + #define DLL_PUBLIC __declspec(dllexport) +#else + #if defined __GNUC__ + #define DLL_PUBLIC __attribute__ ((visibility("default"))) + #else + #pragma message ("Compiler does not support symbol visibility.") + #define DLL_PUBLIC + #endif +#endif + +int DLL_PUBLIC func() { + return 0; +} diff --git a/test cases/common/207 install name_prefix name_suffix/meson.build b/test cases/common/207 install name_prefix name_suffix/meson.build new file mode 100644 index 0000000..4539999 --- /dev/null +++ b/test cases/common/207 install name_prefix name_suffix/meson.build @@ -0,0 +1,10 @@ +project('library with name_prefix name_suffix test', 'c') + +shared_library('foo', 'libfile.c', name_prefix: '', install : true) +static_library('bar', 'libfile.c', name_prefix: '', install : true) + +shared_library('baz', 'libfile.c', name_suffix: 'cheese', install : true) +static_library('qux', 'libfile.c', name_suffix: 'cheese', install : true) + +shared_library('corge', 'libfile.c', name_prefix: 'bow', name_suffix: 'stern', install : true) +static_library('grault', 'libfile.c', name_prefix: 'bow', name_suffix: 'stern', install : true) diff --git a/test cases/common/25 library versions/installed_files.txt b/test cases/common/25 library versions/installed_files.txt index c842ed8..938e063 100644 --- a/test cases/common/25 library versions/installed_files.txt +++ b/test cases/common/25 library versions/installed_files.txt @@ -1,2 +1,3 @@ usr/lib/prefixsomelib.suffix +usr/lib/prefixsomelib?implib ?msvc:usr/lib/prefixsomelib.pdb diff --git a/test cases/common/25 library versions/lib.c b/test cases/common/25 library versions/lib.c index 67b6f4d..10019dc 100644 --- a/test cases/common/25 library versions/lib.c +++ b/test cases/common/25 library versions/lib.c @@ -1,3 +1,14 @@ -int myFunc() { +#if defined _WIN32 || defined __CYGWIN__ + #define DLL_PUBLIC __declspec(dllexport) +#else + #if defined __GNUC__ + #define DLL_PUBLIC __attribute__ ((visibility("default"))) + #else + #pragma message ("Compiler does not support symbol visibility.") + #define DLL_PUBLIC + #endif +#endif + +int DLL_PUBLIC myFunc() { return 55; } diff --git a/test cases/java/8 codegen custom target/com/mesonbuild/meson.build b/test cases/java/8 codegen custom target/com/mesonbuild/meson.build index 67b98a4..0309941 100644 --- a/test cases/java/8 codegen custom target/com/mesonbuild/meson.build +++ b/test cases/java/8 codegen custom target/com/mesonbuild/meson.build @@ -4,5 +4,5 @@ config_file = custom_target('confgen', input : 'Config.java.in', output : 'Config.java', command : [python, '-c', - 'import shutil; import sys; shutil.copy(sys.argv[1], sys.argv[2])', + 'import shutil, sys, time; time.sleep(1); shutil.copy(sys.argv[1], sys.argv[2])', '@INPUT@', '@OUTPUT@']) diff --git a/test cases/unit/46 native file binary/meson.build b/test cases/unit/46 native file binary/meson.build new file mode 100644 index 0000000..4489ac1 --- /dev/null +++ b/test cases/unit/46 native file binary/meson.build @@ -0,0 +1,21 @@ +project('test project') + +case = get_option('case') + +if case == 'find_program' + prog = find_program('bash') + result = run_command(prog, ['--version']) + assert(result.stdout().strip().endswith('12345'), 'Didn\'t load bash from config file') +elif case == 'config_dep' + add_languages('cpp') + dep = dependency('llvm') + assert(dep.get_configtool_variable('version').endswith('12345'), 'Didn\'t load llvm from config file') +elif case == 'python3' + prog = import('python3').find_python() + result = run_command(prog, ['--version']) + assert(result.stdout().strip().endswith('12345'), 'Didn\'t load python3 from config file') +elif case == 'python' + prog = import('python').find_installation() + result = run_command(prog, ['--version']) + assert(result.stdout().strip().endswith('12345'), 'Didn\'t load python from config file') +endif diff --git a/test cases/unit/46 native file binary/meson_options.txt b/test cases/unit/46 native file binary/meson_options.txt new file mode 100644 index 0000000..651da0e --- /dev/null +++ b/test cases/unit/46 native file binary/meson_options.txt @@ -0,0 +1,5 @@ +option( + 'case', + type : 'combo', + choices : ['find_program', 'config_dep', 'python3', 'python'] +) diff --git a/test cases/unit/47 testsetup default/envcheck.py b/test cases/unit/47 testsetup default/envcheck.py new file mode 100644 index 0000000..6ba3093 --- /dev/null +++ b/test cases/unit/47 testsetup default/envcheck.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 + +import os + +assert('ENV_A' in os.environ) +assert('ENV_B' in os.environ) +assert('ENV_C' in os.environ) + +print('ENV_A is', os.environ['ENV_A']) +print('ENV_B is', os.environ['ENV_B']) +print('ENV_C is', os.environ['ENV_C']) diff --git a/test cases/unit/47 testsetup default/meson.build b/test cases/unit/47 testsetup default/meson.build new file mode 100644 index 0000000..bdd35b8 --- /dev/null +++ b/test cases/unit/47 testsetup default/meson.build @@ -0,0 +1,23 @@ +project('testsetup default', 'c') + +envcheck = find_program('envcheck.py') + +# Defining ENV_A in test-env should overwrite ENV_A from test setup +env_1 = environment() +env_1.set('ENV_A', '1') +test('test-env', envcheck, env: env_1) + +# Defining default env which is used unless --setup is given or the +# env variable is defined in the test. +env_2 = environment() +env_2.set('ENV_A', '2') +env_2.set('ENV_B', '2') +env_2.set('ENV_C', '2') +add_test_setup('mydefault', env: env_2, is_default: true) + +# Defining a test setup that will update some of the env variables +# from the default test setup. +env_3 = env_2 +env_3.set('ENV_A', '3') +env_3.set('ENV_B', '3') +add_test_setup('other', env: env_3) diff --git a/test cases/windows/7 dll versioning/installed_files.txt b/test cases/windows/7 dll versioning/installed_files.txt index 517620e..62b5c9a 100644 --- a/test cases/windows/7 dll versioning/installed_files.txt +++ b/test cases/windows/7 dll versioning/installed_files.txt @@ -14,9 +14,9 @@ ?msvc:usr/libexec/customdir.dll ?msvc:usr/libexec/customdir.lib ?msvc:usr/libexec/customdir.pdb -?msvc:usr/lib/module.dll -?msvc:usr/lib/module.lib -?msvc:usr/lib/module.pdb +?msvc:usr/lib/modules/module.dll +?msvc:usr/lib/modules/module.lib +?msvc:usr/lib/modules/module.pdb ?gcc:usr/bin/?libsome-0.dll ?gcc:usr/lib/libsome.dll.a ?gcc:usr/bin/?libnoversion.dll @@ -27,5 +27,5 @@ ?gcc:usr/lib/libonlysoversion.dll.a ?gcc:usr/libexec/?libcustomdir.dll ?gcc:usr/libexec/libcustomdir.dll.a -?gcc:usr/lib/?libmodule.dll -?gcc:usr/lib/libmodule.dll.a +?gcc:usr/lib/modules/?libmodule.dll +?gcc:usr/lib/modules/libmodule.dll.a diff --git a/test cases/windows/7 dll versioning/meson.build b/test cases/windows/7 dll versioning/meson.build index 80acf88..983c2c4 100644 --- a/test cases/windows/7 dll versioning/meson.build +++ b/test cases/windows/7 dll versioning/meson.build @@ -49,4 +49,6 @@ shared_library('customdir', 'lib.c', install : true, install_dir : get_option('libexecdir')) -shared_module('module', 'lib.c', install : true) +shared_module('module', 'lib.c', + install : true, + install_dir: join_paths(get_option('libdir'), 'modules')) |