aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild')
-rw-r--r--mesonbuild/backend/vs2010backend.py7
-rw-r--r--mesonbuild/build.py2
-rw-r--r--mesonbuild/compilers/compilers.py12
-rw-r--r--mesonbuild/dependencies/misc.py23
-rw-r--r--mesonbuild/environment.py2
-rw-r--r--mesonbuild/interpreter.py63
-rw-r--r--mesonbuild/mesonlib.py25
-rw-r--r--mesonbuild/mesonmain.py3
-rw-r--r--mesonbuild/modules/gnome.py31
-rw-r--r--mesonbuild/optinterpreter.py9
-rw-r--r--mesonbuild/scripts/scanbuild.py26
-rw-r--r--mesonbuild/wrap/wraptool.py37
12 files changed, 216 insertions, 24 deletions
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index e872a04..6a587ac 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -29,10 +29,9 @@ from ..environment import Environment
def autodetect_vs_version(build):
vs_version = os.getenv('VisualStudioVersion', None)
vs_install_dir = os.getenv('VSINSTALLDIR', None)
- if not vs_version and not vs_install_dir:
- raise MesonException('Could not detect Visual Studio: VisualStudioVersion and VSINSTALLDIR are unset!\n'
- 'Are we inside a Visual Studio build environment? '
- 'You can also try specifying the exact backend to use.')
+ if not vs_install_dir:
+ raise MesonException('Could not detect Visual Studio: Environment variable VSINSTALLDIR is not set!\n'
+ 'Are you running meson from the Visual Studio Developer Command Prompt?')
# VisualStudioVersion is set since Visual Studio 12.0, but sometimes
# vcvarsall.bat doesn't set it, so also use VSINSTALLDIR
if vs_version == '14.0' or 'Visual Studio 14' in vs_install_dir:
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 7757300..4a35bec 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -1776,7 +1776,7 @@ class CustomTargetIndex:
def __repr__(self):
return '<CustomTargetIndex: {!r}[{}]>'.format(
- self.target, self.target.output.index(self.output))
+ self.target, self.target.get_outputs().index(self.output))
def get_outputs(self):
return [self.output]
diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
index 3d50eb0..b14074b 100644
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -49,7 +49,7 @@ cpp_suffixes = lang_suffixes['cpp'] + ('h',)
c_suffixes = lang_suffixes['c'] + ('h',)
# List of languages that can be linked with C code directly by the linker
# used in build.py:process_compilers() and build.py:get_dynamic_linker()
-clike_langs = ('objcpp', 'objc', 'd', 'cpp', 'c', 'fortran',)
+clike_langs = ('d', 'objcpp', 'cpp', 'objc', 'c', 'fortran',)
clike_suffixes = ()
for _l in clike_langs:
clike_suffixes += lang_suffixes[_l]
@@ -863,9 +863,17 @@ class Compiler:
# Not needed on Windows or other platforms that don't use RPATH
# https://github.com/mesonbuild/meson/issues/1897
lpaths = ':'.join([os.path.join(build_dir, p) for p in rpath_paths])
- args += ['-Wl,-rpath-link,' + lpaths]
+
+ # clang expands '-Wl,rpath-link,' to ['-rpath-link'] instead of ['-rpath-link','']
+ # This eats the next argument, which happens to be 'ldstdc++', causing link failures.
+ # We can dodge this problem by not adding any rpath_paths if the argument is empty.
+ if lpaths.strip() != '':
+ args += ['-Wl,-rpath-link,' + lpaths]
return args
+ def thread_flags(self, env):
+ return []
+
GCC_STANDARD = 0
GCC_OSX = 1
diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py
index e7eac1b..ea0711f 100644
--- a/mesonbuild/dependencies/misc.py
+++ b/mesonbuild/dependencies/misc.py
@@ -71,11 +71,6 @@ class BoostDependency(ExternalDependency):
self.is_multithreading = threading == "multi"
self.requested_modules = self.get_requested(kwargs)
- invalid_modules = [c for c in self.requested_modules if 'boost_' + c not in BOOST_LIBS]
- if invalid_modules:
- mlog.warning('Invalid Boost modules: ' + ', '.join(invalid_modules))
- self.log_fail()
- return
self.boost_root = None
self.boost_roots = []
@@ -112,6 +107,24 @@ class BoostDependency(ExternalDependency):
self.log_fail()
return
+ invalid_modules = [c for c in self.requested_modules if 'boost_' + c not in BOOST_LIBS]
+
+ # previous versions of meson allowed include dirs as modules
+ remove = []
+ for m in invalid_modules:
+ if m in os.listdir(os.path.join(self.incdir, 'boost')):
+ mlog.warning('Requested boost library', mlog.bold(m), 'that doesn\'t exist. '
+ 'This will be an error in the future')
+ remove.append(m)
+
+ self.requested_modules = [x for x in self.requested_modules if x not in remove]
+ invalid_modules = [x for x in invalid_modules if x not in remove]
+
+ if invalid_modules:
+ mlog.warning('Invalid Boost modules: ' + ', '.join(invalid_modules))
+ self.log_fail()
+ return
+
mlog.debug('Boost library root dir is', mlog.bold(self.boost_root))
mlog.debug('Boost include directory is', mlog.bold(self.incdir))
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index 858d31d..0c9a2f3 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -608,6 +608,7 @@ class Environment:
p, out, err = Popen_safe(compiler + arg)
except OSError as e:
popen_exceptions[' '.join(compiler + arg)] = e
+ continue
version = search_version(out)
if 'Free Software Foundation' in out:
defines = self.get_gnu_compiler_defines(compiler)
@@ -634,6 +635,7 @@ class Environment:
p, out, err = Popen_safe(compiler + arg)
except OSError as e:
popen_exceptions[' '.join(compiler + arg)] = e
+ continue
version = search_version(out)
if 'Free Software Foundation' in out:
defines = self.get_gnu_compiler_defines(compiler)
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 93f4b1a..e5238a7 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -1667,7 +1667,7 @@ external dependencies (including libraries) must go to "dependencies".''')
search_dir = os.path.join(srcdir, self.subdir)
prog = ExternalProgram(cmd, silent=True, search_dir=search_dir)
if not prog.found():
- raise InterpreterException('Program or command {!r} not found'
+ raise InterpreterException('Program or command {!r} not found '
'or not executable'.format(cmd))
cmd = prog
cmd_path = os.path.relpath(cmd.get_path(), start=srcdir)
@@ -1813,9 +1813,18 @@ to directly access options of other subprojects.''')
if coredata.is_builtin_option(key):
if self.subproject != '':
continue # Only the master project is allowed to set global options.
+ # If this was set on the command line, do not override.
if not self.environment.had_argument_for(key):
self.coredata.set_builtin_option(key, value)
- # If this was set on the command line, do not override.
+ # If we are setting the prefix, then other options which
+ # have prefix-dependent defaults need their value updating,
+ # if they haven't been explicitly set (i.e. have their
+ # default value)
+ if key == 'prefix':
+ for option in coredata.builtin_dir_noprefix_options:
+ if not (self.environment.had_argument_for(option) or
+ any([k.startswith(option + '=') for k in default_options])):
+ self.coredata.set_builtin_option(option, coredata.get_builtin_option_default(option, value))
else:
# Option values set with subproject() default_options override those
# set in project() default_options.
@@ -2131,6 +2140,8 @@ to directly access options of other subprojects.''')
# Check if we've already searched for and found this dep
if identifier in self.coredata.deps:
cached_dep = self.coredata.deps[identifier]
+ mlog.log('Cached dependency', mlog.bold(name),
+ 'found:', mlog.green('YES'))
else:
# Check if exactly the same dep with different version requirements
# was found already.
@@ -2172,13 +2183,26 @@ to directly access options of other subprojects.''')
# a higher level project, try to use it first.
if 'fallback' in kwargs:
dirname, varname = self.get_subproject_infos(kwargs)
+ required = kwargs.get('required', True)
+ wanted = kwargs.get('version', 'undefined')
+ if not isinstance(required, bool):
+ raise DependencyException('Keyword "required" must be a boolean.')
if dirname in self.subprojects:
- subproject = self.subprojects[dirname]
- try:
- # Never add fallback deps to self.coredata.deps
- return subproject.get_variable_method([varname], {})
- except KeyError:
- pass
+ found = self.subprojects[dirname].held_object.project_version
+ valid_version = wanted == 'undefined' or mesonlib.version_compare(found, wanted)
+ if required and not valid_version:
+ m = 'Version {} of {} already loaded, requested incompatible version {}'
+ raise DependencyException(m.format(found, dirname, wanted))
+ elif valid_version:
+ mlog.log('Found a', mlog.green('(cached)'), 'subproject',
+ mlog.bold(os.path.join(self.subproject_dir, dirname)), 'for',
+ mlog.bold(name))
+ subproject = self.subprojects[dirname]
+ try:
+ # Never add fallback deps to self.coredata.deps
+ return subproject.get_variable_method([varname], {})
+ except KeyError:
+ pass
# We need to actually search for this dep
exception = None
@@ -2200,6 +2224,7 @@ to directly access options of other subprojects.''')
# we won't actually read all the build files.
return fallback_dep
if not dep:
+ self.print_nested_info(name)
assert(exception is not None)
raise exception
@@ -2213,6 +2238,28 @@ to directly access options of other subprojects.''')
def func_disabler(self, node, args, kwargs):
return Disabler()
+ def print_nested_info(self, dependency_name):
+ message_templ = '''\nDependency %s not found but it is available in a sub-subproject.
+To use it in the current project, promote it by going in the project source
+root and issuing %s.
+
+'''
+ sprojs = mesonlib.detect_subprojects('subprojects', self.source_root)
+ if dependency_name not in sprojs:
+ return
+ found = sprojs[dependency_name]
+ if len(found) > 1:
+ suffix = 'one of the following commands'
+ else:
+ suffix = 'the following command'
+ message = message_templ % (dependency_name, suffix)
+ cmds = []
+ command_templ = 'meson wrap promote '
+ for l in found:
+ cmds.append(command_templ + l[len(self.source_root)+1:])
+ final_message = message + '\n'.join(cmds)
+ print(final_message)
+
def get_subproject_infos(self, kwargs):
fbinfo = kwargs['fallback']
check_stringlist(fbinfo)
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index 9ad0668..6bf31db 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -881,6 +881,31 @@ def windows_proof_rmtree(f):
# Try one last time and throw if it fails.
shutil.rmtree(f)
+
+def detect_subprojects(spdir_name, current_dir='', result=None):
+ if result is None:
+ result = {}
+ spdir = os.path.join(current_dir, spdir_name)
+ if not os.path.exists(spdir):
+ return result
+ for trial in glob(os.path.join(spdir, '*')):
+ basename = os.path.split(trial)[1]
+ if trial == 'packagecache':
+ continue
+ append_this = True
+ if os.path.isdir(trial):
+ detect_subprojects(spdir_name, trial, result)
+ elif trial.endswith('.wrap') and os.path.isfile(trial):
+ basename = os.path.splitext(basename)[0]
+ else:
+ append_this = False
+ if append_this:
+ if basename in result:
+ result[basename].append(trial)
+ else:
+ result[basename] = [trial]
+ return result
+
class OrderedSet(collections.MutableSet):
"""A set that preserves the order in which items are added, by first
insertion.
diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py
index f261935..20ec304 100644
--- a/mesonbuild/mesonmain.py
+++ b/mesonbuild/mesonmain.py
@@ -28,7 +28,7 @@ default_warning = '1'
def add_builtin_argument(p, name, **kwargs):
k = kwargs.get('dest', name.replace('-', '_'))
c = coredata.get_builtin_option_choices(k)
- b = True if kwargs.get('action', None) in ['store_true', 'store_false'] else False
+ b = kwargs.get('action', None) in ['store_true', 'store_false']
h = coredata.get_builtin_option_description(k)
if not b:
h = h.rstrip('.') + ' (default: %s).' % coredata.get_builtin_option_default(k)
@@ -172,6 +172,7 @@ class MesonApp:
elif self.options.backend == 'vs':
from .backend import vs2010backend
g = vs2010backend.autodetect_vs_version(b)
+ env.coredata.set_builtin_option('backend', g.name)
mlog.log('Auto detected Visual Studio backend:', mlog.bold(g.name))
elif self.options.backend == 'vs2010':
from .backend import vs2010backend
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index 7a01abc..ad99c14 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -776,12 +776,39 @@ This will become a hard error in the future.''')
args += self._unpack_args('--fixxrefargs=', 'fixxref_args', kwargs)
args += self._unpack_args('--mkdbargs=', 'mkdb_args', kwargs)
args += self._unpack_args('--html-assets=', 'html_assets', kwargs, state)
- args += self._unpack_args('--content-files=', 'content_files', kwargs, state)
+
+ depends = []
+ content_files = []
+ for s in mesonlib.extract_as_list(kwargs, 'content_files'):
+ if hasattr(s, 'held_object'):
+ s = s.held_object
+ if isinstance(s, (build.CustomTarget, build.CustomTargetIndex)):
+ depends.append(s)
+ content_files.append(os.path.join(state.environment.get_build_dir(),
+ state.backend.get_target_dir(s),
+ s.get_outputs()[0]))
+ elif isinstance(s, mesonlib.File):
+ content_files.append(s.rel_to_builddir(state.build_to_src))
+ elif isinstance(s, build.GeneratedList):
+ depends.append(s)
+ for gen_src in s.get_outputs():
+ content_files.append(os.path.join(state.environment.get_source_dir(),
+ state.subdir,
+ gen_src))
+ elif isinstance(s, str):
+ content_files.append(os.path.join(state.environment.get_source_dir(),
+ state.subdir,
+ s))
+ else:
+ raise MesonException(
+ 'Invalid object type: {!r}'.format(s.__class__.__name__))
+ args += ['--content-files=' + '@@'.join(content_files)]
+
args += self._unpack_args('--expand-content-files=', 'expand_content_files', kwargs, state)
args += self._unpack_args('--ignore-headers=', 'ignore_headers', kwargs)
args += self._unpack_args('--installdir=', 'install_dir', kwargs, state)
args += self._get_build_args(kwargs, state)
- res = [build.RunTarget(targetname, command[0], command[1:] + args, [], state.subdir, state.subproject)]
+ res = [build.RunTarget(targetname, command[0], command[1:] + args, depends, state.subdir, state.subproject)]
if kwargs.get('install', True):
res.append(build.RunScript(command, args))
return ModuleReturnValue(None, res)
diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py
index f8ccbe6..df945ab 100644
--- a/mesonbuild/optinterpreter.py
+++ b/mesonbuild/optinterpreter.py
@@ -15,6 +15,7 @@
import os, re
import functools
+from . import mlog
from . import mparser
from . import coredata
from . import mesonlib
@@ -146,6 +147,14 @@ class OptionInterpreter:
e.colno = cur.colno
e.file = os.path.join('meson_options.txt')
raise e
+ bad = [o for o in sorted(self.cmd_line_options) if not
+ (o in list(self.options) + forbidden_option_names or
+ any(o.startswith(p) for p in forbidden_prefixes))]
+ if bad:
+ sub = 'In subproject {}: '.format(self.subproject) if self.subproject else ''
+ mlog.warning(
+ '{}Unknown command line options: "{}"\n'
+ 'This will become a hard error in a future Meson release.'.format(sub, ', '.join(bad)))
def reduce_single(self, arg):
if isinstance(arg, str):
diff --git a/mesonbuild/scripts/scanbuild.py b/mesonbuild/scripts/scanbuild.py
index 728214f..f381552 100644
--- a/mesonbuild/scripts/scanbuild.py
+++ b/mesonbuild/scripts/scanbuild.py
@@ -17,6 +17,7 @@ import subprocess
import shutil
import tempfile
from ..environment import detect_ninja
+from ..mesonlib import Popen_safe
def scanbuild(exename, srcdir, blddir, privdir, logdir, args):
with tempfile.TemporaryDirectory(dir=privdir) as scandir:
@@ -34,7 +35,30 @@ def run(args):
privdir = os.path.join(blddir, 'meson-private')
logdir = os.path.join(blddir, 'meson-logs/scanbuild')
shutil.rmtree(logdir, ignore_errors=True)
- exename = os.environ.get('SCANBUILD', 'scan-build')
+ tools = [
+ 'scan-build', # base
+ 'scan-build-5.0', 'scan-build50', # latest stable release
+ 'scan-build-4.0', 'scan-build40', # old stable releases
+ 'scan-build-3.9', 'scan-build39',
+ 'scan-build-3.8', 'scan-build38',
+ 'scan-build-3.7', 'scan-build37',
+ 'scan-build-3.6', 'scan-build36',
+ 'scan-build-3.5', 'scan-build35',
+ 'scan-build-6.0', 'scan-build-devel', # development snapshot
+ ]
+ toolname = 'scan-build'
+ for tool in tools:
+ try:
+ p, out = Popen_safe([tool, '--help'])[:2]
+ except (FileNotFoundError, PermissionError):
+ continue
+ if p.returncode != 0:
+ continue
+ else:
+ toolname = tool
+ break
+
+ exename = os.environ.get('SCANBUILD', toolname)
if not shutil.which(exename):
print('Scan-build not installed.')
return 1
diff --git a/mesonbuild/wrap/wraptool.py b/mesonbuild/wrap/wraptool.py
index 79b00e0..00115cb 100644
--- a/mesonbuild/wrap/wraptool.py
+++ b/mesonbuild/wrap/wraptool.py
@@ -21,6 +21,8 @@ from glob import glob
from .wrap import API_ROOT, open_wrapdburl
+from .. import mesonlib
+
help_templ = '''This program allows you to manage your Wrap dependencies
using the online wrap database http://wrapdb.mesonbuild.com.
@@ -142,6 +144,36 @@ def info(name):
for v in versions:
print(' ', v['branch'], v['revision'])
+def do_promotion(from_path, spdir_name):
+ if os.path.isfile(from_path):
+ assert(from_path.endswith('.wrap'))
+ shutil.copy(from_path, spdir_name)
+ elif os.path.isdir(from_path):
+ sproj_name = os.path.split(from_path)[1]
+ outputdir = os.path.join(spdir_name, sproj_name)
+ if os.path.exists(outputdir):
+ sys.exit('Output dir %s already exists. Will not overwrite.' % outputdir)
+ shutil.copytree(from_path, outputdir, ignore=shutil.ignore_patterns('subprojects'))
+
+def promote(argument):
+ path_segment, subproject_name = os.path.split(argument)
+ spdir_name = 'subprojects'
+ sprojs = mesonlib.detect_subprojects(spdir_name)
+ if subproject_name not in sprojs:
+ sys.exit('Subproject %s not found in directory tree.' % subproject_name)
+ matches = sprojs[subproject_name]
+ if len(matches) == 1:
+ do_promotion(matches[0], spdir_name)
+ return
+ if path_segment == '':
+ print('There are many versions of %s in tree. Please specify which one to promote:\n' % subproject_name)
+ for s in matches:
+ print(s)
+ sys.exit(1)
+ system_native_path_argument = argument.replace('/', os.sep)
+ if system_native_path_argument in matches:
+ do_promotion(argument, spdir_name)
+
def status():
print('Subproject status')
for w in glob('subprojects/*.wrap'):
@@ -189,6 +221,11 @@ def run(args):
print('info requires exactly one argument.')
return 1
info(args[0])
+ elif command == 'promote':
+ if len(args) != 1:
+ print('promote requires exactly one argument.')
+ return 1
+ promote(args[0])
elif command == 'status':
status()
else: