aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mesonbuild/coredata.py260
-rw-r--r--mesonbuild/dependencies/base.py14
-rw-r--r--mesonbuild/msetup.py8
-rw-r--r--mesonbuild/optinterpreter.py9
-rwxr-xr-xrun_cross_test.py2
-rwxr-xr-xrun_unittests.py12
-rw-r--r--test cases/unit/55 pkg_config_path option/extra_path/totally_made_up_dep.pc7
-rw-r--r--test cases/unit/55 pkg_config_path option/meson.build3
8 files changed, 176 insertions, 139 deletions
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index 01db635..739f6e7 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -26,6 +26,7 @@ from .wrap import WrapMode
import ast
import argparse
import configparser
+from typing import Optional, Any, TypeVar, Generic, Type
version = '0.50.999'
backendlist = ['ninja', 'vs', 'vs2010', 'vs2015', 'vs2017', 'xcode']
@@ -269,7 +270,6 @@ class CoreData:
self.cross_compilers = OrderedDict()
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)
self.libdir_cross_fixup()
@@ -335,11 +335,10 @@ class CoreData:
def init_builtins(self):
# Create builtin options with default values
self.builtins = {}
- prefix = get_builtin_option_default('prefix')
- for key in get_builtin_options():
- value = get_builtin_option_default(key, prefix)
- args = [key] + builtin_options[key][1:-1] + [value]
- self.builtins[key] = builtin_options[key][0](*args)
+ for key, opt in builtin_options.items():
+ self.builtins[key] = opt.init_option(key)
+ if opt.separate_cross:
+ self.builtins['cross_' + key] = opt.init_option(key)
def init_backend_options(self, backend_name):
if backend_name == 'ninja':
@@ -462,7 +461,7 @@ class CoreData:
self.builtins['prefix'].set_value(prefix)
for key in builtin_dir_noprefix_options:
if key not in options:
- self.builtins[key].set_value(get_builtin_option_default(key, prefix))
+ self.builtins[key].set_value(builtin_options[key].prefixed_default(key, prefix))
unknown_options = []
for k, v in options.items():
@@ -495,7 +494,7 @@ class CoreData:
from . import optinterpreter
for k, v in default_options.items():
if subproject:
- if optinterpreter.is_invalid_name(k):
+ if optinterpreter.is_invalid_name(k, log=False):
continue
k = subproject + ':' + k
env.cmd_line_options.setdefault(k, v)
@@ -506,14 +505,20 @@ class CoreData:
# languages and setting the backend (builtin options must be set first
# to know which backend we'll use).
options = {}
+
+ # Some options default to environment variables if they are
+ # unset, set those now. These will either be overwritten
+ # below, or they won't.
+ options['pkg_config_path'] = os.environ.get('PKG_CONFIG_PATH', '').split(':')
+
for k, v in env.cmd_line_options.items():
if subproject:
if not k.startswith(subproject + ':'):
continue
- elif k not in get_builtin_options():
+ elif k not in builtin_options:
if ':' in k:
continue
- if optinterpreter.is_invalid_name(k):
+ if optinterpreter.is_invalid_name(k, log=False):
continue
options[k] = v
@@ -656,80 +661,10 @@ def save(obj, build_dir):
os.replace(tempfilename, filename)
return filename
-def get_builtin_options():
- return list(builtin_options.keys())
-
-def is_builtin_option(optname):
- return optname in get_builtin_options()
-
-def get_builtin_option_choices(optname):
- if is_builtin_option(optname):
- if builtin_options[optname][0] == UserComboOption:
- return builtin_options[optname][2]
- elif builtin_options[optname][0] == UserBooleanOption:
- return [True, False]
- elif builtin_options[optname][0] == UserFeatureOption:
- return UserFeatureOption.static_choices
- else:
- return None
- else:
- raise RuntimeError('Tried to get the supported values for an unknown builtin option \'%s\'.' % optname)
-
-def get_builtin_option_description(optname):
- if is_builtin_option(optname):
- return builtin_options[optname][1]
- else:
- raise RuntimeError('Tried to get the description for an unknown builtin option \'%s\'.' % optname)
-
-def get_builtin_option_action(optname):
- default = builtin_options[optname][2]
- if default is True:
- return 'store_false'
- elif default is False:
- return 'store_true'
- return None
-
-def get_builtin_option_default(optname, prefix=''):
- if is_builtin_option(optname):
- o = builtin_options[optname]
- if o[0] == UserComboOption:
- return o[3]
- if o[0] == UserIntegerOption:
- return o[4]
- try:
- return builtin_dir_noprefix_options[optname][prefix]
- except KeyError:
- pass
- return o[2]
- else:
- raise RuntimeError('Tried to get the default value for an unknown builtin option \'%s\'.' % optname)
-
-def get_builtin_option_cmdline_name(name):
- if name == 'warning_level':
- return '--warnlevel'
- else:
- return '--' + name.replace('_', '-')
-
-def add_builtin_argument(p, name):
- kwargs = {}
- c = get_builtin_option_choices(name)
- b = get_builtin_option_action(name)
- h = get_builtin_option_description(name)
- if not b:
- h = h.rstrip('.') + ' (default: %s).' % get_builtin_option_default(name)
- else:
- kwargs['action'] = b
- if c and not b:
- kwargs['choices'] = c
- kwargs['default'] = argparse.SUPPRESS
- kwargs['dest'] = name
-
- cmdline_name = get_builtin_option_cmdline_name(name)
- p.add_argument(cmdline_name, help=h, **kwargs)
def register_builtin_arguments(parser):
- for n in builtin_options:
- add_builtin_argument(parser, n)
+ for n, b in builtin_options.items():
+ b.add_to_argparse(n, parser)
parser.add_argument('-D', action='append', dest='projectoptions', default=[], metavar="option",
help='Set the value of an option, can be used several times to set multiple options.')
@@ -747,48 +682,129 @@ def parse_cmd_line_options(args):
args.cmd_line_options = create_options_dict(args.projectoptions)
# Merge builtin options set with --option into the dict.
- for name in builtin_options:
- value = getattr(args, name, None)
- if value is not None:
- if name in args.cmd_line_options:
- cmdline_name = get_builtin_option_cmdline_name(name)
- raise MesonException(
- 'Got argument {0} as both -D{0} and {1}. Pick one.'.format(name, cmdline_name))
- args.cmd_line_options[name] = value
- delattr(args, name)
+ for name, builtin in builtin_options.items():
+ names = [name]
+ if builtin.separate_cross:
+ names.append('cross_' + name)
+ for name in names:
+ value = getattr(args, name, None)
+ if value is not None:
+ if name in args.cmd_line_options:
+ cmdline_name = BuiltinOption.argparse_name_to_arg(name)
+ raise MesonException(
+ 'Got argument {0} as both -D{0} and {1}. Pick one.'.format(name, cmdline_name))
+ args.cmd_line_options[name] = value
+ delattr(args, name)
+
+
+_U = TypeVar('_U', bound=UserOption)
+
+class BuiltinOption(Generic[_U]):
+
+ """Class for a builtin option type.
+
+ Currently doesn't support UserIntegerOption, or a few other cases.
+ """
+
+ def __init__(self, opt_type: Type[_U], description: str, default: Any, yielding: Optional[bool] = None, *,
+ choices: Any = None, separate_cross: bool = False):
+ self.opt_type = opt_type
+ self.description = description
+ self.default = default
+ self.choices = choices
+ self.yielding = yielding
+ self.separate_cross = separate_cross
+
+ def init_option(self, name: str) -> _U:
+ """Create an instance of opt_type and return it."""
+ keywords = {'yielding': self.yielding, 'value': self.default}
+ if self.choices:
+ keywords['choices'] = self.choices
+ return self.opt_type(name, self.description, **keywords)
+
+ def _argparse_action(self) -> Optional[str]:
+ if self.default is True:
+ return 'store_false'
+ elif self.default is False:
+ return 'store_true'
+ return None
+
+ def _argparse_choices(self) -> Any:
+ if self.opt_type is UserBooleanOption:
+ return [True, False]
+ elif self.opt_type is UserFeatureOption:
+ return UserFeatureOption.static_choices
+ return self.choices
+
+ @staticmethod
+ def argparse_name_to_arg(name: str) -> str:
+ if name == 'warning_level':
+ return '--warnlevel'
+ else:
+ return '--' + name.replace('_', '-')
+
+ def prefixed_default(self, name: str, prefix: str = '') -> Any:
+ if self.opt_type in [UserComboOption, UserIntegerOption]:
+ return self.default
+ try:
+ return builtin_dir_noprefix_options[name][prefix]
+ except KeyError:
+ pass
+ return self.default
+
+ def add_to_argparse(self, name: str, parser: argparse.ArgumentParser) -> None:
+ kwargs = {}
+
+ c = self._argparse_choices()
+ b = self._argparse_action()
+ h = self.description
+ if not b:
+ h = '{} (default: {}).'.format(h.rstrip('.'), self.prefixed_default(name))
+ else:
+ kwargs['action'] = b
+ if c and not b:
+ kwargs['choices'] = c
+ kwargs['default'] = argparse.SUPPRESS
+ kwargs['dest'] = name
+
+ cmdline_name = self.argparse_name_to_arg(name)
+ parser.add_argument(cmdline_name, help=h, **kwargs)
+ if self.separate_cross:
+ kwargs['dest'] = 'cross_' + name
+ parser.add_argument(self.argparse_name_to_arg('cross_' + name), help=h + ' (for host in cross compiles)', **kwargs)
+
builtin_options = {
- 'buildtype': [UserComboOption, 'Build type to use', ['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom'], 'debug'],
- 'strip': [UserBooleanOption, 'Strip targets on install', False],
- 'unity': [UserComboOption, 'Unity build', ['on', 'off', 'subprojects'], 'off'],
- 'prefix': [UserStringOption, 'Installation prefix', default_prefix()],
- 'libdir': [UserStringOption, 'Library directory', default_libdir()],
- 'libexecdir': [UserStringOption, 'Library executable directory', default_libexecdir()],
- 'bindir': [UserStringOption, 'Executable directory', 'bin'],
- 'sbindir': [UserStringOption, 'System executable directory', 'sbin'],
- 'includedir': [UserStringOption, 'Header file directory', 'include'],
- 'datadir': [UserStringOption, 'Data file directory', 'share'],
- 'mandir': [UserStringOption, 'Manual page directory', 'share/man'],
- 'infodir': [UserStringOption, 'Info page directory', 'share/info'],
- 'localedir': [UserStringOption, 'Locale data directory', 'share/locale'],
- 'sysconfdir': [UserStringOption, 'Sysconf data directory', 'etc'],
- 'localstatedir': [UserStringOption, 'Localstate data directory', 'var'],
- 'sharedstatedir': [UserStringOption, 'Architecture-independent data directory', 'com'],
- 'werror': [UserBooleanOption, 'Treat warnings as errors', False],
- 'warning_level': [UserComboOption, 'Compiler warning level to use', ['0', '1', '2', '3'], '1'],
- 'layout': [UserComboOption, 'Build directory layout', ['mirror', 'flat'], 'mirror'],
- 'default_library': [UserComboOption, 'Default library type', ['shared', 'static', 'both'], 'shared'],
- 'backend': [UserComboOption, 'Backend to use', backendlist, 'ninja'],
- 'stdsplit': [UserBooleanOption, 'Split stdout and stderr in test logs', True],
- 'errorlogs': [UserBooleanOption, "Whether to print the logs from failing tests", True],
- 'install_umask': [UserUmaskOption, 'Default umask to apply on permissions of installed files', '022'],
- 'auto_features': [UserFeatureOption, "Override value of all 'auto' features", 'auto'],
- 'optimization': [UserComboOption, 'Optimization level', ['0', 'g', '1', '2', '3', 's'], '0'],
- 'debug': [UserBooleanOption, 'Debug', True],
- 'wrap_mode': [UserComboOption, 'Wrap mode', ['default',
- 'nofallback',
- 'nodownload',
- 'forcefallback'], 'default'],
+ 'buildtype': BuiltinOption(UserComboOption, 'Build type to use', 'debug',
+ choices=['plain', 'debug', 'debugoptimized', 'release', 'minsize', 'custom']),
+ 'strip': BuiltinOption(UserBooleanOption, 'Strip targets on install', False),
+ 'unity': BuiltinOption(UserComboOption, 'Unity build', 'off', choices=['on', 'off', 'subprojects']),
+ 'prefix': BuiltinOption(UserStringOption, 'Installation prefix', default_prefix()),
+ 'libdir': BuiltinOption(UserStringOption, 'Library directory', default_libdir()),
+ 'libexecdir': BuiltinOption(UserStringOption, 'Library executable directory', default_libexecdir()),
+ 'bindir': BuiltinOption(UserStringOption, 'Executable directory', 'bin'),
+ 'sbindir': BuiltinOption(UserStringOption, 'System executable directory', 'sbin'),
+ 'includedir': BuiltinOption(UserStringOption, 'Header file directory', 'include'),
+ 'datadir': BuiltinOption(UserStringOption, 'Data file directory', 'share'),
+ 'mandir': BuiltinOption(UserStringOption, 'Manual page directory', 'share/man'),
+ 'infodir': BuiltinOption(UserStringOption, 'Info page directory', 'share/info'),
+ 'localedir': BuiltinOption(UserStringOption, 'Locale data directory', 'share/locale'),
+ 'sysconfdir': BuiltinOption(UserStringOption, 'Sysconf data directory', 'etc'),
+ 'localstatedir': BuiltinOption(UserStringOption, 'Localstate data directory', 'var'),
+ 'sharedstatedir': BuiltinOption(UserStringOption, 'Architecture-independent data directory', 'com'),
+ 'werror': BuiltinOption(UserBooleanOption, 'Treat warnings as errors', False),
+ 'warning_level': BuiltinOption(UserComboOption, 'Compiler warning level to use', '1', choices=['0', '1', '2', '3']),
+ 'layout': BuiltinOption(UserComboOption, 'Build directory layout', 'mirror', choices=['mirror', 'flat']),
+ 'default_library': BuiltinOption(UserComboOption, 'Default library type', 'shared', choices=['shared', 'static', 'both']),
+ 'backend': BuiltinOption(UserComboOption, 'Backend to use', 'ninja', choices=backendlist),
+ 'stdsplit': BuiltinOption(UserBooleanOption, 'Split stdout and stderr in test logs', True),
+ 'errorlogs': BuiltinOption(UserBooleanOption, "Whether to print the logs from failing tests", True),
+ 'install_umask': BuiltinOption(UserUmaskOption, 'Default umask to apply on permissions of installed files', '022'),
+ 'auto_features': BuiltinOption(UserFeatureOption, "Override value of all 'auto' features", 'auto'),
+ 'optimization': BuiltinOption(UserComboOption, 'Optimization level', '0', choices=['0', 'g', '1', '2', '3', 's']),
+ 'debug': BuiltinOption(UserBooleanOption, 'Debug', True),
+ 'wrap_mode': BuiltinOption(UserComboOption, 'Wrap mode', 'default', choices=['default', 'nofallback', 'nodownload', 'forcefallback']),
+ 'pkg_config_path': BuiltinOption(UserArrayOption, 'List of additional paths for pkg-config to search', [], separate_cross=True),
}
# Special prefix-dependent defaults for installation directories that reside in
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py
index ddc56fc..f74eabb 100644
--- a/mesonbuild/dependencies/base.py
+++ b/mesonbuild/dependencies/base.py
@@ -617,11 +617,19 @@ class PkgConfigDependency(ExternalDependency):
return rc, out
def _call_pkgbin(self, args, env=None):
+ # Always copy the environment since we're going to modify it
+ # with pkg-config variables
if env is None:
- fenv = env
- env = os.environ
+ env = os.environ.copy()
else:
- fenv = frozenset(env.items())
+ env = env.copy()
+
+ if self.want_cross:
+ extra_paths = self.env.coredata.get_builtin_option('cross_pkg_config_path')
+ else:
+ extra_paths = self.env.coredata.get_builtin_option('pkg_config_path')
+ env['PKG_CONFIG_PATH'] = ':'.join([p for p in extra_paths])
+ fenv = frozenset(env.items())
targs = tuple(args)
cache = PkgConfigDependency.pkgbin_cache
if (self.pkgbin, targs, fenv) not in cache:
diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py
index 6e8ca83..ef0511d 100644
--- a/mesonbuild/msetup.py
+++ b/mesonbuild/msetup.py
@@ -149,13 +149,6 @@ class MesonApp:
sys.exit(1)
return src_dir, build_dir
- def check_pkgconfig_envvar(self, env):
- curvar = os.environ.get('PKG_CONFIG_PATH', '')
- if curvar != env.coredata.pkgconf_envvar:
- mlog.warning('PKG_CONFIG_PATH has changed between invocations from "%s" to "%s".' %
- (env.coredata.pkgconf_envvar, curvar))
- env.coredata.pkgconf_envvar = curvar
-
def generate(self):
env = environment.Environment(self.source_dir, self.build_dir, self.options)
mlog.initialize(env.get_log_dir(), self.options.fatal_warnings)
@@ -169,7 +162,6 @@ class MesonApp:
mlog.debug('Main binary:', sys.executable)
mlog.debug('Python system:', platform.system())
mlog.log(mlog.bold('The Meson build system'))
- self.check_pkgconfig_envvar(env)
mlog.log('Version:', coredata.version)
mlog.log('Source dir:', mlog.bold(self.source_dir))
mlog.log('Build dir:', mlog.bold(self.build_dir))
diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py
index 85f6897..e64ed4e 100644
--- a/mesonbuild/optinterpreter.py
+++ b/mesonbuild/optinterpreter.py
@@ -20,19 +20,20 @@ from . import coredata
from . import mesonlib
from . import compilers
-forbidden_option_names = coredata.get_builtin_options()
+forbidden_option_names = set(coredata.builtin_options.keys())
forbidden_prefixes = [lang + '_' for lang in compilers.all_languages] + ['b_', 'backend_']
reserved_prefixes = ['cross_']
-def is_invalid_name(name):
+def is_invalid_name(name: str, *, log: bool = True) -> bool:
if name in forbidden_option_names:
return True
pref = name.split('_')[0] + '_'
if pref in forbidden_prefixes:
return True
if pref in reserved_prefixes:
- from . import mlog
- mlog.deprecation('Option uses prefix "%s", which is reserved for Meson. This will become an error in the future.' % pref)
+ if log:
+ from . import mlog
+ mlog.deprecation('Option uses prefix "%s", which is reserved for Meson. This will become an error in the future.' % pref)
return False
class OptionException(mesonlib.MesonException):
diff --git a/run_cross_test.py b/run_cross_test.py
index b2ef6be..8d18123 100755
--- a/run_cross_test.py
+++ b/run_cross_test.py
@@ -34,7 +34,7 @@ def runtests(cross_file, failfast):
commontests = [('common', gather_tests(Path('test cases', 'common')), False)]
try:
(passing_tests, failing_tests, skipped_tests) = \
- run_tests(commontests, 'meson-cross-test-run', failfast, ['--cross', cross_file])
+ run_tests(commontests, 'meson-cross-test-run', failfast, ['--cross-file', cross_file])
except StopException:
pass
print('\nTotal passed cross tests:', passing_tests)
diff --git a/run_unittests.py b/run_unittests.py
index d803e12..19426b8 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -4837,9 +4837,9 @@ endian = 'little'
testdir = os.path.join(self.unit_test_dir, '58 pkgconfig relative paths')
pkg_dir = os.path.join(testdir, 'pkgconfig')
self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'librelativepath.pc')))
- os.environ['PKG_CONFIG_PATH'] = pkg_dir
env = get_fake_env(testdir, self.builddir, self.prefix)
+ env.coredata.set_options({'pkg_config_path': pkg_dir}, '')
kwargs = {'required': True, 'silent': True}
relative_path_dep = PkgConfigDependency('librelativepath', env, kwargs)
self.assertTrue(relative_path_dep.found())
@@ -5068,6 +5068,11 @@ endian = 'little'
# Assert that
self.assertEqual(len(line.split(lib)), 2, msg=(lib, line))
+ @skipIfNoPkgconfig
+ def test_pkg_config_option(self):
+ testdir = os.path.join(self.unit_test_dir, '55 pkg_config_path option')
+ self.init(testdir, extra_args=['-Dpkg_config_path=' + os.path.join(testdir, 'extra_path')])
+
def should_run_cross_arm_tests():
return shutil.which('arm-linux-gnueabihf-gcc') and not platform.machine().lower().startswith('arm')
@@ -5164,6 +5169,11 @@ class LinuxCrossMingwTests(BasePlatformTests):
# Must run in-process or we'll get a generic CalledProcessError
self.run_tests(inprocess=True)
+ @skipIfNoPkgconfig
+ def test_cross_pkg_config_option(self):
+ testdir = os.path.join(self.unit_test_dir, '55 pkg_config_path option')
+ self.init(testdir, extra_args=['-Dcross_pkg_config_path=' + os.path.join(testdir, 'extra_path')])
+
class PythonTests(BasePlatformTests):
'''
diff --git a/test cases/unit/55 pkg_config_path option/extra_path/totally_made_up_dep.pc b/test cases/unit/55 pkg_config_path option/extra_path/totally_made_up_dep.pc
new file mode 100644
index 0000000..6d08687
--- /dev/null
+++ b/test cases/unit/55 pkg_config_path option/extra_path/totally_made_up_dep.pc
@@ -0,0 +1,7 @@
+prefix=/
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: totally_made_up_dep
+Description: completely and totally made up for a test case
+Version: 1.2.3 \ No newline at end of file
diff --git a/test cases/unit/55 pkg_config_path option/meson.build b/test cases/unit/55 pkg_config_path option/meson.build
new file mode 100644
index 0000000..623c3a2
--- /dev/null
+++ b/test cases/unit/55 pkg_config_path option/meson.build
@@ -0,0 +1,3 @@
+project('pkg_config_path option')
+
+dependency('totally_made_up_dep', method : 'pkg-config')