aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml33
-rw-r--r--README.md12
-rw-r--r--authors.txt9
-rw-r--r--ci/appveyor-install.bat11
-rwxr-xr-xci/appveyor-test.sh6
-rw-r--r--manual tests/4 standalone binaries/meson.build4
-rw-r--r--mesonbuild/backend/backends.py87
-rw-r--r--mesonbuild/backend/ninjabackend.py85
-rw-r--r--mesonbuild/backend/vs2010backend.py57
-rw-r--r--mesonbuild/backend/vs2017backend.py27
-rw-r--r--mesonbuild/build.py89
-rw-r--r--mesonbuild/compilers.py128
-rw-r--r--mesonbuild/coredata.py63
-rw-r--r--mesonbuild/dependencies.py30
-rw-r--r--mesonbuild/environment.py70
-rw-r--r--mesonbuild/interpreter.py202
-rw-r--r--mesonbuild/mesonlib.py18
-rw-r--r--mesonbuild/mesonmain.py12
-rw-r--r--mesonbuild/modules/__init__.py10
-rw-r--r--mesonbuild/modules/gnome.py14
-rw-r--r--mesonbuild/modules/python3.py22
-rw-r--r--mesonbuild/modules/windows.py22
-rw-r--r--mesonbuild/mparser.py91
-rw-r--r--mesonbuild/optinterpreter.py7
-rw-r--r--mesonbuild/scripts/meson_exe.py6
-rw-r--r--mesonbuild/scripts/meson_install.py13
-rw-r--r--mesonbuild/wrap/__init__.py31
-rw-r--r--mesonbuild/wrap/wrap.py79
-rwxr-xr-xmesontest.py8
-rwxr-xr-xrun_project_tests.py88
-rwxr-xr-xrun_tests.py11
-rwxr-xr-xrun_unittests.py193
-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/127 cpp and asm/meson.build5
-rw-r--r--test cases/common/127 cpp and asm/symbol-underscore.h2
-rw-r--r--test cases/common/135 generated assembly/main.c3
-rw-r--r--test cases/common/135 generated assembly/square-arm.S.in3
-rw-r--r--test cases/common/135 generated assembly/square-x86.S.in3
-rw-r--r--test cases/common/135 generated assembly/square-x86_64.S.in9
-rw-r--r--test cases/common/138 include order/meson.build8
-rw-r--r--test cases/common/138 include order/sub2/meson.build1
-rw-r--r--test cases/common/138 include order/sub4/meson.build2
-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/main.c8
-rw-r--r--test cases/common/141 c cpp and asm/main.cpp11
-rw-r--r--test cases/common/141 c cpp and asm/meson.build23
-rw-r--r--test cases/common/141 c cpp and asm/retval-arm.S8
-rw-r--r--test cases/common/141 c cpp and asm/retval-x86.S8
-rw-r--r--test cases/common/141 c cpp and asm/retval-x86_64.S8
-rw-r--r--test cases/common/141 c cpp and asm/somelib.c3
-rw-r--r--test cases/common/141 c cpp and asm/symbol-underscore.h5
-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.build16
-rw-r--r--test cases/common/143 custom target object output/obj_generator.py18
-rw-r--r--test cases/common/143 custom target object output/objdir/meson.build5
-rw-r--r--test cases/common/143 custom target object output/objdir/source.c3
-rw-r--r--test cases/common/143 custom target object output/progdir/meson.build1
-rw-r--r--test cases/common/143 custom target object output/progdir/prog.c5
-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
-rwxr-xr-xtest cases/common/16 configure file/generator-without-input-file.py14
-rw-r--r--test cases/common/16 configure file/installed_files.txt1
-rw-r--r--test cases/common/16 configure file/meson.build15
-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/87 declare dep/meson.build17
-rw-r--r--test cases/common/9 header install/meson.build2
-rw-r--r--test cases/common/95 dep fallback/meson.build4
-rw-r--r--test cases/failing/14 invalid option name/meson_options.txt2
-rw-r--r--test cases/failing/19 target clash/meson.build2
-rw-r--r--test cases/failing/43 project name colon/meson.build1
-rw-r--r--test cases/linuxlike/10 large file support/meson.build4
-rw-r--r--test cases/linuxlike/7 library versions/meson.build4
-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/python3/1 basic/meson.build10
-rw-r--r--test cases/rust/1 basic/installed_files.txt3
-rw-r--r--test cases/rust/1 basic/meson.build4
-rw-r--r--test cases/rust/1 basic/subdir/meson.build2
-rw-r--r--test cases/rust/1 basic/subdir/prog.rs3
-rw-r--r--test cases/unit/5 compiler detection/meson.build2
-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/windows/5 resources/inc/meson.build1
-rw-r--r--test cases/windows/5 resources/inc/resource/resource.h (renamed from test cases/windows/5 resources/inc/resource.h)0
-rw-r--r--test cases/windows/5 resources/meson.build65
-rw-r--r--test cases/windows/5 resources/res/meson.build4
-rw-r--r--test cases/windows/5 resources/res/myres.rc (renamed from test cases/windows/5 resources/myres.rc)0
-rw-r--r--test cases/windows/5 resources/res/sample.ico (renamed from test cases/windows/5 resources/sample.ico)bin9662 -> 9662 bytes
-rw-r--r--test cases/windows/7 mingw dll versioning/installed_files.txt8
109 files changed, 1611 insertions, 423 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 289758d..c79e250 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -20,13 +20,27 @@ environment:
compiler: msvc2015
backend: vs2015
+ - arch: x86
+ compiler: msys2-mingw
+ backend: ninja
+
- arch: x64
- compiler: msvc2015
+ compiler: msvc2017
backend: ninja
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
- arch: x64
- compiler: msvc2015
- backend: vs2015
+ compiler: msvc2017
+ backend: vs2017
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+
+ - arch: x64
+ compiler: msys2-mingw
+ backend: ninja
+
+ - arch: x64
+ compiler: cygwin
+ backend: ninja
platform:
- x64
@@ -43,15 +57,24 @@ install:
- cmd: echo Using Python at %MESON_PYTHON_PATH%
- cmd: if %compiler%==msvc2010 ( call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" %arch% )
- 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/README.md b/README.md
index e3c1254..b24ce5f 100644
--- a/README.md
+++ b/README.md
@@ -10,12 +10,12 @@ build system.
[![Travis](https://travis-ci.org/mesonbuild/meson.svg?branch=master)](https://travis-ci.org/mesonbuild/meson)
[![Appveyor](https://ci.appveyor.com/api/projects/status/l5c8v71ninew2i3p?svg=true)](https://ci.appveyor.com/project/jpakkane/meson)
-####Dependencies
+#### Dependencies
- [Python](http://python.org) (version 3.4 or newer)
- [Ninja](https://ninja-build.org) (version 1.5 or newer)
-####Installing from source
+#### Installing from source
You can run Meson directly from a revision control checkout or an
extracted tarball. If you wish you can install it locally with the
@@ -44,7 +44,7 @@ This will zip all files inside the source checkout into the script
which includes hundreds of tests, so you might want to temporarily
remove those before running it.
-####Running
+#### Running
Meson requires that you have a source directory and a build directory
and that these two are different. In your source root must exist a file
@@ -77,18 +77,18 @@ Install is the same but it can take an extra argument:
you may need to run this command with sudo.
-####Contributing
+#### Contributing
We love code contributions. See the contributing.txt file for
details.
-####IRC
+#### IRC
The irc channel for Meson is `#mesonbuild` over at Freenode.
-####Further info
+#### Further info
More information about the Meson build system can be found at the
[project's home page](http://mesonbuild.com).
diff --git a/authors.txt b/authors.txt
index c82b288..50c032b 100644
--- a/authors.txt
+++ b/authors.txt
@@ -68,3 +68,12 @@ Rodrigo Lourenço
Sebastian Stang
Marc Becker
Michal Sojka
+Aaron Small
+Joe Baldino
+Peter Harris
+Roger Boerdijk
+melak47
+Philipp Ittershagen
+Dylan Baker
+Aaron Plattner
+Jon Turney
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/manual tests/4 standalone binaries/meson.build b/manual tests/4 standalone binaries/meson.build
index baae796..ad6645f 100644
--- a/manual tests/4 standalone binaries/meson.build
+++ b/manual tests/4 standalone binaries/meson.build
@@ -16,12 +16,12 @@ if host_machine.system() == 'darwin'
install_data('Info.plist',
install_dir : 'Contents')
- meson.set_install_script('osx_bundler.sh')
+ meson.add_install_script('osx_bundler.sh')
endif
if host_machine.system() == 'linux'
install_data('myapp.sh', install_dir : '.')
- meson.set_install_script('linux_bundler.sh')
+ meson.add_install_script('linux_bundler.sh')
endif
extra_link_args = []
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 9749eb4..efc5bff 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -78,6 +78,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:
@@ -105,6 +123,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
@@ -154,8 +178,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'
@@ -163,7 +189,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
@@ -188,7 +214,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
@@ -228,7 +254,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 = []
@@ -263,32 +289,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
@@ -340,6 +368,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
@@ -350,19 +380,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()
@@ -377,9 +407,11 @@ class Backend:
# Set -fPIC for static libraries by default unless explicitly disabled
if isinstance(target, build.StaticLibrary) and target.pic:
commands += compiler.get_pic_args()
- # Add compile args needed to find external dependencies
- # Link args are added while generating the link command
- for dep in target.get_external_deps():
+ # Add compile args needed to find external dependencies. Link args are
+ # added while generating the link command.
+ # NOTE: We must preserve the order in which external deps are
+ # specified, so we reverse the list before iterating over it.
+ for dep in reversed(target.get_external_deps()):
commands += dep.get_compile_args()
# Qt needs -fPIC for executables
# XXX: We should move to -fPIC for all executables
@@ -451,7 +483,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 = []
@@ -601,8 +633,13 @@ class Backend:
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?
+ source_root = self.build_to_src
+ build_root = '.'
outdir = self.get_target_dir(target)
if absolute_outputs:
+ source_root = self.environment.get_source_dir()
+ build_root = self.environment.get_source_dir()
outdir = os.path.join(self.environment.get_build_dir(), outdir)
outputs = []
for i in target.output:
@@ -628,6 +665,10 @@ class Backend:
elif not isinstance(i, str):
err_msg = 'Argument {0} is of unknown type {1}'
raise RuntimeError(err_msg.format(str(i), str(type(i))))
+ elif '@SOURCE_ROOT@' in i:
+ i = i.replace('@SOURCE_ROOT@', source_root)
+ elif '@BUILD_ROOT@' in i:
+ i = i.replace('@BUILD_ROOT@', build_root)
elif '@DEPFILE@' in i:
if target.depfile is None:
msg = 'Custom target {!r} has @DEPFILE@ but no depfile ' \
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index a26222f..5a9462f 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -147,6 +147,7 @@ class NinjaBackend(backends.Backend):
super().__init__(build)
self.name = 'ninja'
self.ninja_filename = 'build.ninja'
+ self.target_arg_cache = {}
self.fortran_deps = {}
self.all_outputs = {}
@@ -336,7 +337,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 +434,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))
@@ -492,7 +493,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
@@ -631,9 +632,9 @@ 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():
+ should_strip = self.get_option_for_target('strip', t)
# Find the installation directory. FIXME: Currently only one
# installation directory is supported for each target
outdir = t.get_custom_install_dir()
@@ -845,7 +846,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()
@@ -879,7 +880,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()
@@ -1012,7 +1013,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).
@@ -1035,7 +1036,7 @@ int dummy;
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'):
+ 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):
@@ -1090,10 +1091,10 @@ 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'))
- depfile = target.name + '.d'
- args += ['--out-dir', target.subdir]
- args += ['--emit', 'dep-info', '--emit', 'link']
+ 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())]
orderdeps = [os.path.join(t.subdir, t.get_filename()) for t in target.link_targets]
linkdirs = OrderedDict()
for d in target.link_targets:
@@ -1250,7 +1251,11 @@ int dummy;
else:
command_template = ' command = {executable} $LINK_ARGS {output_args} $in\n'
cmdlist = []
- if isinstance(static_linker, compilers.ArLinker):
+ # FIXME: Must normalize file names with pathlib.Path before writing
+ # them out to fix this properly on Windows. See:
+ # https://github.com/mesonbuild/meson/issues/1517
+ # https://github.com/mesonbuild/meson/issues/1526
+ if isinstance(static_linker, compilers.ArLinker) and not mesonlib.is_windows():
# `ar` has no options to overwrite archives. It always appends,
# which is never what we want. Delete an existing library first if
# it exists. https://github.com/mesonbuild/meson/issues/1355
@@ -1600,6 +1605,8 @@ rule FORTRAN_DEP_HACK
relout = self.get_target_private_dir(target)
args = [x.replace("@SOURCE_DIR@", self.build_to_src).replace("@BUILD_DIR@", relout)
for x in args]
+ args = [x.replace("@SOURCE_ROOT@", self.build_to_src).replace("@BUILD_ROOT@", '.')
+ for x in args]
cmdlist = exe_arr + self.replace_extra_args(args, genlist)
elem = NinjaBuildElement(self.all_outputs, outfiles, rulename, infilename)
if generator.depfile is not None:
@@ -1805,24 +1812,16 @@ rule FORTRAN_DEP_HACK
incs += compiler.get_include_args(i, False)
return incs
- def generate_single_compile(self, target, outfile, src, is_generated=False, header_deps=[], order_deps=[]):
- """
- Compiles C/C++, ObjC/ObjC++, Fortran, and D sources
- """
- 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))
- extra_orderdeps = []
- compiler = get_compiler_for_source(target.compilers.values(), src)
-
+ 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.
@@ -1834,10 +1833,12 @@ rule FORTRAN_DEP_HACK
# and from `include_directories:` of internal deps of the target.
#
# Target include dirs should override internal deps include dirs.
+ # This is handled in BuildTarget.process_kwargs()
#
# Include dirs from internal deps should override include dirs from
- # external deps.
- for i in target.get_include_dirs():
+ # external deps and must maintain the order in which they are specified.
+ # Hence, we must reverse the list so that the order is preserved.
+ for i in reversed(target.get_include_dirs()):
basedir = i.get_curdir()
for d in i.get_incdirs():
# Avoid superfluous '/.' at the end of paths when d is '.'
@@ -1882,6 +1883,29 @@ rule FORTRAN_DEP_HACK
# Finally add the private dir for the target to the include path. This
# must override everything else and must be the final path added.
commands += compiler.get_include_args(self.get_target_private_dir(target), False)
+ return commands
+
+ def generate_single_compile(self, target, outfile, src, is_generated=False, header_deps=[], order_deps=[]):
+ """
+ Compiles C/C++, ObjC/ObjC++, Fortran, and D sources
+ """
+ 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))
+
+ 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:
+ commands = self.target_arg_cache[key]
+ else:
+ commands = self._generate_single_compile(target, compiler, is_generated)
+ self.target_arg_cache[key] = commands
+ commands = CompilerArgs(commands.compiler, commands)
# FIXME: This file handling is atrocious and broken. We need to
# replace it with File objects used consistently everywhere.
@@ -1891,6 +1915,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:
@@ -1967,7 +1995,6 @@ rule FORTRAN_DEP_HACK
d = os.path.join(self.get_target_private_dir(target), d)
element.add_orderdep(d)
element.add_orderdep(pch_dep)
- element.add_orderdep(extra_orderdeps)
# Convert from GCC-style link argument naming to the naming used by the
# current compiler.
commands = commands.to_native()
@@ -2132,7 +2159,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 5ec49ad..139360c 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -27,6 +27,29 @@ from ..compilers import CompilerArgs
from ..mesonlib import MesonException, File, get_meson_script
from ..environment import Environment
+def autodetect_vs_version(build):
+ vs_version = os.getenv('VisualStudioVersion', None)
+ if vs_version:
+ if vs_version == '14.0':
+ from mesonbuild.backend.vs2015backend import Vs2015Backend
+ return Vs2015Backend(build)
+ if vs_version == '15.0':
+ from mesonbuild.backend.vs2017backend import Vs2017Backend
+ return Vs2017Backend(build)
+ raise MesonException('Could not detect Visual Studio (unknown Visual Studio version: "{}")!\n'
+ 'Please specify the exact backend to use.'.format(vs_version))
+
+ vs_install_dir = os.getenv('VSINSTALLDIR', None)
+ if not vs_install_dir:
+ raise MesonException('Could not detect Visual Studio (neither VisualStudioVersion nor VSINSTALLDIR set in '
+ 'environment)!\nPlease specify the exact backend to use.')
+
+ if 'Visual Studio 10.0' in vs_install_dir:
+ return Vs2010Backend(build)
+
+ raise MesonException('Could not detect Visual Studio (unknown VSINSTALLDIR: "{}")!\n'
+ 'Please specify the exact backend to use.'.format(vs_install_dir))
+
def split_o_flags_args(args):
"""
Splits any /O args and returns them. Does not take care of flags overriding
@@ -62,8 +85,9 @@ class Vs2010Backend(backends.Backend):
self.sources_conflicts = {}
self.platform_toolset = None
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()]:
@@ -127,7 +151,11 @@ class Vs2010Backend(backends.Backend):
args = [x.replace("@INPUT@", infilename).replace('@OUTPUT@', sole_output)
for x in base_args]
args = self.replace_outputs(args, target_private_dir, outfiles_rel)
- args = [x.replace("@SOURCE_DIR@", self.environment.get_source_dir()).replace("@BUILD_DIR@", target_private_dir)
+ args = [x.replace("@SOURCE_DIR@", self.environment.get_source_dir())
+ .replace("@BUILD_DIR@", target_private_dir)
+ for x in args]
+ args = [x.replace("@SOURCE_ROOT@", self.environment.get_source_dir())
+ .replace("@BUILD_ROOT@", self.environment.get_build_dir())
for x in args]
cmd = exe_arr + self.replace_extra_args(args, genlist)
cbs = ET.SubElement(idgroup, 'CustomBuild', Include=infilename)
@@ -354,6 +382,8 @@ class Vs2010Backend(backends.Backend):
p.text = self.platform
pname = ET.SubElement(globalgroup, 'ProjectName')
pname.text = project_name
+ if self.windows_target_platform_version:
+ ET.SubElement(globalgroup, 'WindowsTargetPlatformVersion').text = self.windows_target_platform_version
ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.Default.props')
type_config = ET.SubElement(root, 'PropertyGroup', Label='Configuration')
ET.SubElement(type_config, 'ConfigurationType')
@@ -573,6 +603,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)
@@ -600,6 +632,8 @@ class Vs2010Backend(backends.Backend):
p.text = self.platform
pname = ET.SubElement(globalgroup, 'ProjectName')
pname.text = project_name
+ if self.windows_target_platform_version:
+ ET.SubElement(globalgroup, 'WindowsTargetPlatformVersion').text = self.windows_target_platform_version
ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.Default.props')
# Start configuration
type_config = ET.SubElement(root, 'PropertyGroup', Label='Configuration')
@@ -660,7 +694,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')
@@ -725,12 +759,15 @@ class Vs2010Backend(backends.Backend):
# and from `include_directories:` of internal deps of the target.
#
# Target include dirs should override internal deps include dirs.
+ # This is handled in BuildTarget.process_kwargs()
#
# Include dirs from internal deps should override include dirs from
- # external deps.
+ # external deps and must maintain the order in which they are
+ # specified. Hence, we must reverse so that the order is preserved.
+ #
# These are per-target, but we still add them as per-file because we
# need them to be looked in first.
- for d in target.get_include_dirs():
+ for d in reversed(target.get_include_dirs()):
for i in d.get_incdirs():
curdir = os.path.join(d.get_curdir(), i)
args.append('-I' + self.relpath(curdir, target.subdir)) # build dir
@@ -780,7 +817,7 @@ class Vs2010Backend(backends.Backend):
# Split compile args needed to find external dependencies
# Link args are added while generating the link command
- for d in target.get_external_deps():
+ for d in reversed(target.get_external_deps()):
# Cflags required by external deps might have UNIX-specific flags,
# so filter them out if needed
d_compile_args = compiler.unix_args_to_native(d.get_compile_args())
@@ -811,7 +848,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 = {}
@@ -889,7 +926,7 @@ class Vs2010Backend(backends.Backend):
assert(isinstance(o, str))
additional_objects.append(o)
for o in custom_objs:
- additional_objects.append(self.relpath(o, self.get_target_dir(target)))
+ additional_objects.append(o)
if len(additional_links) > 0:
additional_links.append('%(AdditionalDependencies)')
ET.SubElement(link, 'AdditionalDependencies').text = ';'.join(additional_links)
@@ -1017,6 +1054,8 @@ class Vs2010Backend(backends.Backend):
p.text = self.platform
pname = ET.SubElement(globalgroup, 'ProjectName')
pname.text = project_name
+ if self.windows_target_platform_version:
+ ET.SubElement(globalgroup, 'WindowsTargetPlatformVersion').text = self.windows_target_platform_version
ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.Default.props')
type_config = ET.SubElement(root, 'PropertyGroup', Label='Configuration')
ET.SubElement(type_config, 'ConfigurationType').text = "Utility"
@@ -1096,6 +1135,8 @@ if %%errorlevel%% neq 0 goto :VCEnd'''
p.text = self.platform
pname = ET.SubElement(globalgroup, 'ProjectName')
pname.text = project_name
+ if self.windows_target_platform_version:
+ ET.SubElement(globalgroup, 'WindowsTargetPlatformVersion').text = self.windows_target_platform_version
ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.Default.props')
type_config = ET.SubElement(root, 'PropertyGroup', Label='Configuration')
ET.SubElement(type_config, 'ConfigurationType')
diff --git a/mesonbuild/backend/vs2017backend.py b/mesonbuild/backend/vs2017backend.py
new file mode 100644
index 0000000..35d56f3
--- /dev/null
+++ b/mesonbuild/backend/vs2017backend.py
@@ -0,0 +1,27 @@
+# Copyright 2014-2016 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+from .vs2010backend import Vs2010Backend
+
+
+class Vs2017Backend(Vs2010Backend):
+ def __init__(self, build):
+ super().__init__(build)
+ self.name = 'vs2017'
+ self.platform_toolset = 'v141'
+ self.vs_version = '2017'
+ # WindowsSDKVersion should be set by command prompt.
+ self.windows_target_platform_version = os.getenv('WindowsSDKVersion', None).rstrip('\\')
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index c7e8f8e..537c91b 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -12,15 +12,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import copy, os, re
+from collections import OrderedDict
+
from . import environment
from . import dependencies
from . import mlog
-import copy, os, re
from .mesonlib import File, MesonException
from .mesonlib import flatten, stringlistify, classify_unity_sources
from .mesonlib import get_filenames_templates_dict, substitute_values
-from .environment import for_windows, for_darwin
-from .compilers import is_object, clike_langs, lang_suffixes
+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,
'c_pch': True,
@@ -47,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
@@ -270,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
@@ -282,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):
@@ -291,7 +309,7 @@ class BuildTarget(Target):
self.is_unity = environment.coredata.get_builtin_option('unity')
self.environment = environment
self.sources = []
- self.compilers = {}
+ self.compilers = OrderedDict()
self.objects = []
self.external_deps = []
self.include_dirs = []
@@ -391,13 +409,6 @@ class BuildTarget(Target):
raise InvalidArguments(msg)
@staticmethod
- def can_compile_sources(compiler, sources):
- for s in sources:
- if compiler.can_compile(s):
- return True
- return False
-
- @staticmethod
def can_compile_remove_sources(compiler, sources):
removed = False
for s in sources[:]:
@@ -442,13 +453,18 @@ class BuildTarget(Target):
if not s.endswith(lang_suffixes['vala']):
sources.append(s)
if sources:
- # Add compilers based on the above sources
- for lang, compiler in compilers.items():
- # We try to be conservative because sometimes people add files
- # in the list of sources that we can't determine the type based
- # just on the suffix.
- if self.can_compile_sources(compiler, sources):
- self.compilers[lang] = compiler
+ # For each source, try to add one compiler that can compile it.
+ # It's ok if no compilers can do so, because users are expected to
+ # be able to add arbitrary non-source files to the sources list.
+ for s in sources:
+ for lang, compiler in compilers.items():
+ if compiler.can_compile(s):
+ if lang not in self.compilers:
+ self.compilers[lang] = compiler
+ break
+ # Re-sort according to clike_langs
+ self.compilers = OrderedDict(sorted(self.compilers.items(),
+ key=lambda t: sort_clike(t[0])))
else:
# No source files, target consists of only object files of unknown
# origin. Just add the first clike compiler that we have and hope
@@ -479,14 +495,6 @@ class BuildTarget(Target):
# CSharp and Java targets can't contain any other file types
assert(len(self.compilers) == 1)
return
- if 'rust' in self.compilers:
- firstname = self.sources[0]
- if isinstance(firstname, File):
- firstname = firstname.fname
- first = os.path.split(firstname)[1]
- (base, suffix) = os.path.splitext(first)
- if suffix != '.rs' or self.name != base:
- raise InvalidArguments('In Rust targets, the first source file must be named projectname.rs.')
def get_original_kwargs(self):
return self.kwargs
@@ -598,16 +606,17 @@ class BuildTarget(Target):
for i in self.link_depends:
if not isinstance(i, str):
raise InvalidArguments('Link_depends arguments must be strings.')
- deplist = kwargs.get('dependencies', [])
- if not isinstance(deplist, list):
- deplist = [deplist]
- self.add_deps(deplist)
- # Target-specific include dirs must be added after include dirs from
- # internal deps (added inside self.add_deps()) to override correctly.
+ # Target-specific include dirs must be added BEFORE include dirs from
+ # internal deps (added inside self.add_deps()) to override them.
inclist = kwargs.get('include_directories', [])
if not isinstance(inclist, list):
inclist = [inclist]
self.add_include_dirs(inclist)
+ # Add dependencies (which also have include_directories)
+ deplist = kwargs.get('dependencies', [])
+ 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):
@@ -988,7 +997,8 @@ 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 = ''
@@ -1111,6 +1121,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'
@@ -1244,6 +1266,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):
diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py
index 519a7d5..e6be8b1 100644
--- a/mesonbuild/compilers.py
+++ b/mesonbuild/compilers.py
@@ -57,6 +57,17 @@ clike_suffixes += ('h', 'll', 's')
# All these are only for C-like languages; see `clike_langs` above.
+def sort_clike(lang):
+ '''
+ Sorting function to sort the list of languages according to
+ reversed(compilers.clike_langs) and append the unknown langs in the end.
+ The purpose is to prefer C over C++ for files that can be compiled by
+ both such as assembly, C, etc. Also applies to ObjC, ObjC++, etc.
+ '''
+ if lang not in clike_langs:
+ return 1
+ return -clike_langs.index(lang)
+
def is_header(fname):
if hasattr(fname, 'fname'):
fname = fname.fname
@@ -369,7 +380,7 @@ class CompilerArgs(list):
dedup2_args = ()
# Arg prefixes and args that must be de-duped by returning 1
dedup1_prefixes = ()
- dedup1_args = ('-c', '-S', '-E', '-pipe')
+ dedup1_args = ('-c', '-S', '-E', '-pipe', '-pthread')
compiler = None
def _check_args(self, args):
@@ -413,7 +424,7 @@ class CompilerArgs(list):
can safely remove the previous occurance and add a new one. The same
is true for include paths and library paths with -I and -L. For
these we return `2`. See `dedup2_prefixes` and `dedup2_args`.
- b) Arguments that once specifie cannot be undone, such as `-c` or
+ b) Arguments that once specified cannot be undone, such as `-c` or
`-pipe`. New instances of these can be completely skipped. For these
we return `1`. See `dedup1_prefixes` and `dedup1_args`.
c) Whether it matters where or how many times on the command-line
@@ -514,6 +525,11 @@ class Compiler:
self.version = version
self.base_options = []
+ def __repr__(self):
+ repr_str = "<{0}: v{1} `{2}`>"
+ return repr_str.format(self.__class__.__name__, self.version,
+ ' '.join(self.exelist))
+
def can_compile(self, src):
if hasattr(src, 'fname'):
src = src.fname
@@ -534,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 []
@@ -890,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
@@ -905,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}>
@@ -918,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):
@@ -927,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):
@@ -1125,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():
"""
@@ -1790,7 +1814,7 @@ class SwiftCompiler(Compiler):
source_name = os.path.join(work_dir, src)
output_name = os.path.join(work_dir, 'swifttest')
with open(source_name, 'w') as ofile:
- ofile.write('''1 + 2
+ ofile.write('''print("Swift compilation is working.")
''')
extra_flags = self.get_cross_extra_flags(environment, compile=True, link=True)
pc = subprocess.Popen(self.exelist + extra_flags + ['-emit-executable', '-o', output_name, src], cwd=work_dir)
@@ -2285,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
@@ -2300,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:
@@ -2366,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']
@@ -2772,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']
@@ -2880,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]
@@ -3055,9 +3080,8 @@ class VisualStudioLinker(StaticLinker):
return VisualStudioCCompiler.unix_args_to_native(args)
def get_link_debugfile_args(self, targetfile):
- pdbarr = targetfile.split('.')[:-1]
- pdbarr += ['pdb']
- return ['/DEBUG', '/PDB:' + '.'.join(pdbarr)]
+ # Static libraries do not have PDB files
+ return []
class ArLinker(StaticLinker):
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index e88cdc8..27f1dd7 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -14,11 +14,13 @@
import pickle, os, uuid
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', 'vs2010', 'vs2015', 'xcode']
+backendlist = ['ninja', 'vs', 'vs2010', 'vs2015', 'vs2017', 'xcode']
class UserOption:
def __init__(self, name, description, choices):
@@ -30,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)
@@ -43,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])
@@ -70,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)
@@ -86,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
@@ -119,17 +148,18 @@ 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:
self.cross_file = None
-
- self.compilers = {}
- self.cross_compilers = {}
+ self.wrap_mode = options.wrap_mode
+ self.compilers = OrderedDict()
+ self.cross_compilers = OrderedDict()
self.deps = {}
self.modules = {}
# Only to print a warning if it changes between Meson invocations.
@@ -203,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 e4317f1..7f22ae6 100644
--- a/mesonbuild/dependencies.py
+++ b/mesonbuild/dependencies.py
@@ -112,6 +112,7 @@ class PkgConfigDependency(Dependency):
else:
self.want_cross = environment.is_cross_build()
self.name = name
+ self.modversion = 'none'
# When finding dependencies for cross-compiling, we don't care about
# the 'native' pkg-config
@@ -154,7 +155,6 @@ class PkgConfigDependency(Dependency):
if self.required:
raise DependencyException('{} dependency {!r} not found'
''.format(self.type_string, name))
- self.modversion = 'none'
return
found_msg = [self.type_string + ' dependency', mlog.bold(name), 'found:']
self.version_reqs = kwargs.get('version', None)
@@ -456,7 +456,7 @@ class ExternalProgram:
if suffix in self.windows_exts:
return True
elif os.access(path, os.X_OK):
- return True
+ return not os.path.isdir(path)
return False
def _search_dir(self, name, search_dir):
@@ -1010,7 +1010,7 @@ class QtBaseDependency(Dependency):
corekwargs = {'required': 'false', 'silent': 'true'}
core = PkgConfigDependency(self.qtpkgname + 'Core', env, corekwargs)
# Used by self.compilers_detect()
- self.bindir = core.get_pkgconfig_variable('host_bins')
+ self.bindir = self.get_pkgconfig_host_bins(core)
if not self.bindir:
# If exec_prefix is not defined, the pkg-config file is broken
prefix = core.get_pkgconfig_variable('exec_prefix')
@@ -1119,10 +1119,25 @@ class Qt5Dependency(QtBaseDependency):
def __init__(self, env, kwargs):
QtBaseDependency.__init__(self, 'qt5', env, kwargs)
+ def get_pkgconfig_host_bins(self, core):
+ return core.get_pkgconfig_variable('host_bins')
+
class Qt4Dependency(QtBaseDependency):
def __init__(self, env, kwargs):
QtBaseDependency.__init__(self, 'qt4', env, kwargs)
+ def get_pkgconfig_host_bins(self, core):
+ # Only return one bins dir, because the tools are generally all in one
+ # directory for Qt4, in Qt5, they must all be in one directory. Return
+ # the first one found among the bin variables, in case one tool is not
+ # configured to be built.
+ applications = ['moc', 'uic', 'rcc', 'lupdate', 'lrelease']
+ for application in applications:
+ try:
+ return os.path.dirname(core.get_pkgconfig_variable('%s_location' % application))
+ except MesonException:
+ pass
+
class GnuStepDependency(Dependency):
def __init__(self, environment, kwargs):
Dependency.__init__(self, 'gnustep')
@@ -1468,6 +1483,14 @@ class Python3Dependency(Dependency):
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', [])
@@ -1529,4 +1552,5 @@ packages = {'boost': BoostDependency,
'gl': GLDependency,
'threads': ThreadDependency,
'python3': Python3Dependency,
+ 'valgrind': ValgrindDependency,
}
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index 5217626..7861612 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):
@@ -377,16 +400,30 @@ class Environment:
C, C++, ObjC, ObjC++, Fortran so consolidate it here.
'''
if self.is_cross_build() and want_cross:
- compilers = [mesonlib.stringlistify(self.cross_info.config['binaries'][lang])]
- ccache = []
+ compilers = mesonlib.stringlistify(self.cross_info.config['binaries'][lang])
+ # Ensure ccache exists and remove it if it doesn't
+ if compilers[0] == 'ccache':
+ compilers = compilers[1:]
+ ccache = self.detect_ccache()
+ else:
+ ccache = []
+ # Return value has to be a list of compiler 'choices'
+ compilers = [compilers]
is_cross = True
if self.cross_info.need_exe_wrapper():
exe_wrap = self.cross_info.config['binaries'].get('exe_wrapper', None)
else:
exe_wrap = []
elif evar in os.environ:
- compilers = [shlex.split(os.environ[evar])]
- ccache = []
+ compilers = shlex.split(os.environ[evar])
+ # Ensure ccache exists and remove it if it doesn't
+ if compilers[0] == 'ccache':
+ compilers = compilers[1:]
+ ccache = self.detect_ccache()
+ else:
+ ccache = []
+ # Return value has to be a list of compiler 'choices'
+ compilers = [compilers]
is_cross = False
exe_wrap = None
else:
@@ -756,7 +793,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',
@@ -767,12 +804,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
@@ -780,14 +817,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 d6f76e9..8c8000c 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -20,7 +20,7 @@ from . import mlog
from . import build
from . import optinterpreter
from . import compilers
-from .wrap import wrap
+from .wrap import wrap, WrapMode
from . import mesonlib
from .mesonlib import FileMode, Popen_safe, get_meson_script
from .dependencies import InternalDependency, Dependency
@@ -635,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,
@@ -866,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.')
@@ -1225,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.
@@ -1408,7 +1424,7 @@ class Interpreter(InterpreterBase):
if not isinstance(sources, list):
sources = [sources]
sources = self.source_strings_to_files(self.flatten(sources))
- deps = kwargs.get('dependencies', [])
+ deps = self.flatten(kwargs.get('dependencies', []))
if not isinstance(deps, list):
deps = [deps]
compile_args = mesonlib.stringlistify(kwargs.get('compile_args', []))
@@ -1499,14 +1515,16 @@ class Interpreter(InterpreterBase):
raise InvalidCode('Recursive include of subprojects: %s.' % incpath)
if dirname in self.subprojects:
return self.subprojects[dirname]
- r = wrap.Resolver(os.path.join(self.build.environment.get_source_dir(), self.subproject_dir))
- resolved = r.resolve(dirname)
- if resolved is None:
- msg = 'Subproject directory {!r} does not exist and cannot be downloaded.'
- raise InterpreterException(msg.format(os.path.join(self.subproject_dir, dirname)))
+ subproject_dir_abs = os.path.join(self.environment.get_source_dir(), self.subproject_dir)
+ r = wrap.Resolver(subproject_dir_abs, self.coredata.wrap_mode)
+ try:
+ resolved = r.resolve(dirname)
+ except RuntimeError as e:
+ msg = 'Subproject directory {!r} does not exist and cannot be downloaded:\n{}'
+ 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', [])))
@@ -1612,27 +1630,31 @@ class Interpreter(InterpreterBase):
@stringArgs
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)
- if len(args) < 2:
- raise InvalidArguments('Not enough arguments to project(). Needs at least the project name and one language')
- 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:
@@ -1643,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:
@@ -1754,7 +1776,7 @@ class Interpreter(InterpreterBase):
def add_languages(self, args, required):
success = True
need_cross_compiler = self.environment.is_cross_build() and self.environment.cross_info.need_cross_compiler()
- for lang in args:
+ for lang in sorted(args, key=compilers.sort_clike):
lang = lang.lower()
if lang in self.coredata.compilers:
comp = self.coredata.compilers[lang]
@@ -1771,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='')
@@ -1834,8 +1857,8 @@ class Interpreter(InterpreterBase):
self.validate_arguments(args, 1, [str])
name = args[0]
if '<' in name or '>' in name or '=' in name:
- raise InvalidArguments('''Characters <, > and = are forbidden in target names. To specify version
-requirements use the version keyword argument instead.''')
+ raise InvalidArguments('Characters <, > and = are forbidden in dependency names. To specify'
+ 'version\n requirements use the \'version\' keyword argument instead.')
identifier = dependencies.get_dep_identifier(name, kwargs)
# Check if we want this as a cross-dep or a native-dep
# FIXME: Not all dependencies support such a distinction right now,
@@ -1910,6 +1933,11 @@ requirements use the version keyword argument instead.''')
return fbinfo
def dependency_fallback(self, name, kwargs):
+ if self.coredata.wrap_mode in (WrapMode.nofallback, WrapMode.nodownload):
+ mlog.log('Not looking for a fallback subproject for the dependency',
+ mlog.bold(name), 'because:\nAutomatic wrap-based fallback '
+ 'dependency downloading is disabled.')
+ return None
dirname, varname = self.get_subproject_infos(kwargs)
# Try to execute the subproject
try:
@@ -1932,8 +1960,14 @@ requirements use the version keyword argument instead.''')
try:
dep = self.subprojects[dirname].get_variable_method([varname], {})
except KeyError:
- raise InvalidCode('Fallback variable {!r} in the subproject '
- '{!r} does not exist'.format(varname, dirname))
+ if kwargs.get('required', True):
+ m = 'Fallback variable {!r} in the subproject {!r} does not exist'
+ raise DependencyException(m.format(varname, dirname))
+ # If the dependency is not required, don't raise an exception
+ mlog.log('Also couldn\'t find the dependency', mlog.bold(name),
+ 'in the fallback subproject',
+ mlog.bold(os.path.join(self.subproject_dir, dirname)))
+ return None
if not isinstance(dep, DependencyHolder):
raise InvalidCode('Fallback variable {!r} in the subproject {!r} is '
'not a dependency object.'.format(varname, dirname))
@@ -2268,6 +2302,7 @@ requirements use the version keyword argument instead.''')
'they are mutually exclusive.')
# Validate input
inputfile = None
+ ifile_abs = None
if 'input' in kwargs:
inputfile = kwargs['input']
if isinstance(inputfile, list):
@@ -2277,9 +2312,13 @@ requirements use the version keyword argument instead.''')
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)
- elif 'command' in kwargs:
- raise InterpreterException('Required keyword argument \'input\' missing')
+ 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
output = kwargs['output']
if not isinstance(output, str):
@@ -2296,7 +2335,7 @@ requirements use the version keyword argument instead.''')
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)
@@ -2308,7 +2347,10 @@ requirements use the version keyword argument instead.''')
# We use absolute paths for input and output here because the cwd
# that the command is run from is 'unspecified', so it could change.
# Currently it's builddir/subdir for in_builddir else srcdir/subdir.
- values = mesonlib.get_filenames_templates_dict([ifile_abs], [ofile_abs])
+ if ifile_abs:
+ values = mesonlib.get_filenames_templates_dict([ifile_abs], [ofile_abs])
+ else:
+ values = mesonlib.get_filenames_templates_dict(None, [ofile_abs])
# Substitute @INPUT@, @OUTPUT@, etc here.
cmd = mesonlib.substitute_values(kwargs['command'], values)
mlog.log('Configuring', mlog.bold(output), 'with command')
@@ -2399,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] = {}
-
- args = self.build.projects_link_args[self.subproject].get(lang, []) + args
- self.build.projects_link_args[self.subproject][lang] = args
+ raise InvalidCode('Missing language definition in {}'.format(node.func_name))
- @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()
@@ -2560,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 345a184..a2ab484 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -121,16 +121,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):
@@ -148,15 +150,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)
@@ -219,6 +221,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')
diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py
index 031486c..51041cc 100644
--- a/mesonbuild/mesonmain.py
+++ b/mesonbuild/mesonmain.py
@@ -20,6 +20,7 @@ from . import build
import platform
from . import mlog, coredata
from .mesonlib import MesonException
+from .wrap import WrapMode
parser = argparse.ArgumentParser()
@@ -67,6 +68,10 @@ parser.add_argument('-D', action='append', dest='projectoptions', default=[],
help='Set project options.')
parser.add_argument('-v', '--version', action='version',
version=coredata.version)
+ # See the mesonlib.WrapMode enum for documentation
+parser.add_argument('--wrap-mode', default=WrapMode.default,
+ type=lambda t: getattr(WrapMode, t), choices=WrapMode,
+ help='Special wrap mode to use')
parser.add_argument('directories', nargs='*')
class MesonApp:
@@ -145,12 +150,19 @@ If you want to change option values, use the mesonconf tool instead.'''
if self.options.backend == 'ninja':
from .backend import ninjabackend
g = ninjabackend.NinjaBackend(b)
+ elif self.options.backend == 'vs':
+ from .backend import vs2010backend
+ g = vs2010backend.autodetect_vs_version(b)
+ mlog.log('Auto detected Visual Studio backend:', mlog.bold(g.name))
elif self.options.backend == 'vs2010':
from .backend import vs2010backend
g = vs2010backend.Vs2010Backend(b)
elif self.options.backend == 'vs2015':
from .backend import vs2015backend
g = vs2015backend.Vs2015Backend(b)
+ elif self.options.backend == 'vs2017':
+ from .backend import vs2017backend
+ g = vs2017backend.Vs2017Backend(b)
elif self.options.backend == 'xcode':
from .backend import xcodebackend
g = xcodebackend.XCodeBackend(b)
diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py
index c7f24d4..fde3b91 100644
--- a/mesonbuild/modules/__init__.py
+++ b/mesonbuild/modules/__init__.py
@@ -24,7 +24,11 @@ def find_program(program_name, target_name):
return program
-def get_include_args(environment, include_dirs, prefix='-I'):
+def get_include_args(include_dirs, prefix='-I'):
+ '''
+ Expand include arguments to refer to the source and build dirs
+ by using @SOURCE_ROOT@ and @BUILD_ROOT@ for later substitution
+ '''
if not include_dirs:
return []
@@ -43,8 +47,8 @@ def get_include_args(environment, include_dirs, prefix='-I'):
basedir = dirs.get_curdir()
for d in dirs.get_incdirs():
expdir = os.path.join(basedir, d)
- srctreedir = os.path.join(environment.get_source_dir(), expdir)
- buildtreedir = os.path.join(environment.get_build_dir(), expdir)
+ srctreedir = os.path.join('@SOURCE_ROOT@', expdir)
+ buildtreedir = os.path.join('@BUILD_ROOT@', expdir)
dirs_str += ['%s%s' % (prefix, buildtreedir),
'%s%s' % (prefix, srctreedir)]
for d in dirs.get_extra_build_dirs():
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index 423031d..4b366bf 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -309,7 +309,7 @@ class GnomeModule(ExtensionModule):
if hasattr(dep, 'held_object'):
dep = dep.held_object
if isinstance(dep, InternalDependency):
- cflags.update(get_include_args(state.environment, dep.include_directories))
+ cflags.update(get_include_args(dep.include_directories))
for lib in dep.libraries:
ldflags.update(self._get_link_args(state, lib.held_object, depends, include_rpath))
libdepflags = self._get_dependencies_flags(lib.held_object.get_external_deps(), state, depends, include_rpath,
@@ -398,7 +398,7 @@ class GnomeModule(ExtensionModule):
scan_command += extra_args
scan_command += ['-I' + os.path.join(state.environment.get_source_dir(), state.subdir),
'-I' + os.path.join(state.environment.get_build_dir(), state.subdir)]
- scan_command += get_include_args(state.environment, girtarget.get_include_dirs())
+ scan_command += get_include_args(girtarget.get_include_dirs())
if 'link_with' in kwargs:
link_with = kwargs.pop('link_with')
@@ -525,9 +525,8 @@ class GnomeModule(ExtensionModule):
if not isinstance(incd.held_object, (str, build.IncludeDirs)):
raise MesonException(
'Gir include dirs should be include_directories().')
- scan_command += get_include_args(state.environment, inc_dirs)
- scan_command += get_include_args(state.environment, gir_inc_dirs + inc_dirs,
- prefix='--add-include-path=')
+ scan_command += get_include_args(inc_dirs)
+ scan_command += get_include_args(gir_inc_dirs + inc_dirs, prefix='--add-include-path=')
if isinstance(girtarget, build.Executable):
scan_command += ['--program', girtarget]
@@ -546,8 +545,7 @@ class GnomeModule(ExtensionModule):
typelib_output = '%s-%s.typelib' % (ns, nsversion)
typelib_cmd = [gicompiler, scan_target, '--output', '@OUTPUT@']
- typelib_cmd += get_include_args(state.environment, gir_inc_dirs,
- prefix='--includedir=')
+ typelib_cmd += get_include_args(gir_inc_dirs, prefix='--includedir=')
for incdir in typelib_includes:
typelib_cmd += ["--includedir=" + incdir]
@@ -716,7 +714,7 @@ class GnomeModule(ExtensionModule):
if not isinstance(incd.held_object, (str, build.IncludeDirs)):
raise MesonException(
'Gir include dirs should be include_directories().')
- cflags.update(get_include_args(state.environment, inc_dirs))
+ cflags.update(get_include_args(inc_dirs))
if cflags:
args += ['--cflags=%s' % ' '.join(cflags)]
if ldflags:
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/windows.py b/mesonbuild/modules/windows.py
index 8203789..3fb0107 100644
--- a/mesonbuild/modules/windows.py
+++ b/mesonbuild/modules/windows.py
@@ -14,6 +14,7 @@
import os
+from .. import mlog
from .. import mesonlib, dependencies, build
from ..mesonlib import MesonException
from . import get_include_args
@@ -38,16 +39,29 @@ class WindowsModule(ExtensionModule):
for incd in inc_dirs:
if not isinstance(incd.held_object, (str, build.IncludeDirs)):
raise MesonException('Resource include dirs should be include_directories().')
- extra_args += get_include_args(state.environment, inc_dirs)
+ extra_args += get_include_args(inc_dirs)
if comp.id == 'msvc':
rescomp = dependencies.ExternalProgram('rc', silent=True)
res_args = extra_args + ['/nologo', '/fo@OUTPUT@', '@INPUT@']
suffix = 'res'
else:
- # Pick-up env var WINDRES if set. This is often used for specifying
- # an arch-specific windres.
- rescomp_name = os.environ.get('WINDRES', 'windres')
+ m = 'Argument {!r} has a space which may not work with windres due to ' \
+ 'a MinGW bug: https://sourceware.org/bugzilla/show_bug.cgi?id=4933'
+ for arg in extra_args:
+ if ' ' in arg:
+ mlog.warning(m.format(arg))
+ 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 fe5ccc5..a9f25b1 100644
--- a/mesonbuild/mparser.py
+++ b/mesonbuild/mparser.py
@@ -16,15 +16,41 @@ import re
from .mesonlib import MesonException
class ParseException(MesonException):
- def __init__(self, text, lineno, colno):
- super().__init__(text)
+ def __init__(self, text, line, lineno, colno):
+ # Format as error message, followed by the line with the error, followed by a caret to show the error column.
+ super().__init__("%s\n%s\n%s" % (text, line, '%s^' % (' ' * colno)))
+ self.lineno = lineno
+ self.colno = colno
+
+class BlockParseException(MesonException):
+ def __init__(self, text, line, lineno, colno, start_line, start_lineno, start_colno):
+ # This can be formatted in two ways - one if the block start and end are on the same line, and a different way if they are on different lines.
+
+ if lineno == start_lineno:
+ # If block start and end are on the same line, it is formatted as:
+ # Error message
+ # Followed by the line with the error
+ # Followed by a caret to show the block start
+ # Followed by underscores
+ # Followed by a caret to show the block end.
+ super().__init__("%s\n%s\n%s" % (text, line, '%s^%s^' % (' ' * start_colno, '_' * (colno - start_colno - 1))))
+ else:
+ # If block start and end are on different lines, it is formatted as:
+ # Error message
+ # Followed by the line with the error
+ # Followed by a caret to show the error column.
+ # Followed by a message saying where the block started.
+ # Followed by the line of the block start.
+ # Followed by a caret for the block start.
+ super().__init__("%s\n%s\n%s\nFor a block that started at %d,%d\n%s\n%s" % (text, line, '%s^' % (' ' * colno), start_lineno, start_colno, start_line, "%s^" % (' ' * start_colno)))
self.lineno = lineno
self.colno = colno
class Token:
- def __init__(self, tid, subdir, lineno, colno, bytespan, value):
+ def __init__(self, tid, subdir, line_start, lineno, colno, bytespan, value):
self.tid = tid
self.subdir = subdir
+ self.line_start = line_start
self.lineno = lineno
self.colno = colno
self.bytespan = bytespan
@@ -36,7 +62,8 @@ class Token:
return self.tid == other.tid
class Lexer:
- def __init__(self):
+ def __init__(self, code):
+ self.code = code
self.keywords = {'true', 'false', 'if', 'else', 'elif',
'endif', 'and', 'or', 'not', 'foreach', 'endforeach'}
self.token_specification = [
@@ -73,20 +100,24 @@ class Lexer:
('questionmark', re.compile(r'\?')),
]
- def lex(self, code, subdir):
- lineno = 1
+ def getline(self, line_start):
+ return self.code[line_start:self.code.find('\n', line_start)]
+
+ def lex(self, subdir):
line_start = 0
+ lineno = 1
loc = 0
par_count = 0
bracket_count = 0
col = 0
- while loc < len(code):
+ while loc < len(self.code):
matched = False
value = None
for (tid, reg) in self.token_specification:
- mo = reg.match(code, loc)
+ mo = reg.match(self.code, loc)
if mo:
curline = lineno
+ curline_start = line_start
col = mo.start() - line_start
matched = True
span_start = loc
@@ -105,7 +136,7 @@ class Lexer:
elif tid == 'rbracket':
bracket_count -= 1
elif tid == 'dblquote':
- raise ParseException('Double quotes are not supported. Use single quotes.', lineno, col)
+ raise ParseException('Double quotes are not supported. Use single quotes.', self.getline(line_start), lineno, col)
elif tid == 'string':
value = match_text[1:-1]\
.replace(r"\'", "'")\
@@ -130,10 +161,10 @@ class Lexer:
tid = match_text
else:
value = match_text
- yield Token(tid, subdir, curline, col, bytespan, value)
+ yield Token(tid, subdir, curline_start, curline, col, bytespan, value)
break
if not matched:
- raise ParseException('lexer', lineno, col)
+ raise ParseException('lexer', self.getline(line_start), lineno, col)
class ElementaryNode:
def __init__(self, token):
@@ -178,10 +209,10 @@ class ArrayNode:
self.args = args
class EmptyNode:
- def __init__(self):
+ def __init__(self, lineno, colno):
self.subdir = ''
- self.lineno = 0
- self.colno = 0
+ self.lineno = lineno
+ self.colno = colno
self.value = None
class OrNode:
@@ -288,7 +319,7 @@ class IfClauseNode:
self.lineno = lineno
self.colno = colno
self.ifs = []
- self.elseblock = EmptyNode()
+ self.elseblock = EmptyNode(lineno, colno)
class UMinusNode:
def __init__(self, current_location, value):
@@ -374,7 +405,9 @@ comparison_map = {'equal': '==',
class Parser:
def __init__(self, code, subdir):
- self.stream = Lexer().lex(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
@@ -382,7 +415,7 @@ class Parser:
try:
self.current = next(self.stream)
except StopIteration:
- self.current = Token('eof', '', 0, 0, (0, 0), None)
+ self.current = Token('eof', '', self.current.line_start, self.current.lineno, self.current.colno + self.current.bytespan[1] - self.current.bytespan[0], (0, 0), None)
def accept(self, s):
if self.current.tid == s:
@@ -393,7 +426,12 @@ class Parser:
def expect(self, s):
if self.accept(s):
return True
- raise ParseException('Expecting %s got %s.' % (s, self.current.tid), self.current.lineno, self.current.colno)
+ raise ParseException('Expecting %s got %s.' % (s, self.current.tid), self.lexer.getline(self.current.line_start), self.current.lineno, self.current.colno)
+
+ def block_expect(self, s, block_start):
+ if self.accept(s):
+ return True
+ raise BlockParseException('Expecting %s got %s.' % (s, self.current.tid), self.lexer.getline(self.current.line_start), self.current.lineno, self.current.colno, self.lexer.getline(block_start.line_start), block_start.lineno, block_start.colno)
def parse(self):
block = self.codeblock()
@@ -489,9 +527,10 @@ class Parser:
def e7(self):
left = self.e8()
+ block_start = self.current
if self.accept('lparen'):
args = self.args()
- self.expect('rparen')
+ self.block_expect('rparen', block_start)
if not isinstance(left, IdNode):
raise ParseException('Function call must be applied to plain id',
left.lineno, left.colno)
@@ -508,13 +547,14 @@ class Parser:
return left
def e8(self):
+ block_start = self.current
if self.accept('lparen'):
e = self.statement()
- self.expect('rparen')
+ self.block_expect('rparen', block_start)
return e
elif self.accept('lbracket'):
args = self.args()
- self.expect('rbracket')
+ self.block_expect('rbracket', block_start)
return ArrayNode(args)
else:
return self.e9()
@@ -531,7 +571,7 @@ class Parser:
return NumberNode(t)
if self.accept('string'):
return StringNode(t)
- return EmptyNode()
+ return EmptyNode(self.current.lineno, self.current.colno)
def args(self):
s = self.statement()
@@ -606,15 +646,16 @@ class Parser:
return self.codeblock()
def line(self):
+ block_start = self.current
if self.current == 'eol':
- return EmptyNode()
+ return EmptyNode(self.current.lineno, self.current.colno)
if self.accept('if'):
block = self.ifblock()
- self.expect('endif')
+ self.block_expect('endif', block_start)
return block
if self.accept('foreach'):
block = self.foreachblock()
- self.expect('endforeach')
+ self.block_expect('endforeach', block_start)
return block
return self.statement()
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/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 2544fbc..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
@@ -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/mesonbuild/wrap/__init__.py b/mesonbuild/wrap/__init__.py
index e69de29..019634c 100644
--- a/mesonbuild/wrap/__init__.py
+++ b/mesonbuild/wrap/__init__.py
@@ -0,0 +1,31 @@
+from enum import Enum
+
+# Used for the --wrap-mode command-line argument
+#
+# Special wrap modes:
+# nofallback: Don't download wraps for dependency() fallbacks
+# nodownload: Don't download wraps for all subproject() calls
+#
+# subprojects are used for two purposes:
+# 1. To download and build dependencies by using .wrap
+# files if they are not provided by the system. This is
+# usually expressed via dependency(..., fallback: ...).
+# 2. To download and build 'copylibs' which are meant to be
+# used by copying into your project. This is always done
+# with an explicit subproject() call.
+#
+# --wrap-mode=nofallback will never do (1)
+# --wrap-mode=nodownload will do neither (1) nor (2)
+#
+# If you are building from a release tarball, you should be
+# able to safely use 'nodownload' since upstream is
+# expected to ship all required sources with the tarball.
+#
+# If you are building from a git repository, you will want
+# to use 'nofallback' so that any 'copylib' wraps will be
+# download as subprojects.
+#
+# Note that these options do not affect subprojects that
+# are git submodules since those are only usable in git
+# repositories, and you almost always want to download them.
+WrapMode = Enum('WrapMode', 'default nofallback nodownload')
diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py
index b1efc13..67e4700 100644
--- a/mesonbuild/wrap/wrap.py
+++ b/mesonbuild/wrap/wrap.py
@@ -17,6 +17,8 @@ import contextlib
import urllib.request, os, hashlib, shutil
import subprocess
import sys
+from pathlib import Path
+from . import WrapMode
try:
import ssl
@@ -36,6 +38,13 @@ def build_ssl_context():
ctx.load_default_certs()
return ctx
+def quiet_git(cmd):
+ pc = subprocess.Popen(['git'] + cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ out, err = pc.communicate()
+ if pc.returncode != 0:
+ return False, err
+ return True, out
+
def open_wrapdburl(urlstring):
global ssl_warning_printed
if has_ssl:
@@ -86,29 +95,45 @@ class PackageDefinition:
return 'patch_url' in self.values
class Resolver:
- def __init__(self, subdir_root):
+ def __init__(self, subdir_root, wrap_mode=WrapMode(1)):
+ self.wrap_mode = wrap_mode
self.subdir_root = subdir_root
self.cachedir = os.path.join(self.subdir_root, 'packagecache')
def resolve(self, packagename):
- fname = os.path.join(self.subdir_root, packagename + '.wrap')
- dirname = os.path.join(self.subdir_root, packagename)
- try:
- if os.listdir(dirname):
- # The directory is there and not empty? Great, use it.
+ # Check if the directory is already resolved
+ dirname = Path(os.path.join(self.subdir_root, packagename))
+ subprojdir = os.path.join(*dirname.parts[-2:])
+ if dirname.is_dir():
+ if (dirname / 'meson.build').is_file():
+ # The directory is there and has meson.build? Great, use it.
return packagename
- else:
- mlog.warning('Subproject directory %s is empty, possibly because of an unfinished'
- 'checkout, removing to reclone' % dirname)
- os.rmdir(dirname)
- except NotADirectoryError:
- raise RuntimeError('%s is not a directory, can not use as subproject.' % dirname)
- except FileNotFoundError:
- pass
+ # Is the dir not empty and also not a git submodule dir that is
+ # not checkout properly? Can't do anything, exception!
+ elif next(dirname.iterdir(), None) and not (dirname / '.git').is_file():
+ m = '{!r} is not empty and has no meson.build files'
+ raise RuntimeError(m.format(subprojdir))
+ elif dirname.exists():
+ m = '{!r} already exists and is not a dir; cannot use as subproject'
+ raise RuntimeError(m.format(subprojdir))
+ dirname = str(dirname)
+ # Check if the subproject is a git submodule
+ if self.resolve_git_submodule(dirname):
+ return packagename
+
+ # Don't download subproject data based on wrap file if requested.
+ # Git submodules are ok (see above)!
+ if self.wrap_mode is WrapMode.nodownload:
+ m = 'Automatic wrap-based subproject downloading is disabled'
+ raise RuntimeError(m)
+
+ # Check if there's a .wrap file for this subproject
+ fname = os.path.join(self.subdir_root, packagename + '.wrap')
if not os.path.isfile(fname):
# No wrap file with this name? Give up.
- return None
+ m = 'No {}.wrap found for {!r}'
+ raise RuntimeError(m.format(packagename, subprojdir))
p = PackageDefinition(fname)
if p.type == 'file':
if not os.path.isdir(self.cachedir):
@@ -120,9 +145,31 @@ class Resolver:
elif p.type == "hg":
self.get_hg(p)
else:
- raise RuntimeError('Unreachable code.')
+ raise AssertionError('Unreachable code.')
return p.get('directory')
+ def resolve_git_submodule(self, dirname):
+ # Are we in a git repository?
+ ret, out = quiet_git(['rev-parse'])
+ if not ret:
+ return False
+ # Is `dirname` a submodule?
+ ret, out = quiet_git(['submodule', 'status', dirname])
+ if not ret:
+ return False
+ # Submodule has not been added, add it
+ if out.startswith(b'-'):
+ if subprocess.call(['git', 'submodule', 'update', dirname]) != 0:
+ return False
+ # Submodule was added already, but it wasn't populated. Do a checkout.
+ elif out.startswith(b' '):
+ if subprocess.call(['git', 'checkout', '.'], cwd=dirname):
+ return True
+ else:
+ m = 'Unknown git submodule output: {!r}'
+ raise AssertionError(m.format(out))
+ return True
+
def get_git(self, p):
checkoutdir = os.path.join(self.subdir_root, p.get('directory'))
revno = p.get('revision')
diff --git a/mesontest.py b/mesontest.py
index a1708e3..c4d1178 100755
--- a/mesontest.py
+++ b/mesontest.py
@@ -36,6 +36,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 +154,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 +206,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
diff --git a/run_project_tests.py b/run_project_tests.py
index 3684de5..dc05524 100755
--- a/run_project_tests.py
+++ b/run_project_tests.py
@@ -31,9 +31,11 @@ import xml.etree.ElementTree as ET
import time
import multiprocessing
import concurrent.futures as conc
+import re
from mesonbuild.coredata import backendlist
+
class BuildStep(Enum):
configure = 1
build = 2
@@ -41,6 +43,7 @@ class BuildStep(Enum):
install = 4
clean = 5
+
class TestResult:
def __init__(self, msg, step, stdo, stde, mlog, conftime=0, buildtime=0, testtime=0):
self.msg = msg
@@ -52,6 +55,54 @@ class TestResult:
self.buildtime = buildtime
self.testtime = testtime
+class DummyFuture(conc.Future):
+ '''
+ Dummy Future implementation that executes the provided function when you
+ ask for the result. Used on platforms where sem_open() is not available:
+ MSYS2, OpenBSD, etc: https://bugs.python.org/issue3770
+ '''
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ def set_function(self, fn, *args, **kwargs):
+ self.fn = fn
+ self.fn_args = args
+ self.fn_kwargs = kwargs
+
+ def result(self, **kwargs):
+ try:
+ result = self.fn(*self.fn_args, **self.fn_kwargs)
+ except BaseException as e:
+ self.set_exception(e)
+ else:
+ self.set_result(result)
+ return super().result(**kwargs)
+
+
+class DummyExecutor(conc.Executor):
+ '''
+ Dummy single-thread 'concurrent' executor for use on platforms where
+ sem_open is not available: https://bugs.python.org/issue3770
+ '''
+
+ def __init__(self):
+ from threading import Lock
+ self._shutdown = False
+ self._shutdownLock = Lock()
+
+ def submit(self, fn, *args, **kwargs):
+ with self._shutdownLock:
+ if self._shutdown:
+ raise RuntimeError('Cannot schedule new futures after shutdown')
+ f = DummyFuture()
+ f.set_function(fn, *args, **kwargs)
+ return f
+
+ def shutdown(self, wait=True):
+ with self._shutdownLock:
+ self._shutdown = True
+
+
class AutoDeletedDir:
def __init__(self, d):
self.dir = d
@@ -158,12 +209,18 @@ 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):
@@ -180,13 +237,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)
@@ -194,7 +254,7 @@ def validate_install(srcdir, installdir):
# Windows-specific tests check for the existence of installed PDB
# files, but common tests do not, for obvious reasons. Ignore any
# extra PDB files found.
- if fname not in expected and not fname.endswith('.pdb'):
+ if fname not in expected and not fname.endswith('.pdb') and compiler == 'cl':
ret_msg += 'Extra file {0} found.\n'.format(fname)
return ret_msg
@@ -387,9 +447,9 @@ 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(('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 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))
@@ -421,7 +481,11 @@ def run_tests(all_tests, log_name_base, extra_args):
print('Could not determine number of CPUs due to the following reason:' + str(e))
print('Defaulting to using only one process')
num_workers = 1
- executor = conc.ProcessPoolExecutor(max_workers=num_workers)
+ try:
+ executor = conc.ProcessPoolExecutor(max_workers=num_workers)
+ except ImportError:
+ print('Platform doesn\'t ProcessPoolExecutor, falling back to single-threaded testing\n')
+ executor = DummyExecutor()
for name, test_cases, skipped in all_tests:
current_suite = ET.SubElement(junit_root, 'testsuite', {'name': name, 'tests': str(len(test_cases))})
@@ -441,6 +505,7 @@ def run_tests(all_tests, log_name_base, extra_args):
result = executor.submit(run_test, skipped, t, extra_args, unity_flags + backend_flags, compile_commands, should_fail)
futures.append((testname, t, result))
for (testname, t, result) in futures:
+ sys.stdout.flush()
result = result.result()
if result is None or 'MESON_SKIP_TEST' in result.stdo:
print('Skipping:', t)
@@ -532,6 +597,7 @@ def generate_pb_static(compiler, object_suffix, static_suffix):
return stlibfile
def generate_prebuilt():
+ global compiler
static_suffix = 'a'
if shutil.which('cl'):
compiler = 'cl'
diff --git a/run_tests.py b/run_tests.py
index 5025057..02aa701 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -21,6 +21,12 @@ import subprocess
import platform
from mesonbuild import mesonlib
+def using_vs_backend():
+ for arg in sys.argv[1:]:
+ if arg.startswith('--backend=vs'):
+ return True
+ return False
+
if __name__ == '__main__':
returncode = 0
# Running on a developer machine? Be nice!
@@ -32,7 +38,10 @@ if __name__ == '__main__':
units += ['LinuxlikeTests']
elif mesonlib.is_windows():
units += ['WindowsTests']
- returncode += subprocess.call([sys.executable, 'run_unittests.py', '-v'] + units)
+ # 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)
# 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 9945057..1b24d08 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -24,11 +24,11 @@ from pathlib import PurePath
import mesonbuild.compilers
import mesonbuild.environment
import mesonbuild.mesonlib
-from mesonbuild.mesonlib import is_windows, is_osx
+from mesonbuild.mesonlib import is_windows, is_osx, is_cygwin
from mesonbuild.environment import detect_ninja, Environment
from mesonbuild.dependencies import PkgConfigDependency, ExternalProgram
-if is_windows():
+if is_windows() or is_cygwin():
exe_suffix = '.exe'
else:
exe_suffix = ''
@@ -48,6 +48,7 @@ def get_fake_options(prefix):
import argparse
opts = argparse.Namespace()
opts.cross_file = None
+ opts.wrap_mode = None
opts.prefix = prefix
return opts
@@ -432,7 +433,20 @@ class BasePlatformTests(unittest.TestCase):
def get_compdb(self):
with open(os.path.join(self.builddir, 'compile_commands.json')) as ifile:
- return json.load(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'])
+ for each in contents:
+ # Extract the actual command from the rsp file
+ compiler, rsp = each['command'].split(' @')
+ rsp = os.path.join(self.builddir, rsp)
+ # Replace the command with its contents
+ with open(rsp, 'r', encoding='utf-8') as f:
+ each['command'] = compiler + ' ' + f.read()
+ return contents
def get_meson_log(self):
with open(os.path.join(self.builddir, 'meson-logs', 'meson-log.txt')) as f:
@@ -540,8 +554,10 @@ class AllPlatformTests(BasePlatformTests):
get_fake_options(self.prefix), [])
cc = env.detect_c_compiler(False)
static_linker = env.detect_static_linker(cc)
+ if is_windows():
+ raise unittest.SkipTest('https://github.com/mesonbuild/meson/issues/1526')
if not isinstance(static_linker, mesonbuild.compilers.ArLinker):
- raise unittest.SkipTest('static linker is not `ar`')
+ raise unittest.SkipTest('static linker is not `ar`')
# Configure
self.init(testdir)
# Get name of static library
@@ -712,20 +728,18 @@ class AllPlatformTests(BasePlatformTests):
def test_internal_include_order(self):
testdir = os.path.join(self.common_test_dir, '138 include order')
self.init(testdir)
+ execmd = fxecmd = None
for cmd in self.get_compdb():
- if cmd['file'].endswith('/main.c'):
- cmd = cmd['command']
- break
- else:
- raise Exception('Could not find main.c command')
- if cmd.endswith('.rsp'):
- # Pretend to build so that the rsp files are generated
- self.build(['-d', 'keeprsp', '-n'])
- # Extract the actual command from the rsp file
- rsp = os.path.join(self.builddir, cmd.split('cl @')[1])
- with open(rsp, 'r', encoding='utf-8') as f:
- cmd = f.read()
- incs = [a for a in shlex.split(cmd) if a.startswith("-I")]
+ if 'someexe' in cmd['command']:
+ execmd = cmd['command']
+ continue
+ if 'somefxe' in cmd['command']:
+ fxecmd = cmd['command']
+ continue
+ if not execmd or not fxecmd:
+ raise Exception('Could not find someexe and somfxe commands')
+ # Check include order for 'someexe'
+ incs = [a for a in shlex.split(execmd) if a.startswith("-I")]
self.assertEqual(len(incs), 8)
# target private dir
self.assertPathEqual(incs[0], "-Isub4/someexe@exe")
@@ -743,6 +757,27 @@ class AllPlatformTests(BasePlatformTests):
self.assertPathEqual(incs[6], "-Isub1")
# target internal dependency include_directories: source dir
self.assertPathBasenameEqual(incs[7], 'sub1')
+ # Check include order for 'somefxe'
+ incs = [a for a in shlex.split(fxecmd) if a.startswith('-I')]
+ self.assertEqual(len(incs), 9)
+ # target private dir
+ self.assertPathEqual(incs[0], '-Isomefxe@exe')
+ # target build dir
+ self.assertPathEqual(incs[1], '-I.')
+ # target source dir
+ self.assertPathBasenameEqual(incs[2], os.path.basename(testdir))
+ # target internal dependency correct include_directories: build dir
+ self.assertPathEqual(incs[3], "-Isub4")
+ # target internal dependency correct include_directories: source dir
+ self.assertPathBasenameEqual(incs[4], 'sub4')
+ # target internal dependency dep include_directories: build dir
+ self.assertPathEqual(incs[5], "-Isub1")
+ # target internal dependency dep include_directories: source dir
+ self.assertPathBasenameEqual(incs[6], 'sub1')
+ # target internal dependency wrong include_directories: build dir
+ self.assertPathEqual(incs[7], "-Isub2")
+ # target internal dependency wrong include_directories: source dir
+ self.assertPathBasenameEqual(incs[8], 'sub2')
def test_compiler_detection(self):
'''
@@ -798,6 +833,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):
@@ -845,6 +882,88 @@ class AllPlatformTests(BasePlatformTests):
self.assertEqual(wcc.get_exelist(), wrappercc)
self.assertEqual(wlinker.get_exelist(), wrapperlinker)
+ def test_always_prefer_c_compiler_for_asm(self):
+ testdir = os.path.join(self.common_test_dir, '141 c cpp and asm')
+ # Skip if building with MSVC
+ env = Environment(testdir, self.builddir, self.meson_command,
+ get_fake_options(self.prefix), [])
+ if env.detect_c_compiler(False).get_id() == 'msvc':
+ raise unittest.SkipTest('MSVC can\'t compile assembly')
+ self.init(testdir)
+ commands = {'c-asm': {}, 'cpp-asm': {}, 'cpp-c-asm': {}, 'c-cpp-asm': {}}
+ for cmd in self.get_compdb():
+ # Get compiler
+ split = shlex.split(cmd['command'])
+ if split[0] == 'ccache':
+ compiler = split[1]
+ else:
+ compiler = split[0]
+ # Classify commands
+ if 'Ic-asm' in cmd['command']:
+ if cmd['file'].endswith('.S'):
+ commands['c-asm']['asm'] = compiler
+ elif cmd['file'].endswith('.c'):
+ commands['c-asm']['c'] = compiler
+ else:
+ raise AssertionError('{!r} found in cpp-asm?'.format(cmd['command']))
+ elif 'Icpp-asm' in cmd['command']:
+ if cmd['file'].endswith('.S'):
+ commands['cpp-asm']['asm'] = compiler
+ elif cmd['file'].endswith('.cpp'):
+ commands['cpp-asm']['cpp'] = compiler
+ else:
+ raise AssertionError('{!r} found in cpp-asm?'.format(cmd['command']))
+ elif 'Ic-cpp-asm' in cmd['command']:
+ if cmd['file'].endswith('.S'):
+ commands['c-cpp-asm']['asm'] = compiler
+ elif cmd['file'].endswith('.c'):
+ commands['c-cpp-asm']['c'] = compiler
+ elif cmd['file'].endswith('.cpp'):
+ commands['c-cpp-asm']['cpp'] = compiler
+ else:
+ raise AssertionError('{!r} found in c-cpp-asm?'.format(cmd['command']))
+ elif 'Icpp-c-asm' in cmd['command']:
+ if cmd['file'].endswith('.S'):
+ commands['cpp-c-asm']['asm'] = compiler
+ elif cmd['file'].endswith('.c'):
+ commands['cpp-c-asm']['c'] = compiler
+ elif cmd['file'].endswith('.cpp'):
+ commands['cpp-c-asm']['cpp'] = compiler
+ else:
+ raise AssertionError('{!r} found in cpp-c-asm?'.format(cmd['command']))
+ else:
+ raise AssertionError('Unknown command {!r} found'.format(cmd['command']))
+ # Check that .S files are always built with the C compiler
+ self.assertEqual(commands['c-asm']['asm'], commands['c-asm']['c'])
+ self.assertEqual(commands['c-asm']['asm'], commands['cpp-asm']['asm'])
+ self.assertEqual(commands['cpp-asm']['asm'], commands['c-cpp-asm']['c'])
+ self.assertEqual(commands['c-cpp-asm']['asm'], commands['c-cpp-asm']['c'])
+ self.assertEqual(commands['cpp-c-asm']['asm'], commands['cpp-c-asm']['c'])
+ self.assertNotEqual(commands['cpp-asm']['asm'], commands['cpp-asm']['cpp'])
+ self.assertNotEqual(commands['c-cpp-asm']['c'], commands['c-cpp-asm']['cpp'])
+ self.assertNotEqual(commands['cpp-c-asm']['c'], commands['cpp-c-asm']['cpp'])
+ # Check that the c-asm target is always linked with the C linker
+ build_ninja = os.path.join(self.builddir, 'build.ninja')
+ with open(build_ninja, 'r', encoding='utf-8') as f:
+ contents = f.read()
+ 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)])
+
class WindowsTests(BasePlatformTests):
'''
@@ -990,6 +1109,25 @@ class LinuxlikeTests(BasePlatformTests):
self.assertIn("'-Werror'", vala_command)
self.assertIn("'-Werror'", c_command)
+ def test_qt5dependency_pkgconfig_detection(self):
+ '''
+ Test that qt4 and qt5 detection with pkgconfig works.
+ '''
+ # Verify Qt4 or Qt5 can be found with pkg-config
+ if not shutil.which('pkg-config'):
+ raise unittest.SkipTest('pkg-config not found')
+ qt4 = subprocess.call(['pkg-config', '--exists', 'QtCore'])
+ qt5 = subprocess.call(['pkg-config', '--exists', 'Qt5Core'])
+ 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)
+ # 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'
+ mesonlog = self.get_meson_log()
+ self.assertTrue(msg in mesonlog or msg2 in mesonlog)
+
def test_qt5dependency_qmake_detection(self):
'''
Test that qt5 detection with qmake works. This can't be an ordinary
@@ -1207,6 +1345,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/127 cpp and asm/meson.build b/test cases/common/127 cpp and asm/meson.build
index ac7adb3..9160775 100644
--- a/test cases/common/127 cpp and asm/meson.build
+++ b/test cases/common/127 cpp and asm/meson.build
@@ -1,5 +1,6 @@
project('c++ and assembly test', 'cpp')
+cpp = meson.get_compiler('cpp')
cpu = host_machine.cpu_family()
supported_cpus = ['arm', 'x86', 'x86_64']
@@ -8,6 +9,10 @@ if not supported_cpus.contains(cpu)
error('MESON_SKIP_TEST unsupported cpu:' + cpu)
endif
+if cpp.symbols_have_underscore_prefix()
+ add_project_arguments('-DMESON_TEST__UNDERSCORE_SYMBOL', language : 'cpp')
+endif
+
sources = ['trivial.cc']
# If the compiler cannot compile assembly, don't use it
if meson.get_compiler('cpp').get_id() != 'msvc'
diff --git a/test cases/common/127 cpp and asm/symbol-underscore.h b/test cases/common/127 cpp and asm/symbol-underscore.h
index 508cf50..d0f3ef9 100644
--- a/test cases/common/127 cpp and asm/symbol-underscore.h
+++ b/test cases/common/127 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/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-arm.S.in b/test cases/common/135 generated assembly/square-arm.S.in
index b13c8a0..168c980 100644
--- a/test cases/common/135 generated assembly/square-arm.S.in
+++ b/test cases/common/135 generated assembly/square-arm.S.in
@@ -2,7 +2,8 @@
.text
.globl SYMBOL_NAME(square_unsigned)
-#ifndef __APPLE__
+/* Only supported on Linux with GAS */
+# ifdef __linux__
.type square_unsigned,%function
#endif
diff --git a/test cases/common/135 generated assembly/square-x86.S.in b/test cases/common/135 generated assembly/square-x86.S.in
index 31688b1..19dd9f5 100644
--- a/test cases/common/135 generated assembly/square-x86.S.in
+++ b/test cases/common/135 generated assembly/square-x86.S.in
@@ -21,7 +21,8 @@ END
.text
.globl SYMBOL_NAME(square_unsigned)
-# ifndef __APPLE__
+/* Only supported on Linux with GAS */
+# ifdef __linux__
.type square_unsigned,@function
# endif
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 5aedd81..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
@@ -1,7 +1,5 @@
#include "symbol-underscore.h"
-#include "symbol-underscore.h"
-
#ifdef _MSC_VER /* MSVC on Windows */
PUBLIC SYMBOL_NAME(square_unsigned)
@@ -20,16 +18,17 @@ END
.text
.globl SYMBOL_NAME(square_unsigned)
-# ifndef __APPLE__
+/* Only supported on Linux with GAS */
+# ifdef __linux__
.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/138 include order/meson.build b/test cases/common/138 include order/meson.build
index f744ae7..c79cb0a 100644
--- a/test cases/common/138 include order/meson.build
+++ b/test cases/common/138 include order/meson.build
@@ -19,4 +19,12 @@ subdir('sub3')
# The directory where the target resides
subdir('sub4')
+# Test that the order in which internal dependencies are specified is
+# preserved. This is needed especially when subprojects get involved and
+# multiple build-root config.h files exist, and we must be sure that the
+# correct one is found: https://github.com/mesonbuild/meson/issues/1495
+f = executable('somefxe', 'sub4/main.c',
+ dependencies : [correctinc, dep, wronginc])
+
test('eh', e)
+test('oh', f)
diff --git a/test cases/common/138 include order/sub2/meson.build b/test cases/common/138 include order/sub2/meson.build
index 7b49d6a..b1e6190 100644
--- a/test cases/common/138 include order/sub2/meson.build
+++ b/test cases/common/138 include order/sub2/meson.build
@@ -1 +1,2 @@
j = include_directories('.')
+wronginc = declare_dependency(include_directories : j)
diff --git a/test cases/common/138 include order/sub4/meson.build b/test cases/common/138 include order/sub4/meson.build
index 538899a..ab4c455 100644
--- a/test cases/common/138 include order/sub4/meson.build
+++ b/test cases/common/138 include order/sub4/meson.build
@@ -2,3 +2,5 @@ e = executable('someexe', 'main.c',
c_args : ['-I' + sub3],
include_directories : j,
dependencies : dep)
+
+correctinc = declare_dependency(include_directories : include_directories('.'))
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/main.c b/test cases/common/141 c cpp and asm/main.c
new file mode 100644
index 0000000..8976723
--- /dev/null
+++ b/test cases/common/141 c cpp and asm/main.c
@@ -0,0 +1,8 @@
+#include <stdio.h>
+
+int get_retval(void);
+
+int main(int argc, char **argv) {
+ printf("C seems to be working.\n");
+ return get_retval();
+}
diff --git a/test cases/common/141 c cpp and asm/main.cpp b/test cases/common/141 c cpp and asm/main.cpp
new file mode 100644
index 0000000..c089870
--- /dev/null
+++ b/test cases/common/141 c cpp and asm/main.cpp
@@ -0,0 +1,11 @@
+#include <iostream>
+
+extern "C" {
+ int get_retval(void);
+ int get_cval(void);
+}
+
+int main(int argc, char **argv) {
+ std::cout << "C++ seems to be working." << std::endl;
+ return get_retval();
+}
diff --git a/test cases/common/141 c cpp and asm/meson.build b/test cases/common/141 c cpp and asm/meson.build
new file mode 100644
index 0000000..2c3610e
--- /dev/null
+++ b/test cases/common/141 c cpp and asm/meson.build
@@ -0,0 +1,23 @@
+project('c cpp and asm', 'c', 'cpp')
+
+cpu = host_machine.cpu_family()
+cc = meson.get_compiler('c')
+
+supported_cpus = ['arm', 'x86', 'x86_64']
+
+if not supported_cpus.contains(cpu)
+ error('MESON_SKIP_TEST unsupported cpu:' + cpu)
+endif
+
+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']))
+test('test-cpp-c-asm', executable('cpp-c-asm', ['main.cpp', 'somelib.c', 'retval-' + cpu + '.S']))
diff --git a/test cases/common/141 c cpp and asm/retval-arm.S b/test cases/common/141 c cpp and asm/retval-arm.S
new file mode 100644
index 0000000..8b37197
--- /dev/null
+++ b/test cases/common/141 c cpp and asm/retval-arm.S
@@ -0,0 +1,8 @@
+#include "symbol-underscore.h"
+
+.text
+.globl SYMBOL_NAME(get_retval)
+
+SYMBOL_NAME(get_retval):
+ mov r0, #0
+ mov pc, lr
diff --git a/test cases/common/141 c cpp and asm/retval-x86.S b/test cases/common/141 c cpp and asm/retval-x86.S
new file mode 100644
index 0000000..06bd75c
--- /dev/null
+++ b/test cases/common/141 c cpp and asm/retval-x86.S
@@ -0,0 +1,8 @@
+#include "symbol-underscore.h"
+
+.text
+.globl SYMBOL_NAME(get_retval)
+
+SYMBOL_NAME(get_retval):
+ xorl %eax, %eax
+ retl
diff --git a/test cases/common/141 c cpp and asm/retval-x86_64.S b/test cases/common/141 c cpp and asm/retval-x86_64.S
new file mode 100644
index 0000000..638921e
--- /dev/null
+++ b/test cases/common/141 c cpp and asm/retval-x86_64.S
@@ -0,0 +1,8 @@
+#include "symbol-underscore.h"
+
+.text
+.globl SYMBOL_NAME(get_retval)
+
+SYMBOL_NAME(get_retval):
+ xorl %eax, %eax
+ retq
diff --git a/test cases/common/141 c cpp and asm/somelib.c b/test cases/common/141 c cpp and asm/somelib.c
new file mode 100644
index 0000000..e585b8e
--- /dev/null
+++ b/test cases/common/141 c cpp and asm/somelib.c
@@ -0,0 +1,3 @@
+int get_cval (void) {
+ return 0;
+}
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
new file mode 100644
index 0000000..d0f3ef9
--- /dev/null
+++ b/test cases/common/141 c cpp and asm/symbol-underscore.h
@@ -0,0 +1,5 @@
+#if defined(MESON_TEST__UNDERSCORE_SYMBOL)
+# define SYMBOL_NAME(name) _##name
+#else
+# define SYMBOL_NAME(name) name
+#endif
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/143 custom target object output/meson.build b/test cases/common/143 custom target object output/meson.build
new file mode 100644
index 0000000..ede165b
--- /dev/null
+++ b/test cases/common/143 custom target object output/meson.build
@@ -0,0 +1,16 @@
+project('custom target object output', 'c')
+
+comp = find_program('obj_generator.py')
+
+if host_machine.system() == 'windows'
+ outputname = '@BASENAME@.obj'
+else
+ outputname = '@BASENAME@.o'
+endif
+
+cc = meson.get_compiler('c').cmd_array().get(-1)
+
+subdir('objdir')
+subdir('progdir')
+
+test('objgen', e)
diff --git a/test cases/common/143 custom target object output/obj_generator.py b/test cases/common/143 custom target object output/obj_generator.py
new file mode 100644
index 0000000..a33872a
--- /dev/null
+++ b/test cases/common/143 custom target object output/obj_generator.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python3
+
+# Mimic a binary that generates an object file (e.g. windres).
+
+import sys, subprocess
+
+if __name__ == '__main__':
+ if len(sys.argv) != 4:
+ print(sys.argv[0], 'compiler input_file output_file')
+ sys.exit(1)
+ compiler = sys.argv[1]
+ ifile = sys.argv[2]
+ ofile = sys.argv[3]
+ if compiler.endswith('cl'):
+ cmd = [compiler, '/nologo', '/MDd', '/Fo' + ofile, '/c', ifile]
+ else:
+ cmd = [compiler, '-c', ifile, '-o', ofile]
+ sys.exit(subprocess.call(cmd))
diff --git a/test cases/common/143 custom target object output/objdir/meson.build b/test cases/common/143 custom target object output/objdir/meson.build
new file mode 100644
index 0000000..0d7f6c2
--- /dev/null
+++ b/test cases/common/143 custom target object output/objdir/meson.build
@@ -0,0 +1,5 @@
+# Generate an object file manually.
+object = custom_target('object',
+ input : 'source.c',
+ output : outputname,
+ command : [comp, cc, '@INPUT@', '@OUTPUT@'])
diff --git a/test cases/common/143 custom target object output/objdir/source.c b/test cases/common/143 custom target object output/objdir/source.c
new file mode 100644
index 0000000..7779b33
--- /dev/null
+++ b/test cases/common/143 custom target object output/objdir/source.c
@@ -0,0 +1,3 @@
+int func1_in_obj() {
+ return 0;
+}
diff --git a/test cases/common/143 custom target object output/progdir/meson.build b/test cases/common/143 custom target object output/progdir/meson.build
new file mode 100644
index 0000000..4216c24
--- /dev/null
+++ b/test cases/common/143 custom target object output/progdir/meson.build
@@ -0,0 +1 @@
+e = executable('prog', 'prog.c', object)
diff --git a/test cases/common/143 custom target object output/progdir/prog.c b/test cases/common/143 custom target object output/progdir/prog.c
new file mode 100644
index 0000000..c1ece33
--- /dev/null
+++ b/test cases/common/143 custom target object output/progdir/prog.c
@@ -0,0 +1,5 @@
+int func1_in_obj();
+
+int main(int argc, char **argv) {
+ return func1_in_obj();
+}
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/generator-without-input-file.py b/test cases/common/16 configure file/generator-without-input-file.py
new file mode 100755
index 0000000..2ee059e
--- /dev/null
+++ b/test cases/common/16 configure file/generator-without-input-file.py
@@ -0,0 +1,14 @@
+#!/usr/bin/env python3
+
+import sys, os
+from pathlib import Path
+
+if len(sys.argv) != 2:
+ print("Wrong amount of parameters.")
+
+build_dir = Path(os.environ['MESON_BUILD_ROOT'])
+subdir = Path(os.environ['MESON_SUBDIR'])
+outputf = Path(sys.argv[1])
+
+with outputf.open('w') as ofile:
+ ofile.write("#define ZERO_RESULT 0\n")
diff --git a/test cases/common/16 configure file/installed_files.txt b/test cases/common/16 configure file/installed_files.txt
index d9fee12..542516e 100644
--- a/test cases/common/16 configure file/installed_files.txt
+++ b/test cases/common/16 configure file/installed_files.txt
@@ -1,3 +1,4 @@
usr/share/appdir/config2.h
+usr/share/appdir/config2b.h
usr/share/appdireh/config2-1.h
usr/share/appdirok/config2-2.h
diff --git a/test cases/common/16 configure file/meson.build b/test cases/common/16 configure file/meson.build
index bff041b..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())
@@ -36,6 +41,16 @@ configure_file(input : 'dummy.dat',
install_dir : 'share/appdir')
run_command(check_file, join_paths(meson.current_build_dir(), 'config2.h'))
+# Same again as before, but an input file should not be required in
+# this case where we use a command/script to generate the output file.
+genscript2b = '@0@/generator-without-input-file.py'.format(meson.current_source_dir())
+ofile2b = '@0@/config2b.h'.format(meson.current_build_dir())
+configure_file(
+ output : 'config2b.h',
+ command : [genprog, genscript2b, ofile2b],
+ install_dir : 'share/appdir')
+run_command(check_file, join_paths(meson.current_build_dir(), 'config2b.h'))
+
found_script = find_program('generator.py')
# More configure_file tests in here
subdir('subdir')
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/87 declare dep/meson.build b/test cases/common/87 declare dep/meson.build
index 74333a2..e427def 100644
--- a/test cases/common/87 declare dep/meson.build
+++ b/test cases/common/87 declare dep/meson.build
@@ -5,3 +5,20 @@ subdir('entity')
exe = executable('dep_user', 'main.c',
dependencies : entity_dep)
test('dep', exe)
+
+# just to make sure [] works as a no-op dep here
+executable('dummy', 'main.c',
+ dependencies : [entity_dep, []])
+
+# simple case
+declare_dependency(dependencies : entity_dep)
+
+# nested deps should be flattened
+declare_dependency(dependencies : [entity_dep])
+declare_dependency(dependencies : [[entity_dep]])
+
+# check that [] properly works as a no-op dep in declare_dependency() too
+declare_dependency(dependencies : [])
+declare_dependency(dependencies : [[]])
+declare_dependency(dependencies : [entity_dep, []])
+declare_dependency(dependencies : [[], entity_dep])
diff --git a/test cases/common/9 header install/meson.build b/test cases/common/9 header install/meson.build
index 7f3ce51..7dfbddb 100644
--- a/test cases/common/9 header install/meson.build
+++ b/test cases/common/9 header install/meson.build
@@ -1,4 +1,4 @@
-project('header install', 'c')
+project('header install')
as_array = ['subdir.h']
diff --git a/test cases/common/95 dep fallback/meson.build b/test cases/common/95 dep fallback/meson.build
index 9358d29..a96520e 100644
--- a/test cases/common/95 dep fallback/meson.build
+++ b/test cases/common/95 dep fallback/meson.build
@@ -5,7 +5,11 @@ bob = dependency('boblib', fallback : ['boblib', 'bob_dep'], required: false,
if not bob.found()
error('Bob is actually needed')
endif
+# boblib subproject exists, but sita_dep doesn't exist
+sita = dependency('sitalib', fallback : ['boblib', 'sita_dep'], required: false)
+# jimmylib subproject doesn't exist
jimmy = dependency('jimmylib', fallback : ['jimmylib', 'jimmy_dep'], required: false)
+# dummylib subproject fails to configure
dummy = dependency('dummylib', fallback : ['dummylib', 'dummy_dep'], required: false)
gensrc_py = find_program('gensrc.py')
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 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/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/7 library versions/meson.build b/test cases/linuxlike/7 library versions/meson.build
index 48b75ad..451e42e 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',
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/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/rust/1 basic/installed_files.txt b/test cases/rust/1 basic/installed_files.txt
index c7dab9f..9dea55f 100644
--- a/test cases/rust/1 basic/installed_files.txt
+++ b/test cases/rust/1 basic/installed_files.txt
@@ -1 +1,2 @@
-usr/bin/prog?exe
+usr/bin/program?exe
+usr/bin/program2?exe
diff --git a/test cases/rust/1 basic/meson.build b/test cases/rust/1 basic/meson.build
index 7cd84b6..076d86b 100644
--- a/test cases/rust/1 basic/meson.build
+++ b/test cases/rust/1 basic/meson.build
@@ -1,4 +1,6 @@
project('rustprog', 'rust')
-e = executable('prog', 'prog.rs', install : true)
+e = executable('program', 'prog.rs', install : true)
test('rusttest', e)
+
+subdir('subdir')
diff --git a/test cases/rust/1 basic/subdir/meson.build b/test cases/rust/1 basic/subdir/meson.build
new file mode 100644
index 0000000..51b385b
--- /dev/null
+++ b/test cases/rust/1 basic/subdir/meson.build
@@ -0,0 +1,2 @@
+e = executable('program2', 'prog.rs', install : true)
+test('rusttest2', e)
diff --git a/test cases/rust/1 basic/subdir/prog.rs b/test cases/rust/1 basic/subdir/prog.rs
new file mode 100644
index 0000000..b171a80
--- /dev/null
+++ b/test cases/rust/1 basic/subdir/prog.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("rust compiler is working");
+}
diff --git a/test cases/unit/5 compiler detection/meson.build b/test cases/unit/5 compiler detection/meson.build
index 5491c64..8b47bd4 100644
--- a/test cases/unit/5 compiler detection/meson.build
+++ b/test cases/unit/5 compiler detection/meson.build
@@ -3,6 +3,6 @@ project('trivial test',
meson_version : '>=0.27.0')
executable('trivialc', 'trivial.c')
-executable('trivialcpp', 'trivial.cpp')
+executable('trivialcpp', 'trivial.cc')
executable('trivialobjc', 'trivial.m')
executable('trivialobjcpp', 'trivial.mm')
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/windows/5 resources/inc/meson.build b/test cases/windows/5 resources/inc/meson.build
new file mode 100644
index 0000000..b8b511a
--- /dev/null
+++ b/test cases/windows/5 resources/inc/meson.build
@@ -0,0 +1 @@
+inc = include_directories('resource')
diff --git a/test cases/windows/5 resources/inc/resource.h b/test cases/windows/5 resources/inc/resource/resource.h
index dbdd509..dbdd509 100644
--- a/test cases/windows/5 resources/inc/resource.h
+++ b/test cases/windows/5 resources/inc/resource/resource.h
diff --git a/test cases/windows/5 resources/meson.build b/test cases/windows/5 resources/meson.build
index 3c13634..ddb7d6e 100644
--- a/test cases/windows/5 resources/meson.build
+++ b/test cases/windows/5 resources/meson.build
@@ -1,9 +1,66 @@
project('winmain', 'c')
-win = import('windows')
-res = win.compile_resources('myres.rc',
- include_directories : include_directories('inc')
-)
+# 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' 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
+ src_parts = meson.source_root().split('/')
+ build_parts = meson.build_root().split('/')
+
+ # Get the common path (which might just be '/' or 'C:/')
+ common = []
+ done = false
+ count = 0
+ if src_parts.length() > build_parts.length()
+ parts = build_parts
+ other = src_parts
+ else
+ parts = src_parts
+ other = build_parts
+ endif
+ foreach part : parts
+ if not done and part == other.get(count)
+ common += [part]
+ else
+ done = true
+ endif
+ count += 1
+ endforeach
+
+ # Create path components to go down from the build root to the common path
+ count = 0
+ rel = build_parts
+ foreach build : build_parts
+ if count < build_parts.length() - common.length()
+ rel += ['..']
+ endif
+ count += 1
+ endforeach
+
+ # Create path components to go up from the common path to the build root
+ count = 0
+ foreach src : src_parts
+ if count >= common.length()
+ rel += [src]
+ endif
+ count += 1
+ endforeach
+
+ build_to_src = '/'.join(rel)
+
+ if build_to_src.contains(' ')
+ message('build_to_src is: ' + build_to_src)
+ error('MESON_SKIP_TEST build_to_src has spaces')
+ endif
+ # Welcome to the end of this conditional.
+ # We hope you never have to implement something like this.
+endif
+
+subdir('inc')
+subdir('res')
exe = executable('prog', 'prog.c',
res,
diff --git a/test cases/windows/5 resources/res/meson.build b/test cases/windows/5 resources/res/meson.build
new file mode 100644
index 0000000..184854e
--- /dev/null
+++ b/test cases/windows/5 resources/res/meson.build
@@ -0,0 +1,4 @@
+win = import('windows')
+
+res = win.compile_resources('myres.rc',
+ include_directories : inc)
diff --git a/test cases/windows/5 resources/myres.rc b/test cases/windows/5 resources/res/myres.rc
index 802bc7b..802bc7b 100644
--- a/test cases/windows/5 resources/myres.rc
+++ b/test cases/windows/5 resources/res/myres.rc
diff --git a/test cases/windows/5 resources/sample.ico b/test cases/windows/5 resources/res/sample.ico
index 24bd3d9..24bd3d9 100644
--- a/test cases/windows/5 resources/sample.ico
+++ b/test cases/windows/5 resources/res/sample.ico
Binary files differ
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..661005c 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,8 @@
-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