aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild')
-rw-r--r--mesonbuild/backend/ninjabackend.py25
-rw-r--r--mesonbuild/dependencies/base.py166
-rw-r--r--mesonbuild/environment.py4
-rw-r--r--mesonbuild/interpreter.py26
-rw-r--r--mesonbuild/modules/dlang.py141
5 files changed, 347 insertions, 15 deletions
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 09c4904..c248ae8 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -73,8 +73,8 @@ class NinjaBuildElement:
self.infilenames = [infilenames]
else:
self.infilenames = infilenames
- self.deps = set()
- self.orderdeps = set()
+ self.deps = OrderedSet()
+ self.orderdeps = OrderedSet()
self.elems = []
self.all_outputs = all_outputs
@@ -2623,25 +2623,32 @@ rule FORTRAN_DEP_HACK%s
dependencies = target.get_dependencies()
internal = self.build_target_link_arguments(linker, dependencies)
commands += internal
- # For 'automagic' deps: Boost and GTest. Also dependency('threads').
- # pkg-config puts the thread flags itself via `Cflags:`
- for d in target.external_deps:
- if d.need_threads():
- commands += linker.thread_link_flags(self.environment)
- elif d.need_openmp():
- commands += linker.openmp_flags()
# Only non-static built targets need link args and link dependencies
if not isinstance(target, build.StaticLibrary):
+ # For 'automagic' deps: Boost and GTest. Also dependency('threads').
+ # pkg-config puts the thread flags itself via `Cflags:`
+ need_threads = False
+ need_openmp = False
+
commands += target.link_args
# External deps must be last because target link libraries may depend on them.
for dep in target.get_external_deps():
# Extend without reordering or de-dup to preserve `-L -l` sets
# https://github.com/mesonbuild/meson/issues/1718
commands.extend_direct(dep.get_link_args())
+ need_threads |= dep.need_threads()
+ need_openmp |= dep.need_openmp()
for d in target.get_dependencies():
if isinstance(d, build.StaticLibrary):
for dep in d.get_external_deps():
+ need_threads |= dep.need_threads()
+ need_openmp |= dep.need_openmp()
commands.extend_direct(dep.get_link_args())
+ if need_openmp:
+ commands += linker.openmp_flags()
+ if need_threads:
+ commands += linker.thread_link_flags(self.environment)
+
# Add link args for c_* or cpp_* build options. Currently this only
# adds c_winlibs and cpp_winlibs when building for Windows. This needs
# to be after all internal and external libraries so that unresolved
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py
index 0fe702f..864fd73 100644
--- a/mesonbuild/dependencies/base.py
+++ b/mesonbuild/dependencies/base.py
@@ -19,6 +19,7 @@ import copy
import os
import re
import stat
+import json
import shlex
import shutil
import textwrap
@@ -31,7 +32,6 @@ from ..compilers import clib_langs
from ..mesonlib import MesonException, OrderedSet
from ..mesonlib import Popen_safe, version_compare_many, version_compare, listify
-
# These must be defined in this file to avoid cyclical references.
packages = {}
_packages_accept_language = set()
@@ -59,6 +59,8 @@ class DependencyMethods(Enum):
CUPSCONFIG = 'cups-config'
PCAPCONFIG = 'pcap-config'
LIBWMFCONFIG = 'libwmf-config'
+ # Misc
+ DUB = 'dub'
class Dependency:
@@ -790,6 +792,162 @@ class PkgConfigDependency(ExternalDependency):
# a path rather than the raw dlname
return os.path.basename(dlname)
+class DubDependency(ExternalDependency):
+ class_dubbin = None
+
+ def __init__(self, name, environment, kwargs):
+ super().__init__('dub', environment, 'd', kwargs)
+ self.name = name
+ self.compiler = super().get_compiler()
+
+ if 'required' in kwargs:
+ self.required = kwargs.get('required')
+
+ if DubDependency.class_dubbin is None:
+ self.dubbin = self.check_dub()
+ DubDependency.class_dubbin = self.dubbin
+ else:
+ self.dubbin = DubDependency.class_dubbin
+
+ if not self.dubbin:
+ if self.required:
+ raise DependencyException('DUB not found.')
+ self.is_found = False
+ mlog.log('Dependency', mlog.bold(name), 'found:', mlog.red('NO'))
+ return
+
+ mlog.debug('Determining dependency {!r} with DUB executable '
+ '{!r}'.format(name, self.dubbin.get_path()))
+
+ # Ask dub for the package
+ ret, res = self._call_dubbin(['describe', name])
+
+ if ret != 0:
+ if self.required:
+ raise DependencyException('Dependency {!r} not found'.format(name))
+ self.is_found = False
+ mlog.log('Dependency', mlog.bold(name), 'found:', mlog.red('NO'))
+ return
+
+ j = json.loads(res)
+ comp = self.compiler.get_id().replace('llvm', 'ldc').replace('gcc', 'gdc')
+ for package in j['packages']:
+ if package['name'] == name:
+ if j['compiler'] != comp:
+ msg = ['Dependency', mlog.bold(name), 'found but it was compiled with']
+ msg += [mlog.bold(j['compiler']), 'and we are using', mlog.bold(comp)]
+ mlog.error(*msg)
+ if self.required:
+ raise DependencyException('Dependency {!r} not found'.format(name))
+ self.is_found = False
+ mlog.log('Dependency', mlog.bold(name), 'found:', mlog.red('NO'))
+ return
+
+ self.version = package['version']
+ self.pkg = package
+ break
+
+ # Check if package version meets the requirements
+ found_msg = ['Dependency', mlog.bold(name), 'found:']
+ if self.version_reqs is None:
+ self.is_found = True
+ else:
+ if not isinstance(self.version_reqs, (str, list)):
+ raise DependencyException('Version argument must be string or list.')
+ if isinstance(self.version_reqs, str):
+ self.version_reqs = [self.version_reqs]
+ (self.is_found, not_found, found) = \
+ version_compare_many(self.version, self.version_reqs)
+ if not self.is_found:
+ found_msg += [mlog.red('NO'),
+ 'found {!r} but need:'.format(self.version),
+ ', '.join(["'{}'".format(e) for e in not_found])]
+ if found:
+ found_msg += ['; matched:',
+ ', '.join(["'{}'".format(e) for e in found])]
+ if not self.silent:
+ mlog.log(*found_msg)
+ if self.required:
+ m = 'Invalid version of dependency, need {!r} {!r} found {!r}.'
+ raise DependencyException(m.format(name, not_found, self.version))
+ return
+
+ found_msg += [mlog.green('YES'), self.version]
+
+ if self.pkg['targetFileName'].endswith('.a'):
+ self.static = True
+
+ self.compile_args = []
+ for flag in self.pkg['dflags']:
+ self.link_args.append(flag)
+ for path in self.pkg['importPaths']:
+ self.compile_args.append('-I' + os.path.join(self.pkg['path'], path))
+
+ self.link_args = []
+ for flag in self.pkg['lflags']:
+ self.link_args.append(flag)
+
+ search_paths = []
+ search_paths.append(os.path.join(self.pkg['path'], self.pkg['targetPath']))
+ found, res = self.__search_paths(search_paths, self.pkg['targetFileName'])
+ for file in res:
+ self.link_args.append(file)
+
+ if not found:
+ if self.required:
+ raise DependencyException('Dependency {!r} not found'.format(name))
+ self.is_found = False
+ mlog.log('Dependency', mlog.bold(name), 'found:', mlog.red('NO'))
+ return
+
+ if not self.silent:
+ mlog.log(*found_msg)
+
+ def get_compiler(self):
+ return self.compiler
+
+ def __search_paths(self, search_paths, target_file):
+ found = False
+ res = []
+ if target_file == '':
+ return True, res
+ for path in search_paths:
+ if os.path.isdir(path):
+ for file in os.listdir(path):
+ if file == target_file:
+ res.append(os.path.join(path, file))
+ found = True
+ return found, res
+
+ def _call_dubbin(self, args, env=None):
+ p, out = Popen_safe(self.dubbin.get_command() + args, env=env)[0:2]
+ return p.returncode, out.strip()
+
+ def check_dub(self):
+ dubbin = ExternalProgram('dub', silent=True)
+ if dubbin.found():
+ try:
+ p, out = Popen_safe(dubbin.get_command() + ['--version'])[0:2]
+ if p.returncode != 0:
+ mlog.warning('Found dub {!r} but couldn\'t run it'
+ ''.format(' '.join(dubbin.get_command())))
+ # Set to False instead of None to signify that we've already
+ # searched for it and not found it
+ dubbin = False
+ except (FileNotFoundError, PermissionError):
+ dubbin = False
+ else:
+ dubbin = False
+ if dubbin:
+ mlog.log('Found DUB:', mlog.bold(dubbin.get_path()),
+ '(%s)' % out.strip())
+ else:
+ mlog.log('Found DUB:', mlog.red('NO'))
+ return dubbin
+
+ @staticmethod
+ def get_methods():
+ return [DependencyMethods.PKGCONFIG, DependencyMethods.DUB]
class ExternalProgram:
windows_exts = ('exe', 'msc', 'com', 'bat', 'cmd')
@@ -1097,6 +1255,7 @@ def find_external_dependency(name, env, kwargs):
raise DependencyException('Keyword "required" must be a boolean.')
if not isinstance(kwargs.get('method', ''), str):
raise DependencyException('Keyword "method" must be a string.')
+ method = kwargs.get('method', '')
lname = name.lower()
if lname in packages:
if lname not in _packages_accept_language and 'language' in kwargs:
@@ -1113,6 +1272,11 @@ def find_external_dependency(name, env, kwargs):
if 'language' in kwargs:
# Remove check when PkgConfigDependency supports language.
raise DependencyException('%s dependency does not accept "language" keyword argument' % (lname, ))
+ if 'dub' == method:
+ dubdep = DubDependency(name, env, kwargs)
+ if required and not dubdep.found():
+ mlog.log('Dependency', mlog.bold(name), 'found:', mlog.red('NO'))
+ return dubdep
pkg_exc = None
pkgdep = None
try:
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index 0aa0b32..be92bfd 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -84,6 +84,8 @@ known_cpu_families = (
'parisc',
'ppc',
'ppc64',
+ 'riscv32',
+ 'riscv64',
'sparc64',
'x86',
'x86_64'
@@ -465,6 +467,8 @@ This is probably wrong, it should always point to the native compiler.''' % evar
C, C++, ObjC, ObjC++, Fortran, CS so consolidate it here.
'''
if self.is_cross_build() and want_cross:
+ if lang not in self.cross_info.config['binaries']:
+ raise EnvironmentException('{!r} compiler binary not defined in cross file'.format(lang))
compilers = mesonlib.stringlistify(self.cross_info.config['binaries'][lang])
# Ensure ccache exists and remove it if it doesn't
if compilers[0] == 'ccache':
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 2c54eae..cf2e826 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -92,12 +92,13 @@ class FeatureOptionHolder(InterpreterObject, ObjectHolder):
def auto_method(self, args, kwargs):
return self.held_object.is_auto()
-def extract_required_kwarg(kwargs):
+def extract_required_kwarg(kwargs, subproject):
val = kwargs.get('required', True)
disabled = False
required = False
feature = None
if isinstance(val, FeatureOptionHolder):
+ FeatureNew('User option "feature"', '0.47.0').use(subproject)
option = val.held_object
feature = val.name
if option.is_disabled():
@@ -376,6 +377,7 @@ class DependencyHolder(InterpreterObject, ObjectHolder):
self.methods.update({'found': self.found_method,
'type_name': self.type_name_method,
'version': self.version_method,
+ 'name': self.name_method,
'get_pkgconfig_variable': self.pkgconfig_method,
'get_configtool_variable': self.configtool_method,
'partial_dependency': self.partial_dependency_method,
@@ -398,6 +400,11 @@ class DependencyHolder(InterpreterObject, ObjectHolder):
def version_method(self, args, kwargs):
return self.held_object.get_version()
+ @noPosargs
+ @permittedKwargs({})
+ def name_method(self, args, kwargs):
+ return self.held_object.get_name()
+
@permittedKwargs({'define_variable'})
def pkgconfig_method(self, args, kwargs):
args = listify(args)
@@ -1418,7 +1425,7 @@ class CompilerHolder(InterpreterObject):
if not isinstance(libname, str):
raise InterpreterException('Library name not a string.')
- disabled, required, feature = extract_required_kwarg(kwargs)
+ disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
if disabled:
mlog.log('Library', mlog.bold(libname), 'skipped: feature', mlog.bold(feature), 'disabled')
lib = dependencies.ExternalLibrary(libname, None,
@@ -1868,6 +1875,7 @@ class Interpreter(InterpreterBase):
self.global_args_frozen = False # implies self.project_args_frozen
self.subprojects = {}
self.subproject_stack = []
+ self.configure_file_outputs = {}
# Passed from the outside, only used in subprojects.
if default_project_options:
self.default_project_options = default_project_options.copy()
@@ -2464,7 +2472,7 @@ external dependencies (including libraries) must go to "dependencies".''')
@permittedKwargs(permitted_kwargs['add_languages'])
@stringArgs
def func_add_languages(self, node, args, kwargs):
- disabled, required, feature = extract_required_kwarg(kwargs)
+ disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
if disabled:
for lang in sorted(args, key=compilers.sort_clink):
mlog.log('Compiler for language', mlog.bold(lang), 'skipped: feature', mlog.bold(feature), 'disabled')
@@ -2725,7 +2733,7 @@ external dependencies (including libraries) must go to "dependencies".''')
if not args:
raise InterpreterException('No program name specified.')
- disabled, required, feature = extract_required_kwarg(kwargs)
+ disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
if disabled:
mlog.log('Program', mlog.bold(' '.join(args)), 'skipped: feature', mlog.bold(feature), 'disabled')
return ExternalProgramHolder(dependencies.NonExistingExternalProgram())
@@ -2840,7 +2848,7 @@ external dependencies (including libraries) must go to "dependencies".''')
name = args[0]
display_name = name if name else '(anonymous)'
- disabled, required, feature = extract_required_kwarg(kwargs)
+ disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
if disabled:
mlog.log('Dependency', mlog.bold(display_name), 'skipped: feature', mlog.bold(feature), 'disabled')
return DependencyHolder(NotFoundDependency(self.environment), self.subproject)
@@ -3453,8 +3461,16 @@ root and issuing %s.
raise InterpreterException('@INPUT@ used as command argument, but no input file specified.')
# Validate output
output = kwargs['output']
+ ofile_rpath = os.path.join(self.subdir, output)
if not isinstance(output, str):
raise InterpreterException('Output file name must be a string')
+ if ofile_rpath in self.configure_file_outputs:
+ mesonbuildfile = os.path.join(self.subdir, 'meson.build')
+ current_call = "{}:{}".format(mesonbuildfile, self.current_lineno)
+ first_call = "{}:{}".format(mesonbuildfile, self.configure_file_outputs[ofile_rpath])
+ mlog.warning('Output file', mlog.bold(ofile_rpath, True), 'for configure_file() at', current_call, 'overwrites configure_file() output at', first_call)
+ else:
+ self.configure_file_outputs[ofile_rpath] = self.current_lineno
if ifile_abs:
values = mesonlib.get_filenames_templates_dict([ifile_abs], None)
outputs = mesonlib.substitute_values([output], values)
diff --git a/mesonbuild/modules/dlang.py b/mesonbuild/modules/dlang.py
new file mode 100644
index 0000000..d4f62e4
--- /dev/null
+++ b/mesonbuild/modules/dlang.py
@@ -0,0 +1,141 @@
+# Copyright 2018 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.
+
+# This file contains the detection logic for external dependencies that
+# are UI-related.
+
+import json
+import os
+
+from . import ExtensionModule
+
+from .. import mlog
+
+from ..mesonlib import (
+ Popen_safe, MesonException
+)
+
+from ..dependencies.base import (
+ ExternalProgram, DubDependency
+)
+
+from ..interpreter import DependencyHolder
+
+class DlangModule(ExtensionModule):
+ class_dubbin = None
+ init_dub = False
+
+ def __init__(self, interpreter):
+ super().__init__(interpreter)
+ self.snippets.add('generate_dub_file')
+
+ def _init_dub(self):
+ if DlangModule.class_dubbin is None:
+ self.dubbin = DubDependency.class_dubbin
+ DlangModule.class_dubbin = self.dubbin
+ else:
+ self.dubbin = DlangModule.class_dubbin
+
+ if DlangModule.class_dubbin is None:
+ self.dubbin = self.check_dub()
+ DlangModule.class_dubbin = self.dubbin
+ else:
+ self.dubbin = DlangModule.class_dubbin
+
+ if not self.dubbin:
+ if not self.dubbin:
+ raise MesonException('DUB not found.')
+
+ def generate_dub_file(self, interpreter, state, args, kwargs):
+ if not DlangModule.init_dub:
+ self._init_dub()
+
+ if len(args) < 2:
+ raise MesonException('Missing arguments')
+
+ config = {
+ 'name': args[0]
+ }
+
+ config_path = os.path.join(args[1], 'dub.json')
+ if os.path.exists(config_path):
+ with open(config_path, 'r', encoding='utf8') as ofile:
+ try:
+ config = json.load(ofile)
+ except ValueError:
+ mlog.warning('Failed to load the data in dub.json')
+
+ warn_publishing = ['description', 'license']
+ for arg in warn_publishing:
+ if arg not in kwargs and \
+ arg not in config:
+ mlog.warning('Without', mlog.bold(arg), 'the DUB package can\'t be published')
+
+ for key, value in kwargs.items():
+ if key == 'dependencies':
+ config[key] = {}
+ if isinstance(value, list):
+ for dep in value:
+ if isinstance(dep, DependencyHolder):
+ name = dep.method_call('name', [], [])
+ ret, res = self._call_dubbin(['describe', name])
+ if ret == 0:
+ version = dep.method_call('version', [], [])
+ if version is None:
+ config[key][name] = ''
+ else:
+ config[key][name] = version
+ elif isinstance(value, DependencyHolder):
+ name = value.method_call('name', [], [])
+ ret, res = self._call_dubbin(['describe', name])
+ if ret == 0:
+ version = value.method_call('version', [], [])
+ if version is None:
+ config[key][name] = ''
+ else:
+ config[key][name] = version
+ else:
+ config[key] = value
+
+ with open(config_path, 'w', encoding='utf8') as ofile:
+ ofile.write(json.dumps(config, indent=4, ensure_ascii=False))
+
+ def _call_dubbin(self, args, env=None):
+ p, out = Popen_safe(self.dubbin.get_command() + args, env=env)[0:2]
+ return p.returncode, out.strip()
+
+ def check_dub(self):
+ dubbin = ExternalProgram('dub', silent=True)
+ if dubbin.found():
+ try:
+ p, out = Popen_safe(dubbin.get_command() + ['--version'])[0:2]
+ if p.returncode != 0:
+ mlog.warning('Found dub {!r} but couldn\'t run it'
+ ''.format(' '.join(dubbin.get_command())))
+ # Set to False instead of None to signify that we've already
+ # searched for it and not found it
+ dubbin = False
+ except (FileNotFoundError, PermissionError):
+ dubbin = False
+ else:
+ dubbin = False
+ if dubbin:
+ mlog.log('Found DUB:', mlog.bold(dubbin.get_path()),
+ '(%s)' % out.strip())
+ else:
+ mlog.log('Found DUB:', mlog.red('NO'))
+ return dubbin
+
+def initialize(*args, **kwargs):
+ return DlangModule(*args, **kwargs)