Add boost_root support to properties files (#7210)

* Add boost_root support to properties files

This commit implements `boost_root`, `boost_includedir`, and
`boost_librarydir` variable support to native and cross properties
files. The search order is currently environment variables, then
these variables, and finally a platform-dependent search.

* Add preliminary boost_root / boost_includedir tests

Each test contains a fake "version.hpp", as that's how boost detection is
currently being done. We look for this file relative to the root directory,
which probably shouldn't be allowed (it previously was for BOOST_LIBRARYDIR
but not for BOOST_ROOT). It also cannot help with breakage detection in
libraries, however it looks like this wasn't getting tested beforehand.

I've given the two unique version numbers that shouldn't be present in any
stock version of boost (001 and 002).

* Add return type to detect_split_root

* Return empty list when nothing found in BOOST_ROOT, rather than None

* Update boost_root tests

* Create nativefile.ini based on location of run_project_tests.py
* Add fake libraries to ensure boost_librarydir is being used

* Require all search paths for boost to be absolute

* Redo boost search ordering

To better match things like pkg-config, we now look through native/cross files,
then environment variables, then system locations for boost installations.

Path detection does not fall back from one method to the next for properties or
environment variables--if boost_root, boost_librarydir, or boost_includedir is
specified, they must be sufficient to find boost. Likewise for BOOST_ROOT and
friends. pkg-config detection is still optional falling back to system-wide
detection, for Conan.

(Also, fix a typo in test 33's nativefile)

* Correct return type for detect_roots

* Correct boost dependency search order in documentation

* Print debug information for boost library finding, to resolve CI issues

* Handle native/cross file templates in a more consistent way

All tests can now create a `nativefile.ini.in` if they need to use some
parameter that the testing framework knows about but they can't.

* Pass str--rather than PosixPath--to os.path.exists, for Python35

* Look for boost minor versions, rather than boost patch versions in test cases

* Drop fake dylib versions of boost_regex

* Prefer get_env_var to use of os.environ

* Correct error reporting for relative BOOST_ROOT paths

* Bump version this appears in. Also, change "properties file" to "machine file" as that appears to be the more common language.
pull/7472/head
cmcneish 5 years ago committed by GitHub
parent 7f1e9b7492
commit a7a6a4833f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      docs/markdown/Dependencies.md
  2. 146
      mesonbuild/dependencies/boost.py
  3. 28
      run_project_tests.py
  4. 3
      test cases/frameworks/32 boost root/boost/include/boost/version.hpp
  5. 0
      test cases/frameworks/32 boost root/boost/lib/boost_regex-vc142-mt-gd-x32-0_1.lib
  6. 0
      test cases/frameworks/32 boost root/boost/lib/boost_regex-vc142-mt-gd-x64-0_1.lib
  7. 0
      test cases/frameworks/32 boost root/boost/lib/libboost_regex.so.0.1.0
  8. 6
      test cases/frameworks/32 boost root/meson.build
  9. 2
      test cases/frameworks/32 boost root/nativefile.ini.in
  10. 3
      test cases/frameworks/33 boost split root/boost/extra-dir/include/boost/version.hpp
  11. 0
      test cases/frameworks/33 boost split root/boost/lib/boost_regex-vc142-mt-gd-x32-0_2.lib
  12. 0
      test cases/frameworks/33 boost split root/boost/lib/boost_regex-vc142-mt-gd-x64-0_2.lib
  13. 0
      test cases/frameworks/33 boost split root/boost/lib/libboost_regex.so.0.2.0
  14. 6
      test cases/frameworks/33 boost split root/meson.build
  15. 3
      test cases/frameworks/33 boost split root/nativefile.ini.in

@ -288,8 +288,12 @@ You can call `dependency` multiple times with different modules and
use those to link against your targets.
If your boost headers or libraries are in non-standard locations you
can set the BOOST_ROOT, BOOST_INCLUDEDIR, and/or BOOST_LIBRARYDIR
environment variables.
can set the `BOOST_ROOT`, or the `BOOST_INCLUDEDIR` and `BOOST_LIBRARYDIR`
environment variables. *(added in 0.56.0)* You can also set these
parameters as `boost_root`, `boost_include`, and `boost_librarydir` in your
native or cross machine file. Note that machine file variables are
preferred to environment variables, and that specifying any of these
disables system-wide search for boost.
You can set the argument `threading` to `single` to use boost
libraries that have been compiled for single-threaded use instead.

@ -20,6 +20,7 @@ from pathlib import Path
from .. import mlog
from .. import mesonlib
from ..envconfig import get_env_var
from ..environment import Environment
from .base import DependencyException, ExternalDependency, PkgConfigDependency
@ -163,8 +164,8 @@ class BoostLibraryFile():
if not tags:
return
# Without any tags mt is assumed, however, an absents of mt in the name
# with tags present indicates that the lib was build without mt support
# Without any tags mt is assumed, however, an absence of mt in the name
# with tags present indicates that the lib was built without mt support
self.mt = False
for i in tags:
if i == 'mt':
@ -367,36 +368,27 @@ class BoostDependency(ExternalDependency):
self.arch = environment.machines[self.for_machine].cpu_family
self.arch = boost_arch_map.get(self.arch, None)
# Prefere BOOST_INCLUDEDIR and BOOST_LIBRARYDIR if preset
boost_manual_env = [x in os.environ for x in ['BOOST_INCLUDEDIR', 'BOOST_LIBRARYDIR']]
if all(boost_manual_env):
inc_dir = Path(os.environ['BOOST_INCLUDEDIR'])
lib_dir = Path(os.environ['BOOST_LIBRARYDIR'])
mlog.debug('Trying to find boost with:')
mlog.debug(' - BOOST_INCLUDEDIR = {}'.format(inc_dir))
mlog.debug(' - BOOST_LIBRARYDIR = {}'.format(lib_dir))
boost_inc_dir = None
for j in [inc_dir / 'version.hpp', inc_dir / 'boost' / 'version.hpp']:
if j.is_file():
boost_inc_dir = self._include_dir_from_version_header(j)
break
if not boost_inc_dir:
self.is_found = False
# First, look for paths specified in a machine file
props = self.env.properties[self.for_machine]
boost_property_env = [props.get('boost_includedir'), props.get('boost_librarydir'), props.get('boost_root')]
if any(boost_property_env):
self.detect_boost_machine_file(props)
return
self.is_found = self.run_check([boost_inc_dir], [lib_dir])
# Next, look for paths in the environment
boost_manual_env_list = ['BOOST_INCLUDEDIR', 'BOOST_LIBRARYDIR', 'BOOST_ROOT', 'BOOSTROOT']
boost_manual_env = [get_env_var(self.for_machine, self.env.is_cross_build, x) for x in boost_manual_env_list]
if any(boost_manual_env):
self.detect_boost_env()
return
elif any(boost_manual_env):
mlog.warning('Both BOOST_INCLUDEDIR *and* BOOST_LIBRARYDIR have to be set (one is not enough). Ignoring.')
# A) Detect potential boost root directories (uses also BOOST_ROOT env var)
roots = self.detect_roots()
roots = list(mesonlib.OrderedSet(roots))
# Finally, look for paths from .pc files and from searching the filesystem
self.detect_roots()
# B) Foreach candidate
def check_and_set_roots(self, roots) -> None:
roots = list(mesonlib.OrderedSet(roots))
for j in roots:
# 1. Look for the boost headers (boost/version.pp)
# 1. Look for the boost headers (boost/version.hpp)
mlog.debug('Checking potential boost root {}'.format(j.as_posix()))
inc_dirs = self.detect_inc_dirs(j)
inc_dirs = sorted(inc_dirs, reverse=True) # Prefer the newer versions
@ -411,6 +403,74 @@ class BoostDependency(ExternalDependency):
self.boost_root = j
break
def detect_boost_machine_file(self, props) -> None:
incdir = props.get('boost_includedir')
libdir = props.get('boost_librarydir')
if incdir and libdir:
inc_dir = Path(props['boost_includedir'])
lib_dir = Path(props['boost_librarydir'])
if not inc_dir.is_absolute() or not lib_dir.is_absolute():
raise DependencyException('Paths given for boost_includedir and boost_librarydir in machine file must be absolute')
mlog.debug('Trying to find boost with:')
mlog.debug(' - boost_includedir = {}'.format(inc_dir))
mlog.debug(' - boost_librarydir = {}'.format(lib_dir))
return self.detect_split_root(inc_dir, lib_dir)
elif incdir or libdir:
raise DependencyException('Both boost_includedir *and* boost_librarydir have to be set in your machine file (one is not enough)')
rootdir = props.get('boost_root')
# It shouldn't be possible to get here without something in boost_root
assert(rootdir)
raw_paths = mesonlib.stringlistify(rootdir)
paths = [Path(x) for x in raw_paths]
if paths and any([not x.is_absolute() for x in paths]):
raise DependencyException('boost_root path given in machine file must be absolute')
self.check_and_set_roots(paths)
def detect_boost_env(self):
boost_includedir = get_env_var(self.for_machine, self.env.is_cross_build, 'BOOST_INCLUDEDIR')
boost_librarydir = get_env_var(self.for_machine, self.env.is_cross_build, 'BOOST_LIBRARYDIR')
boost_manual_env = [boost_includedir, boost_librarydir]
if all(boost_manual_env):
inc_dir = Path(boost_includedir)
lib_dir = Path(boost_librarydir)
if not inc_dir.is_absolute() or not lib_dir.is_absolute():
raise DependencyException('Paths given in BOOST_INCLUDEDIR and BOOST_LIBRARYDIR must be absolute')
mlog.debug('Trying to find boost with:')
mlog.debug(' - BOOST_INCLUDEDIR = {}'.format(inc_dir))
mlog.debug(' - BOOST_LIBRARYDIR = {}'.format(lib_dir))
return self.detect_split_root(inc_dir, lib_dir)
elif any(boost_manual_env):
raise DependencyException('Both BOOST_INCLUDEDIR *and* BOOST_LIBRARYDIR have to be set (one is not enough). Ignoring.')
boost_root = get_env_var(self.for_machine, self.env.is_cross_build, 'BOOST_ROOT')
boostroot = get_env_var(self.for_machine, self.env.is_cross_build, 'BOOSTROOT')
# It shouldn't be possible to get here without something in BOOST_ROOT or BOOSTROOT
assert(boost_root or boostroot)
for path, name in [(boost_root, 'BOOST_ROOT'), (boostroot, 'BOOSTROOT')]:
if path:
raw_paths = path.split(os.pathsep)
paths = [Path(x) for x in raw_paths]
if paths and any([not x.is_absolute() for x in paths]):
raise DependencyException('Paths in {} must be absolute'.format(name))
break
self.check_and_set_roots(paths)
def run_check(self, inc_dirs: T.List[BoostIncludeDir], lib_dirs: T.List[Path]) -> bool:
mlog.debug(' - potential library dirs: {}'.format([x.as_posix() for x in lib_dirs]))
mlog.debug(' - potential include dirs: {}'.format([x.path.as_posix() for x in inc_dirs]))
@ -560,6 +620,12 @@ class BoostDependency(ExternalDependency):
except (KeyError, IndexError, AttributeError):
pass
# mlog.debug(' - static: {}'.format(self.static))
# mlog.debug(' - not explicit static: {}'.format(not self.explicit_static))
# mlog.debug(' - mt: {}'.format(self.multithreading))
# mlog.debug(' - version: {}'.format(lib_vers))
# mlog.debug(' - arch: {}'.format(self.arch))
# mlog.debug(' - vscrt: {}'.format(vscrt))
libs = [x for x in libs if x.static == self.static or not self.explicit_static]
libs = [x for x in libs if x.mt == self.multithreading]
libs = [x for x in libs if x.version_matches(lib_vers)]
@ -592,20 +658,22 @@ class BoostDependency(ExternalDependency):
libs += [BoostLibraryFile(i)]
return [x for x in libs if x.is_boost()] # Filter out no boost libraries
def detect_roots(self) -> T.List[Path]:
roots = [] # type: T.List[Path]
def detect_split_root(self, inc_dir, lib_dir) -> None:
boost_inc_dir = None
for j in [inc_dir / 'version.hpp', inc_dir / 'boost' / 'version.hpp']:
if j.is_file():
boost_inc_dir = self._include_dir_from_version_header(j)
break
if not boost_inc_dir:
self.is_found = False
return
# Add roots from the environment
for i in ['BOOST_ROOT', 'BOOSTROOT']:
if i in os.environ:
raw_paths = os.environ[i].split(os.pathsep)
paths = [Path(x) for x in raw_paths]
if paths and any([not x.is_absolute() for x in paths]):
raise DependencyException('Paths in {} must be absolute'.format(i))
roots += paths
return roots # Do not add system paths if BOOST_ROOT is present
self.is_found = self.run_check([boost_inc_dir], [lib_dir])
def detect_roots(self) -> None:
roots = [] # type: T.List[Path]
# Try getting the BOOST_ROOT from a boost.pc if it exists. This primarely
# Try getting the BOOST_ROOT from a boost.pc if it exists. This primarily
# allows BoostDependency to find boost from Conan. See #5438
try:
boost_pc = PkgConfigDependency('boost', self.env, {'required': False})
@ -660,7 +728,7 @@ class BoostDependency(ExternalDependency):
tmp = [x.resolve() for x in tmp]
roots += tmp
return roots
self.check_and_set_roots(roots)
def log_details(self) -> str:
res = ''

@ -41,6 +41,7 @@ from mesonbuild import compilers
from mesonbuild import mesonlib
from mesonbuild import mlog
from mesonbuild import mtest
from mesonbuild.build import ConfigurationData
from mesonbuild.mesonlib import MachineChoice, Popen_safe
from mesonbuild.coredata import backendlist, version as meson_version
@ -475,6 +476,28 @@ def create_deterministic_builddir(test: TestDef, use_tmpdir: bool) -> str:
os.mkdir(abs_pathname)
return abs_pathname
def format_parameter_file(file_basename: str, test: TestDef, test_build_dir: str) -> Path:
confdata = ConfigurationData()
confdata.values = {'MESON_TEST_ROOT': (str(test.path.absolute()), 'base directory of current test')}
template = test.path / (file_basename + '.in')
destination = Path(test_build_dir) / file_basename
mesonlib.do_conf_file(str(template), str(destination), confdata, 'meson')
return destination
def detect_parameter_files(test: TestDef, test_build_dir: str) -> (Path, Path):
nativefile = test.path / 'nativefile.ini'
crossfile = test.path / 'crossfile.ini'
if os.path.exists(str(test.path / 'nativefile.ini.in')):
nativefile = format_parameter_file('nativefile.ini', test, test_build_dir)
if os.path.exists(str(test.path / 'crossfile.ini.in')):
crossfile = format_parameter_file('crossfile.ini', test, test_build_dir)
return nativefile, crossfile
def run_test(test: TestDef, extra_args, compiler, backend, flags, commands, should_fail, use_tmp: bool):
if test.skip:
return None
@ -497,8 +520,9 @@ def _run_test(test: TestDef, test_build_dir: str, install_dir: str, extra_args,
if 'libdir' not in test.do_not_set_opts:
gen_args += ['--libdir', 'lib']
gen_args += [test.path.as_posix(), test_build_dir] + flags + extra_args
nativefile = test.path / 'nativefile.ini'
crossfile = test.path / 'crossfile.ini'
nativefile, crossfile = detect_parameter_files(test, test_build_dir)
if nativefile.exists():
gen_args.extend(['--native-file', nativefile.as_posix()])
if crossfile.exists():

@ -0,0 +1,3 @@
#define BOOST_VERSION 100
#error This is not a real version of boost

@ -0,0 +1,6 @@
project('boosttest', 'cpp')
dep = dependency('boost', modules : 'regex', required: false)
assert(dep.found(), 'expected to find a fake version of boost')
assert(dep.version() == '0.1.0', 'expected to find version 0.1.0')

@ -0,0 +1,2 @@
[properties]
boost_root = '@MESON_TEST_ROOT@/boost'

@ -0,0 +1,3 @@
#define BOOST_VERSION 200
#error This is not a real version of boost

@ -0,0 +1,6 @@
project('boosttest', 'cpp')
dep = dependency('boost', modules : 'regex', required: false)
assert(dep.found(), 'expected to find a fake version of boost')
assert(dep.version() == '0.2.0', 'expected to find version 0.2.0')

@ -0,0 +1,3 @@
[properties]
boost_includedir = '@MESON_TEST_ROOT@/boost/extra-dir/include'
boost_librarydir = '@MESON_TEST_ROOT@/boost/lib'
Loading…
Cancel
Save