aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml21
-rw-r--r--README.md12
-rw-r--r--mesonbuild/backend/ninjabackend.py32
-rw-r--r--mesonbuild/backend/vs2010backend.py43
-rw-r--r--mesonbuild/backend/vs2017backend.py27
-rw-r--r--mesonbuild/compilers.py91
-rw-r--r--mesonbuild/coredata.py4
-rw-r--r--mesonbuild/dependencies.py2
-rw-r--r--mesonbuild/environment.py22
-rw-r--r--mesonbuild/interpreter.py49
-rw-r--r--mesonbuild/mesonmain.py12
-rw-r--r--mesonbuild/mparser.py9
-rw-r--r--mesonbuild/wrap/__init__.py31
-rw-r--r--mesonbuild/wrap/wrap.py79
-rwxr-xr-xrun_unittests.py1
-rw-r--r--test cases/common/117 custom target capture/meson.build4
-rw-r--r--test cases/common/139 compute int/config.h.in2
-rw-r--r--test cases/common/139 compute int/foobar.h6
-rw-r--r--test cases/common/139 compute int/meson.build35
-rw-r--r--test cases/common/139 compute int/prog.c.in16
-rw-r--r--test cases/common/40 logic ops/meson.build6
-rw-r--r--test cases/windows/8 msvc dll versioning/copyfile.py6
-rw-r--r--test cases/windows/8 msvc dll versioning/meson.build3
23 files changed, 426 insertions, 87 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 289758d..8f215c9 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -20,6 +20,16 @@ environment:
compiler: msvc2015
backend: vs2015
+ - arch: x86
+ compiler: msvc2017
+ backend: ninja
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+
+ - arch: x86
+ compiler: msvc2017
+ backend: vs2017
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+
- arch: x64
compiler: msvc2015
backend: ninja
@@ -28,6 +38,16 @@ environment:
compiler: msvc2015
backend: vs2015
+ - arch: x64
+ compiler: msvc2017
+ backend: ninja
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+
+ - arch: x64
+ compiler: msvc2017
+ backend: vs2017
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+
platform:
- x64
@@ -43,6 +63,7 @@ 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% )
build_script:
- cmd: echo No build step.
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/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index e0106b2..a167c46 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -146,6 +146,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 = {}
@@ -1802,17 +1803,7 @@ 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):
# 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)
@@ -1881,6 +1872,24 @@ 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))
+ 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.
@@ -1966,7 +1975,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()
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index e512f0e..929fa93 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -27,6 +27,29 @@ from ..compilers import CompilerArgs
from ..mesonlib import MesonException, File
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,6 +85,7 @@ 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):
basename = os.path.basename(source.fname)
@@ -354,6 +378,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')
@@ -403,7 +429,16 @@ class Vs2010Backend(backends.Backend):
# from the target dir, not the build root.
target.absolute_paths = True
(srcs, ofilenames, cmd) = self.eval_custom_target_command(target, True)
- ET.SubElement(customstep, 'Command').text = ' '.join(self.quote_arguments(cmd))
+ # Always use a wrapper because MSBuild eats random characters when
+ # there are many arguments.
+ tdir_abs = os.path.join(self.environment.get_build_dir(), self.get_target_dir(target))
+ exe_data = self.serialise_executable(target.command[0], cmd[1:],
+ # All targets run from the target dir
+ tdir_abs,
+ capture=ofilenames[0] if target.capture else None)
+ wrapper_cmd = [sys.executable, self.environment.get_build_command(),
+ '--internal', 'exe', exe_data]
+ ET.SubElement(customstep, 'Command').text = ' '.join(self.quote_arguments(wrapper_cmd))
ET.SubElement(customstep, 'Outputs').text = ';'.join(ofilenames)
ET.SubElement(customstep, 'Inputs').text = ';'.join(srcs)
ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets')
@@ -589,6 +624,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')
@@ -1009,6 +1046,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"
@@ -1088,6 +1127,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..8301790
--- /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)
diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py
index e814693..85b56a6 100644
--- a/mesonbuild/compilers.py
+++ b/mesonbuild/compilers.py
@@ -1001,44 +1001,75 @@ class CCompiler(Compiler):
mlog.debug(se)
return RunResult(True, pe.returncode, so, se)
- def _bisect_compiles(self, t, fargs, env, extra_args, dependencies):
- # FIXME: Does not actually do bisection right now
- for i in range(1, 1024):
- fargs['size'] = i
- if self.compiles(t.format(**fargs), env, extra_args, dependencies):
- if self.id == 'msvc':
- # MSVC refuses to construct an array of zero size, so
- # the test only succeeds when i is sizeof(element) + 1
- return i - 1
- return i
+ def _compile_int(self, expression, prefix, env, extra_args, dependencies):
+ fargs = {'prefix': prefix, 'expression': expression}
+ t = '''#include <stdio.h>
+ {prefix}
+ int main() {{ static int a[1-2*!({expression})]; a[0]=0; return 0; }}'''
+ return self.compiles(t.format(**fargs), env, extra_args, dependencies)
+
+ def cross_compute_int(self, expression, l, h, guess, prefix, env, extra_args, dependencies):
+ if isinstance(guess, int):
+ if self._compile_int('%s == %d' % (expression, guess), prefix, env, extra_args, dependencies):
+ return guess
+
+ cur = l
+ while l < h:
+ cur = int((l + h) / 2)
+ if cur == l:
+ break
+
+ if self._compile_int('%s >= %d' % (expression, cur), prefix, env, extra_args, dependencies):
+ l = cur
+ else:
+ h = cur
+
+ if self._compile_int('%s == %d' % (expression, cur), prefix, env, extra_args, dependencies):
+ return cur
raise EnvironmentException('Cross-compile check overflowed')
- def cross_sizeof(self, element, prefix, env, extra_args=None, dependencies=None):
+ def compute_int(self, expression, l, h, guess, prefix, env, extra_args=None, dependencies=None):
+ if extra_args is None:
+ extra_args = []
+ if self.is_cross:
+ return self.cross_compute_int(expression, l, h, guess, prefix, env, extra_args, dependencies)
+ fargs = {'prefix': prefix, 'expression': expression}
+ t = '''#include<stdio.h>
+ {prefix}
+ int main(int argc, char **argv) {{
+ printf("%ld\\n", (long)({expression}));
+ return 0;
+ }};'''
+ res = self.run(t.format(**fargs), env, extra_args, dependencies)
+ if not res.compiled:
+ return -1
+ if res.returncode != 0:
+ raise EnvironmentException('Could not run compute_int test binary.')
+ return int(res.stdout)
+
+ def cross_sizeof(self, typename, prefix, env, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
- fargs = {'prefix': prefix, 'name': element}
+ fargs = {'prefix': prefix, 'type': typename}
t = '''#include <stdio.h>
{prefix}
int main(int argc, char **argv) {{
- {name} something;
+ {type} something;
}}'''
if not self.compiles(t.format(**fargs), env, extra_args, dependencies):
return -1
- t = '''#include <stdio.h>
- {prefix}
- int temparray[{size}-sizeof({name})];'''
- return self._bisect_compiles(t, fargs, env, extra_args, dependencies)
+ return self.cross_compute_int('sizeof(%s)' % typename, 1, 128, None, prefix, env, extra_args, dependencies)
- def sizeof(self, element, prefix, env, extra_args=None, dependencies=None):
+ def sizeof(self, typename, prefix, env, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
- fargs = {'prefix': prefix, 'name': element}
+ fargs = {'prefix': prefix, 'type': typename}
if self.is_cross:
- return self.cross_sizeof(element, prefix, env, extra_args, dependencies)
+ return self.cross_sizeof(typename, prefix, env, extra_args, dependencies)
t = '''#include<stdio.h>
{prefix}
int main(int argc, char **argv) {{
- printf("%ld\\n", (long)(sizeof({name})));
+ printf("%ld\\n", (long)(sizeof({type})));
return 0;
}};'''
res = self.run(t.format(**fargs), env, extra_args, dependencies)
@@ -1048,32 +1079,34 @@ class CCompiler(Compiler):
raise EnvironmentException('Could not run sizeof test binary.')
return int(res.stdout)
- def cross_alignment(self, typename, env, extra_args=None, dependencies=None):
+ def cross_alignment(self, typename, prefix, env, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
- fargs = {'type': typename}
+ fargs = {'prefix': prefix, 'type': typename}
t = '''#include <stdio.h>
+ {prefix}
int main(int argc, char **argv) {{
{type} something;
}}'''
if not self.compiles(t.format(**fargs), env, extra_args, dependencies):
return -1
t = '''#include <stddef.h>
+ {prefix}
struct tmp {{
char c;
{type} target;
- }};
- int testarray[{size}-offsetof(struct tmp, target)];'''
- return self._bisect_compiles(t, fargs, env, extra_args, dependencies)
+ }};'''
+ return self.cross_compute_int('offsetof(struct tmp, target)', 1, 1024, None, t.format(**fargs), env, extra_args, dependencies)
- def alignment(self, typename, env, extra_args=None, dependencies=None):
+ def alignment(self, typename, prefix, env, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
if self.is_cross:
- return self.cross_alignment(typename, env, extra_args, dependencies)
- fargs = {'type': typename}
+ return self.cross_alignment(typename, prefix, env, extra_args, dependencies)
+ fargs = {'prefix': prefix, 'type': typename}
t = '''#include <stdio.h>
#include <stddef.h>
+ {prefix}
struct tmp {{
char c;
{type} target;
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index e88cdc8..9562211 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -18,7 +18,7 @@ from .mesonlib import MesonException, commonpath
from .mesonlib import default_libdir, default_libexecdir, default_prefix
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):
@@ -127,7 +127,7 @@ class CoreData:
self.cross_file = os.path.join(os.getcwd(), options.cross_file)
else:
self.cross_file = None
-
+ self.wrap_mode = options.wrap_mode
self.compilers = {}
self.cross_compilers = {}
self.deps = {}
diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py
index e4317f1..031e106 100644
--- a/mesonbuild/dependencies.py
+++ b/mesonbuild/dependencies.py
@@ -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):
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index 5217626..92040c4 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -377,16 +377,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:
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index c16b668..6e8cf1a 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
from .dependencies import InternalDependency, Dependency
@@ -632,6 +632,7 @@ class CompilerHolder(InterpreterObject):
self.methods.update({'compiles': self.compiles_method,
'links': self.links_method,
'get_id': self.get_id_method,
+ 'compute_int': self.compute_int_method,
'sizeof': self.sizeof_method,
'has_header': self.has_header_method,
'has_header_symbol': self.has_header_symbol_method,
@@ -700,8 +701,12 @@ class CompilerHolder(InterpreterObject):
raise InterpreterException('Alignment method takes exactly one positional argument.')
check_stringlist(args)
typename = args[0]
+ prefix = kwargs.get('prefix', '')
+ if not isinstance(prefix, str):
+ raise InterpreterException('Prefix argument of sizeof must be a string.')
extra_args = mesonlib.stringlistify(kwargs.get('args', []))
- result = self.compiler.alignment(typename, self.environment, extra_args)
+ deps = self.determine_dependencies(kwargs)
+ result = self.compiler.alignment(typename, prefix, self.environment, extra_args, deps)
mlog.log('Checking for alignment of "', mlog.bold(typename), '": ', result, sep='')
return result
@@ -823,6 +828,29 @@ class CompilerHolder(InterpreterObject):
mlog.log('Checking for type "', mlog.bold(typename), '": ', hadtxt, sep='')
return had
+ def compute_int_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('Compute_int takes exactly one argument.')
+ check_stringlist(args)
+ expression = args[0]
+ prefix = kwargs.get('prefix', '')
+ l = kwargs.get('low', -1024)
+ h = kwargs.get('high', 1024)
+ guess = kwargs.get('guess', None)
+ if not isinstance(prefix, str):
+ raise InterpreterException('Prefix argument of compute_int must be a string.')
+ if not isinstance(l, int):
+ raise InterpreterException('Low argument of compute_int must be an int.')
+ if not isinstance(h, int):
+ raise InterpreterException('High argument of compute_int must be an int.')
+ if guess is not None and not isinstance(guess, int):
+ raise InterpreterException('Guess argument of compute_int must be an int.')
+ extra_args = self.determine_args(kwargs)
+ deps = self.determine_dependencies(kwargs)
+ res = self.compiler.compute_int(expression, l, h, guess, prefix, self.environment, extra_args, deps)
+ mlog.log('Computing int of "%s": %d' % (expression, res))
+ return res
+
def sizeof_method(self, args, kwargs):
if len(args) != 1:
raise InterpreterException('Sizeof takes exactly one argument.')
@@ -1470,11 +1498,13 @@ 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
@@ -1881,6 +1911,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:
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/mparser.py b/mesonbuild/mparser.py
index 6e1e398..fe5ccc5 100644
--- a/mesonbuild/mparser.py
+++ b/mesonbuild/mparser.py
@@ -193,9 +193,10 @@ class OrNode:
self.right = right
class AndNode:
- def __init__(self, lineno, colno, left, right):
- self.lineno = lineno
- self.colno = colno
+ def __init__(self, left, right):
+ self.subdir = left.subdir
+ self.lineno = left.lineno
+ self.colno = left.colno
self.left = left
self.right = right
@@ -436,7 +437,7 @@ class Parser:
def e3(self):
left = self.e4()
while self.accept('and'):
- left = AndNode(left.lineno, left.colno, left, self.e4())
+ left = AndNode(left, self.e4())
return left
def e4(self):
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..fcacc16 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)
+ 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/run_unittests.py b/run_unittests.py
index bba5bb8..a11e3a5 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -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
diff --git a/test cases/common/117 custom target capture/meson.build b/test cases/common/117 custom target capture/meson.build
index 16b6ac9..58a69ca 100644
--- a/test cases/common/117 custom target capture/meson.build
+++ b/test cases/common/117 custom target capture/meson.build
@@ -2,10 +2,6 @@ project('custom target', 'c')
python3 = import('python3').find_python()
-if meson.backend().startswith('vs')
- error('MESON_SKIP_TEST: capturing of custom target output is broken with the VS backends')
-endif
-
# Note that this will not add a dependency to the compiler executable.
# Code will not be rebuilt if it changes.
comp = '@0@/@1@'.format(meson.current_source_dir(), 'my_compiler.py')
diff --git a/test cases/common/139 compute int/config.h.in b/test cases/common/139 compute int/config.h.in
new file mode 100644
index 0000000..ad8d077
--- /dev/null
+++ b/test cases/common/139 compute int/config.h.in
@@ -0,0 +1,2 @@
+#define INTSIZE @INTSIZE@
+#define FOOBAR_IN_CONFIG_H @FOOBAR@
diff --git a/test cases/common/139 compute int/foobar.h b/test cases/common/139 compute int/foobar.h
new file mode 100644
index 0000000..fd3cb5e
--- /dev/null
+++ b/test cases/common/139 compute int/foobar.h
@@ -0,0 +1,6 @@
+#ifndef __FOOBAR_H__
+#define __FOOBAR_H__
+
+#define FOOBAR_IN_FOOBAR_H 10
+
+#endif /*__FOOBAR_H__*/
diff --git a/test cases/common/139 compute int/meson.build b/test cases/common/139 compute int/meson.build
new file mode 100644
index 0000000..43553fe
--- /dev/null
+++ b/test cases/common/139 compute int/meson.build
@@ -0,0 +1,35 @@
+project('compute int', 'c', 'cpp')
+
+inc = include_directories('.')
+
+# Test with C
+cc = meson.get_compiler('c')
+
+intsize = cc.compute_int('sizeof(int)', low : 1, high : 16, guess : 4)
+foobar = cc.compute_int('FOOBAR_IN_FOOBAR_H', prefix : '#include "foobar.h"', include_directories : inc)
+
+cd = configuration_data()
+cd.set('INTSIZE', intsize)
+cd.set('FOOBAR', foobar)
+cd.set('CONFIG', 'config.h')
+configure_file(input : 'config.h.in', output : 'config.h', configuration : cd)
+s = configure_file(input : 'prog.c.in', output : 'prog.c', configuration : cd)
+
+e = executable('prog', s)
+test('compute int test', e)
+
+# Test with C++
+cpp = meson.get_compiler('cpp')
+
+intsize = cpp.compute_int('sizeof(int)')
+foobar = cpp.compute_int('FOOBAR_IN_FOOBAR_H', prefix : '#include "foobar.h"', include_directories : inc)
+
+cdpp = configuration_data()
+cdpp.set('INTSIZE', intsize)
+cdpp.set('FOOBAR', foobar)
+cdpp.set('CONFIG', 'config.hpp')
+configure_file(input : 'config.h.in', output : 'config.hpp', configuration : cdpp)
+spp = configure_file(input : 'prog.c.in', output : 'prog.cc', configuration : cdpp)
+
+epp = executable('progpp', spp)
+test('compute int test c++', epp)
diff --git a/test cases/common/139 compute int/prog.c.in b/test cases/common/139 compute int/prog.c.in
new file mode 100644
index 0000000..3ff1463
--- /dev/null
+++ b/test cases/common/139 compute int/prog.c.in
@@ -0,0 +1,16 @@
+#include "@CONFIG@"
+#include <stdio.h>
+#include <wchar.h>
+#include "foobar.h"
+
+int main(int argc, char **argv) {
+ if(INTSIZE != sizeof(int)) {
+ fprintf(stderr, "Mismatch: computed int size %d, actual size %d.\n", INTSIZE, (int)sizeof(int));
+ return 1;
+ }
+ if(FOOBAR_IN_CONFIG_H != FOOBAR_IN_FOOBAR_H) {
+ fprintf(stderr, "Mismatch: computed int %d, should be %d.\n", FOOBAR_IN_CONFIG_H, FOOBAR_IN_FOOBAR_H);
+ return 1;
+ }
+ return 0;
+}
diff --git a/test cases/common/40 logic ops/meson.build b/test cases/common/40 logic ops/meson.build
index e975c7e..897054e 100644
--- a/test cases/common/40 logic ops/meson.build
+++ b/test cases/common/40 logic ops/meson.build
@@ -87,3 +87,9 @@ if t and t and t and t and t and t and t and t and f
else
message('Ok.')
endif
+
+if t and t or t
+ message('Ok.')
+else
+ error('Combination of and-or failed.')
+endif
diff --git a/test cases/windows/8 msvc dll versioning/copyfile.py b/test cases/windows/8 msvc dll versioning/copyfile.py
new file mode 100644
index 0000000..ff42ac3
--- /dev/null
+++ b/test cases/windows/8 msvc dll versioning/copyfile.py
@@ -0,0 +1,6 @@
+#!/usr/bin/env python3
+
+import sys
+import shutil
+
+shutil.copyfile(sys.argv[1], sys.argv[2])
diff --git a/test cases/windows/8 msvc dll versioning/meson.build b/test cases/windows/8 msvc dll versioning/meson.build
index 3f15e76..b72c5ec 100644
--- a/test cases/windows/8 msvc dll versioning/meson.build
+++ b/test cases/windows/8 msvc dll versioning/meson.build
@@ -28,11 +28,12 @@ onlysoversion = shared_library('onlysoversion', 'lib.c',
# Hack to make the executables below depend on the shared libraries above
# without actually adding them as `link_with` dependencies since we want to try
# linking to them with -lfoo linker arguments.
+cp = find_program('copyfile.py')
out = custom_target('library-dependency-hack',
input : 'exe.orig.c',
output : 'exe.c',
depends : [some, noversion, onlyversion, onlysoversion],
- command : ['cp', '@INPUT@', '@OUTPUT@'])
+ command : [cp, '@INPUT@', '@OUTPUT@'])
# Manually test if the linker can find the above libraries
# i.e., whether they were generated with the right naming scheme