aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml25
-rw-r--r--authors.txt8
-rw-r--r--ci/appveyor-install.bat11
-rwxr-xr-xci/appveyor-test.sh6
-rw-r--r--mesonbuild/backend/backends.py98
-rw-r--r--mesonbuild/backend/ninjabackend.py185
-rw-r--r--mesonbuild/backend/vs2010backend.py63
-rw-r--r--mesonbuild/build.py110
-rw-r--r--mesonbuild/compilers.py101
-rw-r--r--mesonbuild/coredata.py54
-rw-r--r--mesonbuild/dependencies.py296
-rw-r--r--mesonbuild/environment.py52
-rw-r--r--mesonbuild/interpreter.py170
-rw-r--r--mesonbuild/mesonlib.py51
-rw-r--r--mesonbuild/mlog.py13
-rw-r--r--mesonbuild/modules/gnome.py6
-rw-r--r--mesonbuild/modules/pkgconfig.py5
-rw-r--r--mesonbuild/modules/python3.py22
-rw-r--r--mesonbuild/modules/qt4.py7
-rw-r--r--mesonbuild/modules/qt5.py7
-rw-r--r--mesonbuild/modules/windows.py14
-rw-r--r--mesonbuild/mparser.py1
-rw-r--r--mesonbuild/optinterpreter.py7
-rw-r--r--mesonbuild/scripts/commandrunner.py11
-rw-r--r--mesonbuild/scripts/gtkdochelper.py2
-rw-r--r--mesonbuild/scripts/meson_exe.py6
-rw-r--r--mesonbuild/scripts/meson_install.py15
-rwxr-xr-xmesontest.py30
-rwxr-xr-xrun_project_tests.py200
-rwxr-xr-xrun_tests.py128
-rwxr-xr-xrun_unittests.py220
-rw-r--r--test cases/common/123 subproject project arguments/exe.c4
-rw-r--r--test cases/common/123 subproject project arguments/exe.cpp4
-rw-r--r--test cases/common/123 subproject project arguments/meson.build5
-rw-r--r--test cases/common/123 subproject project arguments/subprojects/subexe/subexe.c4
-rw-r--r--test cases/common/125 shared module/meson.build3
-rw-r--r--test cases/common/125 shared module/module.c17
-rw-r--r--test cases/common/126 llvm ir and assembly/square-x86_64.S4
-rw-r--r--test cases/common/135 generated assembly/main.c3
-rw-r--r--test cases/common/135 generated assembly/square-x86_64.S.in4
-rwxr-xr-xtest cases/common/139 custom target multiple outputs/generator.py14
-rw-r--r--test cases/common/139 custom target multiple outputs/installed_files.txt6
-rw-r--r--test cases/common/139 custom target multiple outputs/meson.build28
-rw-r--r--test cases/common/139 override options/four.c9
-rw-r--r--test cases/common/139 override options/meson.build8
-rw-r--r--test cases/common/139 override options/one.c3
-rw-r--r--test cases/common/139 override options/three.c7
-rw-r--r--test cases/common/139 override options/two.c6
-rw-r--r--test cases/common/140 get define/meson.build31
-rw-r--r--test cases/common/140 get define/meson_options.txt1
-rw-r--r--test cases/common/141 c cpp and asm/meson.build5
-rw-r--r--test cases/common/141 c cpp and asm/symbol-underscore.h2
-rw-r--r--test cases/common/141 mesonintrospect from scripts/check_env.py20
-rw-r--r--test cases/common/141 mesonintrospect from scripts/meson.build14
-rw-r--r--test cases/common/142 compute int/config.h.in (renamed from test cases/common/139 compute int/config.h.in)0
-rw-r--r--test cases/common/142 compute int/foobar.h (renamed from test cases/common/139 compute int/foobar.h)0
-rw-r--r--test cases/common/142 compute int/meson.build (renamed from test cases/common/139 compute int/meson.build)0
-rw-r--r--test cases/common/142 compute int/prog.c.in (renamed from test cases/common/139 compute int/prog.c.in)0
-rw-r--r--test cases/common/143 custom target object output/meson.build (renamed from test cases/common/139 custom target object output/meson.build)0
-rw-r--r--test cases/common/143 custom target object output/obj_generator.py (renamed from test cases/common/139 custom target object output/obj_generator.py)0
-rw-r--r--test cases/common/143 custom target object output/objdir/meson.build (renamed from test cases/common/139 custom target object output/objdir/meson.build)0
-rw-r--r--test cases/common/143 custom target object output/objdir/source.c (renamed from test cases/common/139 custom target object output/objdir/source.c)0
-rw-r--r--test cases/common/143 custom target object output/progdir/meson.build (renamed from test cases/common/139 custom target object output/progdir/meson.build)0
-rw-r--r--test cases/common/143 custom target object output/progdir/prog.c (renamed from test cases/common/139 custom target object output/progdir/prog.c)0
-rw-r--r--test cases/common/144 empty build file/meson.build2
-rw-r--r--test cases/common/144 empty build file/subdir/meson.build0
-rw-r--r--test cases/common/16 configure file/meson.build5
-rw-r--r--test cases/common/22 header in file list/prog.c2
-rw-r--r--test cases/common/23 global arg/meson.build2
-rw-r--r--test cases/common/23 global arg/prog.c4
-rw-r--r--test cases/common/23 global arg/prog.cc4
-rw-r--r--test cases/common/37 has header/meson.build18
-rw-r--r--test cases/common/64 custom header generator/meson.build2
-rw-r--r--test cases/common/64 custom header generator/somefile.txt0
-rw-r--r--test cases/failing/14 invalid option name/meson_options.txt2
-rw-r--r--test cases/failing/19 target clash/meson.build2
-rwxr-xr-xtest cases/failing/43 custom target outputs not matching install_dirs/generator.py16
-rw-r--r--test cases/failing/43 custom target outputs not matching install_dirs/installed_files.txt6
-rw-r--r--test cases/failing/43 custom target outputs not matching install_dirs/meson.build13
-rw-r--r--test cases/failing/43 project name colon/meson.build1
-rw-r--r--test cases/frameworks/4 qt/meson.build14
-rw-r--r--test cases/frameworks/4 qt/meson_options.txt1
-rw-r--r--test cases/linuxlike/10 large file support/meson.build4
-rw-r--r--test cases/linuxlike/11 runpath rpath ldlibrarypath/lib.c3
-rw-r--r--test cases/linuxlike/11 runpath rpath ldlibrarypath/lib1/meson.build2
-rw-r--r--test cases/linuxlike/11 runpath rpath ldlibrarypath/lib2/meson.build3
-rw-r--r--test cases/linuxlike/11 runpath rpath ldlibrarypath/main.c11
-rw-r--r--test cases/linuxlike/11 runpath rpath ldlibrarypath/meson.build14
-rw-r--r--test cases/linuxlike/7 library versions/installed_files.txt1
-rw-r--r--test cases/linuxlike/7 library versions/meson.build6
-rw-r--r--test cases/linuxlike/8 subproject library install/meson.build4
-rw-r--r--test cases/objc/2 nsstring/meson.build2
-rw-r--r--test cases/osx/2 library versions/installed_files.txt1
-rw-r--r--test cases/osx/2 library versions/meson.build2
-rw-r--r--test cases/python3/1 basic/meson.build10
-rw-r--r--test cases/unit/6 std override/meson.build10
-rw-r--r--test cases/unit/6 std override/prog03.cpp6
-rw-r--r--test cases/unit/6 std override/prog11.cpp6
-rw-r--r--test cases/unit/6 std override/progp.cpp6
-rw-r--r--test cases/vala/7 shared library/installed_files.txt8
-rw-r--r--test cases/vala/7 shared library/lib/meson.build26
-rw-r--r--test cases/vala/9 gir/installed_files.txt2
-rw-r--r--test cases/vala/9 gir/meson.build2
-rw-r--r--test cases/windows/10 vs module defs generated/meson.build7
-rw-r--r--test cases/windows/10 vs module defs generated/prog.c5
-rw-r--r--test cases/windows/10 vs module defs generated/subdir/meson.build9
-rw-r--r--test cases/windows/10 vs module defs generated/subdir/somedll.c5
-rw-r--r--test cases/windows/10 vs module defs generated/subdir/somedll.def.in2
-rw-r--r--test cases/windows/5 resources/meson.build2
-rw-r--r--test cases/windows/7 mingw dll versioning/installed_files.txt11
-rw-r--r--test cases/windows/7 mingw dll versioning/meson.build6
-rw-r--r--test cases/windows/8 msvc dll versioning/installed_files.txt3
-rw-r--r--test cases/windows/8 msvc dll versioning/meson.build6
113 files changed, 1740 insertions, 665 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 9264f49..96a4ed8 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -5,6 +5,10 @@ os: Visual Studio 2015
environment:
matrix:
- arch: x86
+ compiler: msys2-mingw
+ backend: ninja
+
+ - arch: x86
compiler: msvc2010
backend: ninja
@@ -20,7 +24,11 @@ environment:
compiler: msvc2015
backend: vs2015
- - arch: x86
+ - arch: x64
+ compiler: cygwin
+ backend: ninja
+
+ - arch: x64
compiler: msys2-mingw
backend: ninja
@@ -34,10 +42,6 @@ environment:
backend: vs2017
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
- - arch: x64
- compiler: msys2-mingw
- backend: ninja
-
platform:
- x64
@@ -55,15 +59,22 @@ install:
- cmd: if %compiler%==msvc2015 ( call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %arch% )
- cmd: if %compiler%==msvc2017 ( call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\VsDevCmd.bat" -arch=%arch% )
- cmd: if %compiler%==msys2-mingw (if %arch%==x86 (set "PATH=C:\msys64\mingw32\bin;%PATH%") else (set "PATH=C:\msys64\mingw64\bin;%PATH%"))
+ - cmd: if %compiler%==cygwin ( call ci\appveyor-install.bat )
build_script:
- cmd: echo No build step.
- - cmd: if %backend%==ninja ( ninja.exe --version ) else ( MSBuild /version & echo. )
+ - cmd: if not %compiler%==cygwin if %backend%==ninja ( ninja.exe --version ) else ( MSBuild /version & echo. )
test_script:
- cmd: echo Running tests for %arch% and %compiler% with the %backend% backend
- - cmd: PATH=%cd%;%MESON_PYTHON_PATH%;%PATH%; && python run_tests.py --backend=%backend%
+ - cmd: set "ORIG_PATH=%PATH%"
+ - cmd: if %compiler%==cygwin ( set "PATH=%CYGWIN_ROOT%\bin;%SYSTEMROOT%\system32" && bash -lc "cd $APPVEYOR_BUILD_FOLDER && ci/appveyor-test.sh" )
+ - cmd: if not %compiler%==cygwin ( set "PATH=%cd%;%MESON_PYTHON_PATH%;%PATH%;" && python run_tests.py --backend=%backend% )
on_finish:
+ - set "PATH=%ORIG_PATH%"
- appveyor PushArtifact meson-test-run.txt -DeploymentName "Text test logs"
- appveyor PushArtifact meson-test-run.xml -DeploymentName "XML test logs"
+
+cache:
+ - C:\cache
diff --git a/authors.txt b/authors.txt
index 0c575e7..9b2ea72 100644
--- a/authors.txt
+++ b/authors.txt
@@ -73,3 +73,11 @@ Joe Baldino
Peter Harris
Roger Boerdijk
melak47
+Philipp Ittershagen
+Dylan Baker
+Aaron Plattner
+Jon Turney
+Wade Berrier
+Richard Hughes
+Rafael Fontenelle
+Michael Olbrich
diff --git a/ci/appveyor-install.bat b/ci/appveyor-install.bat
new file mode 100644
index 0000000..0c1ce44
--- /dev/null
+++ b/ci/appveyor-install.bat
@@ -0,0 +1,11 @@
+set CACHE=C:\cache
+set CYGWIN_MIRROR="http://cygwin.mirror.constant.com"
+
+if _%arch%_ == _x64_ set SETUP=setup-x86_64.exe && set CYGWIN_ROOT=C:\cygwin64
+if _%arch%_ == _x86_ set SETUP=setup-x86.exe && set CYGWIN_ROOT=C:\cygwin
+
+if not exist %CACHE% mkdir %CACHE%
+
+echo Updating Cygwin and installing ninja and test prerequisites
+%CYGWIN_ROOT%\%SETUP% -qnNdO -R "%CYGWIN_ROOT%" -s "%CYGWIN_MIRROR%" -l "%CACHE%" -g -P "ninja,gcc-objc,gcc-objc++,libglib2.0-devel,zlib-devel"
+echo Install done
diff --git a/ci/appveyor-test.sh b/ci/appveyor-test.sh
new file mode 100755
index 0000000..2f29630
--- /dev/null
+++ b/ci/appveyor-test.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+echo ninja $(ninja --version)
+python3 --version -V
+
+python3 run_tests.py --backend=${backend}
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index e37b0df..a77047b 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -20,7 +20,8 @@ from .. import mlog
from .. import compilers
import json
import subprocess
-from ..mesonlib import MesonException, get_compiler_for_source, classify_unity_sources
+from ..mesonlib import MesonException, get_meson_script
+from ..mesonlib import get_compiler_for_source, classify_unity_sources
from ..compilers import CompilerArgs
from collections import OrderedDict
@@ -34,7 +35,7 @@ class CleanTrees:
self.trees = trees
class InstallData:
- def __init__(self, source_dir, build_dir, prefix, strip_bin):
+ def __init__(self, source_dir, build_dir, prefix, strip_bin, mesonintrospect):
self.source_dir = source_dir
self.build_dir = build_dir
self.prefix = prefix
@@ -47,6 +48,7 @@ class InstallData:
self.po = []
self.install_scripts = []
self.install_subdirs = []
+ self.mesonintrospect = mesonintrospect
class ExecutableSerialisation:
def __init__(self, name, fname, cmd_args, env, is_cross, exe_wrapper,
@@ -77,6 +79,24 @@ class TestSerialisation:
self.workdir = workdir
self.extra_paths = extra_paths
+class OptionProxy:
+ def __init__(self, name, value):
+ self.name = name
+ self.value = value
+
+class OptionOverrideProxy:
+ '''Mimic an option list but transparently override
+ selected option values.'''
+ def __init__(self, overrides, options):
+ self.overrides = overrides
+ self.options = options
+
+ def __getitem__(self, option_name):
+ base_opt = self.options[option_name]
+ if option_name in self.overrides:
+ return OptionProxy(base_opt.name, base_opt.validate_value(self.overrides[option_name]))
+ return base_opt
+
# This class contains the basic functionality that is needed by all backends.
# Feel free to move stuff in and out of it as you see fit.
class Backend:
@@ -104,6 +124,12 @@ class Backend:
def get_target_filename_abs(self, target):
return os.path.join(self.environment.get_build_dir(), self.get_target_filename(target))
+ def get_option_for_target(self, option_name, target):
+ if option_name in target.option_overrides:
+ override = target.option_overrides[option_name]
+ return self.environment.coredata.validate_option_value(option_name, override)
+ return self.environment.coredata.get_builtin_option(option_name)
+
def get_target_filename_for_linking(self, target):
# On some platforms (msvc for instance), the file that is used for
# dynamic linking is not the same as the dynamic library itself. This
@@ -153,8 +179,10 @@ class Backend:
compsrcs = classify_unity_sources(target.compilers.values(), unity_src)
def init_language_file(suffix):
- outfilename = os.path.join(self.get_target_private_dir_abs(target),
- self.get_unity_source_filename(target, suffix))
+ unity_src_name = self.get_unity_source_filename(target, suffix)
+ unity_src_subdir = self.get_target_private_dir_abs(target)
+ outfilename = os.path.join(unity_src_subdir,
+ unity_src_name)
outfileabs = os.path.join(self.environment.get_build_dir(),
outfilename)
outfileabs_tmp = outfileabs + '.tmp'
@@ -162,7 +190,7 @@ class Backend:
outfileabs_tmp_dir = os.path.dirname(outfileabs_tmp)
if not os.path.exists(outfileabs_tmp_dir):
os.makedirs(outfileabs_tmp_dir)
- result.append(outfilename)
+ result.append(mesonlib.File(True, unity_src_subdir, unity_src_name))
return open(outfileabs_tmp, 'w')
# For each language, generate a unity source file and return the list
@@ -187,7 +215,7 @@ class Backend:
elif isinstance(obj, mesonlib.File):
obj_list.append(obj.rel_to_builddir(self.build_to_src))
elif isinstance(obj, build.ExtractedObjects):
- obj_list += self.determine_ext_objs(obj, proj_dir_to_build_root)
+ obj_list += self.determine_ext_objs(target, obj, proj_dir_to_build_root)
else:
raise MesonException('Unknown data type in object list.')
return obj_list
@@ -227,7 +255,7 @@ class Backend:
exe_wrapper = self.environment.cross_info.config['binaries'].get('exe_wrapper', None)
else:
exe_wrapper = None
- if mesonlib.is_windows():
+ if mesonlib.is_windows() or mesonlib.is_cygwin():
extra_paths = self.determine_windows_extra_paths(exe)
else:
extra_paths = []
@@ -262,32 +290,34 @@ class Backend:
raise MesonException(m.format(target.name))
return l
- def object_filename_from_source(self, target, source):
+ def object_filename_from_source(self, target, source, is_unity):
if isinstance(source, mesonlib.File):
source = source.fname
# foo.vala files compile down to foo.c and then foo.c.o, not foo.vala.o
if source.endswith('.vala'):
+ if is_unity:
+ return source[:-5] + '.c.' + self.environment.get_object_suffix()
source = os.path.join(self.get_target_private_dir(target), source[:-5] + '.c')
return source.replace('/', '_').replace('\\', '_') + '.' + self.environment.get_object_suffix()
- def determine_ext_objs(self, extobj, proj_dir_to_build_root):
+ def determine_ext_objs(self, target, extobj, proj_dir_to_build_root):
result = []
targetdir = self.get_target_private_dir(extobj.target)
# With unity builds, there's just one object that contains all the
# sources, and we only support extracting all the objects in this mode,
# so just return that.
- if self.environment.coredata.get_builtin_option('unity'):
+ if self.get_option_for_target('unity', target):
comp = get_compiler_for_source(extobj.target.compilers.values(),
extobj.srclist[0])
- # The unity object name uses the full absolute path of the source file
- osrc = os.path.join(self.get_target_private_dir_abs(extobj.target),
- self.get_unity_source_filename(extobj.target,
- comp.get_default_suffix()))
- objname = self.object_filename_from_source(extobj.target, osrc)
+ # There is a potential conflict here, but it is unlikely that
+ # anyone both enables unity builds and has a file called foo-unity.cpp.
+ osrc = self.get_unity_source_filename(extobj.target,
+ comp.get_default_suffix())
+ objname = self.object_filename_from_source(extobj.target, osrc, True)
objpath = os.path.join(proj_dir_to_build_root, targetdir, objname)
return [objpath]
for osrc in extobj.srclist:
- objname = self.object_filename_from_source(extobj.target, osrc)
+ objname = self.object_filename_from_source(extobj.target, osrc, False)
objpath = os.path.join(proj_dir_to_build_root, targetdir, objname)
result.append(objpath)
return result
@@ -339,6 +369,8 @@ class Backend:
# various sources in the order in which they must override each other
# starting from hard-coded defaults followed by build options and so on.
commands = CompilerArgs(compiler)
+
+ copt_proxy = OptionOverrideProxy(target.option_overrides, self.environment.coredata.compiler_options)
# First, the trivial ones that are impossible to override.
#
# Add -nostdinc/-nostdinc++ if needed; can't be overriden
@@ -349,19 +381,19 @@ class Backend:
# we weren't explicitly asked to not emit warnings (for Vala, f.ex)
if no_warn_args:
commands += compiler.get_no_warn_args()
- elif self.environment.coredata.get_builtin_option('buildtype') != 'plain':
- commands += compiler.get_warn_args(self.environment.coredata.get_builtin_option('warning_level'))
+ elif self.get_option_for_target('buildtype', target) != 'plain':
+ commands += compiler.get_warn_args(self.get_option_for_target('warning_level', target))
# Add -Werror if werror=true is set in the build options set on the
# command-line or default_options inside project(). This only sets the
# action to be done for warnings if/when they are emitted, so it's ok
# to set it after get_no_warn_args() or get_warn_args().
- if self.environment.coredata.get_builtin_option('werror'):
+ if self.get_option_for_target('werror', target):
commands += compiler.get_werror_args()
# Add compile args for c_* or cpp_* build options set on the
# command-line or default_options inside project().
- commands += compiler.get_option_compile_args(self.environment.coredata.compiler_options)
+ commands += compiler.get_option_compile_args(copt_proxy)
# Add buildtype args: optimization level, debugging, etc.
- commands += compiler.get_buildtype_args(self.environment.coredata.get_builtin_option('buildtype'))
+ commands += compiler.get_buildtype_args(self.get_option_for_target('buildtype', target))
# Add compile args added using add_project_arguments()
commands += self.build.get_project_args(compiler, target.subproject)
# Add compile args added using add_global_arguments()
@@ -452,7 +484,7 @@ class Backend:
exe_wrapper = self.environment.cross_info.config['binaries'].get('exe_wrapper', None)
else:
exe_wrapper = None
- if mesonlib.is_windows():
+ if mesonlib.is_windows() or mesonlib.is_cygwin():
extra_paths = self.determine_windows_extra_paths(exe)
else:
extra_paths = []
@@ -570,7 +602,7 @@ class Backend:
for t in target.get_generated_sources():
if not isinstance(t, build.CustomTarget):
continue
- for f in t.output:
+ for f in t.get_outputs():
if self.environment.is_library(f):
libs.append(os.path.join(self.get_target_dir(t), f))
return libs
@@ -600,6 +632,22 @@ class Backend:
srcs += fname
return srcs
+ def get_custom_target_depend_files(self, target, absolute_paths=False):
+ deps = []
+ for i in target.depend_files:
+ if isinstance(i, mesonlib.File):
+ if absolute_paths:
+ deps.append(i.absolute_path(self.environment.get_source_dir(),
+ self.environment.get_build_dir()))
+ else:
+ deps.append(i.rel_to_builddir(self.build_to_src))
+ else:
+ if absolute_paths:
+ deps.append(os.path.join(self.environment.get_build_dir(), i))
+ else:
+ deps.append(os.path.join(self.build_to_src, i))
+ return deps
+
def eval_custom_target_command(self, target, absolute_outputs=False):
# We want the outputs to be absolute only when using the VS backend
# XXX: Maybe allow the vs backend to use relative paths too?
@@ -611,7 +659,7 @@ class Backend:
build_root = self.environment.get_source_dir()
outdir = os.path.join(self.environment.get_build_dir(), outdir)
outputs = []
- for i in target.output:
+ for i in target.get_outputs():
outputs.append(os.path.join(outdir, i))
inputs = self.get_custom_target_sources(target)
# Evaluate the command list
@@ -685,7 +733,7 @@ class Backend:
def run_postconf_scripts(self):
env = {'MESON_SOURCE_ROOT': self.environment.get_source_dir(),
'MESON_BUILD_ROOT': self.environment.get_build_dir(),
- }
+ 'MESONINTROSPECT': get_meson_script(self.environment, 'mesonintrospect')}
child_env = os.environ.copy()
child_env.update(env)
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 63db854..98a2110 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -20,7 +20,8 @@ from .. import mlog
from .. import dependencies
from .. import compilers
from ..compilers import CompilerArgs
-from ..mesonlib import File, MesonException, get_compiler_for_source, Popen_safe
+from ..mesonlib import File, MesonException
+from ..mesonlib import get_meson_script, get_compiler_for_source, Popen_safe
from .backends import CleanTrees, InstallData
from ..build import InvalidArguments
import os, sys, pickle, re
@@ -165,14 +166,20 @@ class NinjaBackend(backends.Backend):
int dummy;
''')
- pc, stdo = Popen_safe(['cl', '/showIncludes', '/c', 'incdetect.c'],
- cwd=self.environment.get_scratch_dir())[0:2]
-
- for line in stdo.split('\n'):
- if line.endswith('stdio.h'):
- matchstr = ':'.join(line.split(':')[0:2]) + ':'
- with open(tempfilename, 'a') as binfile:
- binfile.write('msvc_deps_prefix = ' + matchstr + '\n')
+ # The output of cl dependency information is language
+ # and locale dependent. Any attempt at converting it to
+ # Python strings leads to failure. We _must_ do this detection
+ # in raw byte mode and write the result in raw bytes.
+ pc = subprocess.Popen(['cl', '/showIncludes', '/c', 'incdetect.c'],
+ cwd=self.environment.get_scratch_dir(),
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdo, _) = pc.communicate()
+
+ for line in stdo.split(b'\r\n'):
+ if line.endswith(b'stdio.h'):
+ matchstr = b':'.join(line.split(b':')[0:2]) + b':'
+ with open(tempfilename, 'ab') as binfile:
+ binfile.write(b'msvc_deps_prefix = ' + matchstr + b'\n')
return open(tempfilename, 'a')
raise MesonException('Could not determine vs dep dependency prefix string.')
@@ -336,7 +343,7 @@ int dummy;
outname = self.get_target_filename(target)
obj_list = []
use_pch = self.environment.coredata.base_options.get('b_pch', False)
- is_unity = self.environment.coredata.get_builtin_option('unity')
+ is_unity = self.get_option_for_target('unity', target)
if use_pch and target.has_pch():
pch_objects = self.generate_pch(target, outfile)
else:
@@ -433,7 +440,7 @@ int dummy;
obj_list += self.flatten_object_list(target)
if is_unity:
for src in self.generate_unity_files(target, unity_src):
- obj_list.append(self.generate_single_compile(target, outfile, RawFilename(src), True, unity_deps + header_deps))
+ obj_list.append(self.generate_single_compile(target, outfile, src, True, unity_deps + header_deps))
linker = self.determine_linker(target)
elem = self.generate_link(target, outfile, outname, obj_list, linker, pch_objects)
self.generate_shlib_aliases(target, self.get_target_dir(target))
@@ -467,6 +474,7 @@ int dummy;
self.custom_target_generator_inputs(target, outfile)
(srcs, ofilenames, cmd) = self.eval_custom_target_command(target)
deps = self.unwrap_dep_list(target)
+ deps += self.get_custom_target_depend_files(target)
desc = 'Generating {0} with a {1} command.'
if target.build_always:
deps.append('PHONY')
@@ -475,11 +483,6 @@ int dummy;
else:
rulename = 'CUSTOM_COMMAND_DEP'
elem = NinjaBuildElement(self.all_outputs, ofilenames, rulename, srcs)
- for i in target.depend_files:
- if isinstance(i, mesonlib.File):
- deps.append(i.rel_to_builddir(self.build_to_src))
- else:
- deps.append(os.path.join(self.build_to_src, i))
elem.add_dep(deps)
for d in target.extra_depends:
# Add a dependency on all the outputs of this target
@@ -492,7 +495,7 @@ int dummy;
# the project, we need to set PATH so the DLLs are found. We use
# a serialized executable wrapper for that and check if the
# CustomTarget command needs extra paths first.
- if target.capture or (mesonlib.is_windows() and
+ if target.capture or ((mesonlib.is_windows() or mesonlib.is_cygwin()) and
self.determine_windows_extra_paths(target.command[0])):
exe_data = self.serialise_executable(target.command[0], cmd[1:],
# All targets are built from the build dir
@@ -515,7 +518,7 @@ int dummy;
self.processed_targets[target.name + target.type_suffix()] = True
def generate_run_target(self, target, outfile):
- runnerscript = [sys.executable, self.environment.get_build_command(), '--internal', 'commandrunner']
+ cmd = [sys.executable, self.environment.get_build_command(), '--internal', 'commandrunner']
deps = self.unwrap_dep_list(target)
arg_strings = []
for i in target.args:
@@ -531,7 +534,10 @@ int dummy;
else:
raise AssertionError('Unreachable code in generate_run_target: ' + str(i))
elem = NinjaBuildElement(self.all_outputs, target.name, 'CUSTOM_COMMAND', [])
- cmd = runnerscript + [self.environment.get_source_dir(), self.environment.get_build_dir(), target.subdir]
+ cmd += [self.environment.get_source_dir(),
+ self.environment.get_build_dir(),
+ target.subdir,
+ get_meson_script(self.environment, 'mesonintrospect')]
texe = target.command
try:
texe = texe.held_object
@@ -608,7 +614,8 @@ int dummy;
d = InstallData(self.environment.get_source_dir(),
self.environment.get_build_dir(),
self.environment.get_prefix(),
- strip_bin)
+ strip_bin,
+ get_meson_script(self.environment, 'mesonintrospect'))
elem = NinjaBuildElement(self.all_outputs, 'install', 'CUSTOM_COMMAND', 'PHONY')
elem.add_dep('all')
elem.add_item('DESC', 'Installing files.')
@@ -627,41 +634,89 @@ int dummy;
pickle.dump(d, ofile)
def generate_target_install(self, d):
- should_strip = self.environment.coredata.get_builtin_option('strip')
for t in self.build.get_targets().values():
- if t.should_install():
- # Find the installation directory. FIXME: Currently only one
- # installation directory is supported for each target
- outdir = t.get_custom_install_dir()
- if outdir is not None:
- pass
- elif isinstance(t, build.SharedLibrary):
- # For toolchains/platforms that need an import library for
+ if not t.should_install():
+ continue
+ # Find the installation directory.
+ outdirs = t.get_custom_install_dir()
+ custom_install_dir = False
+ if outdirs[0] is not None and outdirs[0] is not True:
+ # Either the value is set, or is set to False which means
+ # we want this specific output out of many outputs to not
+ # be installed.
+ custom_install_dir = True
+ elif isinstance(t, build.SharedModule):
+ outdirs[0] = self.environment.get_shared_module_dir()
+ elif isinstance(t, build.SharedLibrary):
+ outdirs[0] = self.environment.get_shared_lib_dir()
+ elif isinstance(t, build.StaticLibrary):
+ outdirs[0] = self.environment.get_static_lib_dir()
+ elif isinstance(t, build.Executable):
+ outdirs[0] = self.environment.get_bindir()
+ else:
+ assert(isinstance(t, build.BuildTarget))
+ # XXX: Add BuildTarget-specific install dir cases here
+ outdirs[0] = self.environment.get_libdir()
+ # Sanity-check the outputs and install_dirs
+ num_outdirs, num_out = len(outdirs), len(t.get_outputs())
+ if num_outdirs != 1 and num_outdirs != num_out:
+ m = 'Target {!r} has {} outputs: {!r}, but only {} "install_dir"s were found.\n' \
+ "Pass 'false' for outputs that should not be installed and 'true' for\n" \
+ 'using the default installation directory for an output.'
+ raise MesonException(m.format(t.name, num_out, t.get_outputs(), num_outdirs))
+ # Install the target output(s)
+ if isinstance(t, build.BuildTarget):
+ should_strip = self.get_option_for_target('strip', t)
+ # Install primary build output (library/executable/jar, etc)
+ # Done separately because of strip/aliases/rpath
+ if outdirs[0] is not False:
+ i = [self.get_target_filename(t), outdirs[0],
+ t.get_aliases(), should_strip, t.install_rpath]
+ d.targets.append(i)
+ # On toolchains/platforms that use an import library for
# linking (separate from the shared library with all the
- # code), we need to install the import library (dll.a/.lib)
- if t.get_import_filename():
+ # code), we need to install that too (dll.a/.lib).
+ if isinstance(t, build.SharedLibrary) and t.get_import_filename():
+ if custom_install_dir:
+ # If the DLL is installed into a custom directory,
+ # install the import library into the same place so
+ # it doesn't go into a surprising place
+ implib_install_dir = outdirs[0]
+ else:
+ implib_install_dir = self.environment.get_import_lib_dir()
# Install the import library.
i = [self.get_target_filename_for_linking(t),
- self.environment.get_import_lib_dir(),
+ implib_install_dir,
# It has no aliases, should not be stripped, and
# doesn't have an install_rpath
{}, False, '']
d.targets.append(i)
- outdir = self.environment.get_shared_lib_dir()
- elif isinstance(t, build.StaticLibrary):
- outdir = self.environment.get_static_lib_dir()
- elif isinstance(t, build.Executable):
- outdir = self.environment.get_bindir()
- else:
- # XXX: Add BuildTarget-specific install dir cases here
- outdir = self.environment.get_libdir()
- if isinstance(t, build.BuildTarget):
- i = [self.get_target_filename(t), outdir, t.get_aliases(),
- should_strip, t.install_rpath]
- d.targets.append(i)
- elif isinstance(t, build.CustomTarget):
+ # Install secondary outputs. Only used for Vala right now.
+ if num_outdirs > 1:
+ for output, outdir in zip(t.get_outputs()[1:], outdirs[1:]):
+ # User requested that we not install this output
+ if outdir is False:
+ continue
+ f = os.path.join(self.get_target_dir(t), output)
+ d.targets.append([f, outdir, {}, False, None])
+ elif isinstance(t, build.CustomTarget):
+ # If only one install_dir is specified, assume that all
+ # outputs will be installed into it. This is for
+ # backwards-compatibility and because it makes sense to
+ # avoid repetition since this is a common use-case.
+ #
+ # To selectively install only some outputs, pass `false` as
+ # the install_dir for the corresponding output by index
+ if num_outdirs == 1 and num_out > 1:
for output in t.get_outputs():
f = os.path.join(self.get_target_dir(t), output)
+ d.targets.append([f, outdirs[0], {}, False, None])
+ else:
+ for output, outdir in zip(t.get_outputs(), outdirs):
+ # User requested that we not install this output
+ if outdir is False:
+ continue
+ f = os.path.join(self.get_target_dir(t), output)
d.targets.append([f, outdir, {}, False, None])
def generate_custom_install_script(self, d):
@@ -729,9 +784,7 @@ int dummy;
def generate_tests(self, outfile):
self.serialise_tests()
- meson_exe = self.environment.get_build_command()
- (base, ext) = os.path.splitext(meson_exe)
- test_exe = base + 'test' + ext
+ test_exe = get_meson_script(self.environment, 'mesontest')
cmd = [sys.executable, test_exe, '--no-rebuild']
if not self.environment.coredata.get_builtin_option('stdsplit'):
cmd += ['--no-stdsplit']
@@ -843,7 +896,7 @@ int dummy;
return args, deps
def generate_cs_target(self, target, outfile):
- buildtype = self.environment.coredata.get_builtin_option('buildtype')
+ buildtype = self.get_option_for_target('buildtype', target)
fname = target.get_filename()
outname_rel = os.path.join(self.get_target_dir(target), fname)
src_list = target.get_sources()
@@ -877,7 +930,7 @@ int dummy;
def generate_single_java_compile(self, src, target, compiler, outfile):
args = []
- args += compiler.get_buildtype_args(self.environment.coredata.get_builtin_option('buildtype'))
+ args += compiler.get_buildtype_args(self.get_option_for_target('buildtype', target))
args += self.build.get_global_args(compiler)
args += self.build.get_project_args(compiler, target.subproject)
args += target.get_java_args()
@@ -1010,7 +1063,7 @@ int dummy;
args = []
args += self.build.get_global_args(valac)
args += self.build.get_project_args(valac, target.subproject)
- args += valac.get_buildtype_args(self.environment.coredata.get_builtin_option('buildtype'))
+ args += valac.get_buildtype_args(self.get_option_for_target('buildtype', target))
# Tell Valac to output everything in our private directory. Sadly this
# means it will also preserve the directory components of Vala sources
# found inside the build tree (generated sources).
@@ -1029,11 +1082,22 @@ int dummy;
# Without this, it will write it inside c_out_dir
args += ['--vapi', os.path.join('..', target.vala_vapi)]
valac_outputs.append(vapiname)
+ target.outputs += [target.vala_header, target.vala_vapi]
+ # Install header and vapi to default locations if user requests this
+ if len(target.install_dir) > 1 and target.install_dir[1] is True:
+ target.install_dir[1] = self.environment.get_includedir()
+ if len(target.install_dir) > 2 and target.install_dir[2] is True:
+ target.install_dir[2] = os.path.join(self.environment.get_datadir(), 'vala', 'vapi')
+ # Generate GIR if requested
if isinstance(target.vala_gir, str):
girname = os.path.join(self.get_target_dir(target), target.vala_gir)
args += ['--gir', os.path.join('..', target.vala_gir)]
valac_outputs.append(girname)
- if self.environment.coredata.get_builtin_option('werror'):
+ target.outputs.append(target.vala_gir)
+ # Install GIR to default location if requested by user
+ if len(target.install_dir) > 3 and target.install_dir[3] is True:
+ target.install_dir[3] = os.path.join(self.environment.get_datadir(), 'gir-1.0')
+ if self.get_option_for_target('werror', target):
args += valac.get_werror_args()
for d in target.get_external_deps():
if isinstance(d, dependencies.PkgConfigDependency):
@@ -1088,7 +1152,7 @@ int dummy;
else:
raise InvalidArguments('Unknown target type for rustc.')
args.append(cratetype)
- args += rustc.get_buildtype_args(self.environment.coredata.get_builtin_option('buildtype'))
+ args += rustc.get_buildtype_args(self.get_option_for_target('buildtype', target))
depfile = os.path.join(target.subdir, target.name + '.d')
args += ['--emit', 'dep-info={}'.format(depfile), '--emit', 'link']
args += ['-o', os.path.join(target.subdir, target.get_filename())]
@@ -1810,13 +1874,15 @@ rule FORTRAN_DEP_HACK
return incs
def _generate_single_compile(self, target, compiler, is_generated=False):
+ base_proxy = backends.OptionOverrideProxy(target.option_overrides,
+ self.environment.coredata.base_options)
# Create an empty commands list, and start adding arguments from
# various sources in the order in which they must override each other
commands = CompilerArgs(compiler)
# Add compiler args for compiling this target derived from 'base' build
# options passed on the command-line, in default_options, etc.
# These have the lowest priority.
- commands += compilers.get_base_compile_args(self.environment.coredata.base_options,
+ commands += compilers.get_base_compile_args(base_proxy,
compiler)
# The code generated by valac is usually crap and has tons of unused
# variables and such, so disable warnings for Vala C sources.
@@ -1888,6 +1954,11 @@ rule FORTRAN_DEP_HACK
raise AssertionError('BUG: sources should not contain headers {!r}'.format(src))
if isinstance(src, RawFilename) and src.fname.endswith('.h'):
raise AssertionError('BUG: sources should not contain headers {!r}'.format(src.fname))
+
+ if isinstance(src, str) and src.endswith('.h'):
+ raise AssertionError('BUG: sources should not contain headers {!r}'.format(src))
+ if isinstance(src, RawFilename) and src.fname.endswith('.h'):
+ raise AssertionError('BUG: sources should not contain headers {!r}'.format(src.fname))
compiler = get_compiler_for_source(target.compilers.values(), src)
key = (target, compiler, is_generated)
if key in self.target_arg_cache:
@@ -1905,6 +1976,10 @@ rule FORTRAN_DEP_HACK
abs_src = src.fname
else:
abs_src = os.path.join(self.environment.get_build_dir(), src.fname)
+ elif isinstance(src, mesonlib.File):
+ rel_src = src.rel_to_builddir(self.build_to_src)
+ abs_src = src.absolute_path(self.environment.get_source_dir(),
+ self.environment.get_build_dir())
elif is_generated:
raise AssertionError('BUG: broken generated source file handling for {!r}'.format(src))
else:
@@ -2145,7 +2220,7 @@ rule FORTRAN_DEP_HACK
# Add things like /NOLOGO; usually can't be overriden
commands += linker.get_linker_always_args()
# Add buildtype linker args: optimization level, etc.
- commands += linker.get_buildtype_linker_args(self.environment.coredata.get_builtin_option('buildtype'))
+ commands += linker.get_buildtype_linker_args(self.get_option_for_target('buildtype', target))
# Add /DEBUG and the pdb filename when using MSVC
commands += self.get_link_debugfile_args(linker, target, outname)
# Add link args specific to this BuildTarget type, such as soname args,
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index 8c0cce6..46eab11 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -24,7 +24,7 @@ from .. import mlog
from .. import compilers
from ..build import BuildTarget
from ..compilers import CompilerArgs
-from ..mesonlib import MesonException, File
+from ..mesonlib import MesonException, File, get_meson_script
from ..environment import Environment
def autodetect_vs_version(build):
@@ -87,7 +87,7 @@ class Vs2010Backend(backends.Backend):
self.vs_version = '2010'
self.windows_target_platform_version = None
- def object_filename_from_source(self, target, source):
+ def object_filename_from_source(self, target, source, is_unity=False):
basename = os.path.basename(source.fname)
filename_without_extension = '.'.join(basename.split('.')[:-1])
if basename in self.sources_conflicts[target.get_id()]:
@@ -409,21 +409,25 @@ class Vs2010Backend(backends.Backend):
customstep = ET.SubElement(action, 'PostBuildEvent')
cmd_raw = [target.command] + target.args
cmd = [sys.executable, os.path.join(self.environment.get_script_dir(), 'commandrunner.py'),
- self.environment.get_build_dir(), self.environment.get_source_dir(),
- self.get_target_dir(target)]
+ self.environment.get_build_dir(),
+ self.environment.get_source_dir(),
+ self.get_target_dir(target),
+ get_meson_script(self.environment, 'mesonintrospect')]
for i in cmd_raw:
if isinstance(i, build.BuildTarget):
cmd.append(os.path.join(self.environment.get_build_dir(), self.get_target_filename(i)))
elif isinstance(i, dependencies.ExternalProgram):
cmd += i.get_command()
+ elif isinstance(i, File):
+ relfname = i.rel_to_builddir(self.build_to_src)
+ cmd.append(os.path.join(self.environment.get_build_dir(), relfname))
else:
cmd.append(i)
cmd_templ = '''"%s" ''' * len(cmd)
ET.SubElement(customstep, 'Command').text = cmd_templ % tuple(cmd)
ET.SubElement(customstep, 'Message').text = 'Running custom command.'
ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets')
- tree = ET.ElementTree(root)
- tree.write(ofname, encoding='utf-8', xml_declaration=True)
+ self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname)
def gen_custom_target_vcxproj(self, target, ofname, guid):
root = self.create_basic_crap(target)
@@ -433,6 +437,7 @@ class Vs2010Backend(backends.Backend):
# from the target dir, not the build root.
target.absolute_paths = True
(srcs, ofilenames, cmd) = self.eval_custom_target_command(target, True)
+ depend_files = self.get_custom_target_depend_files(target, True)
# Always use a wrapper because MSBuild eats random characters when
# there are many arguments.
tdir_abs = os.path.join(self.environment.get_build_dir(), self.get_target_dir(target))
@@ -444,11 +449,10 @@ class Vs2010Backend(backends.Backend):
'--internal', 'exe', exe_data]
ET.SubElement(customstep, 'Command').text = ' '.join(self.quote_arguments(wrapper_cmd))
ET.SubElement(customstep, 'Outputs').text = ';'.join(ofilenames)
- ET.SubElement(customstep, 'Inputs').text = ';'.join(srcs)
+ ET.SubElement(customstep, 'Inputs').text = ';'.join([exe_data] + srcs + depend_files)
ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets')
self.generate_custom_generator_commands(target, root)
- tree = ET.ElementTree(root)
- tree.write(ofname, encoding='utf-8', xml_declaration=True)
+ self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname)
@classmethod
def lang_from_source_file(cls, src):
@@ -574,6 +578,13 @@ class Vs2010Backend(backends.Backend):
return c
raise MesonException('Could not find a C or C++ compiler. MSVC can only build C/C++ projects.')
+ def _prettyprint_vcxproj_xml(self, tree, ofname):
+ tree.write(ofname, encoding='utf-8', xml_declaration=True)
+ # ElementTree can not do prettyprinting so do it manually
+ doc = xml.dom.minidom.parse(ofname)
+ with open(ofname, 'w') as of:
+ of.write(doc.toprettyxml())
+
def gen_vcxproj(self, target, ofname, guid):
mlog.debug('Generating vcxproj %s.' % target.name)
entrypoint = 'WinMainCRTStartup'
@@ -601,6 +612,8 @@ class Vs2010Backend(backends.Backend):
# Prefix to use to access the source tree's subdir from the vcxproj dir
proj_to_src_dir = os.path.join(proj_to_src_root, target.subdir)
(sources, headers, objects, languages) = self.split_sources(target.sources)
+ if self.get_option_for_target('unity', target):
+ sources = self.generate_unity_files(target, sources)
compiler = self._get_cl_compiler(target)
buildtype_args = compiler.get_buildtype_args(self.buildtype)
buildtype_link_args = compiler.get_buildtype_linker_args(self.buildtype)
@@ -690,7 +703,7 @@ class Vs2010Backend(backends.Backend):
elif '/Od' in o_flags:
ET.SubElement(type_config, 'Optimization').text = 'Disabled'
# Warning level
- warning_level = self.environment.coredata.get_builtin_option('warning_level')
+ warning_level = self.get_option_for_target('warning_level', target)
ET.SubElement(type_config, 'WarningLevel').text = 'Level' + warning_level
# End configuration
ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.props')
@@ -844,7 +857,7 @@ class Vs2010Backend(backends.Backend):
ET.SubElement(clconf, 'MinimalRebuild').text = 'true'
ET.SubElement(clconf, 'FunctionLevelLinking').text = 'true'
pch_node = ET.SubElement(clconf, 'PrecompiledHeader')
- if self.environment.coredata.get_builtin_option('werror'):
+ if self.get_option_for_target('werror', target):
ET.SubElement(clconf, 'TreatWarningAsError').text = 'true'
# Note: SuppressStartupBanner is /NOLOGO and is 'true' by default
pch_sources = {}
@@ -1016,19 +1029,7 @@ class Vs2010Backend(backends.Backend):
ig = ET.SubElement(root, 'ItemGroup')
pref = ET.SubElement(ig, 'ProjectReference', Include=os.path.join(self.environment.get_build_dir(), 'REGEN.vcxproj'))
ET.SubElement(pref, 'Project').text = self.environment.coredata.regen_guid
- tree = ET.ElementTree(root)
- tree.write(ofname, encoding='utf-8', xml_declaration=True)
- # ElementTree can not do prettyprinting so do it manually
- doc = xml.dom.minidom.parse(ofname)
- with open(ofname, 'w') as of:
- of.write(doc.toprettyxml())
- # World of horror! Python insists on not quoting quotes and
- # fixing the escaped " into " whereas MSVS
- # requires quoted but not fixed elements. Enter horrible hack.
- with open(ofname, 'r') as of:
- txt = of.read()
- with open(ofname, 'w') as of:
- of.write(txt.replace('"', '"'))
+ self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname)
def gen_regenproj(self, project_name, ofname):
root = ET.Element('Project', {'DefaultTargets': 'Build',
@@ -1107,8 +1108,7 @@ if %%errorlevel%% neq 0 goto :VCEnd'''
ET.SubElement(custombuild, 'AdditionalInputs').text = ';'.join(deps)
ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets')
ET.SubElement(root, 'ImportGroup', Label='ExtensionTargets')
- tree = ET.ElementTree(root)
- tree.write(ofname, encoding='utf-8', xml_declaration=True)
+ self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname)
def gen_testproj(self, target_name, ofname):
project_name = target_name
@@ -1162,11 +1162,8 @@ if %%errorlevel%% neq 0 goto :VCEnd'''
postbuild = ET.SubElement(action, 'PostBuildEvent')
ET.SubElement(postbuild, 'Message')
# FIXME: No benchmarks?
- meson_py = self.environment.get_build_command()
- (base, ext) = os.path.splitext(meson_py)
- mesontest_py = base + 'test' + ext
test_command = [sys.executable,
- mesontest_py,
+ get_meson_script(self.environment, 'mesontest'),
'--no-rebuild']
if not self.environment.coredata.get_builtin_option('stdsplit'):
test_command += ['--no-stdsplit']
@@ -1185,8 +1182,4 @@ if %%errorlevel%% neq 0 goto :VCEnd'''
ET.SubElement(postbuild, 'Command').text =\
cmd_templ % ('" "'.join(test_command))
ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets')
- tree = ET.ElementTree(root)
- tree.write(ofname, encoding='utf-8', xml_declaration=True)
- # ElementTree can not do prettyprinting so do it manually
- # doc = xml.dom.minidom.parse(ofname)
- # open(ofname, 'w').write(doc.toprettyxml())
+ self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname)
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index ed0abc4..6c16cf9 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -19,9 +19,9 @@ from . import environment
from . import dependencies
from . import mlog
from .mesonlib import File, MesonException
-from .mesonlib import flatten, stringlistify, classify_unity_sources
+from .mesonlib import flatten, typeslistify, stringlistify, classify_unity_sources
from .mesonlib import get_filenames_templates_dict, substitute_values
-from .environment import for_windows, for_darwin
+from .environment import for_windows, for_darwin, for_cygwin
from .compilers import is_object, clike_langs, sort_clike, lang_suffixes
known_basic_kwargs = {'install': True,
@@ -49,6 +49,7 @@ known_basic_kwargs = {'install': True,
'objects': True,
'native': True,
'build_by_default': True,
+ 'override_options': True,
}
# These contain kwargs supported by both static and shared libraries. These are
@@ -272,6 +273,7 @@ class Target:
self.build_by_default = build_by_default
self.install = False
self.build_always = False
+ self.option_overrides = {}
def get_basename(self):
return self.name
@@ -284,6 +286,20 @@ class Target:
self.build_by_default = kwargs['build_by_default']
if not isinstance(self.build_by_default, bool):
raise InvalidArguments('build_by_default must be a boolean value.')
+ self.option_overrides = self.parse_overrides(kwargs)
+
+ def parse_overrides(self, kwargs):
+ result = {}
+ overrides = stringlistify(kwargs.get('override_options', []))
+ for o in overrides:
+ if '=' not in o:
+ raise InvalidArguments('Overrides must be of form "key=value"')
+ k, v = o.split('=', 1)
+ k = k.strip()
+ v = v.strip()
+ result[k] = v
+ return result
+
class BuildTarget(Target):
def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs):
@@ -302,6 +318,9 @@ class BuildTarget(Target):
self.name_prefix_set = False
self.name_suffix_set = False
self.filename = 'no_name'
+ # The list of all files outputted by this target. Useful in cases such
+ # as Vala which generates .vapi and .h besides the compiled output.
+ self.outputs = [self.filename]
self.need_install = False
self.pch = {}
self.extra_args = {}
@@ -531,7 +550,7 @@ class BuildTarget(Target):
return result
def get_custom_install_dir(self):
- return self.custom_install_dir
+ return self.install_dir
def process_kwargs(self, kwargs, environment):
super().process_kwargs(kwargs)
@@ -578,7 +597,7 @@ class BuildTarget(Target):
if not isinstance(self, Executable):
self.vala_header = kwargs.get('vala_header', self.name + '.h')
self.vala_vapi = kwargs.get('vala_vapi', self.name + '.vapi')
- self.vala_gir = kwargs.get('vala_gir', None)
+ self.vala_gir = kwargs.get('vala_gir', None)
dlist = stringlistify(kwargs.get('d_args', []))
self.add_compiler_args('d', dlist)
self.link_args = kwargs.get('link_args', [])
@@ -604,10 +623,10 @@ class BuildTarget(Target):
if not isinstance(deplist, list):
deplist = [deplist]
self.add_deps(deplist)
- self.custom_install_dir = kwargs.get('install_dir', None)
- if self.custom_install_dir is not None:
- if not isinstance(self.custom_install_dir, str):
- raise InvalidArguments('Custom_install_dir must be a string')
+ # If an item in this list is False, the output corresponding to
+ # the list index of that item will not be installed
+ self.install_dir = typeslistify(kwargs.get('install_dir', [None]),
+ (str, bool))
main_class = kwargs.get('main_class', '')
if not isinstance(main_class, str):
raise InvalidArguments('Main class must be a string')
@@ -678,7 +697,7 @@ class BuildTarget(Target):
return self.filename
def get_outputs(self):
- return [self.filename]
+ return self.outputs
def get_extra_args(self, language):
return self.extra_args.get(language, [])
@@ -984,13 +1003,15 @@ class Executable(BuildTarget):
self.prefix = ''
if not hasattr(self, 'suffix'):
# Executable for Windows or C#/Mono
- if for_windows(is_cross, environment) or 'cs' in self.compilers:
+ if (for_windows(is_cross, environment) or
+ for_cygwin(is_cross, environment) or 'cs' in self.compilers):
self.suffix = 'exe'
else:
self.suffix = ''
self.filename = self.name
if self.suffix:
self.filename += '.' + self.suffix
+ self.outputs = [self.filename]
def type_suffix(self):
return "@exe"
@@ -1018,6 +1039,7 @@ class StaticLibrary(BuildTarget):
else:
self.suffix = 'a'
self.filename = self.prefix + self.name + '.' + self.suffix
+ self.outputs = [self.filename]
def type_suffix(self):
return "@sta"
@@ -1107,6 +1129,18 @@ class SharedLibrary(BuildTarget):
self.filename_tpl = '{0.prefix}{0.name}-{0.soversion}.{0.suffix}'
else:
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)
+ # Shared library is of the form cygfoo.dll
+ # (ld --dll-search-prefix=cyg is the default)
+ prefix = 'cyg'
+ # Import library is called libfoo.dll.a
+ self.import_filename = self.gcc_import_filename
+ if self.soversion:
+ self.filename_tpl = '{0.prefix}{0.name}-{0.soversion}.{0.suffix}'
+ else:
+ self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
elif for_darwin(is_cross, env):
prefix = 'lib'
suffix = 'dylib'
@@ -1134,6 +1168,7 @@ class SharedLibrary(BuildTarget):
if self.suffix is None:
self.suffix = suffix
self.filename = self.filename_tpl.format(self)
+ self.outputs = [self.filename]
def process_kwargs(self, kwargs, environment):
super().process_kwargs(kwargs, environment)
@@ -1159,12 +1194,22 @@ class SharedLibrary(BuildTarget):
# Visual Studio module-definitions file
if 'vs_module_defs' in kwargs:
path = kwargs['vs_module_defs']
- if os.path.isabs(path):
- self.vs_module_defs = File.from_absolute_file(path)
+ if isinstance(path, str):
+ if os.path.isabs(path):
+ self.vs_module_defs = File.from_absolute_file(path)
+ else:
+ self.vs_module_defs = File.from_source_file(environment.source_dir, self.subdir, path)
+ # link_depends can be an absolute path or relative to self.subdir
+ self.link_depends.append(path)
+ elif isinstance(path, File):
+ # When passing a generated file.
+ self.vs_module_defs = path
+ # link_depends can be an absolute path or relative to self.subdir
+ self.link_depends.append(path.absolute_path(environment.source_dir, environment.build_dir))
else:
- self.vs_module_defs = File.from_source_file(environment.source_dir, self.subdir, path)
- # link_depends can be an absolute path or relative to self.subdir
- self.link_depends.append(path)
+ raise InvalidArguments(
+ 'Shared library vs_module_defs must be either a string, '
+ 'or a file object')
def check_unknown_kwargs(self, kwargs):
self.check_unknown_kwargs_int(kwargs, known_lib_kwargs)
@@ -1227,6 +1272,7 @@ class SharedModule(SharedLibrary):
if 'soversion' in kwargs:
raise MesonException('Shared modules must not specify the soversion kwarg.')
super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs)
+ self.import_filename = None
class CustomTarget(Target):
known_kwargs = {'input': True,
@@ -1240,6 +1286,7 @@ class CustomTarget(Target):
'depend_files': True,
'depfile': True,
'build_by_default': True,
+ 'override_options': True,
}
def __init__(self, name, subdir, kwargs, absolute_paths=False):
@@ -1287,12 +1334,16 @@ class CustomTarget(Target):
for c in cmd:
if hasattr(c, 'held_object'):
c = c.held_object
- if isinstance(c, (str, File)):
+ if isinstance(c, str):
+ final_cmd.append(c)
+ elif isinstance(c, File):
+ self.depend_files.append(c)
final_cmd.append(c)
elif isinstance(c, dependencies.ExternalProgram):
if not c.found():
m = 'Tried to use not-found external program {!r} in "command"'
raise InvalidArguments(m.format(c.name))
+ self.depend_files.append(File.from_absolute_file(c.get_path()))
final_cmd += c.get_command()
elif isinstance(c, (BuildTarget, CustomTarget)):
self.dependencies.append(c)
@@ -1310,13 +1361,13 @@ class CustomTarget(Target):
self.sources = [self.sources]
if 'output' not in kwargs:
raise InvalidArguments('Missing keyword argument "output".')
- self.output = kwargs['output']
- if not isinstance(self.output, list):
- self.output = [self.output]
+ self.outputs = kwargs['output']
+ if not isinstance(self.outputs, list):
+ self.outputs = [self.outputs]
# This will substitute values from the input into output and return it.
inputs = get_sources_string_names(self.sources)
values = get_filenames_templates_dict(inputs, [])
- for i in self.output:
+ for i in self.outputs:
if not(isinstance(i, str)):
raise InvalidArguments('Output argument not a string.')
if '/' in i:
@@ -1331,9 +1382,9 @@ class CustomTarget(Target):
m = "Output cannot contain @PLAINNAME@ or @BASENAME@ when " \
"there is more than one input (we can't know which to use)"
raise InvalidArguments(m)
- self.output = substitute_values(self.output, values)
+ self.outputs = substitute_values(self.outputs, values)
self.capture = kwargs.get('capture', False)
- if self.capture and len(self.output) != 1:
+ if self.capture and len(self.outputs) != 1:
raise InvalidArguments('Capturing can only output to a single file.')
if 'command' not in kwargs:
raise InvalidArguments('Missing keyword argument "command".')
@@ -1355,12 +1406,14 @@ class CustomTarget(Target):
raise InvalidArguments('"install" must be boolean.')
if self.install:
if 'install_dir' not in kwargs:
- raise InvalidArguments('"install_dir" not specified.')
- self.install_dir = kwargs['install_dir']
- if not(isinstance(self.install_dir, str)):
- raise InvalidArguments('"install_dir" must be a string.')
+ raise InvalidArguments('"install_dir" must be specified '
+ 'when installing a target')
+ # If an item in this list is False, the output corresponding to
+ # the list index of that item will not be installed
+ self.install_dir = typeslistify(kwargs['install_dir'], (str, bool))
else:
self.install = False
+ self.install_dir = [None]
self.build_always = kwargs.get('build_always', False)
if not isinstance(self.build_always, bool):
raise InvalidArguments('Argument build_always must be a boolean.')
@@ -1393,10 +1446,10 @@ class CustomTarget(Target):
return self.install_dir
def get_outputs(self):
- return self.output
+ return self.outputs
def get_filename(self):
- return self.output[0]
+ return self.outputs[0]
def get_sources(self):
return self.sources
@@ -1458,6 +1511,7 @@ class Jar(BuildTarget):
if not s.endswith('.java'):
raise InvalidArguments('Jar source %s is not a java file.' % s)
self.filename = self.name + '.jar'
+ self.outputs = [self.filename]
self.java_args = kwargs.get('java_args', [])
def get_main_class(self):
diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py
index 5e7db24..e6be8b1 100644
--- a/mesonbuild/compilers.py
+++ b/mesonbuild/compilers.py
@@ -550,11 +550,11 @@ class Compiler:
def get_exelist(self):
return self.exelist[:]
- def get_define(self, *args, **kwargs):
- raise EnvironmentException('%s does not support get_define.' % self.id)
+ def get_builtin_define(self, *args, **kwargs):
+ raise EnvironmentException('%s does not support get_builtin_define.' % self.id)
- def has_define(self, *args, **kwargs):
- raise EnvironmentException('%s does not support has_define.' % self.id)
+ def has_builtin_define(self, *args, **kwargs):
+ raise EnvironmentException('%s does not support has_builtin_define.' % self.id)
def get_always_args(self):
return []
@@ -906,8 +906,6 @@ class CCompiler(Compiler):
return self.sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code)
def has_header(self, hname, prefix, env, extra_args=None, dependencies=None):
- if extra_args is None:
- extra_args = []
fargs = {'prefix': prefix, 'header': hname}
code = '''{prefix}
#ifdef __has_include
@@ -921,8 +919,6 @@ class CCompiler(Compiler):
dependencies, 'preprocess')
def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None, dependencies=None):
- if extra_args is None:
- extra_args = []
fargs = {'prefix': prefix, 'header': hname, 'symbol': symbol}
t = '''{prefix}
#include <{header}>
@@ -934,7 +930,7 @@ class CCompiler(Compiler):
}}'''
return self.compiles(t.format(**fargs), env, extra_args, dependencies)
- def compiles(self, code, env, extra_args=None, dependencies=None, mode='compile'):
+ def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'):
if extra_args is None:
extra_args = []
elif isinstance(extra_args, str):
@@ -943,49 +939,43 @@ class CCompiler(Compiler):
dependencies = []
elif not isinstance(dependencies, list):
dependencies = [dependencies]
- # Add compile flags needed by dependencies
+ # Collect compiler arguments
args = CompilerArgs(self)
for d in dependencies:
+ # Add compile flags needed by dependencies
args += d.get_compile_args()
+ if mode == 'link':
+ # Add link flags needed to find dependencies
+ args += d.get_link_args()
+ # Select a CRT if needed since we're linking
+ if mode == 'link':
+ args += self.get_linker_debug_crt_args()
# Read c_args/cpp_args/etc from the cross-info file (if needed)
- args += self.get_cross_extra_flags(env, compile=True, link=False)
- # Add CFLAGS/CXXFLAGS/OBJCFLAGS/OBJCXXFLAGS from the env
- # We assume that the user has ensured these are compiler-specific
- args += env.coredata.external_args[self.language]
+ args += self.get_cross_extra_flags(env, compile=(mode != 'preprocess'),
+ link=(mode == 'link'))
+ if mode == 'preprocess':
+ # Add CPPFLAGS from the env.
+ args += env.coredata.external_preprocess_args[self.language]
+ elif mode == 'compile':
+ # Add CFLAGS/CXXFLAGS/OBJCFLAGS/OBJCXXFLAGS from the env
+ args += env.coredata.external_args[self.language]
+ elif mode == 'link':
+ # Add LDFLAGS from the env
+ args += env.coredata.external_link_args[self.language]
args += self.get_compiler_check_args()
# extra_args must override all other arguments, so we add them last
args += extra_args
+ return args
+
+ def compiles(self, code, env, extra_args=None, dependencies=None, mode='compile'):
+ args = self._get_compiler_check_args(env, extra_args, dependencies, mode)
# We only want to compile; not link
with self.compile(code, args.to_native(), mode) as p:
return p.returncode == 0
def _links_wrapper(self, code, env, extra_args, dependencies):
"Shares common code between self.links and self.run"
- if extra_args is None:
- extra_args = []
- elif isinstance(extra_args, str):
- extra_args = [extra_args]
- if dependencies is None:
- dependencies = []
- elif not isinstance(dependencies, list):
- dependencies = [dependencies]
- # Add compile and link flags needed by dependencies
- args = CompilerArgs(self)
- for d in dependencies:
- args += d.get_compile_args()
- args += d.get_link_args()
- # Select a CRT if needed since we're linking
- args += self.get_linker_debug_crt_args()
- # Read c_args/c_link_args/cpp_args/cpp_link_args/etc from the
- # cross-info file (if needed)
- args += self.get_cross_extra_flags(env, compile=True, link=True)
- # Add LDFLAGS from the env. We assume that the user has ensured these
- # are compiler-specific
- args += env.coredata.external_link_args[self.language]
- # Add compiler check args such that they override
- args += self.get_compiler_check_args()
- # extra_args must override all other arguments, so we add them last
- args += extra_args
+ args = self._get_compiler_check_args(env, extra_args, dependencies, mode='link')
return self.compile(code, args.to_native())
def links(self, code, env, extra_args=None, dependencies=None):
@@ -1141,6 +1131,24 @@ class CCompiler(Compiler):
raise EnvironmentException('Could not determine alignment of %s. Sorry. You might want to file a bug.' % typename)
return align
+ def get_define(self, dname, prefix, env, extra_args, dependencies):
+ delim = '"MESON_GET_DEFINE_DELIMITER"'
+ fargs = {'prefix': prefix, 'define': dname, 'delim': delim}
+ code = '''
+ #ifndef {define}
+ # define {define}
+ #endif
+ {prefix}
+ {delim}\n{define}'''
+ args = self._get_compiler_check_args(env, extra_args, dependencies,
+ mode='preprocess').to_native()
+ with self.compile(code.format(**fargs), args, 'preprocess') as p:
+ if p.returncode != 0:
+ raise EnvironmentException('Could not get define {!r}'.format(dname))
+ # Get the preprocessed value after the delimiter,
+ # minus the extra newline at the end
+ return p.stdo.split(delim + '\n')[-1][:-1]
+
@staticmethod
def _no_prototype_templ():
"""
@@ -2301,6 +2309,7 @@ class VisualStudioCPPCompiler(VisualStudioCCompiler, CPPCompiler):
GCC_STANDARD = 0
GCC_OSX = 1
GCC_MINGW = 2
+GCC_CYGWIN = 3
CLANG_STANDARD = 0
CLANG_OSX = 1
@@ -2316,7 +2325,7 @@ def get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, i
sostr = ''
else:
sostr = '.' + soversion
- if gcc_type == GCC_STANDARD or gcc_type == GCC_MINGW:
+ if gcc_type in (GCC_STANDARD, GCC_MINGW, GCC_CYGWIN):
# Might not be correct for mingw but seems to work.
return ['-Wl,-soname,%s%s.%s%s' % (prefix, shlib_name, suffix, sostr)]
elif gcc_type == GCC_OSX:
@@ -2382,15 +2391,15 @@ class GnuCompiler:
args[args.index('-Wpedantic')] = '-pedantic'
return args
- def has_define(self, define):
+ def has_builtin_define(self, define):
return define in self.defines
- def get_define(self, define):
+ def get_builtin_define(self, define):
if define in self.defines:
return self.defines[define]
def get_pic_args(self):
- if self.gcc_type in (GCC_MINGW, GCC_OSX):
+ if self.gcc_type in (GCC_CYGWIN, GCC_MINGW, GCC_OSX):
return [] # On Window and OS X, pic is always on.
return ['-fPIC']
@@ -2788,7 +2797,7 @@ class FortranCompiler(Compiler):
return ' '.join(self.exelist)
def get_pic_args(self):
- if self.gcc_type in (GCC_MINGW, GCC_OSX):
+ if self.gcc_type in (GCC_CYGWIN, GCC_MINGW, GCC_OSX):
return [] # On Window and OS X, pic is always on.
return ['-fPIC']
@@ -2896,10 +2905,10 @@ class GnuFortranCompiler(FortranCompiler):
self.defines = defines or {}
self.id = 'gcc'
- def has_define(self, define):
+ def has_builtin_define(self, define):
return define in self.defines
- def get_define(self, define):
+ def get_builtin_define(self, define):
if define in self.defines:
return self.defines[define]
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index 51eeaff..27f1dd7 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -17,6 +17,7 @@ from pathlib import PurePath
from collections import OrderedDict
from .mesonlib import MesonException, commonpath
from .mesonlib import default_libdir, default_libexecdir, default_prefix
+import ast
version = '0.40.0.dev1'
backendlist = ['ninja', 'vs', 'vs2010', 'vs2015', 'vs2017', 'xcode']
@@ -31,6 +32,12 @@ class UserOption:
def parse_string(self, valuestring):
return valuestring
+ # Check that the input is a valid value and return the
+ # "cleaned" or "native" version. For example the Boolean
+ # option could take the string "true" and return True.
+ def validate_value(self, value):
+ raise RuntimeError('Derived option class did not override validate_value.')
+
class UserStringOption(UserOption):
def __init__(self, name, description, value, choices=None):
super().__init__(name, description, choices)
@@ -44,6 +51,10 @@ class UserStringOption(UserOption):
self.validate(newvalue)
self.value = newvalue
+ def validate_value(self, value):
+ self.validate(value)
+ return value
+
class UserBooleanOption(UserOption):
def __init__(self, name, description, value):
super().__init__(name, description, [True, False])
@@ -71,6 +82,9 @@ class UserBooleanOption(UserOption):
def __bool__(self):
return self.value
+ def validate_value(self, value):
+ return self.tobool(value)
+
class UserComboOption(UserOption):
def __init__(self, name, description, choices, value):
super().__init__(name, description, choices)
@@ -87,22 +101,36 @@ class UserComboOption(UserOption):
raise MesonException('Value "%s" for combo option "%s" is not one of the choices. Possible choices are: %s.' % (newvalue, self.name, optionsstring))
self.value = newvalue
+ def validate_value(self, value):
+ if value not in self.choices:
+ raise MesonException('Value %s not one of accepted values.' % value)
+ return value
+
class UserStringArrayOption(UserOption):
def __init__(self, name, description, value, **kwargs):
super().__init__(name, description, kwargs.get('choices', []))
self.set_value(value)
- def set_value(self, newvalue):
- if isinstance(newvalue, str):
- if not newvalue.startswith('['):
- raise MesonException('Valuestring does not define an array: ' + newvalue)
- newvalue = eval(newvalue, {}, {}) # Yes, it is unsafe.
+ def validate(self, value):
+ if isinstance(value, str):
+ if not value.startswith('['):
+ raise MesonException('Valuestring does not define an array: ' + value)
+ newvalue = ast.literal_eval(value)
+ else:
+ newvalue = value
if not isinstance(newvalue, list):
raise MesonException('"{0}" should be a string array, but it is not'.format(str(newvalue)))
for i in newvalue:
if not isinstance(i, str):
raise MesonException('String array element "{0}" is not a string.'.format(str(newvalue)))
- self.value = newvalue
+ return newvalue
+
+ def set_value(self, newvalue):
+ self.value = self.validate(newvalue)
+
+ def validate_value(self, value):
+ self.validate(value)
+ return value
# This class contains all data that must persist over multiple
# invocations of Meson. It is roughly the same thing as
@@ -120,10 +148,11 @@ class CoreData:
self.user_options = {}
self.compiler_options = {}
self.base_options = {}
- # These two, external_*args, are set via env vars CFLAGS, LDFLAGS, etc
+ # These external_*args, are set via env vars CFLAGS, LDFLAGS, etc
# but only when not cross-compiling.
- self.external_args = {}
- self.external_link_args = {}
+ self.external_preprocess_args = {} # CPPFLAGS only
+ self.external_args = {} # CPPFLAGS + CFLAGS
+ self.external_link_args = {} # CFLAGS + LDFLAGS (with MSVC: only LDFLAGS)
if options.cross_file is not None:
self.cross_file = os.path.join(os.getcwd(), options.cross_file)
else:
@@ -204,6 +233,13 @@ class CoreData:
raise RuntimeError('Tried to set unknown builtin option %s.' % optname)
self.builtins[optname].set_value(value)
+ def validate_option_value(self, option_name, override_value):
+ for opts in (self.builtins, self.base_options, self.compiler_options, self.user_options):
+ if option_name in opts:
+ opt = opts[option_name]
+ return opt.validate_value(override_value)
+ raise MesonException('Tried to validate unknown option %s.' % option_name)
+
def load(filename):
load_fail_msg = 'Coredata file {!r} is corrupted. Try with a fresh build tree.'.format(filename)
try:
diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py
index eacb15b..25ca411 100644
--- a/mesonbuild/dependencies.py
+++ b/mesonbuild/dependencies.py
@@ -24,6 +24,7 @@ import sys
import os, stat, glob, shutil
import subprocess
import sysconfig
+from enum import Enum
from collections import OrderedDict
from . mesonlib import MesonException, version_compare, version_compare_many, Popen_safe
from . import mlog
@@ -33,11 +34,35 @@ from .environment import detect_cpu_family, for_windows
class DependencyException(MesonException):
'''Exceptions raised while trying to find dependencies'''
+class DependencyMethods(Enum):
+ # Auto means to use whatever dependency checking mechanisms in whatever order meson thinks is best.
+ AUTO = 'auto'
+ PKGCONFIG = 'pkg-config'
+ QMAKE = 'qmake'
+ # Just specify the standard link arguments, assuming the operating system provides the library.
+ SYSTEM = 'system'
+ # Detect using sdl2-config
+ SDLCONFIG = 'sdlconfig'
+ # This is only supported on OSX - search the frameworks directory by name.
+ EXTRAFRAMEWORK = 'extraframework'
+ # Detect using the sysconfig module.
+ SYSCONFIG = 'sysconfig'
+
class Dependency:
- def __init__(self, type_name='unknown'):
+ def __init__(self, type_name, kwargs):
self.name = "null"
self.is_found = False
self.type_name = type_name
+ method = DependencyMethods(kwargs.get('method', 'auto'))
+
+ # Set the detection method. If the method is set to auto, use any available method.
+ # If method is set to a specific string, allow only that detection method.
+ if method == DependencyMethods.AUTO:
+ self.methods = self.get_methods()
+ elif method in self.get_methods():
+ self.methods = [method]
+ else:
+ raise MesonException('Unsupported detection method: {}, allowed methods are {}'.format(method.value, mlog.format_list(map(lambda x: x.value, [DependencyMethods.AUTO] + self.get_methods()))))
def __repr__(self):
s = '<{0} {1}: {2}>'
@@ -57,6 +82,9 @@ class Dependency:
As an example, gtest-all.cc when using GTest."""
return []
+ def get_methods(self):
+ return [DependencyMethods.AUTO]
+
def get_name(self):
return self.name
@@ -71,7 +99,7 @@ class Dependency:
class InternalDependency(Dependency):
def __init__(self, version, incdirs, compile_args, link_args, libraries, sources, ext_deps):
- super().__init__('internal')
+ super().__init__('internal', {})
self.version = version
self.include_directories = incdirs
self.compile_args = compile_args
@@ -95,7 +123,7 @@ class PkgConfigDependency(Dependency):
class_pkgbin = None
def __init__(self, name, environment, kwargs):
- Dependency.__init__(self, 'pkgconfig')
+ Dependency.__init__(self, 'pkgconfig', kwargs)
self.is_libtool = False
self.required = kwargs.get('required', True)
self.static = kwargs.get('static', False)
@@ -254,6 +282,9 @@ class PkgConfigDependency(Dependency):
def get_link_args(self):
return self.libs
+ def get_methods(self):
+ return [DependencyMethods.PKGCONFIG]
+
def check_pkgconfig(self):
evar = 'PKG_CONFIG'
if evar in os.environ:
@@ -322,7 +353,7 @@ class WxDependency(Dependency):
wx_found = None
def __init__(self, environment, kwargs):
- Dependency.__init__(self, 'wx')
+ Dependency.__init__(self, 'wx', kwargs)
self.is_found = False
self.modversion = 'none'
if WxDependency.wx_found is None:
@@ -542,7 +573,7 @@ class ExternalLibrary(Dependency):
# TODO: Add `lang` to the parent Dependency object so that dependencies can
# be expressed for languages other than C-like
def __init__(self, name, link_args=None, language=None, silent=False):
- super().__init__('external')
+ super().__init__('external', {})
self.name = name
self.is_found = False
self.link_args = []
@@ -582,7 +613,7 @@ class BoostDependency(Dependency):
name2lib = {'test': 'unit_test_framework'}
def __init__(self, environment, kwargs):
- Dependency.__init__(self, 'boost')
+ Dependency.__init__(self, 'boost', kwargs)
self.name = 'boost'
self.environment = environment
self.libdir = ''
@@ -598,12 +629,18 @@ class BoostDependency(Dependency):
self.boost_root = None
if self.boost_root is None:
if self.want_cross:
- raise DependencyException('BOOST_ROOT is needed while cross-compiling')
+ if 'BOOST_INCLUDEDIR' in os.environ:
+ self.incdir = os.environ['BOOST_INCLUDEDIR']
+ else:
+ raise DependencyException('BOOST_ROOT or BOOST_INCLUDEDIR is needed while cross-compiling')
if mesonlib.is_windows():
self.boost_root = self.detect_win_root()
self.incdir = self.boost_root
else:
- self.incdir = '/usr/include'
+ if 'BOOST_INCLUDEDIR' in os.environ:
+ self.incdir = os.environ['BOOST_INCLUDEDIR']
+ else:
+ self.incdir = '/usr/include'
else:
self.incdir = os.path.join(self.boost_root, 'include')
self.boost_inc_subdir = os.path.join(self.incdir, 'boost')
@@ -727,7 +764,9 @@ class BoostDependency(Dependency):
libsuffix = 'so'
globber = 'libboost_*.{}'.format(libsuffix)
- if self.boost_root is None:
+ if 'BOOST_LIBRARYDIR' in os.environ:
+ libdirs = [os.environ['BOOST_LIBRARYDIR']]
+ elif self.boost_root is None:
libdirs = mesonlib.get_library_dirs()
else:
libdirs = [os.path.join(self.boost_root, 'lib')]
@@ -758,6 +797,8 @@ class BoostDependency(Dependency):
args = []
if self.boost_root:
args.append('-L' + os.path.join(self.boost_root, 'lib'))
+ elif 'BOOST_LIBRARYDIR' in os.environ:
+ args.append('-L' + os.environ['BOOST_LIBRARYDIR'])
for module in self.requested_modules:
module = BoostDependency.name2lib.get(module, module)
libname = 'boost_' + module
@@ -795,7 +836,7 @@ class BoostDependency(Dependency):
class GTestDependency(Dependency):
def __init__(self, environment, kwargs):
- Dependency.__init__(self, 'gtest')
+ Dependency.__init__(self, 'gtest', kwargs)
self.main = kwargs.get('main', False)
self.name = 'gtest'
self.libname = 'libgtest.so'
@@ -872,7 +913,7 @@ class GTestDependency(Dependency):
class GMockDependency(Dependency):
def __init__(self, environment, kwargs):
- Dependency.__init__(self, 'gmock')
+ Dependency.__init__(self, 'gmock', kwargs)
# GMock may be a library or just source.
# Work with both.
self.name = 'gmock'
@@ -927,7 +968,7 @@ class GMockDependency(Dependency):
class QtBaseDependency(Dependency):
def __init__(self, name, env, kwargs):
- Dependency.__init__(self, name)
+ Dependency.__init__(self, name, kwargs)
self.name = name
self.qtname = name.capitalize()
self.qtver = name[-1]
@@ -954,26 +995,32 @@ class QtBaseDependency(Dependency):
type_text = 'cross' if env.is_cross_build() else 'native'
found_msg = '{} {} {{}} dependency (modules: {}) found:' \
''.format(self.qtname, type_text, ', '.join(mods))
- from_text = '`pkg-config`'
+ from_text = 'pkg-config'
+
+ # Keep track of the detection methods used, for logging purposes.
+ methods = []
# Prefer pkg-config, then fallback to `qmake -query`
- self._pkgconfig_detect(mods, env, kwargs)
- if not self.is_found:
+ if DependencyMethods.PKGCONFIG in self.methods:
+ self._pkgconfig_detect(mods, env, kwargs)
+ methods.append('pkgconfig')
+ if not self.is_found and DependencyMethods.QMAKE in self.methods:
from_text = self._qmake_detect(mods, env, kwargs)
- if not self.is_found:
- # Reset compile args and link args
- self.cargs = []
- self.largs = []
- from_text = '(checked pkg-config, qmake-{}, and qmake)' \
- ''.format(self.name)
- self.version = 'none'
- if self.required:
- err_msg = '{} {} dependency not found {}' \
- ''.format(self.qtname, type_text, from_text)
- raise DependencyException(err_msg)
- if not self.silent:
- mlog.log(found_msg.format(from_text), mlog.red('NO'))
- return
- from_text = '`{}`'.format(from_text)
+ methods.append('qmake-' + self.name)
+ methods.append('qmake')
+ if not self.is_found:
+ # Reset compile args and link args
+ self.cargs = []
+ self.largs = []
+ from_text = '(checked {})'.format(mlog.format_list(methods))
+ self.version = 'none'
+ if self.required:
+ err_msg = '{} {} dependency not found {}' \
+ ''.format(self.qtname, type_text, from_text)
+ raise DependencyException(err_msg)
+ if not self.silent:
+ mlog.log(found_msg.format(from_text), mlog.red('NO'))
+ return
+ from_text = '`{}`'.format(from_text)
if not self.silent:
mlog.log(found_msg.format(from_text), mlog.green('YES'))
@@ -1043,6 +1090,7 @@ class QtBaseDependency(Dependency):
return
self.version = re.search(self.qtver + '(\.\d+)+', stdo).group(0)
# Query library path, header path, and binary path
+ mlog.log("Found qmake:", mlog.bold(self.qmake.get_name()), '(%s)' % self.version)
stdo = Popen_safe(self.qmake.get_command() + ['-query'])[1]
qvars = {}
for line in stdo.split('\n'):
@@ -1082,7 +1130,7 @@ class QtBaseDependency(Dependency):
libdir = qvars['QT_INSTALL_LIBS']
for m in modules:
fname = 'Qt' + m
- fwdep = ExtraFrameworkDependency(fname, kwargs.get('required', True), libdir)
+ fwdep = ExtraFrameworkDependency(fname, kwargs.get('required', True), libdir, kwargs)
self.cargs.append('-F' + libdir)
if fwdep.found():
self.is_found = True
@@ -1103,6 +1151,9 @@ class QtBaseDependency(Dependency):
def get_link_args(self):
return self.largs
+ def get_methods(self):
+ return [DependencyMethods.PKGCONFIG, DependencyMethods.QMAKE]
+
def found(self):
return self.is_found
@@ -1140,7 +1191,7 @@ class Qt4Dependency(QtBaseDependency):
class GnuStepDependency(Dependency):
def __init__(self, environment, kwargs):
- Dependency.__init__(self, 'gnustep')
+ Dependency.__init__(self, 'gnustep', kwargs)
self.required = kwargs.get('required', True)
self.modules = kwargs.get('modules', [])
self.detect()
@@ -1238,7 +1289,7 @@ why. As a hack filter out everything that is not a flag."""
class AppleFrameworks(Dependency):
def __init__(self, environment, kwargs):
- Dependency.__init__(self, 'appleframeworks')
+ Dependency.__init__(self, 'appleframeworks', kwargs)
modules = kwargs.get('modules', [])
if isinstance(modules, str):
modules = [modules]
@@ -1261,31 +1312,33 @@ class AppleFrameworks(Dependency):
class GLDependency(Dependency):
def __init__(self, environment, kwargs):
- Dependency.__init__(self, 'gl')
+ Dependency.__init__(self, 'gl', kwargs)
self.is_found = False
self.cargs = []
self.linkargs = []
- try:
- pcdep = PkgConfigDependency('gl', environment, kwargs)
- if pcdep.found():
- self.type_name = 'pkgconfig'
+ if DependencyMethods.PKGCONFIG in self.methods:
+ try:
+ pcdep = PkgConfigDependency('gl', environment, kwargs)
+ if pcdep.found():
+ self.type_name = 'pkgconfig'
+ self.is_found = True
+ self.cargs = pcdep.get_compile_args()
+ self.linkargs = pcdep.get_link_args()
+ self.version = pcdep.get_version()
+ return
+ except Exception:
+ pass
+ if DependencyMethods.SYSTEM in self.methods:
+ if mesonlib.is_osx():
self.is_found = True
- self.cargs = pcdep.get_compile_args()
- self.linkargs = pcdep.get_link_args()
- self.version = pcdep.get_version()
+ self.linkargs = ['-framework', 'OpenGL']
+ self.version = '1' # FIXME
+ return
+ if mesonlib.is_windows():
+ self.is_found = True
+ self.linkargs = ['-lopengl32']
+ self.version = '1' # FIXME: unfixable?
return
- except Exception:
- pass
- if mesonlib.is_osx():
- self.is_found = True
- self.linkargs = ['-framework', 'OpenGL']
- self.version = '1' # FIXME
- return
- if mesonlib.is_windows():
- self.is_found = True
- self.linkargs = ['-lopengl32']
- self.version = '1' # FIXME: unfixable?
- return
def get_link_args(self):
return self.linkargs
@@ -1293,48 +1346,57 @@ class GLDependency(Dependency):
def get_version(self):
return self.version
+ def get_methods(self):
+ if mesonlib.is_osx() or mesonlib.is_windows():
+ return [DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM]
+ else:
+ return [DependencyMethods.PKGCONFIG]
+
# There are three different ways of depending on SDL2:
# sdl2-config, pkg-config and OSX framework
class SDL2Dependency(Dependency):
def __init__(self, environment, kwargs):
- Dependency.__init__(self, 'sdl2')
+ Dependency.__init__(self, 'sdl2', kwargs)
self.is_found = False
self.cargs = []
self.linkargs = []
- try:
- pcdep = PkgConfigDependency('sdl2', environment, kwargs)
- if pcdep.found():
- self.type_name = 'pkgconfig'
- self.is_found = True
- self.cargs = pcdep.get_compile_args()
- self.linkargs = pcdep.get_link_args()
- self.version = pcdep.get_version()
- return
- except Exception as e:
- mlog.debug('SDL 2 not found via pkgconfig. Trying next, error was:', str(e))
- pass
- sdlconf = shutil.which('sdl2-config')
- if sdlconf:
- stdo = Popen_safe(['sdl2-config', '--cflags'])[1]
- self.cargs = stdo.strip().split()
- stdo = Popen_safe(['sdl2-config', '--libs'])[1]
- self.linkargs = stdo.strip().split()
- stdo = Popen_safe(['sdl2-config', '--version'])[1]
- self.version = stdo.strip()
- self.is_found = True
- mlog.log('Dependency', mlog.bold('sdl2'), 'found:', mlog.green('YES'),
- self.version, '(%s)' % sdlconf)
- return
- mlog.debug('Could not find sdl2-config binary, trying next.')
- if mesonlib.is_osx():
- fwdep = ExtraFrameworkDependency('sdl2', kwargs.get('required', True))
- if fwdep.found():
+ if DependencyMethods.PKGCONFIG in self.methods:
+ try:
+ pcdep = PkgConfigDependency('sdl2', environment, kwargs)
+ if pcdep.found():
+ self.type_name = 'pkgconfig'
+ self.is_found = True
+ self.cargs = pcdep.get_compile_args()
+ self.linkargs = pcdep.get_link_args()
+ self.version = pcdep.get_version()
+ return
+ except Exception as e:
+ mlog.debug('SDL 2 not found via pkgconfig. Trying next, error was:', str(e))
+ pass
+ if DependencyMethods.SDLCONFIG in self.methods:
+ sdlconf = shutil.which('sdl2-config')
+ if sdlconf:
+ stdo = Popen_safe(['sdl2-config', '--cflags'])[1]
+ self.cargs = stdo.strip().split()
+ stdo = Popen_safe(['sdl2-config', '--libs'])[1]
+ self.linkargs = stdo.strip().split()
+ stdo = Popen_safe(['sdl2-config', '--version'])[1]
+ self.version = stdo.strip()
self.is_found = True
- self.cargs = fwdep.get_compile_args()
- self.linkargs = fwdep.get_link_args()
- self.version = '2' # FIXME
+ mlog.log('Dependency', mlog.bold('sdl2'), 'found:', mlog.green('YES'),
+ self.version, '(%s)' % sdlconf)
return
- mlog.log('Dependency', mlog.bold('sdl2'), 'found:', mlog.red('NO'))
+ mlog.debug('Could not find sdl2-config binary, trying next.')
+ if DependencyMethods.EXTRAFRAMEWORK in self.methods:
+ if mesonlib.is_osx():
+ fwdep = ExtraFrameworkDependency('sdl2', kwargs.get('required', True), None, kwargs)
+ if fwdep.found():
+ self.is_found = True
+ self.cargs = fwdep.get_compile_args()
+ self.linkargs = fwdep.get_link_args()
+ self.version = '2' # FIXME
+ return
+ mlog.log('Dependency', mlog.bold('sdl2'), 'found:', mlog.red('NO'))
def get_compile_args(self):
return self.cargs
@@ -1348,9 +1410,15 @@ class SDL2Dependency(Dependency):
def get_version(self):
return self.version
+ def get_methods(self):
+ if mesonlib.is_osx():
+ return [DependencyMethods.PKGCONFIG, DependencyMethods.SDLCONFIG, DependencyMethods.EXTRAFRAMEWORK]
+ else:
+ return [DependencyMethods.PKGCONFIG, DependencyMethods.SDLCONFIG]
+
class ExtraFrameworkDependency(Dependency):
- def __init__(self, name, required, path=None):
- Dependency.__init__(self, 'extraframeworks')
+ def __init__(self, name, required, path, kwargs):
+ Dependency.__init__(self, 'extraframeworks', kwargs)
self.name = None
self.detect(name, path)
if self.found():
@@ -1394,7 +1462,7 @@ class ExtraFrameworkDependency(Dependency):
class ThreadDependency(Dependency):
def __init__(self, environment, kwargs):
- super().__init__('threads')
+ super().__init__('threads', {})
self.name = 'threads'
self.is_found = True
mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.green('YES'))
@@ -1407,28 +1475,29 @@ class ThreadDependency(Dependency):
class Python3Dependency(Dependency):
def __init__(self, environment, kwargs):
- super().__init__('python3')
+ super().__init__('python3', kwargs)
self.name = 'python3'
self.is_found = False
# We can only be sure that it is Python 3 at this point
self.version = '3'
- try:
- pkgdep = PkgConfigDependency('python3', environment, kwargs)
- if pkgdep.found():
- self.cargs = pkgdep.cargs
- self.libs = pkgdep.libs
- self.version = pkgdep.get_version()
- self.is_found = True
- return
- except Exception:
- pass
+ if DependencyMethods.PKGCONFIG in self.methods:
+ try:
+ pkgdep = PkgConfigDependency('python3', environment, kwargs)
+ if pkgdep.found():
+ self.cargs = pkgdep.cargs
+ self.libs = pkgdep.libs
+ self.version = pkgdep.get_version()
+ self.is_found = True
+ return
+ except Exception:
+ pass
if not self.is_found:
- if mesonlib.is_windows():
+ if mesonlib.is_windows() and DependencyMethods.SYSCONFIG in self.methods:
self._find_libpy3_windows(environment)
- elif mesonlib.is_osx():
+ elif mesonlib.is_osx() and DependencyMethods.EXTRAFRAMEWORK in self.methods:
# In OSX the Python 3 framework does not have a version
# number in its name.
- fw = ExtraFrameworkDependency('python', False)
+ fw = ExtraFrameworkDependency('python', False, None, kwargs)
if fw.found():
self.cargs = fw.get_compile_args()
self.libs = fw.get_link_args()
@@ -1480,9 +1549,25 @@ class Python3Dependency(Dependency):
def get_link_args(self):
return self.libs
+ def get_methods(self):
+ if mesonlib.is_windows():
+ return [DependencyMethods.PKGCONFIG, DependencyMethods.SYSCONFIG]
+ elif mesonlib.is_osx():
+ return [DependencyMethods.PKGCONFIG, DependencyMethods.EXTRAFRAMEWORK]
+ else:
+ return [DependencyMethods.PKGCONFIG]
+
def get_version(self):
return self.version
+class ValgrindDependency(PkgConfigDependency):
+
+ def __init__(self, environment, kwargs):
+ PkgConfigDependency.__init__(self, 'valgrind', environment, kwargs)
+
+ def get_link_args(self):
+ return []
+
def get_dep_identifier(name, kwargs):
elements = [name]
modlist = kwargs.get('modules', [])
@@ -1504,6 +1589,8 @@ def find_external_dependency(name, environment, kwargs):
required = kwargs.get('required', True)
if not isinstance(required, bool):
raise DependencyException('Keyword "required" must be a boolean.')
+ if not isinstance(kwargs.get('method', ''), str):
+ raise DependencyException('Keyword "method" must be a string.')
lname = name.lower()
if lname in packages:
dep = packages[lname](environment, kwargs)
@@ -1519,7 +1606,7 @@ def find_external_dependency(name, environment, kwargs):
except Exception as e:
pkg_exc = e
if mesonlib.is_osx():
- fwdep = ExtraFrameworkDependency(name, required)
+ fwdep = ExtraFrameworkDependency(name, required, None, kwargs)
if required and not fwdep.found():
m = 'Dependency {!r} not found, tried Extra Frameworks ' \
'and Pkg-Config:\n\n' + str(pkg_exc)
@@ -1544,4 +1631,5 @@ packages = {'boost': BoostDependency,
'gl': GLDependency,
'threads': ThreadDependency,
'python3': Python3Dependency,
+ 'valgrind': ValgrindDependency,
}
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index 92040c4..93a41e8 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -102,7 +102,7 @@ def detect_windows_arch(compilers):
platform = os.environ.get('Platform', 'x86').lower()
if platform == 'x86':
return platform
- if compiler.id == 'gcc' and compiler.has_define('__i386__'):
+ if compiler.id == 'gcc' and compiler.has_builtin_define('__i386__'):
return 'x86'
return os_arch
@@ -129,10 +129,10 @@ def detect_cpu_family(compilers):
# to know is to check the compiler defines.
for c in compilers.values():
try:
- if c.has_define('__i386__'):
+ if c.has_builtin_define('__i386__'):
return 'x86'
except mesonlib.MesonException:
- # Ignore compilers that do not support has_define.
+ # Ignore compilers that do not support has_builtin_define.
pass
return 'x86_64'
# Add fixes here as bugs are reported.
@@ -149,7 +149,7 @@ def detect_cpu(compilers):
# Same check as above for cpu_family
for c in compilers.values():
try:
- if c.has_define('__i386__'):
+ if c.has_builtin_define('__i386__'):
return 'i686' # All 64 bit cpus have at least this level of x86 support.
except mesonlib.MesonException:
pass
@@ -158,7 +158,10 @@ def detect_cpu(compilers):
return trial
def detect_system():
- return platform.system().lower()
+ system = platform.system().lower()
+ if system.startswith('cygwin'):
+ return 'cygwin'
+ return system
def for_windows(is_cross, env):
@@ -173,6 +176,20 @@ def for_windows(is_cross, env):
return env.cross_info.config['host_machine']['system'] == 'windows'
return False
+
+def for_cygwin(is_cross, env):
+ """
+ Host machine is cygwin?
+
+ Note: 'host' is the machine on which compiled binaries will run
+ """
+ if not is_cross:
+ return mesonlib.is_cygwin()
+ elif env.cross_info.has_host():
+ return env.cross_info.config['host_machine']['system'] == 'cygwin'
+ return False
+
+
def for_darwin(is_cross, env):
"""
Host machine is Darwin (iOS/OS X)?
@@ -257,6 +274,11 @@ class Environment:
self.exe_suffix = 'exe'
self.object_suffix = 'obj'
self.win_libdir_layout = True
+ elif (not cross and mesonlib.is_cygwin()) \
+ or (cross and self.cross_info.has_host() and self.cross_info.config['host_machine']['system'] == 'cygwin'):
+ self.exe_suffix = 'exe'
+ self.object_suffix = 'o'
+ self.win_libdir_layout = True
else:
self.exe_suffix = ''
self.object_suffix = 'o'
@@ -368,7 +390,8 @@ class Environment:
return GCC_OSX
elif '__MINGW32__' in defines or '__MINGW64__' in defines:
return GCC_MINGW
- # We ignore Cygwin for now, and treat it as a standard GCC
+ elif '__CYGWIN__' in defines:
+ return GCC_CYGWIN
return GCC_STANDARD
def _get_compilers(self, lang, evar, want_cross):
@@ -718,6 +741,10 @@ class Environment:
"Install dir for the import library (library used for linking)"
return self.get_libdir()
+ def get_shared_module_dir(self):
+ "Install dir for shared modules that are loaded at runtime"
+ return self.get_libdir()
+
def get_shared_lib_dir(self):
"Install dir for the shared library"
if self.win_libdir_layout:
@@ -770,7 +797,7 @@ def get_args_from_envvars(compiler):
compiler_is_linker = (compiler.get_exelist() == compiler.get_linker_exelist())
if lang not in ('c', 'cpp', 'objc', 'objcpp', 'fortran', 'd'):
- return [], []
+ return [], [], []
# Compile flags
cflags_mapping = {'c': 'CFLAGS',
@@ -781,12 +808,12 @@ def get_args_from_envvars(compiler):
'd': 'DFLAGS'}
compile_flags = os.environ.get(cflags_mapping[lang], '')
log_var(cflags_mapping[lang], compile_flags)
- compile_flags = compile_flags.split()
+ compile_flags = shlex.split(compile_flags)
# Link flags (same for all languages)
link_flags = os.environ.get('LDFLAGS', '')
log_var('LDFLAGS', link_flags)
- link_flags = link_flags.split()
+ link_flags = shlex.split(link_flags)
if compiler_is_linker:
# When the compiler is used as a wrapper around the linker (such as
# with GCC and Clang), the compile flags can be needed while linking
@@ -794,14 +821,15 @@ def get_args_from_envvars(compiler):
# this when the linker is stand-alone such as with MSVC C/C++, etc.
link_flags = compile_flags + link_flags
- # Pre-processof rlags (not for fortran)
+ # Pre-processor flags (not for fortran or D)
preproc_flags = ''
if lang in ('c', 'cpp', 'objc', 'objcpp'):
preproc_flags = os.environ.get('CPPFLAGS', '')
log_var('CPPFLAGS', preproc_flags)
- compile_flags += preproc_flags.split()
+ preproc_flags = shlex.split(preproc_flags)
+ compile_flags += preproc_flags
- return compile_flags, link_flags
+ return preproc_flags, compile_flags, link_flags
class CrossBuildInfo:
def __init__(self, filename):
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 0c6d980..18f91ea 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -22,7 +22,7 @@ from . import optinterpreter
from . import compilers
from .wrap import wrap, WrapMode
from . import mesonlib
-from .mesonlib import FileMode, Popen_safe
+from .mesonlib import FileMode, Popen_safe, get_meson_script
from .dependencies import InternalDependency, Dependency
from .interpreterbase import InterpreterBase
from .interpreterbase import check_stringlist, noPosargs, noKwargs, stringArgs
@@ -72,20 +72,21 @@ class TryRunResultHolder(InterpreterObject):
class RunProcess(InterpreterObject):
- def __init__(self, command_array, source_dir, build_dir, subdir, in_builddir=False):
+ def __init__(self, command_array, source_dir, build_dir, subdir, mesonintrospect, in_builddir=False):
super().__init__()
- pc, self.stdout, self.stderr = self.run_command(command_array, source_dir, build_dir, subdir, in_builddir)
+ pc, self.stdout, self.stderr = self.run_command(command_array, source_dir, build_dir, subdir, mesonintrospect, in_builddir)
self.returncode = pc.returncode
self.methods.update({'returncode': self.returncode_method,
'stdout': self.stdout_method,
'stderr': self.stderr_method,
})
- def run_command(self, command_array, source_dir, build_dir, subdir, in_builddir):
+ def run_command(self, command_array, source_dir, build_dir, subdir, mesonintrospect, in_builddir):
cmd_name = command_array[0]
env = {'MESON_SOURCE_ROOT': source_dir,
'MESON_BUILD_ROOT': build_dir,
- 'MESON_SUBDIR': subdir}
+ 'MESON_SUBDIR': subdir,
+ 'MESONINTROSPECT': mesonintrospect}
if in_builddir:
cwd = os.path.join(build_dir, subdir)
else:
@@ -634,6 +635,7 @@ class CompilerHolder(InterpreterObject):
'get_id': self.get_id_method,
'compute_int': self.compute_int_method,
'sizeof': self.sizeof_method,
+ 'get_define': self.get_define_method,
'has_header': self.has_header_method,
'has_header_symbol': self.has_header_symbol_method,
'run': self.run_method,
@@ -865,6 +867,20 @@ class CompilerHolder(InterpreterObject):
mlog.log('Checking for size of "%s": %d' % (element, esize))
return esize
+ def get_define_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('get_define() takes exactly one argument.')
+ check_stringlist(args)
+ element = args[0]
+ prefix = kwargs.get('prefix', '')
+ if not isinstance(prefix, str):
+ raise InterpreterException('Prefix argument of get_define() must be a string.')
+ extra_args = self.determine_args(kwargs)
+ deps = self.determine_dependencies(kwargs)
+ value = self.compiler.get_define(element, prefix, self.environment, extra_args, deps)
+ mlog.log('Checking for value of define "%s": %s' % (element, value))
+ return value
+
def compiles_method(self, args, kwargs):
if len(args) != 1:
raise InterpreterException('compiles method takes exactly one argument.')
@@ -1093,7 +1109,7 @@ class MesonMain(InterpreterObject):
found = self._found_source_scripts[key]
else:
found = dependencies.ExternalProgram(name, search_dir=search_dir)
- if found:
+ if found.found():
self._found_source_scripts[key] = found
else:
raise InterpreterException('Script {!r} not found'.format(name))
@@ -1224,7 +1240,8 @@ class Interpreter(InterpreterBase):
self.builtin.update({'meson': MesonMain(build, self)})
self.generators = []
self.visited_subdirs = {}
- self.args_frozen = False
+ self.project_args_frozen = False
+ self.global_args_frozen = False # implies self.project_args_frozen
self.subprojects = {}
self.subproject_stack = []
self.default_project_options = default_project_options[:] # Passed from the outside, only used in subprojects.
@@ -1474,8 +1491,8 @@ class Interpreter(InterpreterBase):
in_builddir = kwargs.get('in_builddir', False)
if not isinstance(in_builddir, bool):
raise InterpreterException('in_builddir must be boolean.')
- return RunProcess(args, self.environment.source_dir, self.environment.build_dir,
- self.subdir, in_builddir)
+ return RunProcess(args, self.environment.source_dir, self.environment.build_dir, self.subdir,
+ get_meson_script(self.environment, 'mesonintrospect'), in_builddir)
@stringArgs
def func_gettext(self, nodes, args, kwargs):
@@ -1507,7 +1524,7 @@ class Interpreter(InterpreterBase):
raise InterpreterException(msg.format(os.path.join(self.subproject_dir, dirname), e))
subdir = os.path.join(self.subproject_dir, resolved)
os.makedirs(os.path.join(self.build.environment.get_build_dir(), subdir), exist_ok=True)
- self.args_frozen = True
+ self.global_args_frozen = True
mlog.log('\nExecuting subproject ', mlog.bold(dirname), '.\n', sep='')
subi = Interpreter(self.build, self.backend, dirname, subdir, self.subproject_dir,
mesonlib.stringlistify(kwargs.get('default_options', [])))
@@ -1615,25 +1632,29 @@ class Interpreter(InterpreterBase):
def func_project(self, node, args, kwargs):
if len(args) < 1:
raise InvalidArguments('Not enough arguments to project(). Needs at least the project name.')
+ proj_name = args[0]
+ proj_langs = args[1:]
+ if ':' in proj_name:
+ raise InvalidArguments("Project name {!r} must not contain ':'".format(proj_name))
default_options = kwargs.get('default_options', [])
if self.environment.first_invocation and (len(default_options) > 0 or
len(self.default_project_options) > 0):
self.parse_default_options(default_options)
if not self.is_subproject():
- self.build.project_name = args[0]
+ self.build.project_name = proj_name
if os.path.exists(self.option_file):
oi = optinterpreter.OptionInterpreter(self.subproject,
self.build.environment.cmd_line_options.projectoptions,
)
oi.process(self.option_file)
self.build.environment.merge_options(oi.options)
- self.active_projectname = args[0]
+ self.active_projectname = proj_name
self.project_version = kwargs.get('version', 'undefined')
if self.build.project_version is None:
self.build.project_version = self.project_version
proj_license = mesonlib.stringlistify(kwargs.get('license', 'unknown'))
- self.build.dep_manifest[args[0]] = {'version': self.project_version,
- 'license': proj_license}
+ self.build.dep_manifest[proj_name] = {'version': self.project_version,
+ 'license': proj_license}
if self.subproject in self.build.projects:
raise InvalidCode('Second call to project().')
if not self.is_subproject() and 'subproject_dir' in kwargs:
@@ -1644,9 +1665,9 @@ class Interpreter(InterpreterBase):
pv = kwargs['meson_version']
if not mesonlib.version_compare(cv, pv):
raise InterpreterException('Meson version is %s but project requires %s.' % (cv, pv))
- self.build.projects[self.subproject] = args[0]
- mlog.log('Project name: ', mlog.bold(args[0]), sep='')
- self.add_languages(args[1:], True)
+ self.build.projects[self.subproject] = proj_name
+ mlog.log('Project name: ', mlog.bold(proj_name), sep='')
+ self.add_languages(proj_langs, True)
langs = self.coredata.compilers.keys()
if 'vala' in langs:
if 'c' not in langs:
@@ -1772,9 +1793,10 @@ class Interpreter(InterpreterBase):
raise
mlog.log('Native %s compiler: ' % lang, mlog.bold(' '.join(comp.get_exelist())), ' (%s %s)' % (comp.id, comp.version), sep='')
if not comp.get_language() in self.coredata.external_args:
- (ext_compile_args, ext_link_args) = environment.get_args_from_envvars(comp)
- self.coredata.external_args[comp.get_language()] = ext_compile_args
- self.coredata.external_link_args[comp.get_language()] = ext_link_args
+ (preproc_args, compile_args, link_args) = environment.get_args_from_envvars(comp)
+ self.coredata.external_preprocess_args[comp.get_language()] = preproc_args
+ self.coredata.external_args[comp.get_language()] = compile_args
+ self.coredata.external_link_args[comp.get_language()] = link_args
self.build.add_compiler(comp)
if need_cross_compiler:
mlog.log('Cross %s compiler: ' % lang, mlog.bold(' '.join(cross_comp.get_exelist())), ' (%s %s)' % (cross_comp.id, cross_comp.version), sep='')
@@ -2221,7 +2243,7 @@ class Interpreter(InterpreterBase):
if 'install_mode' not in kwargs:
return None
install_mode = []
- mode = mesonlib.stringintlistify(kwargs.get('install_mode', []))
+ mode = mesonlib.typeslistify(kwargs.get('install_mode', []), (str, int))
for m in mode:
# We skip any arguments that are set to `false`
if m is False:
@@ -2290,7 +2312,11 @@ class Interpreter(InterpreterBase):
inputfile = inputfile[0]
if not isinstance(inputfile, (str, mesonlib.File)):
raise InterpreterException('Input must be a string or a file')
- ifile_abs = os.path.join(self.environment.source_dir, self.subdir, inputfile)
+ if isinstance(inputfile, str):
+ inputfile = os.path.join(self.subdir, inputfile)
+ else:
+ inputfile = inputfile.relative_name()
+ ifile_abs = os.path.join(self.environment.source_dir, inputfile)
elif 'command' in kwargs and '@INPUT@' in kwargs['command']:
raise InterpreterException('@INPUT@ used as command argument, but no input file specified.')
# Validate output
@@ -2309,7 +2335,7 @@ class Interpreter(InterpreterBase):
if inputfile is not None:
# Normalize the path of the conffile to avoid duplicates
# This is especially important to convert '/' to '\' on Windows
- conffile = os.path.normpath(os.path.join(self.subdir, inputfile))
+ conffile = os.path.normpath(inputfile)
if conffile not in self.build_def_files:
self.build_def_files.append(conffile)
os.makedirs(os.path.join(self.environment.build_dir, self.subdir), exist_ok=True)
@@ -2415,83 +2441,51 @@ different subdirectory.
@stringArgs
def func_add_global_arguments(self, node, args, kwargs):
- if self.subproject != '':
- msg = 'Global arguments can not be set in subprojects because ' \
- 'there is no way to make that reliable.\nPlease only call ' \
- 'this if is_subproject() returns false. Alternatively, ' \
- 'define a variable that\ncontains your language-specific ' \
- 'arguments and add it to the appropriate *_args kwarg ' \
- 'in each target.'
- raise InvalidCode(msg)
- if self.args_frozen:
- msg = 'Tried to set global arguments after a build target has ' \
- 'been declared.\nThis is not permitted. Please declare all ' \
- 'global arguments before your targets.'
- raise InvalidCode(msg)
- if 'language' not in kwargs:
- raise InvalidCode('Missing language definition in add_global_arguments')
- lang = kwargs['language'].lower()
- if lang in self.build.global_args:
- self.build.global_args[lang] += args
- else:
- self.build.global_args[lang] = args
+ self.add_global_arguments(node, self.build.global_args, args, kwargs)
@stringArgs
def func_add_global_link_arguments(self, node, args, kwargs):
+ self.add_global_arguments(node, self.build.global_link_args, args, kwargs)
+
+ @stringArgs
+ def func_add_project_arguments(self, node, args, kwargs):
+ self.add_project_arguments(node, self.build.projects_args, args, kwargs)
+
+ @stringArgs
+ def func_add_project_link_arguments(self, node, args, kwargs):
+ self.add_project_arguments(node, self.build.projects_link_args, args, kwargs)
+
+ def add_global_arguments(self, node, argsdict, args, kwargs):
if self.subproject != '':
- msg = 'Global link arguments can not be set in subprojects because ' \
+ msg = 'Function \'{}\' cannot be used in subprojects because ' \
'there is no way to make that reliable.\nPlease only call ' \
'this if is_subproject() returns false. Alternatively, ' \
'define a variable that\ncontains your language-specific ' \
'arguments and add it to the appropriate *_args kwarg ' \
- 'in each target.'
+ 'in each target.'.format(node.func_name)
raise InvalidCode(msg)
- if self.args_frozen:
- msg = 'Tried to set global link arguments after a build target has ' \
- 'been declared.\nThis is not permitted. Please declare all ' \
- 'global arguments before your targets.'
+ frozen = self.project_args_frozen or self.global_args_frozen
+ self.add_arguments(node, argsdict, frozen, args, kwargs)
+
+ def add_project_arguments(self, node, argsdict, args, kwargs):
+ if self.subproject not in argsdict:
+ argsdict[self.subproject] = {}
+ self.add_arguments(node, argsdict[self.subproject],
+ self.project_args_frozen, args, kwargs)
+
+ def add_arguments(self, node, argsdict, args_frozen, args, kwargs):
+ if args_frozen:
+ msg = 'Tried to use \'{}\' after a build target has been declared.\n' \
+ 'This is not permitted. Please declare all ' \
+ 'arguments before your targets.'.format(node.func_name)
raise InvalidCode(msg)
- if 'language' not in kwargs:
- raise InvalidCode('Missing language definition in add_global_link_arguments')
- lang = kwargs['language'].lower()
- if lang in self.build.global_link_args:
- self.build.global_link_args[lang] += args
- else:
- self.build.global_link_args[lang] = args
- @stringArgs
- def func_add_project_link_arguments(self, node, args, kwargs):
- if self.args_frozen:
- msg = 'Tried to set project link arguments after a build target has ' \
- 'been declared.\nThis is not permitted. Please declare all ' \
- 'project link arguments before your targets.'
- raise InvalidCode(msg)
if 'language' not in kwargs:
- raise InvalidCode('Missing language definition in add_project_link_arguments')
- lang = kwargs['language'].lower()
- if self.subproject not in self.build.projects_link_args:
- self.build.projects_link_args[self.subproject] = {}
+ raise InvalidCode('Missing language definition in {}'.format(node.func_name))
- args = self.build.projects_link_args[self.subproject].get(lang, []) + args
- self.build.projects_link_args[self.subproject][lang] = args
-
- @stringArgs
- def func_add_project_arguments(self, node, args, kwargs):
- if self.args_frozen:
- msg = 'Tried to set project arguments after a build target has ' \
- 'been declared.\nThis is not permitted. Please declare all ' \
- 'project arguments before your targets.'
- raise InvalidCode(msg)
-
- if 'language' not in kwargs:
- raise InvalidCode('Missing language definition in add_project_arguments')
-
- if self.subproject not in self.build.projects_args:
- self.build.projects_args[self.subproject] = {}
-
- lang = kwargs['language'].lower()
- args = self.build.projects_args[self.subproject].get(lang, []) + args
- self.build.projects_args[self.subproject][lang] = args
+ for lang in mesonlib.stringlistify(kwargs['language']):
+ lang = lang.lower()
+ argsdict[lang] = argsdict.get(lang, []) + args
def func_environment(self, node, args, kwargs):
return EnvironmentVariablesHolder()
@@ -2576,7 +2570,7 @@ different subdirectory.
self.add_cross_stdlib_info(target)
l = targetholder(target, self)
self.add_target(name, l.held_object)
- self.args_frozen = True
+ self.project_args_frozen = True
return l
def get_used_languages(self, target):
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index f0bf9ee..ae2f88c 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -122,16 +122,18 @@ class File:
self.is_built = is_built
self.subdir = subdir
self.fname = fname
+ assert(isinstance(self.subdir, str))
+ assert(isinstance(self.fname, str))
def __str__(self):
- return os.path.join(self.subdir, self.fname)
+ return self.relative_name()
def __repr__(self):
ret = '<File: {0}'
if not self.is_built:
ret += ' (not built)'
ret += '>'
- return ret.format(os.path.join(self.subdir, self.fname))
+ return ret.format(self.relative_name())
@staticmethod
def from_source_file(source_root, subdir, fname):
@@ -149,15 +151,15 @@ class File:
def rel_to_builddir(self, build_to_src):
if self.is_built:
- return os.path.join(self.subdir, self.fname)
+ return self.relative_name()
else:
return os.path.join(build_to_src, self.subdir, self.fname)
def absolute_path(self, srcdir, builddir):
+ absdir = srcdir
if self.is_built:
- return os.path.join(builddir, self.subdir, self.fname)
- else:
- return os.path.join(srcdir, self.subdir, self.fname)
+ absdir = builddir
+ return os.path.join(absdir, self.relative_name())
def endswith(self, ending):
return self.fname.endswith(ending)
@@ -174,6 +176,15 @@ class File:
def relative_name(self):
return os.path.join(self.subdir, self.fname)
+def get_meson_script(env, script):
+ '''
+ Given the path of `meson.py`/`meson`, get the path of a meson script such
+ as `mesonintrospect` or `mesontest`.
+ '''
+ meson_py = env.get_build_command()
+ (base, ext) = os.path.splitext(meson_py)
+ return os.path.join(os.path.dirname(base), script + ext)
+
def get_compiler_for_source(compilers, src):
for comp in compilers:
if comp.can_compile(src):
@@ -211,6 +222,10 @@ def is_windows():
platname = platform.system().lower()
return platname == 'windows' or 'mingw' in platname
+def is_cygwin():
+ platname = platform.system().lower()
+ return platname.startswith('cygwin')
+
def is_debianlike():
return os.path.isfile('/etc/debian_version')
@@ -453,25 +468,22 @@ def replace_if_different(dst, dst_tmp):
else:
os.unlink(dst_tmp)
-def stringintlistify(item):
- if isinstance(item, (str, int)):
+def typeslistify(item, types):
+ '''
+ Ensure that type(@item) is one of @types or a
+ list of items all of which are of type @types
+ '''
+ if isinstance(item, types):
item = [item]
if not isinstance(item, list):
- raise MesonException('Item must be a list, a string, or an int')
+ raise MesonException('Item must be a list or one of {!r}'.format(types))
for i in item:
- if not isinstance(i, (str, int, type(None))):
- raise MesonException('List item must be a string or an int')
+ if i is not None and not isinstance(i, types):
+ raise MesonException('List item must be one of {!r}'.format(types))
return item
def stringlistify(item):
- if isinstance(item, str):
- item = [item]
- if not isinstance(item, list):
- raise MesonException('Item is not a list')
- for i in item:
- if not isinstance(i, str):
- raise MesonException('List item not a string.')
- return item
+ return typeslistify(item, str)
def expand_arguments(args):
expended_args = []
@@ -493,6 +505,7 @@ def expand_arguments(args):
def Popen_safe(args, write=None, stderr=subprocess.PIPE, **kwargs):
p = subprocess.Popen(args, universal_newlines=True,
+ close_fds=False,
stdout=subprocess.PIPE,
stderr=stderr, **kwargs)
o, e = p.communicate(write)
diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py
index bad756a..82ee6ba 100644
--- a/mesonbuild/mlog.py
+++ b/mesonbuild/mlog.py
@@ -99,3 +99,16 @@ def log(*args, **kwargs):
def warning(*args, **kwargs):
log(yellow('WARNING:'), *args, **kwargs)
+
+# Format a list for logging purposes as a string. It separates
+# all but the last item with commas, and the last with 'and'.
+def format_list(list):
+ l = len(list)
+ if l > 2:
+ return ' and '.join([', '.join(list[:-1]), list[-1]])
+ elif l == 2:
+ return ' and '.join(list)
+ elif l == 1:
+ return list[0]
+ else:
+ return ''
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index 481a250..6921472 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -766,6 +766,8 @@ class GnomeModule(ExtensionModule):
cmd += ['--interface-prefix', kwargs.pop('interface_prefix')]
if 'namespace' in kwargs:
cmd += ['--c-namespace', kwargs.pop('namespace')]
+ if kwargs.get('object_manager', False):
+ cmd += ['--c-generate-object-manager']
# https://git.gnome.org/browse/glib/commit/?id=ee09bb704fe9ccb24d92dd86696a0e6bb8f0dc1a
if mesonlib.version_compare(self._get_native_glib_version(state), '>= 2.51.3'):
@@ -999,7 +1001,7 @@ class GnomeModule(ExtensionModule):
target.get_subdir())
outdir = os.path.join(state.environment.get_build_dir(),
target.get_subdir())
- outfile = target.output[0][:-5] # Strip .vapi
+ outfile = target.get_outputs()[0][:-5] # Strip .vapi
ret.append('--vapidir=' + outdir)
ret.append('--girdir=' + outdir)
ret.append('--pkg=' + outfile)
@@ -1066,7 +1068,7 @@ class GnomeModule(ExtensionModule):
link_with += self._get_vapi_link_with(i.held_object)
subdir = os.path.join(state.environment.get_build_dir(),
i.held_object.get_subdir())
- gir_file = os.path.join(subdir, i.held_object.output[0])
+ gir_file = os.path.join(subdir, i.held_object.get_outputs()[0])
cmd.append(gir_file)
else:
raise MesonException('Input must be a str or GirTarget')
diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py
index e46c239..e79371f 100644
--- a/mesonbuild/modules/pkgconfig.py
+++ b/mesonbuild/modules/pkgconfig.py
@@ -76,8 +76,9 @@ class PkgConfigModule(ExtensionModule):
if isinstance(l, str):
yield l
else:
- if l.custom_install_dir:
- yield '-L${prefix}/%s ' % l.custom_install_dir
+ install_dir = l.get_custom_install_dir()[0]
+ if install_dir:
+ yield '-L${prefix}/%s ' % install_dir
else:
yield '-L${libdir}'
lname = self._get_lname(l, msg, pcfile)
diff --git a/mesonbuild/modules/python3.py b/mesonbuild/modules/python3.py
index 53e28c4..9f01043 100644
--- a/mesonbuild/modules/python3.py
+++ b/mesonbuild/modules/python3.py
@@ -13,11 +13,13 @@
# limitations under the License.
import sys
+import sysconfig
from .. import mesonlib, dependencies
from . import ExtensionModule
from mesonbuild.modules import ModuleReturnValue
+
class Python3Module(ExtensionModule):
def __init__(self):
super().__init__()
@@ -45,5 +47,25 @@ class Python3Module(ExtensionModule):
py3 = dependencies.ExternalProgram('python3', sys.executable, silent=True)
return ModuleReturnValue(py3, [py3])
+ def language_version(self, state, args, kwargs):
+ if args or kwargs:
+ raise mesonlib.MesonException('language_version() takes no arguments.')
+ return ModuleReturnValue(sysconfig.get_python_version(), [])
+
+ def sysconfig_path(self, state, args, kwargs):
+ if len(args) != 1:
+ raise mesonlib.MesonException('sysconfig_path() requires passing the name of path to get.')
+ if kwargs:
+ raise mesonlib.MesonException('sysconfig_path() does not accept keywords.')
+ path_name = args[0]
+ valid_names = sysconfig.get_path_names()
+ if path_name not in valid_names:
+ raise mesonlib.MesonException('{} is not a valid path name {}.'.format(path_name, valid_names))
+
+ # Get a relative path without a prefix, e.g. lib/python3.6/site-packages
+ path = sysconfig.get_path(path_name, vars={'base': ''})[1:]
+ return ModuleReturnValue(path, [])
+
+
def initialize():
return Python3Module()
diff --git a/mesonbuild/modules/qt4.py b/mesonbuild/modules/qt4.py
index 7146739..0386291 100644
--- a/mesonbuild/modules/qt4.py
+++ b/mesonbuild/modules/qt4.py
@@ -24,14 +24,14 @@ from . import ModuleReturnValue
class Qt4Module(ExtensionModule):
tools_detected = False
- def _detect_tools(self, env):
+ def _detect_tools(self, env, method):
if self.tools_detected:
return
mlog.log('Detecting Qt4 tools')
# FIXME: We currently require Qt4 to exist while importing the module.
# We should make it gracefully degrade and not create any targets if
# the import is marked as 'optional' (not implemented yet)
- kwargs = {'required': 'true', 'modules': 'Core', 'silent': 'true'}
+ kwargs = {'required': 'true', 'modules': 'Core', 'silent': 'true', 'method': method}
qt4 = Qt4Dependency(env, kwargs)
# Get all tools and then make sure that they are the right version
self.moc, self.uic, self.rcc = qt4.compilers_detect()
@@ -113,7 +113,8 @@ class Qt4Module(ExtensionModule):
if not isinstance(sources, list):
sources = [sources]
sources += args[1:]
- self._detect_tools(state.environment)
+ method = kwargs.get('method', 'auto')
+ self._detect_tools(state.environment, method)
err_msg = "{0} sources specified and couldn't find {1}, " \
"please check your qt4 installation"
if len(moc_headers) + len(moc_sources) > 0 and not self.moc.found():
diff --git a/mesonbuild/modules/qt5.py b/mesonbuild/modules/qt5.py
index 2a87a80..6497694 100644
--- a/mesonbuild/modules/qt5.py
+++ b/mesonbuild/modules/qt5.py
@@ -24,14 +24,14 @@ from . import ModuleReturnValue
class Qt5Module(ExtensionModule):
tools_detected = False
- def _detect_tools(self, env):
+ def _detect_tools(self, env, method):
if self.tools_detected:
return
mlog.log('Detecting Qt5 tools')
# FIXME: We currently require Qt5 to exist while importing the module.
# We should make it gracefully degrade and not create any targets if
# the import is marked as 'optional' (not implemented yet)
- kwargs = {'required': 'true', 'modules': 'Core', 'silent': 'true'}
+ kwargs = {'required': 'true', 'modules': 'Core', 'silent': 'true', 'method': method}
qt5 = Qt5Dependency(env, kwargs)
# Get all tools and then make sure that they are the right version
self.moc, self.uic, self.rcc = qt5.compilers_detect()
@@ -119,7 +119,8 @@ class Qt5Module(ExtensionModule):
if not isinstance(sources, list):
sources = [sources]
sources += args[1:]
- self._detect_tools(state.environment)
+ method = kwargs.get('method', 'auto')
+ self._detect_tools(state.environment, method)
err_msg = "{0} sources specified and couldn't find {1}, " \
"please check your qt5 installation"
if len(moc_headers) + len(moc_sources) > 0 and not self.moc.found():
diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py
index c233d90..3fb0107 100644
--- a/mesonbuild/modules/windows.py
+++ b/mesonbuild/modules/windows.py
@@ -51,9 +51,17 @@ class WindowsModule(ExtensionModule):
for arg in extra_args:
if ' ' in arg:
mlog.warning(m.format(arg))
- # Pick-up env var WINDRES if set. This is often used for specifying
- # an arch-specific windres.
- rescomp_name = os.environ.get('WINDRES', 'windres')
+ rescomp_name = None
+ # FIXME: Does not handle `native: true` executables, see
+ # https://github.com/mesonbuild/meson/issues/1531
+ 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.
+ rescomp_name = state.environment.cross_info.config['binaries'].get('windres')
+ if rescomp_name is None:
+ # Pick-up env var WINDRES if set. This is often used for
+ # specifying an arch-specific windres.
+ rescomp_name = os.environ.get('WINDRES', 'windres')
rescomp = dependencies.ExternalProgram(rescomp_name, silent=True)
res_args = extra_args + ['@INPUT@', '@OUTPUT@']
suffix = 'o'
diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py
index 7a91539..a9f25b1 100644
--- a/mesonbuild/mparser.py
+++ b/mesonbuild/mparser.py
@@ -407,6 +407,7 @@ class Parser:
def __init__(self, code, subdir):
self.lexer = Lexer(code)
self.stream = self.lexer.lex(subdir)
+ self.current = Token('eof', '', 0, 0, 0, (0, 0), None)
self.getsym()
self.in_ternary = False
diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py
index 10b8fab..f9e7f26 100644
--- a/mesonbuild/optinterpreter.py
+++ b/mesonbuild/optinterpreter.py
@@ -75,15 +75,16 @@ class OptionInterpreter:
self.cmd_line_options = {}
for o in command_line_options:
if self.subproject != '': # Strip the beginning.
+ # Ignore options that aren't for this subproject
if not o.startswith(self.sbprefix):
continue
- else:
- if ':' in o:
- continue
try:
(key, value) = o.split('=', 1)
except ValueError:
raise OptionException('Option {!r} must have a value separated by equals sign.'.format(o))
+ # Ignore subproject options if not fetching subproject options
+ if self.subproject == '' and ':' in key:
+ continue
self.cmd_line_options[key] = value
def process(self, option_file):
diff --git a/mesonbuild/scripts/commandrunner.py b/mesonbuild/scripts/commandrunner.py
index cf2770d..87e3b8b 100644
--- a/mesonbuild/scripts/commandrunner.py
+++ b/mesonbuild/scripts/commandrunner.py
@@ -17,11 +17,11 @@ what to run, sets up the environment and executes the command."""
import sys, os, subprocess, shutil
-def run_command(source_dir, build_dir, subdir, command, arguments):
+def run_command(source_dir, build_dir, subdir, mesonintrospect, command, arguments):
env = {'MESON_SOURCE_ROOT': source_dir,
'MESON_BUILD_ROOT': build_dir,
'MESON_SUBDIR': subdir,
- }
+ 'MESONINTROSPECT': mesonintrospect}
cwd = os.path.join(source_dir, subdir)
child_env = os.environ.copy()
child_env.update(env)
@@ -47,9 +47,10 @@ def run(args):
src_dir = args[0]
build_dir = args[1]
subdir = args[2]
- command = args[3]
- arguments = args[4:]
- pc = run_command(src_dir, build_dir, subdir, command, arguments)
+ mesonintrospect = args[3]
+ command = args[4]
+ arguments = args[5:]
+ pc = run_command(src_dir, build_dir, subdir, mesonintrospect, command, arguments)
pc.wait()
return pc.returncode
diff --git a/mesonbuild/scripts/gtkdochelper.py b/mesonbuild/scripts/gtkdochelper.py
index 53ed07f..434225e 100644
--- a/mesonbuild/scripts/gtkdochelper.py
+++ b/mesonbuild/scripts/gtkdochelper.py
@@ -107,7 +107,7 @@ def build_gtkdoc(source_root, build_root, doc_subdir, src_subdirs,
gtkdoc_run_check(scan_cmd, abs_out)
if gobject_typesfile:
- scanobjs_cmd = ['gtkdoc-scangobj'] + scanobjs_args + [gobject_typesfile,
+ scanobjs_cmd = ['gtkdoc-scangobj'] + scanobjs_args + ['--types=' + gobject_typesfile,
'--module=' + module,
'--cflags=' + cflags,
'--ldflags=' + ldflags]
diff --git a/mesonbuild/scripts/meson_exe.py b/mesonbuild/scripts/meson_exe.py
index 5c5c317..643e1af 100644
--- a/mesonbuild/scripts/meson_exe.py
+++ b/mesonbuild/scripts/meson_exe.py
@@ -29,8 +29,12 @@ def is_windows():
platname = platform.system().lower()
return platname == 'windows' or 'mingw' in platname
+def is_cygwin():
+ platname = platform.system().lower()
+ return 'cygwin' in platname
+
def run_with_mono(fname):
- if fname.endswith('.exe') and not is_windows():
+ if fname.endswith('.exe') and not (is_windows() or is_cygwin()):
return True
return False
diff --git a/mesonbuild/scripts/meson_install.py b/mesonbuild/scripts/meson_install.py
index 8fb9e04..2205f1a 100644
--- a/mesonbuild/scripts/meson_install.py
+++ b/mesonbuild/scripts/meson_install.py
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import sys, pickle, os, shutil, subprocess, gzip, platform
+import sys, pickle, os, shutil, subprocess, gzip, platform, errno
from glob import glob
from . import depfixer
from . import destdir_join
@@ -34,6 +34,15 @@ def set_mode(path, mode):
except PermissionError as e:
msg = '{!r}: Unable to set owner {!r} and group {!r}: {}, ignoring...'
print(msg.format(path, mode.owner, mode.group, e.strerror))
+ except LookupError as e:
+ msg = '{!r}: Non-existent owner {!r} or group {!r}: ignoring...'
+ print(msg.format(path, mode.owner, mode.group))
+ except OSError as e:
+ if e.errno == errno.EINVAL:
+ msg = '{!r}: Non-existent numeric owner {!r} or group {!r}: ignoring...'
+ print(msg.format(path, mode.owner, mode.group))
+ else:
+ raise
# Must set permissions *after* setting owner/group otherwise the
# setuid/setgid bits will get wiped by chmod
# NOTE: On Windows you can set read/write perms; the rest are ignored
@@ -174,7 +183,7 @@ def run_install_script(d):
'MESON_BUILD_ROOT': d.build_dir,
'MESON_INSTALL_PREFIX': d.prefix,
'MESON_INSTALL_DESTDIR_PREFIX': d.fullprefix,
- }
+ 'MESONINTROSPECT': d.mesonintrospect}
child_env = os.environ.copy()
child_env.update(env)
@@ -193,7 +202,7 @@ def run_install_script(d):
def is_elf_platform():
platname = platform.system().lower()
- if platname == 'darwin' or platname == 'windows':
+ if platname == 'darwin' or platname == 'windows' or platname == 'cygwin':
return False
return True
diff --git a/mesontest.py b/mesontest.py
index a1708e3..b59d1ed 100755
--- a/mesontest.py
+++ b/mesontest.py
@@ -21,6 +21,8 @@ import subprocess, sys, os, argparse
import pickle
from mesonbuild import build
from mesonbuild import environment
+from mesonbuild.dependencies import ExternalProgram
+from mesonbuild import mlog
import time, datetime, multiprocessing, json
import concurrent.futures as conc
@@ -36,6 +38,10 @@ def is_windows():
platname = platform.system().lower()
return platname == 'windows' or 'mingw' in platname
+def is_cygwin():
+ platname = platform.system().lower()
+ return 'cygwin' in platname
+
def determine_worker_count():
varname = 'MESON_TESTTHREADS'
if varname in os.environ:
@@ -150,7 +156,7 @@ def write_json_log(jsonlogfile, test_name, result):
jsonlogfile.write(json.dumps(jresult) + '\n')
def run_with_mono(fname):
- if fname.endswith('.exe') and not is_windows():
+ if fname.endswith('.exe') and not (is_windows() or is_cygwin()):
return True
return False
@@ -202,7 +208,7 @@ class TestHarness:
child_env.update(test.env)
if len(test.extra_paths) > 0:
- child_env['PATH'] += ';'.join([''] + test.extra_paths)
+ child_env['PATH'] += os.pathsep.join([''] + test.extra_paths)
# If MALLOC_PERTURB_ is not set, or if it is set to an empty value,
# (i.e., the test or the environment don't explicitly set it), set
@@ -278,7 +284,16 @@ class TestHarness:
result_str = '%s %s %s%s%s%5.2f s' % \
(num, name, padding1, result.res, padding2, result.duration)
if not self.options.quiet or result.res != 'OK':
- print(result_str)
+ if result.res != 'OK' and mlog.colorize_console:
+ if result.res == 'FAIL' or result.res == 'TIMEOUT':
+ decorator = mlog.red
+ elif result.res == 'SKIP':
+ decorator = mlog.yellow
+ else:
+ sys.exit('Unreachable code was ... well ... reached.')
+ print(decorator(result_str).get_text(True))
+ else:
+ print(result_str)
result_str += "\n\n" + result.get_log()
if (result.returncode != GNU_SKIP_RETURNCODE) \
and (result.returncode != 0) != result.should_fail:
@@ -570,12 +585,21 @@ def run(args):
print('Can not be both quiet and verbose at the same time.')
return 1
+ check_bin = None
if options.gdb:
options.verbose = True
if options.wrapper:
print('Must not specify both a wrapper and gdb at the same time.')
return 1
+ check_bin = 'gdb'
+
+ if options.wrapper:
+ check_bin = options.wrapper[0]
+ if check_bin is not None:
+ exe = ExternalProgram(check_bin, silent=True)
+ if not exe.found():
+ sys.exit("Could not find requested program: %s" % check_bin)
options.wd = os.path.abspath(options.wd)
if not options.no_rebuild:
diff --git a/run_project_tests.py b/run_project_tests.py
index 28de638..bcb375d 100755
--- a/run_project_tests.py
+++ b/run_project_tests.py
@@ -26,13 +26,15 @@ from mesonbuild import mesonlib
from mesonbuild import mlog
from mesonbuild import mesonmain
from mesonbuild.mesonlib import stringlistify, Popen_safe
+from mesonbuild.coredata import backendlist
import argparse
import xml.etree.ElementTree as ET
import time
import multiprocessing
import concurrent.futures as conc
+import re
-from mesonbuild.coredata import backendlist
+from run_tests import get_backend_commands, get_backend_args_for_dir, Backend
class BuildStep(Enum):
@@ -41,6 +43,7 @@ class BuildStep(Enum):
test = 3
install = 4
clean = 5
+ validate = 6
class TestResult:
@@ -152,50 +155,36 @@ def stop_handler(signal, frame):
signal.signal(signal.SIGINT, stop_handler)
signal.signal(signal.SIGTERM, stop_handler)
-# unity_flags = ['--unity']
-unity_flags = []
+# Needed when running cross tests because we don't generate prebuilt files
+compiler = None
-backend_flags = None
-compile_commands = None
-test_commands = None
-install_commands = []
-clean_commands = []
-
-def setup_commands(backend):
- global backend_flags, compile_commands, test_commands, install_commands, clean_commands
+def setup_commands(optbackend):
+ global do_debug, backend, backend_flags
+ global compile_commands, clean_commands, test_commands, install_commands, uninstall_commands
+ backend = optbackend
msbuild_exe = shutil.which('msbuild')
- if (backend and backend.startswith('vs')) or (backend is None and msbuild_exe is not None):
- if backend is None:
- backend = 'vs2010'
+ # Auto-detect backend if unspecified
+ if backend is None:
+ if msbuild_exe is not None:
+ backend = 'vs' # Meson will auto-detect VS version to use
+ elif mesonlib.is_osx():
+ backend = 'xcode'
+ else:
+ backend = 'ninja'
+ # Set backend arguments for Meson
+ if backend.startswith('vs'):
backend_flags = ['--backend=' + backend]
- compile_commands = ['msbuild']
- test_commands = ['msbuild', 'RUN_TESTS.vcxproj']
- elif backend == 'xcode' or (backend is None and mesonlib.is_osx()):
+ backend = Backend.vs
+ elif backend == 'xcode':
backend_flags = ['--backend=xcode']
- compile_commands = ['xcodebuild']
- test_commands = ['xcodebuild', '-target', 'RUN_TESTS']
+ backend = Backend.xcode
+ elif backend == 'ninja':
+ backend_flags = ['--backend=ninja']
+ backend = Backend.ninja
else:
- backend_flags = []
- # We need at least 1.6 because of -w dupbuild=err
- ninja_command = environment.detect_ninja(version='1.6')
- if ninja_command is None:
- raise RuntimeError('Could not find Ninja v1.6 or newer')
- if do_debug:
- compile_commands = [ninja_command, '-v']
- else:
- compile_commands = [ninja_command]
- compile_commands += ['-w', 'dupbuild=err']
- test_commands = [ninja_command, 'test', 'benchmark']
- install_commands = [ninja_command, 'install']
- clean_commands = [ninja_command, 'clean']
-
-def get_compile_commands_for_dir(compile_commands, test_build_dir):
- if 'msbuild' in compile_commands[0]:
- sln_name = glob(os.path.join(test_build_dir, '*.sln'))[0]
- comp = compile_commands + [os.path.split(sln_name)[-1]]
- else:
- comp = compile_commands
- return comp
+ raise RuntimeError('Unknown backend: {!r}'.format(backend))
+ compile_commands, clean_commands, test_commands, install_commands, \
+ uninstall_commands = get_backend_commands(backend, do_debug)
def get_relative_files_list_from_dir(fromdir):
paths = []
@@ -208,15 +197,21 @@ def get_relative_files_list_from_dir(fromdir):
paths.append(path)
return paths
-def platform_fix_exe_name(fname):
- if not fname.endswith('?exe'):
- return fname
- fname = fname[:-4]
- if mesonlib.is_windows():
- return fname + '.exe'
+def platform_fix_name(fname):
+ if '?lib' in fname:
+ if mesonlib.is_cygwin():
+ fname = re.sub(r'\?lib(.*)\.dll$', r'cyg\1.dll', fname)
+ else:
+ fname = re.sub(r'\?lib', 'lib', fname)
+
+ if fname.endswith('?exe'):
+ fname = fname[:-4]
+ if mesonlib.is_windows() or mesonlib.is_cygwin():
+ return fname + '.exe'
+
return fname
-def validate_install(srcdir, installdir):
+def validate_install(srcdir, installdir, compiler):
# List of installed files
info_file = os.path.join(srcdir, 'installed_files.txt')
# If this exists, the test does not install any other files
@@ -230,13 +225,16 @@ def validate_install(srcdir, installdir):
elif os.path.exists(info_file):
with open(info_file) as f:
for line in f:
- expected[platform_fix_exe_name(line.strip())] = False
+ expected[platform_fix_name(line.strip())] = False
# Check if expected files were found
for fname in expected:
if os.path.exists(os.path.join(installdir, fname)):
expected[fname] = True
for (fname, found) in expected.items():
if not found:
+ # Ignore missing PDB files if we aren't using cl
+ if fname.endswith('.pdb') and compiler != 'cl':
+ continue
ret_msg += 'Expected file {0} missing.\n'.format(fname)
# Check if there are any unexpected files
found = get_relative_files_list_from_dir(installdir)
@@ -307,18 +305,18 @@ def parse_test_args(testdir):
pass
return args
-def run_test(skipped, testdir, extra_args, flags, compile_commands, should_fail):
+def run_test(skipped, testdir, extra_args, compiler, backend, flags, commands, should_fail):
if skipped:
return None
with AutoDeletedDir(tempfile.mkdtemp(prefix='b ', dir='.')) as build_dir:
with AutoDeletedDir(tempfile.mkdtemp(prefix='i ', dir=os.getcwd())) as install_dir:
try:
- return _run_test(testdir, build_dir, install_dir, extra_args, flags, compile_commands, should_fail)
+ return _run_test(testdir, build_dir, install_dir, extra_args, compiler, backend, flags, commands, should_fail)
finally:
mlog.shutdown() # Close the log file because otherwise Windows wets itself.
-def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_commands, should_fail):
- global install_commands, clean_commands
+def _run_test(testdir, test_build_dir, install_dir, extra_args, compiler, backend, flags, commands, should_fail):
+ compile_commands, clean_commands, install_commands, uninstall_commands = commands
test_args = parse_test_args(testdir)
gen_start = time.time()
# Configure in-process
@@ -339,9 +337,9 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_c
if returncode != 0:
return TestResult('Generating the build system failed.', BuildStep.configure, stdo, stde, mesonlog, gen_time)
# Build with subprocess
- comp = get_compile_commands_for_dir(compile_commands, test_build_dir)
+ dir_args = get_backend_args_for_dir(backend, test_build_dir)
build_start = time.time()
- pc, o, e = Popen_safe(comp, cwd=test_build_dir)
+ pc, o, e = Popen_safe(compile_commands + dir_args, cwd=test_build_dir)
build_time = time.time() - build_start
stdo += o
stde += e
@@ -368,25 +366,26 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, flags, compile_c
return TestResult('Test that should have failed to run unit tests succeeded', BuildStep.test, stdo, stde, mesonlog, gen_time)
if returncode != 0:
return TestResult('Running unit tests failed.', BuildStep.test, stdo, stde, mesonlog, gen_time, build_time, test_time)
- if len(install_commands) == 0:
- return TestResult('', BuildStep.install, '', '', mesonlog, gen_time, build_time, test_time)
- env = os.environ.copy()
- env['DESTDIR'] = install_dir
- # Install with subprocess
- pi, o, e = Popen_safe(install_commands, cwd=test_build_dir, env=env)
- stdo += o
- stde += e
- if pi.returncode != 0:
- return TestResult('Running install failed.', BuildStep.install, stdo, stde, mesonlog, gen_time, build_time, test_time)
- if len(clean_commands) != 0:
+ # Do installation, if the backend supports it
+ if len(install_commands) != 0:
env = os.environ.copy()
- # Clean with subprocess
- pi, o, e = Popen_safe(clean_commands, cwd=test_build_dir, env=env)
+ env['DESTDIR'] = install_dir
+ # Install with subprocess
+ pi, o, e = Popen_safe(install_commands, cwd=test_build_dir, env=env)
stdo += o
stde += e
if pi.returncode != 0:
- return TestResult('Running clean failed.', BuildStep.clean, stdo, stde, mesonlog, gen_time, build_time, test_time)
- return TestResult(validate_install(testdir, install_dir), BuildStep.clean, stdo, stde, mesonlog, gen_time, build_time, test_time)
+ return TestResult('Running install failed.', BuildStep.install, stdo, stde, mesonlog, gen_time, build_time, test_time)
+ # Clean with subprocess
+ env = os.environ.copy()
+ pi, o, e = Popen_safe(clean_commands + dir_args, cwd=test_build_dir, env=env)
+ stdo += o
+ stde += e
+ if pi.returncode != 0:
+ return TestResult('Running clean failed.', BuildStep.clean, stdo, stde, mesonlog, gen_time, build_time, test_time)
+ if len(install_commands) == 0:
+ return TestResult('', BuildStep.install, '', '', mesonlog, gen_time, build_time, test_time)
+ return TestResult(validate_install(testdir, install_dir, compiler), BuildStep.validate, stdo, stde, mesonlog, gen_time, build_time, test_time)
def gather_tests(testdir):
tests = [t.replace('\\', '/').split('/', 2)[2] for t in glob(os.path.join(testdir, '*'))]
@@ -411,23 +410,6 @@ def have_java():
return True
return False
-def using_backend(backends):
- if isinstance(backends, str):
- backends = (backends,)
- for backend in backends:
- if backend == 'ninja':
- if not backend_flags:
- return True
- elif backend == 'xcode':
- if backend_flags == '--backend=xcode':
- return True
- elif backend == 'vs':
- if backend_flags.startswith('--backend=vs'):
- return True
- else:
- raise AssertionError('Unknown backend type: ' + backend)
- return False
-
def detect_tests_to_run():
all_tests = []
all_tests.append(('common', gather_tests('test cases/common'), False))
@@ -437,18 +419,18 @@ def detect_tests_to_run():
all_tests.append(('prebuilt', gather_tests('test cases/prebuilt'), False))
all_tests.append(('platform-osx', gather_tests('test cases/osx'), False if mesonlib.is_osx() else True))
- all_tests.append(('platform-windows', gather_tests('test cases/windows'), False if mesonlib.is_windows() else True))
+ all_tests.append(('platform-windows', gather_tests('test cases/windows'), False if mesonlib.is_windows() or mesonlib.is_cygwin() else True))
all_tests.append(('platform-linux', gather_tests('test cases/linuxlike'), False if not (mesonlib.is_osx() or mesonlib.is_windows()) else True))
- all_tests.append(('framework', gather_tests('test cases/frameworks'), False if not mesonlib.is_osx() and not mesonlib.is_windows() else True))
- all_tests.append(('java', gather_tests('test cases/java'), False if using_backend('ninja') and not mesonlib.is_osx() and have_java() else True))
- all_tests.append(('C#', gather_tests('test cases/csharp'), False if using_backend('ninja') and shutil.which('mcs') else True))
- all_tests.append(('vala', gather_tests('test cases/vala'), False if using_backend('ninja') and shutil.which('valac') else True))
- all_tests.append(('rust', gather_tests('test cases/rust'), False if using_backend('ninja') and shutil.which('rustc') else True))
- all_tests.append(('d', gather_tests('test cases/d'), False if using_backend('ninja') and have_d_compiler() else True))
- all_tests.append(('objective c', gather_tests('test cases/objc'), False if using_backend(('ninja', 'xcode')) and not mesonlib.is_windows() else True))
- all_tests.append(('fortran', gather_tests('test cases/fortran'), False if using_backend('ninja') and shutil.which('gfortran') else True))
- all_tests.append(('swift', gather_tests('test cases/swift'), False if using_backend(('ninja', 'xcode')) and shutil.which('swiftc') else True))
- all_tests.append(('python3', gather_tests('test cases/python3'), False if using_backend('ninja') and shutil.which('python3') else True))
+ all_tests.append(('framework', gather_tests('test cases/frameworks'), False if not mesonlib.is_osx() and not mesonlib.is_windows() and not mesonlib.is_cygwin() else True))
+ all_tests.append(('java', gather_tests('test cases/java'), False if backend is Backend.ninja and not mesonlib.is_osx() and have_java() else True))
+ all_tests.append(('C#', gather_tests('test cases/csharp'), False if backend is Backend.ninja and shutil.which('mcs') else True))
+ all_tests.append(('vala', gather_tests('test cases/vala'), False if backend is Backend.ninja and shutil.which('valac') else True))
+ all_tests.append(('rust', gather_tests('test cases/rust'), False if backend is Backend.ninja and shutil.which('rustc') else True))
+ all_tests.append(('d', gather_tests('test cases/d'), False if backend is Backend.ninja and have_d_compiler() else True))
+ all_tests.append(('objective c', gather_tests('test cases/objc'), False if backend in (Backend.ninja, Backend.xcode) and not mesonlib.is_windows() else True))
+ all_tests.append(('fortran', gather_tests('test cases/fortran'), False if backend is Backend.ninja and shutil.which('gfortran') else True))
+ all_tests.append(('swift', gather_tests('test cases/swift'), False if backend in (Backend.ninja, Backend.xcode) and shutil.which('swiftc') else True))
+ all_tests.append(('python3', gather_tests('test cases/python3'), False if backend is Backend.ninja and shutil.which('python3') else True))
return all_tests
def run_tests(all_tests, log_name_base, extra_args):
@@ -463,6 +445,7 @@ def run_tests(all_tests, log_name_base, extra_args):
passing_tests = 0
failing_tests = 0
skipped_tests = 0
+ commands = (compile_commands, clean_commands, install_commands, uninstall_commands)
try:
# This fails in some CI environments for unknown reasons.
@@ -492,7 +475,7 @@ def run_tests(all_tests, log_name_base, extra_args):
should_fail = False
if name.startswith('failing'):
should_fail = name.split('failing-')[1]
- result = executor.submit(run_test, skipped, t, extra_args, unity_flags + backend_flags, compile_commands, should_fail)
+ result = executor.submit(run_test, skipped, t, extra_args, compiler, backend, backend_flags, commands, should_fail)
futures.append((testname, t, result))
for (testname, t, result) in futures:
sys.stdout.flush()
@@ -607,7 +590,7 @@ def generate_prebuilt():
return objectfile, stlibfile
def check_meson_commands_work():
- global meson_command, compile_commands, test_commands, install_commands
+ global backend, meson_command, compile_commands, test_commands, install_commands
testdir = 'test cases/common/1 trivial'
with AutoDeletedDir(tempfile.mkdtemp(prefix='b ', dir='.')) as build_dir:
print('Checking that configuring works...')
@@ -616,8 +599,8 @@ def check_meson_commands_work():
if pc.returncode != 0:
raise RuntimeError('Failed to configure {!r}:\n{}\n{}'.format(testdir, e, o))
print('Checking that building works...')
- compile_cmd = get_compile_commands_for_dir(compile_commands, build_dir)
- pc, o, e = Popen_safe(compile_cmd, cwd=build_dir)
+ dir_args = get_backend_args_for_dir(backend, build_dir)
+ pc, o, e = Popen_safe(compile_commands + dir_args, cwd=build_dir)
if pc.returncode != 0:
raise RuntimeError('Failed to build {!r}:\n{}\n{}'.format(testdir, e, o))
print('Checking that testing works...')
@@ -639,21 +622,6 @@ if __name__ == '__main__':
options = parser.parse_args()
setup_commands(options.backend)
- # Appveyor sets the `platform` environment variable which completely messes
- # up building with the vs2010 and vs2015 backends.
- #
- # Specifically, MSBuild reads the `platform` environment variable to set
- # the configured value for the platform (Win32/x64/arm), which breaks x86
- # builds.
- #
- # Appveyor setting this also breaks our 'native build arch' detection for
- # Windows in environment.py:detect_windows_arch() by overwriting the value
- # of `platform` set by vcvarsall.bat.
- #
- # While building for x86, `platform` should be unset.
- if 'APPVEYOR' in os.environ and os.environ['arch'] == 'x86':
- os.environ.pop('platform')
-
script_dir = os.path.split(__file__)[0]
if script_dir != '':
os.chdir(script_dir)
diff --git a/run_tests.py b/run_tests.py
index 02aa701..d0a67e8 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -20,28 +20,138 @@ import shutil
import subprocess
import platform
from mesonbuild import mesonlib
+from mesonbuild.environment import detect_ninja
+from enum import Enum
+from glob import glob
-def using_vs_backend():
- for arg in sys.argv[1:]:
- if arg.startswith('--backend=vs'):
- return True
- return False
+Backend = Enum('Backend', 'ninja vs xcode')
+
+if mesonlib.is_windows() or mesonlib.is_cygwin():
+ exe_suffix = '.exe'
+else:
+ exe_suffix = ''
+
+def get_backend_args_for_dir(backend, builddir):
+ '''
+ Visual Studio backend needs to be given the solution to build
+ '''
+ if backend is Backend.vs:
+ sln_name = glob(os.path.join(builddir, '*.sln'))[0]
+ return [os.path.split(sln_name)[-1]]
+ return []
+
+def find_vcxproj_with_target(builddir, target):
+ import re, fnmatch
+ t, ext = os.path.splitext(target)
+ if ext:
+ p = '<TargetName>{}</TargetName>\s*<TargetExt>\{}</TargetExt>'.format(t, ext)
+ else:
+ p = '<TargetName>{}</TargetName>'.format(t)
+ for root, dirs, files in os.walk(builddir):
+ for f in fnmatch.filter(files, '*.vcxproj'):
+ f = os.path.join(builddir, f)
+ with open(f, 'r', encoding='utf-8') as o:
+ if re.search(p, o.read(), flags=re.MULTILINE):
+ return f
+ raise RuntimeError('No vcxproj matching {!r} in {!r}'.format(p, builddir))
+
+def get_builddir_target_args(backend, builddir, target):
+ dir_args = []
+ if not target:
+ dir_args = get_backend_args_for_dir(backend, builddir)
+ if target is None:
+ return dir_args
+ if backend is Backend.vs:
+ vcxproj = find_vcxproj_with_target(builddir, target)
+ target_args = [vcxproj]
+ elif backend is Backend.xcode:
+ target_args = ['-target', target]
+ elif backend is Backend.ninja:
+ target_args = [target]
+ else:
+ raise AssertionError('Unknown backend: {!r}'.format(backend))
+ return target_args + dir_args
+
+def get_backend_commands(backend, debug=False):
+ install_cmd = []
+ uninstall_cmd = []
+ if backend is Backend.vs:
+ cmd = ['msbuild']
+ clean_cmd = cmd + ['/target:Clean']
+ test_cmd = cmd + ['RUN_TESTS.vcxproj']
+ elif backend is Backend.xcode:
+ cmd = ['xcodebuild']
+ clean_cmd = cmd + ['-alltargets', 'clean']
+ test_cmd = cmd + ['-target', 'RUN_TESTS']
+ elif backend is Backend.ninja:
+ # We need at least 1.6 because of -w dupbuild=err
+ cmd = [detect_ninja('1.6'), '-w', 'dupbuild=err']
+ if cmd[0] is None:
+ raise RuntimeError('Could not find Ninja v1.6 or newer')
+ if debug:
+ cmd += ['-v']
+ clean_cmd = cmd + ['clean']
+ test_cmd = cmd + ['test', 'benchmark']
+ install_cmd = cmd + ['install']
+ uninstall_cmd = cmd + ['uninstall']
+ else:
+ raise AssertionError('Unknown backend: {!r}'.format(backend))
+ return cmd, clean_cmd, test_cmd, install_cmd, uninstall_cmd
+
+def get_fake_options(prefix):
+ import argparse
+ opts = argparse.Namespace()
+ opts.cross_file = None
+ opts.wrap_mode = None
+ opts.prefix = prefix
+ return opts
+
+class FakeEnvironment(object):
+ def __init__(self):
+ self.cross_info = None
+
+ def is_cross_build(self):
+ return False
if __name__ == '__main__':
returncode = 0
+ # Iterate over list in reverse order to find the last --backend arg
+ backend = Backend.ninja
+ for arg in reversed(sys.argv[1:]):
+ if arg.startswith('--backend'):
+ if arg.startswith('--backend=vs'):
+ backend = Backend.vs
+ elif arg == '--backend=xcode':
+ backend = Backend.xcode
+ break
# Running on a developer machine? Be nice!
if not mesonlib.is_windows() and 'TRAVIS' not in os.environ:
os.nice(20)
+ # Appveyor sets the `platform` environment variable which completely messes
+ # up building with the vs2010 and vs2015 backends.
+ #
+ # Specifically, MSBuild reads the `platform` environment variable to set
+ # the configured value for the platform (Win32/x64/arm), which breaks x86
+ # builds.
+ #
+ # Appveyor setting this also breaks our 'native build arch' detection for
+ # Windows in environment.py:detect_windows_arch() by overwriting the value
+ # of `platform` set by vcvarsall.bat.
+ #
+ # While building for x86, `platform` should be unset.
+ if 'APPVEYOR' in os.environ and os.environ['arch'] == 'x86':
+ os.environ.pop('platform')
+ # Run tests
print('Running unittests.\n')
units = ['InternalTests', 'AllPlatformTests']
if mesonlib.is_linux():
units += ['LinuxlikeTests']
elif mesonlib.is_windows():
units += ['WindowsTests']
- # Unit tests always use the Ninja backend, so just skip them if we're
- # testing the VS backend
- if not using_vs_backend():
- returncode += subprocess.call([sys.executable, 'run_unittests.py', '-v'] + units)
+ # Can't pass arguments to unit tests, so set the backend to use in the environment
+ env = os.environ.copy()
+ env['MESON_UNIT_TEST_BACKEND'] = backend.name
+ returncode += subprocess.call([sys.executable, 'run_unittests.py', '-v'] + units, env=env)
# Ubuntu packages do not have a binary without -6 suffix.
if shutil.which('arm-linux-gnueabihf-gcc-6') and not platform.machine().startswith('arm'):
print('Running cross compilation tests.\n')
diff --git a/run_unittests.py b/run_unittests.py
index be73839..7bdd57b 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -24,14 +24,13 @@ from pathlib import PurePath
import mesonbuild.compilers
import mesonbuild.environment
import mesonbuild.mesonlib
-from mesonbuild.mesonlib import is_windows, is_osx
-from mesonbuild.environment import detect_ninja, Environment
+from mesonbuild.mesonlib import is_windows, is_osx, is_cygwin
+from mesonbuild.environment import Environment
from mesonbuild.dependencies import PkgConfigDependency, ExternalProgram
-if is_windows():
- exe_suffix = '.exe'
-else:
- exe_suffix = ''
+from run_tests import exe_suffix, get_fake_options, FakeEnvironment
+from run_tests import get_builddir_target_args, get_backend_commands, Backend
+
def get_soname(fname):
# HACK, fix to not use shell.
@@ -44,21 +43,6 @@ def get_soname(fname):
return m.group(1)
raise RuntimeError('Could not determine soname:\n\n' + raw_out)
-def get_fake_options(prefix):
- import argparse
- opts = argparse.Namespace()
- opts.cross_file = None
- opts.wrap_mode = None
- opts.prefix = prefix
- return opts
-
-class FakeEnvironment(object):
- def __init__(self):
- self.cross_info = None
-
- def is_cross_build(self):
- return False
-
class InternalTests(unittest.TestCase):
def test_version_number(self):
@@ -346,16 +330,37 @@ class BasePlatformTests(unittest.TestCase):
self.prefix = '/usr'
self.libdir = os.path.join(self.prefix, 'lib')
self.installdir = os.path.join(self.builddir, 'install')
- self.meson_command = [sys.executable, os.path.join(src_root, 'meson.py')]
+ # Get the backend
+ # FIXME: Extract this from argv?
+ self.backend = getattr(Backend, os.environ.get('MESON_UNIT_TEST_BACKEND', 'ninja'))
+ self.meson_command = [sys.executable, os.path.join(src_root, 'meson.py'),
+ '--backend=' + self.backend.name]
self.mconf_command = [sys.executable, os.path.join(src_root, 'mesonconf.py')]
self.mintro_command = [sys.executable, os.path.join(src_root, 'mesonintrospect.py')]
self.mtest_command = [sys.executable, os.path.join(src_root, 'mesontest.py'), '-C', self.builddir]
- self.ninja_command = [detect_ninja(), '-C', self.builddir]
+ # Backend-specific build commands
+ self.build_command, self.clean_command, self.test_command, self.install_command, \
+ self.uninstall_command = get_backend_commands(self.backend)
+ # Test directories
self.common_test_dir = os.path.join(src_root, 'test cases/common')
self.vala_test_dir = os.path.join(src_root, 'test cases/vala')
self.framework_test_dir = os.path.join(src_root, 'test cases/frameworks')
self.unit_test_dir = os.path.join(src_root, 'test cases/unit')
+ # Misc stuff
self.orig_env = os.environ.copy()
+ if self.backend is Backend.ninja:
+ self.no_rebuild_stdout = 'ninja: no work 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)
+
+ def ensure_backend_detects_changes(self):
+ # This is needed to increase the difference between build.ninja's
+ # timestamp and the timestamp of whatever you changed due to a Ninja
+ # bug: https://github.com/ninja-build/ninja/issues/371
+ if self.backend is Backend.ninja:
+ time.sleep(1)
def _print_meson_log(self):
log = os.path.join(self.logdir, 'meson-log.txt')
@@ -370,14 +375,14 @@ class BasePlatformTests(unittest.TestCase):
os.environ = self.orig_env
super().tearDown()
- def _run(self, command):
+ def _run(self, command, workdir=None):
'''
Run a command while printing the stdout and stderr to stdout,
and also return a copy of it
'''
p = subprocess.Popen(command, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, env=os.environ.copy(),
- universal_newlines=True)
+ universal_newlines=True, cwd=workdir)
output = p.communicate()[0]
print(output)
if p.returncode != 0:
@@ -398,47 +403,59 @@ class BasePlatformTests(unittest.TestCase):
raise
self.privatedir = os.path.join(self.builddir, 'meson-private')
- def build(self, extra_args=None):
+ def build(self, target=None, extra_args=None):
if extra_args is None:
extra_args = []
- self._run(self.ninja_command + extra_args)
+ # Add arguments for building the target (if specified),
+ # and using the build dir (if required, with VS)
+ args = get_builddir_target_args(self.backend, self.builddir, target)
+ return self._run(self.build_command + args + extra_args, workdir=self.builddir)
+
+ def clean(self):
+ dir_args = get_builddir_target_args(self.backend, self.builddir, None)
+ self._run(self.clean_command + dir_args, workdir=self.builddir)
def run_tests(self):
- self._run(self.ninja_command + ['test'])
+ self._run(self.test_command, workdir=self.builddir)
def install(self):
+ if self.backend is not Backend.ninja:
+ raise unittest.SkipTest('{!r} backend can\'t install files'.format(self.backend.name))
os.environ['DESTDIR'] = self.installdir
- self._run(self.ninja_command + ['install'])
+ self._run(self.install_command, workdir=self.builddir)
def uninstall(self):
- self._run(self.ninja_command + ['uninstall'])
+ self._run(self.uninstall_command, workdir=self.builddir)
def run_target(self, target):
'''
Run a Ninja target while printing the stdout and stderr to stdout,
and also return a copy of it
'''
- return self._run(self.ninja_command + [target])
+ return self.build(target=target)
def setconf(self, arg, will_build=True):
- # This is needed to increase the difference between build.ninja's
- # timestamp and coredata.dat's timestamp due to a Ninja bug.
- # https://github.com/ninja-build/ninja/issues/371
if will_build:
- time.sleep(1)
+ self.ensure_backend_detects_changes()
self._run(self.mconf_command + [arg, self.builddir])
def wipe(self):
shutil.rmtree(self.builddir)
+ def utime(self, f):
+ self.ensure_backend_detects_changes()
+ os.utime(f)
+
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)
# 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'):
# Pretend to build so that the rsp files are generated
- self.build(['-d', 'keeprsp', '-n'])
+ self.build(extra_args=['-d', 'keeprsp', '-n'])
for each in contents:
# Extract the actual command from the rsp file
compiler, rsp = each['command'].split(' @')
@@ -482,6 +499,40 @@ class BasePlatformTests(unittest.TestCase):
path_basename = PurePath(path).parts[-1]
self.assertEqual(PurePath(path_basename), PurePath(basename), msg)
+ def assertBuildIsNoop(self):
+ ret = self.build()
+ if self.backend is Backend.ninja:
+ self.assertEqual(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)
+ self.assertIn('ClCompile:\n All outputs are up-to-date.', ret)
+ self.assertIn('Link:\n All outputs are up-to-date.', ret)
+ # Ensure that no targets were built
+ clre = re.compile('ClCompile:\n [^\n]*cl', flags=re.IGNORECASE)
+ linkre = re.compile('Link:\n [^\n]*link', flags=re.IGNORECASE)
+ self.assertNotRegex(ret, clre)
+ self.assertNotRegex(ret, linkre)
+ elif self.backend is Backend.xcode:
+ raise unittest.SkipTest('Please help us fix this test on the xcode backend')
+ else:
+ raise RuntimeError('Invalid backend: {!r}'.format(self.backend.name))
+
+ def assertRebuiltTarget(self, target):
+ ret = self.build()
+ if self.backend is Backend.ninja:
+ self.assertIn('Linking target {}'.format(target), ret)
+ elif self.backend is Backend.vs:
+ # Ensure that this target was rebuilt
+ clre = re.compile('ClCompile:\n [^\n]*cl[^\n]*' + target, flags=re.IGNORECASE)
+ linkre = re.compile('Link:\n [^\n]*link[^\n]*' + target, flags=re.IGNORECASE)
+ self.assertRegex(ret, clre)
+ self.assertRegex(ret, linkre)
+ elif self.backend is Backend.xcode:
+ raise unittest.SkipTest('Please help us fix this test on the xcode backend')
+ else:
+ raise RuntimeError('Invalid backend: {!r}'.format(self.backend.name))
+
class AllPlatformTests(BasePlatformTests):
'''
@@ -613,6 +664,8 @@ class AllPlatformTests(BasePlatformTests):
Tests that the Meson introspection API exposes install filenames correctly
https://github.com/mesonbuild/meson/issues/829
'''
+ if self.backend is not Backend.ninja:
+ raise unittest.SkipTest('{!r} backend can\'t install files'.format(self.backend.name))
testdir = os.path.join(self.common_test_dir, '8 install')
self.init(testdir)
intro = self.introspect('--targets')
@@ -722,7 +775,7 @@ class AllPlatformTests(BasePlatformTests):
exe = os.path.join(self.builddir, 'fooprog' + exe_suffix)
self.assertTrue(os.path.exists(genfile))
self.assertFalse(os.path.exists(exe))
- self._run(self.ninja_command + ['fooprog' + exe_suffix])
+ self.build(target=('fooprog' + exe_suffix))
self.assertTrue(os.path.exists(exe))
def test_internal_include_order(self):
@@ -833,6 +886,8 @@ class AllPlatformTests(BasePlatformTests):
self.assertEqual(cc.gcc_type, mesonbuild.compilers.GCC_OSX)
elif is_windows():
self.assertEqual(cc.gcc_type, mesonbuild.compilers.GCC_MINGW)
+ elif is_cygwin():
+ self.assertEqual(cc.gcc_type, mesonbuild.compilers.GCC_CYGWIN)
else:
self.assertEqual(cc.gcc_type, mesonbuild.compilers.GCC_STANDARD)
if isinstance(cc, clang):
@@ -947,6 +1002,61 @@ class AllPlatformTests(BasePlatformTests):
m = re.search('build c-asm.*: c_LINKER', contents)
self.assertIsNotNone(m, msg=contents)
+ def test_preprocessor_checks_CPPFLAGS(self):
+ '''
+ Test that preprocessor compiler checks read CPPFLAGS but not CFLAGS
+ '''
+ testdir = os.path.join(self.common_test_dir, '140 get define')
+ define = 'MESON_TEST_DEFINE_VALUE'
+ # NOTE: this list can't have \n, ' or "
+ # \n is never substituted by the GNU pre-processor via a -D define
+ # ' and " confuse shlex.split() even when they are escaped
+ # % and # confuse the MSVC preprocessor
+ value = 'spaces and fun!@$^&*()-=_+{}[]:;<>?,./~`'
+ os.environ['CPPFLAGS'] = '-D{}="{}"'.format(define, value)
+ os.environ['CFLAGS'] = '-DMESON_FAIL_VALUE=cflags-read'.format(define)
+ self.init(testdir, ['-D{}={}'.format(define, value)])
+
+ def test_custom_target_exe_data_deterministic(self):
+ testdir = os.path.join(self.common_test_dir, '117 custom target capture')
+ self.init(testdir)
+ meson_exe_dat1 = glob(os.path.join(self.privatedir, 'meson_exe*.dat'))
+ self.wipe()
+ self.init(testdir)
+ meson_exe_dat2 = glob(os.path.join(self.privatedir, 'meson_exe*.dat'))
+ self.assertListEqual(meson_exe_dat1, meson_exe_dat2)
+
+ def test_source_changes_cause_rebuild(self):
+ '''
+ Test that changes to sources and headers cause rebuilds, but not
+ changes to unused files (as determined by the dependency file) in the
+ input files list.
+ '''
+ testdir = os.path.join(self.common_test_dir, '22 header in file list')
+ self.init(testdir)
+ self.build()
+ # Immediately rebuilding should not do anything
+ self.assertBuildIsNoop()
+ # Changing mtime of header.h should rebuild everything
+ self.utime(os.path.join(testdir, 'header.h'))
+ self.assertRebuiltTarget('prog')
+
+ def test_custom_target_changes_cause_rebuild(self):
+ '''
+ Test that in a custom target, changes to the input files, the
+ ExternalProgram, and any File objects on the command-line cause
+ a rebuild.
+ '''
+ testdir = os.path.join(self.common_test_dir, '64 custom header generator')
+ self.init(testdir)
+ self.build()
+ # Immediately rebuilding should not do anything
+ self.assertBuildIsNoop()
+ # Changing mtime of these should rebuild everything
+ for f in ('input.def', 'makeheader.py', 'somefile.txt'):
+ self.utime(os.path.join(testdir, f))
+ self.assertRebuiltTarget('prog')
+
class WindowsTests(BasePlatformTests):
'''
@@ -1104,7 +1214,7 @@ class LinuxlikeTests(BasePlatformTests):
if qt4 != 0 or qt5 != 0:
raise unittest.SkipTest('Qt not found with pkg-config')
testdir = os.path.join(self.framework_test_dir, '4 qt')
- self.init(testdir)
+ self.init(testdir, ['-Dmethod=pkg-config'])
# Confirm that the dependency was found with qmake
msg = 'Qt4 native `pkg-config` dependency (modules: Core, Gui) found: YES\n'
msg2 = 'Qt5 native `pkg-config` dependency (modules: Core, Gui) found: YES\n'
@@ -1127,10 +1237,8 @@ class LinuxlikeTests(BasePlatformTests):
if 'Qt version 5' not in output and 'qt5' not in output:
raise unittest.SkipTest('Qmake found, but it is not for Qt 5.')
# Disable pkg-config codepath and force searching with qmake/qmake-qt5
- os.environ['PKG_CONFIG_LIBDIR'] = self.builddir
- os.environ['PKG_CONFIG_PATH'] = self.builddir
testdir = os.path.join(self.framework_test_dir, '4 qt')
- self.init(testdir)
+ self.init(testdir, ['-Dmethod=qmake'])
# Confirm that the dependency was found with qmake
msg = 'Qt5 native `qmake-qt5` dependency (modules: Core) found: YES\n'
msg2 = 'Qt5 native `qmake` dependency (modules: Core) found: YES\n'
@@ -1214,15 +1322,6 @@ class LinuxlikeTests(BasePlatformTests):
Oargs = [arg for arg in cmd if arg.startswith('-O')]
self.assertEqual(Oargs, [Oflag, '-O0'])
- def test_custom_target_exe_data_deterministic(self):
- testdir = os.path.join(self.common_test_dir, '117 custom target capture')
- self.init(testdir)
- meson_exe_dat1 = glob(os.path.join(self.privatedir, 'meson_exe*.dat'))
- self.wipe()
- self.init(testdir)
- meson_exe_dat2 = glob(os.path.join(self.privatedir, 'meson_exe*.dat'))
- self.assertListEqual(meson_exe_dat1, meson_exe_dat2)
-
def _test_stds_impl(self, testdir, compiler, p):
lang_std = p + '_std'
# Check that all the listed -std=xxx options for this compiler work
@@ -1328,6 +1427,27 @@ class LinuxlikeTests(BasePlatformTests):
# The chown failed nonfatally if we're not root
self.assertEqual(0, statf.st_uid)
+ def test_cpp_std_override(self):
+ testdir = os.path.join(self.unit_test_dir, '6 std override')
+ self.init(testdir)
+ compdb = self.get_compdb()
+ for i in compdb:
+ if 'prog03' in i['file']:
+ c03_comp = i['command']
+ if 'prog11' in i['file']:
+ c11_comp = i['command']
+ if 'progp' in i['file']:
+ plain_comp = i['command']
+ self.assertNotEqual(len(plain_comp), 0)
+ self.assertIn('-std=c++03', c03_comp)
+ self.assertNotIn('-std=c++11', c03_comp)
+ self.assertIn('-std=c++11', c11_comp)
+ self.assertNotIn('-std=c++03', c11_comp)
+ self.assertNotIn('-std=c++03', plain_comp)
+ self.assertNotIn('-std=c++11', plain_comp)
+ # Now werror
+ self.assertIn('-Werror', plain_comp)
+ self.assertNotIn('-Werror', c03_comp)
class RewriterTests(unittest.TestCase):
diff --git a/test cases/common/123 subproject project arguments/exe.c b/test cases/common/123 subproject project arguments/exe.c
index b04344a..d6440f0 100644
--- a/test cases/common/123 subproject project arguments/exe.c
+++ b/test cases/common/123 subproject project arguments/exe.c
@@ -18,6 +18,10 @@
#error
#endif
+#ifndef PROJECT_OPTION_C_CPP
+#error
+#endif
+
int main(int argc, char **argv) {
return 0;
}
diff --git a/test cases/common/123 subproject project arguments/exe.cpp b/test cases/common/123 subproject project arguments/exe.cpp
index 7ffe098..8471c6f 100644
--- a/test cases/common/123 subproject project arguments/exe.cpp
+++ b/test cases/common/123 subproject project arguments/exe.cpp
@@ -18,6 +18,10 @@
#error
#endif
+#ifndef PROJECT_OPTION_C_CPP
+#error
+#endif
+
int main(int argc, char **argv) {
return 0;
}
diff --git a/test cases/common/123 subproject project arguments/meson.build b/test cases/common/123 subproject project arguments/meson.build
index aee803c..90d4c05 100644
--- a/test cases/common/123 subproject project arguments/meson.build
+++ b/test cases/common/123 subproject project arguments/meson.build
@@ -4,10 +4,13 @@ project('project options tester', 'c', 'cpp',
add_global_arguments('-DGLOBAL_ARGUMENT', language: 'c')
add_project_arguments('-DPROJECT_OPTION', language: 'c')
-add_project_arguments('-DPROJECT_OPTION_1', language: 'c')
add_project_arguments('-DPROJECT_OPTION_CPP', language: 'cpp')
+add_project_arguments('-DPROJECT_OPTION_C_CPP', language: ['c', 'cpp'])
sub = subproject('subexe', version : '1.0.0')
+
+add_project_arguments('-DPROJECT_OPTION_1', language: 'c')
+
e = executable('exe', 'exe.c')
e = executable('execpp', 'exe.cpp')
test('exetest', e)
diff --git a/test cases/common/123 subproject project arguments/subprojects/subexe/subexe.c b/test cases/common/123 subproject project arguments/subprojects/subexe/subexe.c
index 6ebd752..f748afc 100644
--- a/test cases/common/123 subproject project arguments/subprojects/subexe/subexe.c
+++ b/test cases/common/123 subproject project arguments/subprojects/subexe/subexe.c
@@ -6,6 +6,10 @@
#error
#endif
+#ifdef PROJECT_OPTION_C_CPP
+#error
+#endif
+
#ifndef GLOBAL_ARGUMENT
#error
#endif
diff --git a/test cases/common/125 shared module/meson.build b/test cases/common/125 shared module/meson.build
index 7c15bcc..d96d8fc 100644
--- a/test cases/common/125 shared module/meson.build
+++ b/test cases/common/125 shared module/meson.build
@@ -5,7 +5,8 @@ l = shared_library('runtime', 'runtime.c')
# Do NOT link the module with the runtime library. This
# is a common approach for plugins that are only used
# with dlopen. Any symbols are resolved dynamically
-# at runtime
+# at runtime. This requires extra help on Windows, so
+# should be avoided unless really neccessary.
m = shared_module('mymodule', 'module.c')
e = executable('prog', 'prog.c', link_with : l, dependencies : dl)
test('import test', e, args : m)
diff --git a/test cases/common/125 shared module/module.c b/test cases/common/125 shared module/module.c
index 56078c5..181b760 100644
--- a/test cases/common/125 shared module/module.c
+++ b/test cases/common/125 shared module/module.c
@@ -9,14 +9,24 @@
#endif
#endif
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__CYGWIN__)
#include <stdio.h>
-#include <windows.h>
-#include <tlhelp32.h>
typedef int (*fptr) (void);
+#ifdef __CYGWIN__
+
+#include <dlfcn.h>
+
+fptr find_any_f (const char *name) {
+ return (fptr) dlsym(RTLD_DEFAULT, name);
+}
+#else /* _WIN32 */
+
+#include <windows.h>
+#include <tlhelp32.h>
+
/* Unlike Linux and OS X, when a library is loaded, all the symbols aren't
* loaded into a single namespace. You must fetch the symbol by iterating over
* all loaded modules. Code for finding the function from any of the loaded
@@ -45,6 +55,7 @@ fptr find_any_f (const char *name) {
CloseHandle (snapshot);
return f;
}
+#endif
int DLL_PUBLIC func() {
fptr f;
diff --git a/test cases/common/126 llvm ir and assembly/square-x86_64.S b/test cases/common/126 llvm ir and assembly/square-x86_64.S
index 4adc31e..1452f47 100644
--- a/test cases/common/126 llvm ir and assembly/square-x86_64.S
+++ b/test cases/common/126 llvm ir and assembly/square-x86_64.S
@@ -19,12 +19,12 @@ END
.text
.globl SYMBOL_NAME(square_unsigned)
-# ifdef _WIN32 /* MinGW */
+# if defined(_WIN32) || defined(__CYGWIN__) /* msabi */
SYMBOL_NAME(square_unsigned):
imull %ecx, %ecx
movl %ecx, %eax
retq
-# else /* Linux and OS X */
+# else /* sysvabi */
SYMBOL_NAME(square_unsigned):
imull %edi, %edi
movl %edi, %eax
diff --git a/test cases/common/135 generated assembly/main.c b/test cases/common/135 generated assembly/main.c
index 97fe723..b669cba 100644
--- a/test cases/common/135 generated assembly/main.c
+++ b/test cases/common/135 generated assembly/main.c
@@ -1,5 +1,8 @@
#include <stdio.h>
+#if defined(_WIN32) || defined(__CYGWIN__)
+ __declspec(dllimport)
+#endif
unsigned square_unsigned (unsigned a);
int
diff --git a/test cases/common/135 generated assembly/square-x86_64.S.in b/test cases/common/135 generated assembly/square-x86_64.S.in
index b6d7fb0..0834f16 100644
--- a/test cases/common/135 generated assembly/square-x86_64.S.in
+++ b/test cases/common/135 generated assembly/square-x86_64.S.in
@@ -23,12 +23,12 @@ END
.type square_unsigned,@function
# endif
-# ifdef _WIN32 /* MinGW */
+# if defined(_WIN32) || defined(__CYGWIN__) /* msabi */
SYMBOL_NAME(square_unsigned):
imull %ecx, %ecx
movl %ecx, %eax
retq
-# else /* Linux and OS X */
+# else /* sysvabi */
SYMBOL_NAME(square_unsigned):
imull %edi, %edi
movl %edi, %eax
diff --git a/test cases/common/139 custom target multiple outputs/generator.py b/test cases/common/139 custom target multiple outputs/generator.py
new file mode 100755
index 0000000..39dbd11
--- /dev/null
+++ b/test cases/common/139 custom target multiple outputs/generator.py
@@ -0,0 +1,14 @@
+#!/usr/bin/env python3
+
+import sys, os
+
+if len(sys.argv) != 3:
+ print(sys.argv[0], '<namespace>', '<output dir>')
+
+name = sys.argv[1]
+odir = sys.argv[2]
+
+with open(os.path.join(odir, name + '.h'), 'w') as f:
+ f.write('int func();\n')
+with open(os.path.join(odir, name + '.sh'), 'w') as f:
+ f.write('#!/bin/bash')
diff --git a/test cases/common/139 custom target multiple outputs/installed_files.txt b/test cases/common/139 custom target multiple outputs/installed_files.txt
new file mode 100644
index 0000000..21e1249
--- /dev/null
+++ b/test cases/common/139 custom target multiple outputs/installed_files.txt
@@ -0,0 +1,6 @@
+usr/include/diff.h
+usr/include/first.h
+usr/bin/diff.sh
+usr/bin/second.sh
+opt/same.h
+opt/same.sh
diff --git a/test cases/common/139 custom target multiple outputs/meson.build b/test cases/common/139 custom target multiple outputs/meson.build
new file mode 100644
index 0000000..6412864
--- /dev/null
+++ b/test cases/common/139 custom target multiple outputs/meson.build
@@ -0,0 +1,28 @@
+project('multiple outputs install', 'c')
+
+gen = find_program('generator.py')
+
+custom_target('different-install-dirs',
+ output : ['diff.h', 'diff.sh'],
+ command : [gen, 'diff', '@OUTDIR@'],
+ install : true,
+ install_dir : [join_paths(get_option('prefix'), get_option('includedir')),
+ join_paths(get_option('prefix'), get_option('bindir'))])
+
+custom_target('same-install-dir',
+ output : ['same.h', 'same.sh'],
+ command : [gen, 'same', '@OUTDIR@'],
+ install : true,
+ install_dir : '/opt')
+
+custom_target('only-install-first',
+ output : ['first.h', 'first.sh'],
+ command : [gen, 'first', '@OUTDIR@'],
+ install : true,
+ install_dir : [join_paths(get_option('prefix'), get_option('includedir')), false])
+
+custom_target('only-install-second',
+ output : ['second.h', 'second.sh'],
+ command : [gen, 'second', '@OUTDIR@'],
+ install : true,
+ install_dir : [false, join_paths(get_option('prefix'), get_option('bindir'))])
diff --git a/test cases/common/139 override options/four.c b/test cases/common/139 override options/four.c
new file mode 100644
index 0000000..54f8491
--- /dev/null
+++ b/test cases/common/139 override options/four.c
@@ -0,0 +1,9 @@
+int func();
+
+static int duplicate_func() {
+ return -4;
+}
+
+int main(int argc, char **argv) {
+ return duplicate_func() + func();
+}
diff --git a/test cases/common/139 override options/meson.build b/test cases/common/139 override options/meson.build
new file mode 100644
index 0000000..0db0513
--- /dev/null
+++ b/test cases/common/139 override options/meson.build
@@ -0,0 +1,8 @@
+project('option override', 'c',
+ default_options : 'unity=true')
+
+executable('mustunity', 'one.c', 'two.c')
+executable('notunity', 'three.c', 'four.c',
+ override_options : ['unity=false'])
+
+
diff --git a/test cases/common/139 override options/one.c b/test cases/common/139 override options/one.c
new file mode 100644
index 0000000..14fe9d6
--- /dev/null
+++ b/test cases/common/139 override options/one.c
@@ -0,0 +1,3 @@
+static int hidden_func() {
+ return 0;
+}
diff --git a/test cases/common/139 override options/three.c b/test cases/common/139 override options/three.c
new file mode 100644
index 0000000..305a575
--- /dev/null
+++ b/test cases/common/139 override options/three.c
@@ -0,0 +1,7 @@
+static int duplicate_func() {
+ return 4;
+}
+
+int func() {
+ return duplicate_func();
+}
diff --git a/test cases/common/139 override options/two.c b/test cases/common/139 override options/two.c
new file mode 100644
index 0000000..04b1d3f
--- /dev/null
+++ b/test cases/common/139 override options/two.c
@@ -0,0 +1,6 @@
+/*
+ * Requires a Unity build. Otherwise hidden_func is not specified.
+ */
+int main(int argc, char **argv) {
+ return hidden_func();
+}
diff --git a/test cases/common/140 get define/meson.build b/test cases/common/140 get define/meson.build
new file mode 100644
index 0000000..5ce4b36
--- /dev/null
+++ b/test cases/common/140 get define/meson.build
@@ -0,0 +1,31 @@
+project('get define', 'c', 'cpp')
+
+host_system = host_machine.system()
+
+foreach lang : ['c', 'cpp']
+ cc = meson.get_compiler(lang)
+ if host_system == 'linux'
+ d = cc.get_define('__linux__')
+ assert(d == '1', '__linux__ value is @0@ instead of 1'.format(d))
+ elif host_system == 'darwin'
+ d = cc.get_define('__APPLE__')
+ assert(d == '1', '__APPLE__ value is @0@ instead of 1'.format(d))
+ elif host_system == 'windows'
+ d = cc.get_define('_WIN32')
+ assert(d == '1', '_WIN32 value is @0@ instead of 1'.format(d))
+ elif host_system == 'cygwin'
+ d = cc.get_define('__CYGWIN__')
+ assert(d == '1', '__CYGWIN__ value is @0@ instead of 1'.format(d))
+ else
+ error('Please report a bug and help us improve support for this platform')
+ endif
+
+ # Check that an undefined value is empty.
+ have = cc.get_define('MESON_FAIL_VALUE')
+ assert(have == '', 'MESON_FAIL_VALUE value is "@0@" instead of ""'.format(have))
+
+ # This is used in the test_preprocessor_checks_CPPFLAGS() unit test.
+ have = cc.get_define('MESON_TEST_DEFINE_VALUE')
+ expect = get_option('MESON_TEST_DEFINE_VALUE')
+ assert(have == expect, 'MESON_TEST_DEFINE_VALUE value is "@0@" instead of "@1@"'.format(have, expect))
+endforeach
diff --git a/test cases/common/140 get define/meson_options.txt b/test cases/common/140 get define/meson_options.txt
new file mode 100644
index 0000000..a88cecd
--- /dev/null
+++ b/test cases/common/140 get define/meson_options.txt
@@ -0,0 +1 @@
+option('MESON_TEST_DEFINE_VALUE', type : 'string', default : '')
diff --git a/test cases/common/141 c cpp and asm/meson.build b/test cases/common/141 c cpp and asm/meson.build
index 9c90434..2c3610e 100644
--- a/test cases/common/141 c cpp and asm/meson.build
+++ b/test cases/common/141 c cpp and asm/meson.build
@@ -1,6 +1,7 @@
project('c cpp and asm', 'c', 'cpp')
cpu = host_machine.cpu_family()
+cc = meson.get_compiler('c')
supported_cpus = ['arm', 'x86', 'x86_64']
@@ -12,6 +13,10 @@ if meson.get_compiler('c').get_id() == 'msvc'
error('MESON_SKIP_TEST MSVC can\'t compile assembly')
endif
+if cc.symbols_have_underscore_prefix()
+ add_project_arguments('-DMESON_TEST__UNDERSCORE_SYMBOL', language: 'c')
+endif
+
test('test-c-asm', executable('c-asm', ['main.c', 'retval-' + cpu + '.S']))
test('test-cpp-asm', executable('cpp-asm', ['main.cpp', 'retval-' + cpu + '.S']))
test('test-c-cpp-asm', executable('c-cpp-asm', ['somelib.c', 'main.cpp', 'retval-' + cpu + '.S']))
diff --git a/test cases/common/141 c cpp and asm/symbol-underscore.h b/test cases/common/141 c cpp and asm/symbol-underscore.h
index 508cf50..d0f3ef9 100644
--- a/test cases/common/141 c cpp and asm/symbol-underscore.h
+++ b/test cases/common/141 c cpp and asm/symbol-underscore.h
@@ -1,4 +1,4 @@
-#if defined(__WIN32__) || defined(__APPLE__)
+#if defined(MESON_TEST__UNDERSCORE_SYMBOL)
# define SYMBOL_NAME(name) _##name
#else
# define SYMBOL_NAME(name) name
diff --git a/test cases/common/141 mesonintrospect from scripts/check_env.py b/test cases/common/141 mesonintrospect from scripts/check_env.py
new file mode 100644
index 0000000..dc8ad63
--- /dev/null
+++ b/test cases/common/141 mesonintrospect from scripts/check_env.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+
+do_print = False
+
+if len(sys.argv) > 1:
+ do_print = bool(sys.argv[1])
+
+if 'MESONINTROSPECT' not in os.environ:
+ raise RuntimeError('MESONINTROSPECT not found')
+
+mesonintrospect = os.environ['MESONINTROSPECT']
+
+if not os.path.isfile(mesonintrospect):
+ raise RuntimeError('{!r} does not exist'.format(mesonintrospect))
+
+if do_print:
+ print(mesonintrospect, end='')
diff --git a/test cases/common/141 mesonintrospect from scripts/meson.build b/test cases/common/141 mesonintrospect from scripts/meson.build
new file mode 100644
index 0000000..f78710b
--- /dev/null
+++ b/test cases/common/141 mesonintrospect from scripts/meson.build
@@ -0,0 +1,14 @@
+project('mesonintrospect from scripts', 'c')
+
+python = import('python3').find_python()
+
+ret = run_command(python, ['check_env.py', '1'])
+if ret.returncode() == 0
+ find_program(ret.stdout())
+else
+ message(ret.stdout())
+ message(ret.stderr())
+endif
+
+meson.add_postconf_script('check_env.py')
+meson.add_install_script('check_env.py')
diff --git a/test cases/common/139 compute int/config.h.in b/test cases/common/142 compute int/config.h.in
index ad8d077..ad8d077 100644
--- a/test cases/common/139 compute int/config.h.in
+++ b/test cases/common/142 compute int/config.h.in
diff --git a/test cases/common/139 compute int/foobar.h b/test cases/common/142 compute int/foobar.h
index fd3cb5e..fd3cb5e 100644
--- a/test cases/common/139 compute int/foobar.h
+++ b/test cases/common/142 compute int/foobar.h
diff --git a/test cases/common/139 compute int/meson.build b/test cases/common/142 compute int/meson.build
index 43553fe..43553fe 100644
--- a/test cases/common/139 compute int/meson.build
+++ b/test cases/common/142 compute int/meson.build
diff --git a/test cases/common/139 compute int/prog.c.in b/test cases/common/142 compute int/prog.c.in
index 3ff1463..3ff1463 100644
--- a/test cases/common/139 compute int/prog.c.in
+++ b/test cases/common/142 compute int/prog.c.in
diff --git a/test cases/common/139 custom target object output/meson.build b/test cases/common/143 custom target object output/meson.build
index ede165b..ede165b 100644
--- a/test cases/common/139 custom target object output/meson.build
+++ b/test cases/common/143 custom target object output/meson.build
diff --git a/test cases/common/139 custom target object output/obj_generator.py b/test cases/common/143 custom target object output/obj_generator.py
index a33872a..a33872a 100644
--- a/test cases/common/139 custom target object output/obj_generator.py
+++ b/test cases/common/143 custom target object output/obj_generator.py
diff --git a/test cases/common/139 custom target object output/objdir/meson.build b/test cases/common/143 custom target object output/objdir/meson.build
index 0d7f6c2..0d7f6c2 100644
--- a/test cases/common/139 custom target object output/objdir/meson.build
+++ b/test cases/common/143 custom target object output/objdir/meson.build
diff --git a/test cases/common/139 custom target object output/objdir/source.c b/test cases/common/143 custom target object output/objdir/source.c
index 7779b33..7779b33 100644
--- a/test cases/common/139 custom target object output/objdir/source.c
+++ b/test cases/common/143 custom target object output/objdir/source.c
diff --git a/test cases/common/139 custom target object output/progdir/meson.build b/test cases/common/143 custom target object output/progdir/meson.build
index 4216c24..4216c24 100644
--- a/test cases/common/139 custom target object output/progdir/meson.build
+++ b/test cases/common/143 custom target object output/progdir/meson.build
diff --git a/test cases/common/139 custom target object output/progdir/prog.c b/test cases/common/143 custom target object output/progdir/prog.c
index c1ece33..c1ece33 100644
--- a/test cases/common/139 custom target object output/progdir/prog.c
+++ b/test cases/common/143 custom target object output/progdir/prog.c
diff --git a/test cases/common/144 empty build file/meson.build b/test cases/common/144 empty build file/meson.build
new file mode 100644
index 0000000..73d0397
--- /dev/null
+++ b/test cases/common/144 empty build file/meson.build
@@ -0,0 +1,2 @@
+project('subdir with empty meson.build test', 'c')
+subdir('subdir')
diff --git a/test cases/common/144 empty build file/subdir/meson.build b/test cases/common/144 empty build file/subdir/meson.build
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test cases/common/144 empty build file/subdir/meson.build
diff --git a/test cases/common/16 configure file/meson.build b/test cases/common/16 configure file/meson.build
index 8271ca3..8ffc28c 100644
--- a/test cases/common/16 configure file/meson.build
+++ b/test cases/common/16 configure file/meson.build
@@ -22,6 +22,11 @@ e = executable('inctest', 'prog.c',
cfile)
test('inctest', e)
+# Test if we can also pass files() as input
+configure_file(input : files('config.h.in'),
+ output : 'config2.h',
+ configuration : conf)
+
# Now generate a header file with an external script.
genprog = import('python3').find_python()
scriptfile = '@0@/generator.py'.format(meson.current_source_dir())
diff --git a/test cases/common/22 header in file list/prog.c b/test cases/common/22 header in file list/prog.c
index 0314ff1..fbedab8 100644
--- a/test cases/common/22 header in file list/prog.c
+++ b/test cases/common/22 header in file list/prog.c
@@ -1 +1,3 @@
+#include "header.h"
+
int main(int argc, char **argv) { return 0; }
diff --git a/test cases/common/23 global arg/meson.build b/test cases/common/23 global arg/meson.build
index aec5c2d..d7fd428 100644
--- a/test cases/common/23 global arg/meson.build
+++ b/test cases/common/23 global arg/meson.build
@@ -3,6 +3,8 @@ project('global arg test', 'cpp', 'c')
add_global_arguments('-DMYTHING', language : 'c')
add_global_arguments('-DMYCPPTHING', language : 'cpp')
+add_global_arguments('-DMYCANDCPPTHING', language: ['c', 'cpp'])
+
exe1 = executable('prog', 'prog.c')
exe2 = executable('prog2', 'prog.cc')
diff --git a/test cases/common/23 global arg/prog.c b/test cases/common/23 global arg/prog.c
index df91777..ace5a0a 100644
--- a/test cases/common/23 global arg/prog.c
+++ b/test cases/common/23 global arg/prog.c
@@ -6,6 +6,10 @@
#error "Wrong global argument set"
#endif
+#ifndef MYCANDCPPTHING
+#error "Global argument not set"
+#endif
+
int main(int argc, char **argv) {
return 0;
}
diff --git a/test cases/common/23 global arg/prog.cc b/test cases/common/23 global arg/prog.cc
index 342fdd0..0ffd85e 100644
--- a/test cases/common/23 global arg/prog.cc
+++ b/test cases/common/23 global arg/prog.cc
@@ -6,6 +6,10 @@
#error "Global argument not set"
#endif
+#ifndef MYCANDCPPTHING
+#error "Global argument not set"
+#endif
+
int main(int argc, char **argv) {
return 0;
}
diff --git a/test cases/common/37 has header/meson.build b/test cases/common/37 has header/meson.build
index 4299ce5..b53849c 100644
--- a/test cases/common/37 has header/meson.build
+++ b/test cases/common/37 has header/meson.build
@@ -11,19 +11,19 @@ configure_file(input : non_existant_header,
# Test that the fallback to __has_include also works on all compilers
if host_system != 'darwin'
- args = [[], ['-U__has_include']]
+ fallbacks = ['', '\n#undef __has_include']
else
# On Darwin's clang you can't redefine builtin macros so the above doesn't work
- args = [[]]
+ fallbacks = ['']
endif
-foreach arg : args
+foreach fallback : fallbacks
foreach comp : [meson.get_compiler('c'), meson.get_compiler('cpp')]
- assert(comp.has_header('stdio.h', args : arg), 'Stdio missing.')
+ assert(comp.has_header('stdio.h', prefix : fallback), 'Stdio missing.')
# stdio.h doesn't actually need stdlib.h, but just test that setting the
# prefix does not result in an error.
- assert(comp.has_header('stdio.h', prefix : '#include <stdlib.h>', args : arg),
+ assert(comp.has_header('stdio.h', prefix : '#include <stdlib.h>' + fallback),
'Stdio missing.')
# XInput.h should not require type definitions from windows.h, but it does
@@ -32,9 +32,9 @@ foreach arg : args
# We only do this check on MSVC because MinGW often defines its own wrappers
# that pre-include windows.h
if comp.get_id() == 'msvc'
- assert(comp.has_header('XInput.h', prefix : '#include <windows.h>', args : arg),
+ assert(comp.has_header('XInput.h', prefix : '#include <windows.h>' + fallback),
'XInput.h should not be missing on Windows')
- assert(comp.has_header('XInput.h', prefix : '#define _X86_', args : arg),
+ assert(comp.has_header('XInput.h', prefix : '#define _X86_' + fallback),
'XInput.h should not need windows.h')
endif
@@ -42,13 +42,13 @@ foreach arg : args
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80005
# https://github.com/mesonbuild/meson/issues/1458
if host_system == 'linux'
- assert(comp.has_header('linux/if.h', args : arg),
+ assert(comp.has_header('linux/if.h', prefix : fallback),
'Could not find <linux/if.h>')
endif
# This header exists in the source and the builddir, but we still must not
# find it since we are looking in the system directories.
- assert(not comp.has_header(non_existant_header, args : arg),
+ assert(not comp.has_header(non_existant_header, prefix : fallback),
'Found non-existant header.')
endforeach
endforeach
diff --git a/test cases/common/64 custom header generator/meson.build b/test cases/common/64 custom header generator/meson.build
index b422401..bcc9a53 100644
--- a/test cases/common/64 custom header generator/meson.build
+++ b/test cases/common/64 custom header generator/meson.build
@@ -5,7 +5,7 @@ gen = find_program('makeheader.py')
generated_h = custom_target('makeheader.py',
output : 'myheader.lh', # Suffix not .h to ensure this works with custom suffixes, too.
input : 'input.def',
-command : [gen, '@INPUT0@', '@OUTPUT0@'])
+command : [gen, '@INPUT0@', '@OUTPUT0@', files('somefile.txt')])
prog = executable('prog', 'prog.c', generated_h)
test('gentest', prog)
diff --git a/test cases/common/64 custom header generator/somefile.txt b/test cases/common/64 custom header generator/somefile.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test cases/common/64 custom header generator/somefile.txt
diff --git a/test cases/failing/14 invalid option name/meson_options.txt b/test cases/failing/14 invalid option name/meson_options.txt
index c656402..aab6ae8 100644
--- a/test cases/failing/14 invalid option name/meson_options.txt
+++ b/test cases/failing/14 invalid option name/meson_options.txt
@@ -1 +1 @@
-option('invalid/name', type : 'boolean', value : false) \ No newline at end of file
+option('invalid:name', type : 'boolean', value : false)
diff --git a/test cases/failing/19 target clash/meson.build b/test cases/failing/19 target clash/meson.build
index 070631b..fbc757c 100644
--- a/test cases/failing/19 target clash/meson.build
+++ b/test cases/failing/19 target clash/meson.build
@@ -7,7 +7,7 @@ project('clash', 'c')
# This test might fail to work on different backends or when
# output location is redirected.
-if host_machine.system() == 'windows'
+if host_machine.system() == 'windows' or host_machine.system() == 'cygwin'
error('This is expected.')
endif
diff --git a/test cases/failing/43 custom target outputs not matching install_dirs/generator.py b/test cases/failing/43 custom target outputs not matching install_dirs/generator.py
new file mode 100755
index 0000000..4ac6179
--- /dev/null
+++ b/test cases/failing/43 custom target outputs not matching install_dirs/generator.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python3
+
+import sys, os
+
+if len(sys.argv) != 3:
+ print(sys.argv[0], '<namespace>', '<output dir>')
+
+name = sys.argv[1]
+odir = sys.argv[2]
+
+with open(os.path.join(odir, name + '.h'), 'w') as f:
+ f.write('int func();\n')
+with open(os.path.join(odir, name + '.c'), 'w') as f:
+ f.write('int main(int argc, char *argv[]) { return 0; }')
+with open(os.path.join(odir, name + '.sh'), 'w') as f:
+ f.write('#!/bin/bash')
diff --git a/test cases/failing/43 custom target outputs not matching install_dirs/installed_files.txt b/test cases/failing/43 custom target outputs not matching install_dirs/installed_files.txt
new file mode 100644
index 0000000..21e1249
--- /dev/null
+++ b/test cases/failing/43 custom target outputs not matching install_dirs/installed_files.txt
@@ -0,0 +1,6 @@
+usr/include/diff.h
+usr/include/first.h
+usr/bin/diff.sh
+usr/bin/second.sh
+opt/same.h
+opt/same.sh
diff --git a/test cases/failing/43 custom target outputs not matching install_dirs/meson.build b/test cases/failing/43 custom target outputs not matching install_dirs/meson.build
new file mode 100644
index 0000000..45bd7b3
--- /dev/null
+++ b/test cases/failing/43 custom target outputs not matching install_dirs/meson.build
@@ -0,0 +1,13 @@
+project('outputs not matching install_dirs', 'c')
+
+gen = find_program('generator.py')
+
+if meson.backend() != 'ninja'
+ error('Failing manually, test is only for the ninja backend')
+endif
+
+custom_target('too-few-install-dirs',
+ output : ['toofew.h', 'toofew.c', 'toofew.sh'],
+ command : [gen, 'toofew', '@OUTDIR@'],
+ install : true,
+ install_dir : [join_paths(get_option('prefix'), get_option('includedir')), false])
diff --git a/test cases/failing/43 project name colon/meson.build b/test cases/failing/43 project name colon/meson.build
new file mode 100644
index 0000000..53e947e
--- /dev/null
+++ b/test cases/failing/43 project name colon/meson.build
@@ -0,0 +1 @@
+project('name with :')
diff --git a/test cases/frameworks/4 qt/meson.build b/test cases/frameworks/4 qt/meson.build
index fec5959..468b9c9 100644
--- a/test cases/frameworks/4 qt/meson.build
+++ b/test cases/frameworks/4 qt/meson.build
@@ -9,17 +9,17 @@ foreach qt : ['qt4', 'qt5']
qt_modules += qt5_modules
endif
# Test that invalid modules are indeed not found
- fakeqtdep = dependency(qt, modules : ['DefinitelyNotFound'], required : false)
+ fakeqtdep = dependency(qt, modules : ['DefinitelyNotFound'], required : false, method : get_option('method'))
if fakeqtdep.found()
error('Invalid qt dep incorrectly found!')
endif
# Test that partially-invalid modules are indeed not found
- fakeqtdep = dependency(qt, modules : ['Core', 'DefinitelyNotFound'], required : false)
+ fakeqtdep = dependency(qt, modules : ['Core', 'DefinitelyNotFound'], required : false, method : get_option('method'))
if fakeqtdep.found()
error('Invalid qt dep incorrectly found!')
endif
# If qt4 modules are found, test that. qt5 is required.
- qtdep = dependency(qt, modules : qt_modules, required : qt == 'qt5')
+ qtdep = dependency(qt, modules : qt_modules, required : qt == 'qt5', method : get_option('method'))
if qtdep.found()
qtmodule = import(qt)
@@ -30,10 +30,11 @@ foreach qt : ['qt4', 'qt5']
moc_headers : ['mainWindow.h'], # These need to be fed through the moc tool before use.
ui_files : 'mainWindow.ui', # XML files that need to be compiled with the uic tol.
qresources : ['stuff.qrc', 'stuff2.qrc'], # Resource file for rcc compiler.
+ method : get_option('method')
)
# Test that setting a unique name with a positional argument works
- qtmodule.preprocess(qt + 'teststuff', qresources : ['stuff.qrc'])
+ qtmodule.preprocess(qt + 'teststuff', qresources : ['stuff.qrc'], method : get_option('method'))
qexe = executable(qt + 'app',
sources : ['main.cpp', 'mainWindow.cpp', # Sources that don't need preprocessing.
@@ -43,7 +44,7 @@ foreach qt : ['qt4', 'qt5']
# We need a console test application because some test environments
# do not have an X server.
- qtcore = dependency(qt, modules : 'Core')
+ qtcore = dependency(qt, modules : 'Core', method : get_option('method'))
qtcoreapp = executable(qt + 'core', 'q5core.cpp',
dependencies : qtcore)
@@ -55,7 +56,8 @@ foreach qt : ['qt4', 'qt5']
# files from sources.
manpreprocessed = qtmodule.preprocess(
moc_sources : 'manualinclude.cpp',
- moc_headers : 'manualinclude.h')
+ moc_headers : 'manualinclude.h',
+ method : get_option('method'))
qtmaninclude = executable(qt + 'maninclude',
sources : ['manualinclude.cpp', manpreprocessed],
diff --git a/test cases/frameworks/4 qt/meson_options.txt b/test cases/frameworks/4 qt/meson_options.txt
new file mode 100644
index 0000000..bc1069e
--- /dev/null
+++ b/test cases/frameworks/4 qt/meson_options.txt
@@ -0,0 +1 @@
+option('method', type : 'string', value : 'auto', description : 'The method to use to find Qt')
diff --git a/test cases/linuxlike/10 large file support/meson.build b/test cases/linuxlike/10 large file support/meson.build
index aa4eccf..6683345 100644
--- a/test cases/linuxlike/10 large file support/meson.build
+++ b/test cases/linuxlike/10 large file support/meson.build
@@ -1,5 +1,9 @@
project('trivial test', 'c')
+if host_machine.system() == 'cygwin'
+ error('MESON_SKIP_TEST _FILE_OFFSET_BITS not yet supported on Cygwin.')
+endif
+
cc = meson.get_compiler('c')
size = cc.sizeof('off_t')
diff --git a/test cases/linuxlike/11 runpath rpath ldlibrarypath/lib.c b/test cases/linuxlike/11 runpath rpath ldlibrarypath/lib.c
new file mode 100644
index 0000000..2784e01
--- /dev/null
+++ b/test cases/linuxlike/11 runpath rpath ldlibrarypath/lib.c
@@ -0,0 +1,3 @@
+int some_symbol (void) {
+ return RET_VALUE;
+}
diff --git a/test cases/linuxlike/11 runpath rpath ldlibrarypath/lib1/meson.build b/test cases/linuxlike/11 runpath rpath ldlibrarypath/lib1/meson.build
new file mode 100644
index 0000000..cbc75bd
--- /dev/null
+++ b/test cases/linuxlike/11 runpath rpath ldlibrarypath/lib1/meson.build
@@ -0,0 +1,2 @@
+lib1 = shared_library('testeh', libsrc,
+ c_args : '-DRET_VALUE=1')
diff --git a/test cases/linuxlike/11 runpath rpath ldlibrarypath/lib2/meson.build b/test cases/linuxlike/11 runpath rpath ldlibrarypath/lib2/meson.build
new file mode 100644
index 0000000..f2c6dcc
--- /dev/null
+++ b/test cases/linuxlike/11 runpath rpath ldlibrarypath/lib2/meson.build
@@ -0,0 +1,3 @@
+lib2 = shared_library('esteh', libsrc,
+ name_prefix : 'libt',
+ c_args : '-DRET_VALUE=2')
diff --git a/test cases/linuxlike/11 runpath rpath ldlibrarypath/main.c b/test cases/linuxlike/11 runpath rpath ldlibrarypath/main.c
new file mode 100644
index 0000000..c9eff99
--- /dev/null
+++ b/test cases/linuxlike/11 runpath rpath ldlibrarypath/main.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+
+int some_symbol (void);
+
+int main (int argc, char *argv[]) {
+ int ret = some_symbol ();
+ if (ret == 1)
+ return 0;
+ fprintf (stderr, "ret was %i instead of 1\n", ret);
+ return -1;
+}
diff --git a/test cases/linuxlike/11 runpath rpath ldlibrarypath/meson.build b/test cases/linuxlike/11 runpath rpath ldlibrarypath/meson.build
new file mode 100644
index 0000000..b49da66
--- /dev/null
+++ b/test cases/linuxlike/11 runpath rpath ldlibrarypath/meson.build
@@ -0,0 +1,14 @@
+project('runpath rpath ldlibrarypath', 'c')
+
+libsrc = files('lib.c')
+
+subdir('lib1')
+subdir('lib2')
+
+lib2dir = meson.current_build_dir() + '/lib2'
+
+e = executable('testexe', 'main.c',
+ link_with : lib1)
+
+test('ld-library-path-test', e,
+ env : ['LD_LIBRARY_PATH=' + lib2dir])
diff --git a/test cases/linuxlike/7 library versions/installed_files.txt b/test cases/linuxlike/7 library versions/installed_files.txt
index b997e53..763a907 100644
--- a/test cases/linuxlike/7 library versions/installed_files.txt
+++ b/test cases/linuxlike/7 library versions/installed_files.txt
@@ -7,3 +7,4 @@ usr/lib/libonlyversion.so.1
usr/lib/libonlyversion.so.1.4.5
usr/lib/libonlysoversion.so
usr/lib/libonlysoversion.so.5
+usr/lib/libmodule.so
diff --git a/test cases/linuxlike/7 library versions/meson.build b/test cases/linuxlike/7 library versions/meson.build
index 48b75ad..d156eb0 100644
--- a/test cases/linuxlike/7 library versions/meson.build
+++ b/test cases/linuxlike/7 library versions/meson.build
@@ -1,5 +1,9 @@
project('library versions', 'c')
+if host_machine.system() == 'cygwin'
+ error('MESON_SKIP_TEST linuxlike soversions not supported on Cygwin.')
+endif
+
some = shared_library('some', 'lib.c',
version : '1.2.3',
soversion : '0',
@@ -43,3 +47,5 @@ test('manually linked 3', executable('manuallink3', out,
test('manually linked 4', executable('manuallink4', out,
link_args : ['-L.', '-lonlysoversion', rpath_arg]))
+
+shared_module('module', 'lib.c', install : true)
diff --git a/test cases/linuxlike/8 subproject library install/meson.build b/test cases/linuxlike/8 subproject library install/meson.build
index 63e57cf..ff55799 100644
--- a/test cases/linuxlike/8 subproject library install/meson.build
+++ b/test cases/linuxlike/8 subproject library install/meson.build
@@ -2,5 +2,9 @@ project('subproj lib install', 'c',
version : '2.3.4',
license : 'mylicense')
+if host_machine.system() == 'cygwin'
+ error('MESON_SKIP_TEST linuxlike soversions not supported on Cygwin.')
+endif
+
# Test that the subproject library gets installed
subproject('sublib', version : '1.0.0')
diff --git a/test cases/objc/2 nsstring/meson.build b/test cases/objc/2 nsstring/meson.build
index ec496a2..a877d74 100644
--- a/test cases/objc/2 nsstring/meson.build
+++ b/test cases/objc/2 nsstring/meson.build
@@ -2,6 +2,8 @@ project('nsstring', 'objc')
if host_machine.system() == 'darwin'
dep = dependency('appleframeworks', modules : 'foundation')
+elif host_machine.system() == 'cygwin'
+ error('MESON_SKIP_TEST GNUstep is not packaged for Cygwin.')
else
dep = dependency('gnustep')
if host_machine.system() == 'linux' and meson.get_compiler('objc').get_id() == 'clang'
diff --git a/test cases/osx/2 library versions/installed_files.txt b/test cases/osx/2 library versions/installed_files.txt
index fc76046..de7b078 100644
--- a/test cases/osx/2 library versions/installed_files.txt
+++ b/test cases/osx/2 library versions/installed_files.txt
@@ -5,3 +5,4 @@ usr/lib/libonlyversion.dylib
usr/lib/libonlyversion.1.dylib
usr/lib/libonlysoversion.dylib
usr/lib/libonlysoversion.5.dylib
+usr/lib/libmodule.dylib
diff --git a/test cases/osx/2 library versions/meson.build b/test cases/osx/2 library versions/meson.build
index b1962ca..9624998 100644
--- a/test cases/osx/2 library versions/meson.build
+++ b/test cases/osx/2 library versions/meson.build
@@ -39,3 +39,5 @@ test('manually linked 3', executable('manuallink3', out,
test('manually linked 4', executable('manuallink4', out,
link_args : ['-L.', '-lonlysoversion']))
+
+shared_module('module', 'lib.c', install : true)
diff --git a/test cases/python3/1 basic/meson.build b/test cases/python3/1 basic/meson.build
index 9d5f874..111b717 100644
--- a/test cases/python3/1 basic/meson.build
+++ b/test cases/python3/1 basic/meson.build
@@ -3,6 +3,16 @@ project('python sample', 'c')
py3_mod = import('python3')
py3 = py3_mod.find_python()
+py3_version = py3_mod.language_version()
+if py3_version.version_compare('< 3.2')
+ error('Invalid python version!?')
+endif
+
+py3_purelib = py3_mod.sysconfig_path('purelib')
+if not py3_purelib.endswith('site-packages')
+ error('Python3 purelib path seems invalid?')
+endif
+
main = files('prog.py')
test('toplevel', py3, args : main)
diff --git a/test cases/unit/6 std override/meson.build b/test cases/unit/6 std override/meson.build
new file mode 100644
index 0000000..ef2baac
--- /dev/null
+++ b/test cases/unit/6 std override/meson.build
@@ -0,0 +1,10 @@
+project('cpp std override', 'cpp',
+ default_options : ['cpp_std=c++03',
+ 'werror=true'])
+
+executable('plain', 'progp.cpp',
+ override_options : 'cpp_std=none')
+executable('v03', 'prog03.cpp',
+ override_options : 'werror=false')
+executable('v11', 'prog11.cpp',
+ override_options : 'cpp_std=c++11')
diff --git a/test cases/unit/6 std override/prog03.cpp b/test cases/unit/6 std override/prog03.cpp
new file mode 100644
index 0000000..d30abc9
--- /dev/null
+++ b/test cases/unit/6 std override/prog03.cpp
@@ -0,0 +1,6 @@
+#include<iostream>
+
+int main(int argc, char **argv) {
+ std::cout << "I am a c++03 test program.\n";
+ return 0;
+}
diff --git a/test cases/unit/6 std override/prog11.cpp b/test cases/unit/6 std override/prog11.cpp
new file mode 100644
index 0000000..dde1fc0
--- /dev/null
+++ b/test cases/unit/6 std override/prog11.cpp
@@ -0,0 +1,6 @@
+#include<iostream>
+
+int main(int argc, char **argv) {
+ std::cout << "I am a C++11 test program.\n";
+ return 0;
+}
diff --git a/test cases/unit/6 std override/progp.cpp b/test cases/unit/6 std override/progp.cpp
new file mode 100644
index 0000000..b9bd97f
--- /dev/null
+++ b/test cases/unit/6 std override/progp.cpp
@@ -0,0 +1,6 @@
+#include<iostream>
+
+int main(int argc, char **argv) {
+ std::cout << "I am a test program of undefined C++ standard.\n";
+ return 0;
+}
diff --git a/test cases/vala/7 shared library/installed_files.txt b/test cases/vala/7 shared library/installed_files.txt
new file mode 100644
index 0000000..f70e439
--- /dev/null
+++ b/test cases/vala/7 shared library/installed_files.txt
@@ -0,0 +1,8 @@
+usr/lib/libinstalled_vala_lib.so
+usr/lib/libinstalled_vala_all.so
+usr/include/installed_vala_all.h
+usr/include/valah/installed_vala_all_nolib.h
+usr/include/installed_vala_onlyh.h
+usr/share/vala/vapi/installed_vala_all.vapi
+usr/share/vala-1.0/vapi/installed_vala_all_nolib.vapi
+usr/share/vala/vapi/installed_vala_onlyvapi.vapi
diff --git a/test cases/vala/7 shared library/lib/meson.build b/test cases/vala/7 shared library/lib/meson.build
index 8eca0d4..78646a8 100644
--- a/test cases/vala/7 shared library/lib/meson.build
+++ b/test cases/vala/7 shared library/lib/meson.build
@@ -1 +1,27 @@
l = shared_library('valalib', 'mylib.vala', dependencies : valadeps)
+
+shared_library('installed_vala_lib', 'mylib.vala',
+ dependencies : valadeps,
+ install : true)
+
+shared_library('installed_vala_all', 'mylib.vala',
+ dependencies : valadeps,
+ install : true,
+ install_dir : [true, true, true])
+
+shared_library('installed_vala_all_nolib', 'mylib.vala',
+ dependencies : valadeps,
+ install : true,
+ install_dir : [false,
+ join_paths(get_option('includedir'), 'valah'),
+ join_paths(get_option('datadir'), 'vala-1.0', 'vapi')])
+
+shared_library('installed_vala_onlyh', 'mylib.vala',
+ dependencies : valadeps,
+ install : true,
+ install_dir : [false, get_option('includedir'), false])
+
+shared_library('installed_vala_onlyvapi', 'mylib.vala',
+ dependencies : valadeps,
+ install : true,
+ install_dir : [false, false, join_paths(get_option('datadir'), 'vala', 'vapi')])
diff --git a/test cases/vala/9 gir/installed_files.txt b/test cases/vala/9 gir/installed_files.txt
new file mode 100644
index 0000000..7a0e055
--- /dev/null
+++ b/test cases/vala/9 gir/installed_files.txt
@@ -0,0 +1,2 @@
+usr/lib/libfoo.so
+usr/share/gir-1.0/Foo-1.0.gir
diff --git a/test cases/vala/9 gir/meson.build b/test cases/vala/9 gir/meson.build
index c0a8f54..1a09bec 100644
--- a/test cases/vala/9 gir/meson.build
+++ b/test cases/vala/9 gir/meson.build
@@ -5,6 +5,8 @@ gobject = dependency('gobject-2.0')
g_ir_compiler = find_program('g-ir-compiler')
foo = shared_library('foo', 'foo.vala',
+ install : true,
+ install_dir : [true, false, false, true],
vala_gir: 'Foo-1.0.gir',
dependencies: [glib, gobject])
diff --git a/test cases/windows/10 vs module defs generated/meson.build b/test cases/windows/10 vs module defs generated/meson.build
new file mode 100644
index 0000000..5ce1a20
--- /dev/null
+++ b/test cases/windows/10 vs module defs generated/meson.build
@@ -0,0 +1,7 @@
+project('generated_dll_module_defs', 'c')
+
+if meson.get_compiler('c').get_id() == 'msvc'
+ subdir('subdir')
+ exe = executable('prog', 'prog.c', link_with : shlib)
+ test('runtest', exe)
+endif
diff --git a/test cases/windows/10 vs module defs generated/prog.c b/test cases/windows/10 vs module defs generated/prog.c
new file mode 100644
index 0000000..f35f4a0
--- /dev/null
+++ b/test cases/windows/10 vs module defs generated/prog.c
@@ -0,0 +1,5 @@
+int somedllfunc();
+
+int main(int argc, char **argv) {
+ return somedllfunc() == 42 ? 0 : 1;
+}
diff --git a/test cases/windows/10 vs module defs generated/subdir/meson.build b/test cases/windows/10 vs module defs generated/subdir/meson.build
new file mode 100644
index 0000000..5d390a0
--- /dev/null
+++ b/test cases/windows/10 vs module defs generated/subdir/meson.build
@@ -0,0 +1,9 @@
+conf = configuration_data()
+conf.set('func', 'somedllfunc')
+def_file = configure_file(
+ input: 'somedll.def.in',
+ output: 'somedll.def',
+ configuration : conf,
+)
+
+shlib = shared_library('somedll', 'somedll.c', vs_module_defs : def_file)
diff --git a/test cases/windows/10 vs module defs generated/subdir/somedll.c b/test cases/windows/10 vs module defs generated/subdir/somedll.c
new file mode 100644
index 0000000..df255e3
--- /dev/null
+++ b/test cases/windows/10 vs module defs generated/subdir/somedll.c
@@ -0,0 +1,5 @@
+#ifdef _MSC_VER
+int somedllfunc() {
+ return 42;
+}
+#endif
diff --git a/test cases/windows/10 vs module defs generated/subdir/somedll.def.in b/test cases/windows/10 vs module defs generated/subdir/somedll.def.in
new file mode 100644
index 0000000..c29207c
--- /dev/null
+++ b/test cases/windows/10 vs module defs generated/subdir/somedll.def.in
@@ -0,0 +1,2 @@
+EXPORTS
+ @func@
diff --git a/test cases/windows/5 resources/meson.build b/test cases/windows/5 resources/meson.build
index e92a43c..ddb7d6e 100644
--- a/test cases/windows/5 resources/meson.build
+++ b/test cases/windows/5 resources/meson.build
@@ -3,7 +3,7 @@ project('winmain', 'c')
# MinGW windres has a bug due to which it doesn't parse args with space properly:
# https://github.com/mesonbuild/meson/pull/1346
# https://sourceware.org/bugzilla/show_bug.cgi?id=4933
-if meson.get_compiler('c').get_id() == 'gcc'
+if meson.get_compiler('c').get_id() == 'gcc' and host_machine.system() == 'windows'
# Construct build_to_src and skip this test if it has spaces
# because then the -I flag to windres will also have spaces
# and we know the test will fail
diff --git a/test cases/windows/7 mingw dll versioning/installed_files.txt b/test cases/windows/7 mingw dll versioning/installed_files.txt
index ebad9e4..26e14a7 100644
--- a/test cases/windows/7 mingw dll versioning/installed_files.txt
+++ b/test cases/windows/7 mingw dll versioning/installed_files.txt
@@ -1,8 +1,11 @@
-usr/bin/libsome-0.dll
+usr/bin/?libsome-0.dll
usr/lib/libsome.dll.a
-usr/bin/libnoversion.dll
+usr/bin/?libnoversion.dll
usr/lib/libnoversion.dll.a
-usr/bin/libonlyversion-1.dll
+usr/bin/?libonlyversion-1.dll
usr/lib/libonlyversion.dll.a
-usr/bin/libonlysoversion-5.dll
+usr/bin/?libonlysoversion-5.dll
usr/lib/libonlysoversion.dll.a
+usr/libexec/?libcustomdir.dll
+usr/libexec/libcustomdir.dll.a
+usr/lib/?libmodule.dll
diff --git a/test cases/windows/7 mingw dll versioning/meson.build b/test cases/windows/7 mingw dll versioning/meson.build
index d1fe73a..1d6562c 100644
--- a/test cases/windows/7 mingw dll versioning/meson.build
+++ b/test cases/windows/7 mingw dll versioning/meson.build
@@ -47,3 +47,9 @@ test('manually linked 3', executable('manuallink3', out,
test('manually linked 4', executable('manuallink4', out,
link_args : ['-L.', '-lonlysoversion']))
+
+shared_library('customdir', 'lib.c',
+ install : true,
+ install_dir : get_option('libexecdir'))
+
+shared_module('module', 'lib.c', install : true)
diff --git a/test cases/windows/8 msvc dll versioning/installed_files.txt b/test cases/windows/8 msvc dll versioning/installed_files.txt
index ae0fa1f..df43343 100644
--- a/test cases/windows/8 msvc dll versioning/installed_files.txt
+++ b/test cases/windows/8 msvc dll versioning/installed_files.txt
@@ -8,3 +8,6 @@ usr/bin/onlyversion-1.dll
usr/lib/onlyversion.lib
usr/bin/onlysoversion-5.dll
usr/lib/onlysoversion.lib
+usr/libexec/customdir.dll
+usr/libexec/customdir.lib
+usr/lib/module.dll
diff --git a/test cases/windows/8 msvc dll versioning/meson.build b/test cases/windows/8 msvc dll versioning/meson.build
index b72c5ec..4074747 100644
--- a/test cases/windows/8 msvc dll versioning/meson.build
+++ b/test cases/windows/8 msvc dll versioning/meson.build
@@ -48,3 +48,9 @@ test('manually linked 3', executable('manuallink3', out,
test('manually linked 4', executable('manuallink4', out,
link_args : ['-L.', '-lonlysoversion']))
+
+shared_library('customdir', 'lib.c',
+ install : true,
+ install_dir : get_option('libexecdir'))
+
+shared_module('module', 'lib.c', install : true)