aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild')
-rw-r--r--mesonbuild/coredata.py76
-rw-r--r--mesonbuild/dependencies/base.py15
-rw-r--r--mesonbuild/dependencies/ui.py66
-rw-r--r--mesonbuild/environment.py35
-rw-r--r--mesonbuild/interpreter.py21
-rw-r--r--mesonbuild/modules/python.py4
-rw-r--r--mesonbuild/modules/python3.py5
-rw-r--r--mesonbuild/modules/qt.py9
-rw-r--r--mesonbuild/modules/qt4.py7
-rw-r--r--mesonbuild/modules/qt5.py7
-rw-r--r--mesonbuild/modules/windows.py11
-rw-r--r--mesonbuild/msetup.py4
12 files changed, 209 insertions, 51 deletions
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index 245741a..ae37576 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -183,6 +183,7 @@ class UserArrayOption(UserOption):
', '.join(bad), ', '.join(self.choices)))
return newvalue
+
class UserFeatureOption(UserComboOption):
static_choices = ['enabled', 'disabled', 'auto']
@@ -198,6 +199,72 @@ class UserFeatureOption(UserComboOption):
def is_auto(self):
return self.value == 'auto'
+
+def load_configs(filenames):
+ """Load native files."""
+ def gen():
+ for f in filenames:
+ f = os.path.expanduser(os.path.expandvars(f))
+ if os.path.exists(f):
+ yield f
+ continue
+ elif sys.platform != 'win32':
+ f = os.path.basename(f)
+ paths = [
+ os.environ.get('XDG_DATA_HOME', os.path.expanduser('~/.local/share')),
+ ] + os.environ.get('XDG_DATA_DIRS', '/usr/local/share:/usr/share').split(':')
+ for path in paths:
+ path_to_try = os.path.join(path, 'meson', 'native', f)
+ if os.path.isfile(path_to_try):
+ yield path_to_try
+ break
+ else:
+ raise MesonException('Cannot find specified native file: ' + f)
+ continue
+
+ raise MesonException('Cannot find specified native file: ' + f)
+
+ config = configparser.SafeConfigParser()
+ config.read(gen())
+ return config
+
+
+def _get_section(config, section):
+ if config.has_section(section):
+ final = {}
+ for k, v in config.items(section):
+ # Windows paths...
+ v = v.replace('\\', '\\\\')
+ try:
+ final[k] = ast.literal_eval(v)
+ except SyntaxError:
+ raise MesonException(
+ 'Malformed value in native file variable: {}'.format(v))
+ return final
+ return {}
+
+
+class ConfigData:
+
+ """Contains configuration information provided by the user for the build."""
+
+ def __init__(self, config=None):
+ if config:
+ self.binaries = _get_section(config, 'binaries')
+ # global is a keyword and globals is a builtin, rather than mangle it,
+ # use a similar word
+ self.universal = _get_section(config, 'globals')
+ self.subprojects = {s: _get_section(config, s) for s in config.sections()
+ if s not in {'binaries', 'globals'}}
+ else:
+ self.binaries = {}
+ self.universal = {}
+ self.subprojects = {}
+
+ def get_binaries(self, name):
+ return self.binaries.get(name, None)
+
+
# This class contains all data that must persist over multiple
# invocations of Meson. It is roughly the same thing as
# cmakecache.
@@ -229,6 +296,15 @@ class CoreData:
self.deps = OrderedDict()
# Only to print a warning if it changes between Meson invocations.
self.pkgconf_envvar = os.environ.get('PKG_CONFIG_PATH', '')
+ self.config_files = self.__load_config_files(options.native_file)
+
+ @staticmethod
+ def __load_config_files(filenames):
+ if not filenames:
+ return []
+ filenames = [os.path.abspath(os.path.expanduser(os.path.expanduser(f)))
+ for f in filenames]
+ return filenames
@staticmethod
def __load_cross_file(filename):
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py
index e67f4c0..b1d79bb 100644
--- a/mesonbuild/dependencies/base.py
+++ b/mesonbuild/dependencies/base.py
@@ -399,6 +399,8 @@ class ConfigToolDependency(ExternalDependency):
'Falling back to searching PATH. This may find a '
'native version of {0}!'.format(self.tool_name))
tools = self.tools
+ elif self.tool_name in self.env.config_info.binaries:
+ tools = [self.env.config_info.binaries[self.tool_name]]
else:
tools = self.tools
@@ -500,7 +502,8 @@ class PkgConfigDependency(ExternalDependency):
if self.required:
raise DependencyException('Pkg-config binary missing from cross file')
else:
- potential_pkgbin = ExternalProgram.from_cross_info(environment.cross_info, 'pkgconfig')
+ potential_pkgbin = ExternalProgram.from_bin_list(
+ environment.cross_info.config['binaries'], 'pkgconfig')
if potential_pkgbin.found():
self.pkgbin = potential_pkgbin
else:
@@ -1076,10 +1079,10 @@ class ExternalProgram:
return ' '.join(self.command)
@staticmethod
- def from_cross_info(cross_info, name):
- if name not in cross_info.config['binaries']:
+ def from_bin_list(bins, name):
+ if name not in bins:
return NonExistingExternalProgram()
- command = cross_info.config['binaries'][name]
+ command = bins[name]
if not isinstance(command, (list, str)):
raise MesonException('Invalid type {!r} for binary {!r} in cross file'
''.format(command, name))
@@ -1238,8 +1241,8 @@ class ExternalProgram:
class NonExistingExternalProgram(ExternalProgram):
"A program that will never exist"
- def __init__(self):
- self.name = 'nonexistingprogram'
+ def __init__(self, name='nonexistingprogram'):
+ self.name = name
self.command = [None]
self.path = None
diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py
index e3b371d..b589889 100644
--- a/mesonbuild/dependencies/ui.py
+++ b/mesonbuild/dependencies/ui.py
@@ -30,7 +30,7 @@ from ..mesonlib import (
from ..environment import detect_cpu
from .base import DependencyException, DependencyMethods
-from .base import ExternalDependency, ExternalProgram
+from .base import ExternalDependency, ExternalProgram, NonExistingExternalProgram
from .base import ExtraFrameworkDependency, PkgConfigDependency
from .base import ConfigToolDependency
@@ -230,21 +230,46 @@ class QtBaseDependency(ExternalDependency):
self.from_text = mlog.format_list(methods)
self.version = None
- def compilers_detect(self):
+ def compilers_detect(self, interp_obj):
"Detect Qt (4 or 5) moc, uic, rcc in the specified bindir or in PATH"
- if self.bindir or for_windows(self.env.is_cross_build(), self.env):
- moc = ExternalProgram(os.path.join(self.bindir, 'moc'), silent=True)
- uic = ExternalProgram(os.path.join(self.bindir, 'uic'), silent=True)
- rcc = ExternalProgram(os.path.join(self.bindir, 'rcc'), silent=True)
- lrelease = ExternalProgram(os.path.join(self.bindir, 'lrelease'), silent=True)
- else:
- # We don't accept unsuffixed 'moc', 'uic', and 'rcc' because they
- # are sometimes older, or newer versions.
- moc = ExternalProgram('moc-' + self.name, silent=True)
- uic = ExternalProgram('uic-' + self.name, silent=True)
- rcc = ExternalProgram('rcc-' + self.name, silent=True)
- lrelease = ExternalProgram('lrelease-' + self.name, silent=True)
- return moc, uic, rcc, lrelease
+ # It is important that this list does not change order as the order of
+ # the returned ExternalPrograms will change as well
+ bins = ['moc', 'uic', 'rcc', 'lrelease']
+ found = {b: NonExistingExternalProgram(name='{}-{}'.format(b, self.name))
+ for b in bins}
+
+ def gen_bins():
+ for b in bins:
+ yield '{}-{}'.format(b, self.name), b, False
+ yield b, b, self.required
+
+ for b, name, required in gen_bins():
+ if found[name].found():
+ continue
+
+ # prefer the <tool>-qt<version> of the tool to the plain one, as we
+ # don't know what the unsuffixed one points to without calling it.
+ p = interp_obj.find_program_impl([b], silent=True, required=required).held_object
+ if not p.found():
+ continue
+
+ if b.startswith('lrelease'):
+ arg = ['-version']
+ elif mesonlib.version_compare(self.version, '>= 5'):
+ arg = ['--version']
+ else:
+ arg = ['-v']
+
+ # Ensure that the version of qt and each tool are the same
+ _, out, err = mesonlib.Popen_safe(p.get_command() + arg)
+ if b.startswith('lrelease') or not self.version.startswith('4'):
+ care = out
+ else:
+ care = err
+ if mesonlib.version_compare(self.version, '== {}'.format(care.split(' ')[-1])):
+ found[name] = p
+
+ return tuple([found[b] for b in bins])
def _pkgconfig_detect(self, mods, kwargs):
# We set the value of required to False so that we can try the
@@ -302,8 +327,15 @@ class QtBaseDependency(ExternalDependency):
def _find_qmake(self, qmake):
# Even when cross-compiling, if a cross-info qmake is not specified, we
# fallback to using the qmake in PATH because that's what we used to do
- if self.env.is_cross_build() and 'qmake' in self.env.cross_info.config['binaries']:
- return ExternalProgram.from_cross_info(self.env.cross_info, 'qmake')
+ if self.env.is_cross_build():
+ if 'qmake' in self.env.cross_info.config['binaries']:
+ return ExternalProgram.from_bin_list(self.env.cross_info.config['binaries'], 'qmake')
+ elif self.env.config_info:
+ # Prefer suffixed to unsuffixed version
+ p = ExternalProgram.from_bin_list(self.env.config_info.binaries, 'qmake-' + self.name)
+ if p.found():
+ return p
+ return ExternalProgram.from_bin_list(self.env.config_info.binaries, 'qmake')
return ExternalProgram(qmake, silent=True)
def _qmake_detect(self, mods, kwargs):
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index 80917ed..01a7c51 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -340,7 +340,8 @@ class Environment:
self.cross_info = CrossBuildInfo(self.coredata.cross_file)
if 'exe_wrapper' in self.cross_info.config['binaries']:
from .dependencies import ExternalProgram
- self.exe_wrapper = ExternalProgram.from_cross_info(self.cross_info, 'exe_wrapper')
+ self.exe_wrapper = ExternalProgram.from_bin_list(
+ self.cross_info.config['binaries'], 'exe_wrapper')
if 'host_machine' in self.cross_info.config:
self.machines.host = MachineInfo.from_literal(
self.cross_info.config['host_machine'])
@@ -351,6 +352,12 @@ class Environment:
self.cross_info = None
self.machines.default_missing()
+ if self.coredata.config_files:
+ self.config_info = coredata.ConfigData(
+ coredata.load_configs(self.coredata.config_files))
+ else:
+ self.config_info = coredata.ConfigData()
+
self.cmd_line_options = options.cmd_line_options.copy()
# List of potential compilers.
@@ -505,7 +512,10 @@ class Environment:
The list of compilers is detected in the exact same way for
C, C++, ObjC, ObjC++, Fortran, CS so consolidate it here.
'''
+ is_cross = False
+ exe_wrap = None
evar = BinaryTable.evarMap[lang]
+
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))
@@ -521,13 +531,13 @@ class Environment:
shlex.split(os.environ[evar]))
# Return value has to be a list of compiler 'choices'
compilers = [compilers]
- is_cross = False
- exe_wrap = None
+ elif lang in self.config_info.binaries:
+ compilers, ccache = BinaryTable.parse_entry(
+ mesonlib.stringlistify(self.config_info.binaries[lang]))
+ compilers = [compilers]
else:
compilers = getattr(self, 'default_' + lang)
ccache = BinaryTable.detect_ccache()
- is_cross = False
- exe_wrap = None
return compilers, ccache, is_cross, exe_wrap
def _handle_exceptions(self, exceptions, binaries, bintype='compiler'):
@@ -798,7 +808,11 @@ class Environment:
self._handle_exceptions(popen_exceptions, compilers)
def detect_java_compiler(self):
- exelist = ['javac']
+ if 'java' in self.config_info.binaries:
+ exelist = mesonlib.stringlistify(self.config_info.binaries['java'])
+ else:
+ exelist = ['javac']
+
try:
p, out, err = Popen_safe(exelist + ['-version'])
except OSError:
@@ -831,6 +845,8 @@ class Environment:
def detect_vala_compiler(self):
if 'VALAC' in os.environ:
exelist = shlex.split(os.environ['VALAC'])
+ elif 'vala' in self.config_info.binaries:
+ exelist = mesonlib.stringlistify(self.config_info.binaries['vala'])
else:
exelist = ['valac']
try:
@@ -875,6 +891,8 @@ class Environment:
elif self.is_cross_build() and want_cross:
exelist = mesonlib.stringlistify(self.cross_info.config['binaries']['d'])
is_cross = True
+ elif 'd' in self.config_info.binaries:
+ exelist = mesonlib.stringlistify(self.config_info.binaries['d'])
elif shutil.which("ldc2"):
exelist = ['ldc2']
elif shutil.which("ldc"):
@@ -912,7 +930,10 @@ class Environment:
raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
def detect_swift_compiler(self):
- exelist = ['swiftc']
+ if 'swift' in self.config_info.binaries:
+ exelist = mesonlib.stringlistify(self.config_info.binaries['swift'])
+ else:
+ exelist = ['swiftc']
try:
p, _, err = Popen_safe(exelist + ['-v'])
except OSError:
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index d7a5b66..e820afb 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -2748,8 +2748,7 @@ external dependencies (including libraries) must go to "dependencies".''')
self.coredata. base_options[optname] = oobj
self.emit_base_options_warnings(enabled_opts)
- def program_from_cross_file(self, prognames, silent=False):
- cross_info = self.environment.cross_info
+ def _program_from_file(self, prognames, bins, silent):
for p in prognames:
if hasattr(p, 'held_object'):
p = p.held_object
@@ -2757,11 +2756,19 @@ external dependencies (including libraries) must go to "dependencies".''')
continue # Always points to a local (i.e. self generated) file.
if not isinstance(p, str):
raise InterpreterException('Executable name must be a string')
- prog = ExternalProgram.from_cross_info(cross_info, p)
+ prog = ExternalProgram.from_bin_list(bins, p)
if prog.found():
return ExternalProgramHolder(prog)
return None
+ def program_from_cross_file(self, prognames, silent=False):
+ bins = self.environment.cross_info.config['binaries']
+ return self._program_from_file(prognames, bins, silent)
+
+ def program_from_config_file(self, prognames, silent=False):
+ bins = self.environment.config_info.binaries
+ return self._program_from_file(prognames, bins, silent)
+
def program_from_system(self, args, silent=False):
# Search for scripts relative to current subdir.
# Do not cache found programs because find_program('foobar')
@@ -2816,10 +2823,14 @@ external dependencies (including libraries) must go to "dependencies".''')
def find_program_impl(self, args, native=False, required=True, silent=True):
if not isinstance(args, list):
args = [args]
+
progobj = self.program_from_overrides(args, silent=silent)
- if progobj is None and self.build.environment.is_cross_build():
- if not native:
+ if progobj is None:
+ if self.build.environment.is_cross_build() and not native:
progobj = self.program_from_cross_file(args, silent=silent)
+ else:
+ progobj = self.program_from_config_file(args, silent=silent)
+
if progobj is None:
progobj = self.program_from_system(args, silent=silent)
if required and (progobj is None or not progobj.found()):
diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py
index 954220b..3b2bf07 100644
--- a/mesonbuild/modules/python.py
+++ b/mesonbuild/modules/python.py
@@ -480,7 +480,9 @@ class PythonModule(ExtensionModule):
if len(args) > 1:
raise InvalidArguments('find_installation takes zero or one positional argument.')
- if args:
+ if 'python' in state.environment.config_info.binaries:
+ name_or_path = state.environment.config_info.binaries['python']
+ elif args:
name_or_path = args[0]
if not isinstance(name_or_path, str):
raise InvalidArguments('find_installation argument must be a string.')
diff --git a/mesonbuild/modules/python3.py b/mesonbuild/modules/python3.py
index 5bda5ab..f664632 100644
--- a/mesonbuild/modules/python3.py
+++ b/mesonbuild/modules/python3.py
@@ -48,7 +48,10 @@ class Python3Module(ExtensionModule):
@noKwargs
def find_python(self, state, args, kwargs):
- py3 = dependencies.ExternalProgram('python3', mesonlib.python_command, silent=True)
+ options = [state.environment.config_info.binaries.get('python3')]
+ if not options[0]: # because this would be [None]
+ options = ['python3', mesonlib.python_command]
+ py3 = dependencies.ExternalProgram(*options, silent=True)
return ModuleReturnValue(py3, [py3])
@noKwargs
diff --git a/mesonbuild/modules/qt.py b/mesonbuild/modules/qt.py
index 7a2c338..367b15b 100644
--- a/mesonbuild/modules/qt.py
+++ b/mesonbuild/modules/qt.py
@@ -18,7 +18,7 @@ from .. import build
from ..mesonlib import MesonException, Popen_safe, extract_as_list, File
from ..dependencies import Dependency, Qt4Dependency, Qt5Dependency
import xml.etree.ElementTree as ET
-from . import ModuleReturnValue, get_include_args
+from . import ModuleReturnValue, get_include_args, ExtensionModule
from ..interpreterbase import permittedKwargs, FeatureNewKwargs
_QT_DEPS_LUT = {
@@ -27,10 +27,11 @@ _QT_DEPS_LUT = {
}
-class QtBaseModule:
+class QtBaseModule(ExtensionModule):
tools_detected = False
- def __init__(self, qt_version=5):
+ def __init__(self, interpreter, qt_version=5):
+ ExtensionModule.__init__(self, interpreter)
self.qt_version = qt_version
def _detect_tools(self, env, method):
@@ -43,7 +44,7 @@ class QtBaseModule:
kwargs = {'required': 'true', 'modules': 'Core', 'silent': 'true', 'method': method}
qt = _QT_DEPS_LUT[self.qt_version](env, kwargs)
# Get all tools and then make sure that they are the right version
- self.moc, self.uic, self.rcc, self.lrelease = qt.compilers_detect()
+ self.moc, self.uic, self.rcc, self.lrelease = qt.compilers_detect(self.interpreter)
# Moc, uic and rcc write their version strings to stderr.
# Moc and rcc return a non-zero result when doing so.
# What kind of an idiot thought that was a good idea?
diff --git a/mesonbuild/modules/qt4.py b/mesonbuild/modules/qt4.py
index 29992d5..112e3e4 100644
--- a/mesonbuild/modules/qt4.py
+++ b/mesonbuild/modules/qt4.py
@@ -14,14 +14,13 @@
from .. import mlog
from .qt import QtBaseModule
-from . import ExtensionModule
-class Qt4Module(ExtensionModule, QtBaseModule):
+class Qt4Module(QtBaseModule):
def __init__(self, interpreter):
- QtBaseModule.__init__(self, qt_version=4)
- ExtensionModule.__init__(self, interpreter)
+ QtBaseModule.__init__(self, interpreter, qt_version=4)
+
def initialize(*args, **kwargs):
mlog.warning('rcc dependencies will not work properly until this upstream issue is fixed:',
diff --git a/mesonbuild/modules/qt5.py b/mesonbuild/modules/qt5.py
index 19623ac..96a7964 100644
--- a/mesonbuild/modules/qt5.py
+++ b/mesonbuild/modules/qt5.py
@@ -14,14 +14,13 @@
from .. import mlog
from .qt import QtBaseModule
-from . import ExtensionModule
-class Qt5Module(ExtensionModule, QtBaseModule):
+class Qt5Module(QtBaseModule):
def __init__(self, interpreter):
- QtBaseModule.__init__(self, qt_version=5)
- ExtensionModule.__init__(self, interpreter)
+ QtBaseModule.__init__(self, interpreter, qt_version=5)
+
def initialize(*args, **kwargs):
mlog.warning('rcc dependencies will not work reliably until this upstream issue is fixed:',
diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py
index 4d0f244..d185d89 100644
--- a/mesonbuild/modules/windows.py
+++ b/mesonbuild/modules/windows.py
@@ -49,8 +49,8 @@ class WindowsModule(ExtensionModule):
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.
- cross_info = state.environment.cross_info
- rescomp = ExternalProgram.from_cross_info(cross_info, 'windres')
+ bins = state.environment.cross_info.config['binaries']
+ rescomp = ExternalProgram.from_bin_list(bins, 'windres')
if not rescomp or not rescomp.found():
if 'WINDRES' in os.environ:
@@ -59,6 +59,13 @@ class WindowsModule(ExtensionModule):
rescomp = ExternalProgram('windres', command=os.environ.get('WINDRES'), silent=True)
if not rescomp or not rescomp.found():
+ # Take windres from the config file after the environment, which is
+ # in keeping with the expectations on unix-like OSes that
+ # environment variables trump config files.
+ bins = state.environment.config_info.binaries
+ rescomp = ExternalProgram.from_bin_list(bins, 'windres')
+
+ if not rescomp or not rescomp.found():
comp = self.detect_compiler(state.compilers)
if comp.id == 'msvc' or comp.id == 'clang-cl':
rescomp = ExternalProgram('rc', silent=True)
diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py
index ce03f43..f9a5e1c 100644
--- a/mesonbuild/msetup.py
+++ b/mesonbuild/msetup.py
@@ -29,6 +29,10 @@ def add_arguments(parser):
coredata.register_builtin_arguments(parser)
parser.add_argument('--cross-file', default=None,
help='File describing cross compilation environment.')
+ parser.add_argument('--native-file',
+ default=[],
+ action='append',
+ help='File containing overrides for native compilation environment.')
parser.add_argument('-v', '--version', action='version',
version=coredata.version)
parser.add_argument('--profile-self', action='store_true', dest='profile',