aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml10
-rw-r--r--docs/markdown/Dependencies.md15
-rw-r--r--docs/markdown/Gnome-module.md4
-rw-r--r--docs/markdown/Qt5-module.md8
-rw-r--r--docs/markdown/Reference-manual.md2
-rw-r--r--docs/markdown/Release-notes-for-0.43.0/001-boost-platform-improvement.md10
-rw-r--r--docs/markdown/snippets/wrap-svn.md4
-rw-r--r--manual tests/10 svn wrap/meson.build10
-rw-r--r--manual tests/10 svn wrap/prog.c6
-rw-r--r--manual tests/10 svn wrap/subprojects/samplesubproject.wrap4
-rw-r--r--mesonbuild/backend/backends.py13
-rw-r--r--mesonbuild/backend/vs2010backend.py41
-rw-r--r--mesonbuild/build.py27
-rw-r--r--mesonbuild/compilers/c.py34
-rw-r--r--mesonbuild/compilers/compilers.py39
-rw-r--r--mesonbuild/dependencies/base.py6
-rw-r--r--mesonbuild/dependencies/dev.py4
-rw-r--r--mesonbuild/dependencies/misc.py426
-rw-r--r--mesonbuild/interpreter.py187
-rw-r--r--mesonbuild/mesonlib.py59
-rw-r--r--mesonbuild/modules/gnome.py24
-rw-r--r--mesonbuild/modules/qt4.py10
-rw-r--r--mesonbuild/modules/qt5.py10
-rw-r--r--mesonbuild/wrap/wrap.py26
-rwxr-xr-xrun_tests.py2
-rwxr-xr-xrun_unittests.py45
-rw-r--r--test cases/common/145 whole archive/allofme/meson.build1
-rw-r--r--test cases/common/145 whole archive/exe/meson.build2
-rw-r--r--test cases/common/145 whole archive/exe2/meson.build1
-rw-r--r--test cases/common/145 whole archive/meson.build20
-rw-r--r--test cases/common/145 whole archive/shlib/meson.build4
-rw-r--r--test cases/common/145 whole archive/stlib/meson.build1
-rw-r--r--test cases/common/145 whole archive/wholeshlib/meson.build1
-rw-r--r--test cases/failing/61 getoption prefix/meson.build5
-rw-r--r--test cases/failing/61 getoption prefix/subprojects/abc/meson.build1
-rw-r--r--test cases/failing/61 getoption prefix/subprojects/abc/meson_options.txt1
-rw-r--r--test cases/frameworks/1 boost/meson.build5
-rw-r--r--test cases/frameworks/1 boost/nolinkexe.cc12
-rw-r--r--test cases/frameworks/4 qt/meson.build10
-rw-r--r--test cases/frameworks/4 qt/plugin/plugin.cpp7
-rw-r--r--test cases/frameworks/4 qt/plugin/plugin.h11
-rw-r--r--test cases/frameworks/4 qt/plugin/plugin.json3
-rw-r--r--test cases/frameworks/4 qt/pluginInterface/plugin_if.h21
-rw-r--r--test cases/frameworks/7 gnome/gir/dep1/meson.build1
-rw-r--r--test cases/frameworks/7 gnome/gir/meson.build2
-rwxr-xr-xtools/boost_names.py180
46 files changed, 973 insertions, 342 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 60a6fd3..d150c42 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -19,10 +19,12 @@ environment:
- arch: x86
compiler: msvc2015
backend: ninja
+ BOOST_ROOT: C:\Libraries\Boost_1_59_0
- arch: x86
compiler: msvc2015
backend: vs2015
+ BOOST_ROOT: C:\Libraries\Boost_1_59_0
- arch: x64
compiler: cygwin
@@ -36,11 +38,13 @@ environment:
compiler: msvc2017
backend: ninja
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+ BOOST_ROOT: C:\Libraries\Boost_1_64_0
- arch: x64
compiler: msvc2017
backend: vs2017
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+ BOOST_ROOT: C:\Libraries\Boost_1_64_0
platform:
- x64
@@ -59,8 +63,6 @@ init:
install:
- cmd: set "ORIG_PATH=%PATH%"
- # Boost 1.56.0: https://www.appveyor.com/docs/build-environment/#boost
- #- cmd: set "BOOST_ROOT=C:\Libraries\boost"
# Use a Ninja with QuLogic's patch: https://github.com/ninja-build/ninja/issues/1219
- cmd: set "MESON_FIXED_NINJA=1"
- ps: (new-object net.webclient).DownloadFile('http://nirbheek.in/files/binaries/ninja/win32/ninja.exe', 'C:\projects\meson\ninja.exe')
@@ -68,6 +70,10 @@ install:
# For all other archs (including, say, arm), use the x64 python.
- cmd: if %arch%==x86 (set MESON_PYTHON_PATH=C:\python34) else (set MESON_PYTHON_PATH=C:\python34-x64)
+ # Set paths for BOOST dll files
+ - cmd: if %compiler%==msvc2015 ( if %arch%==x86 ( set "PATH=%PATH%;C:\Libraries\boost_1_59_0\lib32-msvc-14.0" ) else ( set "PATH=%PATH%;C:\Libraries\boost_1_59_0\lib64-msvc-14.0" ) )
+ - cmd: if %compiler%==msvc2017 ( if %arch%==x86 ( set "PATH=%PATH%;C:\Libraries\boost_1_64_0\lib32-msvc-14.1" ) else ( set "PATH=%PATH%;C:\Libraries\boost_1_64_0\lib64-msvc-14.1" ) )
+
# Set paths and config for each build type.
- cmd: if %compiler%==msvc2010 ( call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" %arch% )
- cmd: if %compiler%==msvc2015 ( call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %arch% )
diff --git a/docs/markdown/Dependencies.md b/docs/markdown/Dependencies.md
index 187c4fe..c46334c 100644
--- a/docs/markdown/Dependencies.md
+++ b/docs/markdown/Dependencies.md
@@ -50,8 +50,16 @@ pkg-config files. Meson has autodetection support for some of these.
## Boost ##
Boost is not a single dependency but rather a group of different
-libraries. To use Boost with Meson, simply list which Boost modules
-you would like to use.
+libraries. To use Boost headers-only libraries, simply add Boost as a
+dependency.
+
+```meson
+boost_dep = dependency('boost')
+exe = executable('myprog', 'file.cc', dependencies : boost_dep)
+```
+
+To link against boost with Meson, simply list which libraries you would like to
+use.
```meson
boost_dep = dependency('boost', modules : ['thread', 'utility'])
@@ -65,6 +73,9 @@ 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.
+You can set the argument `threading` to `single` to use boost libraries that
+has been compiled for single-threaded use instead.
+
## GTest and GMock ##
GTest and GMock come as sources that must be compiled as part of your
diff --git a/docs/markdown/Gnome-module.md b/docs/markdown/Gnome-module.md
index 99a9c8e..d87e108 100644
--- a/docs/markdown/Gnome-module.md
+++ b/docs/markdown/Gnome-module.md
@@ -81,6 +81,10 @@ tool so see its documentation for more information.
* `includes`: list of gir names to be included, can also be a GirTarget
+* `header`: *(Added 0.43.0)* name of main c header to include for the library, e.g. `glib.h`
+
+* `dependencies`: deps to use during introspection scanning
+
* `include_directories`: extra include paths to look for gir files
* `install`: if true, install the generated files
diff --git a/docs/markdown/Qt5-module.md b/docs/markdown/Qt5-module.md
index 7082309..a8ad73d 100644
--- a/docs/markdown/Qt5-module.md
+++ b/docs/markdown/Qt5-module.md
@@ -5,17 +5,19 @@ tools and steps required for Qt. The module has one method.
## preprocess
-This method takes four keyword arguments, `moc_headers`,
+This method takes five keyword arguments, `moc_headers`,
`moc_sources`, `ui_files` and `qresources` which define the files that
-require preprocessing with `moc`, `uic` and `rcc`. It returns an
+require preprocessing with `moc`, `uic` and `rcc` and 'include_directories' which might be needed by moc. It returns an
opaque object that should be passed to a main build target. A simple
example would look like this:
```meson
qt5 = import('qt5')
qt5_dep = dependency('qt5', modules: ['Core', 'Gui'])
-moc_files = qt5.preprocess(moc_headers : 'myclass.h')
+inc = include_directories('includes')
+moc_files = qt5.preprocess(moc_headers : 'myclass.h', include_directories: inc)
executable('myprog', 'main.cpp', 'myclass.cpp', moc_files,
+ include_directories: inc,
dependencies : qt5_dep)
```
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index f37fb34..3bdd3c5 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -1614,7 +1614,7 @@ during tests. It should be passed as the `env` keyword argument to
tests. It has the following methods.
- `append(varname, value)` appends the given value to the old value of
- the environment variable, e.g. `env.append'('FOO', 'BAR', separator
+ the environment variable, e.g. `env.append('FOO', 'BAR', separator
: ';')` produces `BOB;BAR` if `FOO` had the value `BOB` and plain
`BAR` if the value was not defined. If the separator is not
specified explicitly, the default path separator for the host
diff --git a/docs/markdown/Release-notes-for-0.43.0/001-boost-platform-improvement.md b/docs/markdown/Release-notes-for-0.43.0/001-boost-platform-improvement.md
new file mode 100644
index 0000000..2cd44ce
--- /dev/null
+++ b/docs/markdown/Release-notes-for-0.43.0/001-boost-platform-improvement.md
@@ -0,0 +1,10 @@
+## Portability improvements to Boost Dependency
+
+The Boost dependency has been improved to better detect the various ways to
+install boost on multiple platforms. At the same time the `modules` semantics
+for the dependency has been changed. Previously it was allowed to specify
+header directories as `modules` but it wasn't required. Now, modules are only
+used to specify libraries that require linking.
+
+This is a breaking change and the fix is to remove all modules that aren't
+found.
diff --git a/docs/markdown/snippets/wrap-svn.md b/docs/markdown/snippets/wrap-svn.md
new file mode 100644
index 0000000..cdf155b
--- /dev/null
+++ b/docs/markdown/snippets/wrap-svn.md
@@ -0,0 +1,4 @@
+# wrap-svn
+
+The [Wrap dependency system](Wrap-dependency-system-manual.md) now supports [Subversion](https://subversion.apache.org/) (svn).
+This support is rudimentary. The repository url has to point to a specific (sub)directory containing the `meson.build` file (typically `trunk/`). However, providing a `revision` is supported.
diff --git a/manual tests/10 svn wrap/meson.build b/manual tests/10 svn wrap/meson.build
new file mode 100644
index 0000000..23ef1f1
--- /dev/null
+++ b/manual tests/10 svn wrap/meson.build
@@ -0,0 +1,10 @@
+project('Subversion outchecker', 'c')
+
+sp = subproject('samplesubproject')
+
+exe = executable('gitprog', 'prog.c',
+include_directories : sp.get_variable('subproj_inc'),
+link_with : sp.get_variable('subproj_lib'),
+)
+
+test('maintest', exe)
diff --git a/manual tests/10 svn wrap/prog.c b/manual tests/10 svn wrap/prog.c
new file mode 100644
index 0000000..df38000
--- /dev/null
+++ b/manual tests/10 svn wrap/prog.c
@@ -0,0 +1,6 @@
+#include"subproj.h"
+
+int main(int argc, char **argv) {
+ subproj_function();
+ return 0;
+}
diff --git a/manual tests/10 svn wrap/subprojects/samplesubproject.wrap b/manual tests/10 svn wrap/subprojects/samplesubproject.wrap
new file mode 100644
index 0000000..c8a687e
--- /dev/null
+++ b/manual tests/10 svn wrap/subprojects/samplesubproject.wrap
@@ -0,0 +1,4 @@
+[wrap-svn]
+directory=samplesubproject
+url=https://svn.code.sf.net/p/mesonsubproject/code/trunk
+revision=head
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index e0e2abc..61f7535 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -155,9 +155,18 @@ class Backend:
dirname = 'meson-out'
return dirname
+ def get_target_dir_relative_to(self, t, o):
+ '''Get a target dir relative to another target's directory'''
+ target_dir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(t))
+ othert_dir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(o))
+ return os.path.relpath(target_dir, othert_dir)
+
def get_target_source_dir(self, target):
- dirname = os.path.join(self.build_to_src, self.get_target_dir(target))
- return dirname
+ # if target dir is empty, avoid extraneous trailing / from os.path.join()
+ target_dir = self.get_target_dir(target)
+ if target_dir:
+ return os.path.join(self.build_to_src, target_dir)
+ return self.build_to_src
def get_target_private_dir(self, target):
dirname = os.path.join(self.get_target_dir(target), target.get_basename() + target.type_suffix())
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index 2d18b0e..e4e9696 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -344,6 +344,11 @@ class Vs2010Backend(backends.Backend):
def quote_arguments(self, arr):
return ['"%s"' % i for i in arr]
+ def add_project_reference(self, root, include, projid):
+ ig = ET.SubElement(root, 'ItemGroup')
+ pref = ET.SubElement(ig, 'ProjectReference', Include=include)
+ ET.SubElement(pref, 'Project').text = '{%s}' % projid
+
def create_basic_crap(self, target):
project_name = target.name
root = ET.Element('Project', {'DefaultTargets': "Build",
@@ -537,6 +542,8 @@ class Vs2010Backend(backends.Backend):
if lpath in lpaths:
lpaths.remove(lpath)
lpaths.append(lpath)
+ elif arg.startswith(('/', '-')):
+ other.append(arg)
# It's ok if we miss libraries with non-standard extensions here.
# They will go into the general link arguments.
elif arg.endswith('.lib') or arg.endswith('.a'):
@@ -907,20 +914,26 @@ class Vs2010Backend(backends.Backend):
# *_winlibs that we want to link to are static mingw64 libraries.
extra_link_args += compiler.get_option_link_args(self.environment.coredata.compiler_options)
(additional_libpaths, additional_links, extra_link_args) = self.split_link_args(extra_link_args.to_native())
- if len(extra_link_args) > 0:
- extra_link_args.append('%(AdditionalOptions)')
- ET.SubElement(link, "AdditionalOptions").text = ' '.join(extra_link_args)
- if len(additional_libpaths) > 0:
- additional_libpaths.insert(0, '%(AdditionalLibraryDirectories)')
- ET.SubElement(link, 'AdditionalLibraryDirectories').text = ';'.join(additional_libpaths)
# Add more libraries to be linked if needed
for t in target.get_dependencies():
lobj = self.build.targets[t.get_id()]
linkname = os.path.join(down, self.get_target_filename_for_linking(lobj))
if t in target.link_whole_targets:
- linkname = compiler.get_link_whole_for(linkname)[0]
- additional_links.append(linkname)
+ # /WHOLEARCHIVE:foo must go into AdditionalOptions
+ extra_link_args += compiler.get_link_whole_for(linkname)
+ # To force Visual Studio to build this project even though it
+ # has no sources, we include a reference to the vcxproj file
+ # that builds this target. Technically we should add this only
+ # if the current target has no sources, but it doesn't hurt to
+ # have 'extra' references.
+ trelpath = self.get_target_dir_relative_to(t, target)
+ tvcxproj = os.path.join(trelpath, t.get_id() + '.vcxproj')
+ tid = self.environment.coredata.target_guids[t.get_id()]
+ self.add_project_reference(root, tvcxproj, tid)
+ else:
+ # Other libraries go into AdditionalDependencies
+ additional_links.append(linkname)
for lib in self.get_custom_target_provided_libraries(target):
additional_links.append(self.relpath(lib, self.get_target_dir(target)))
additional_objects = []
@@ -929,6 +942,13 @@ class Vs2010Backend(backends.Backend):
additional_objects.append(o)
for o in custom_objs:
additional_objects.append(o)
+
+ if len(extra_link_args) > 0:
+ extra_link_args.append('%(AdditionalOptions)')
+ ET.SubElement(link, "AdditionalOptions").text = ' '.join(extra_link_args)
+ if len(additional_libpaths) > 0:
+ additional_libpaths.insert(0, '%(AdditionalLibraryDirectories)')
+ ET.SubElement(link, 'AdditionalLibraryDirectories').text = ';'.join(additional_libpaths)
if len(additional_links) > 0:
additional_links.append('%(AdditionalDependencies)')
ET.SubElement(link, 'AdditionalDependencies').text = ';'.join(additional_links)
@@ -1018,9 +1038,8 @@ class Vs2010Backend(backends.Backend):
ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets')
# Reference the regen target.
- ig = ET.SubElement(root, 'ItemGroup')
- pref = ET.SubElement(ig, 'ProjectReference', Include=os.path.join(self.environment.get_build_dir(), 'REGEN.vcxproj'))
- ET.SubElement(pref, 'Project').text = self.environment.coredata.regen_guid
+ regen_vcxproj = os.path.join(self.environment.get_build_dir(), 'REGEN.vcxproj')
+ self.add_project_reference(root, regen_vcxproj, self.environment.coredata.regen_guid)
self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname)
def gen_regenproj(self, project_name, ofname):
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 8c02d1d..9837d5a 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -20,7 +20,7 @@ from . import environment
from . import dependencies
from . import mlog
from .mesonlib import File, MesonException, listify, extract_as_list
-from .mesonlib import flatten, typeslistify, stringlistify, classify_unity_sources
+from .mesonlib import typeslistify, stringlistify, classify_unity_sources
from .mesonlib import get_filenames_templates_dict, substitute_values
from .environment import for_windows, for_darwin, for_cygwin
from .compilers import is_object, clike_langs, sort_clike, lang_suffixes
@@ -357,7 +357,7 @@ class BuildTarget(Target):
self.process_compilers()
self.process_kwargs(kwargs, environment)
self.check_unknown_kwargs(kwargs)
- if not self.sources and not self.generated and not self.objects:
+ if not any([self.sources, self.generated, self.objects, self.link_whole]):
raise InvalidArguments('Build target %s has no sources.' % name)
self.process_compilers_late()
self.validate_sources()
@@ -682,7 +682,7 @@ class BuildTarget(Target):
if 'd' in self.compilers:
self.add_compiler_args('d', self.compilers['d'].get_feature_args(dfeatures))
- self.link_args = flatten(kwargs.get('link_args', []))
+ self.link_args = extract_as_list(kwargs, 'link_args')
for i in self.link_args:
if not isinstance(i, str):
raise InvalidArguments('Link_args arguments must be strings.')
@@ -856,9 +856,7 @@ You probably should put it in link_with instead.''')
return self.external_deps
def link(self, target):
- for t in flatten(target):
- if hasattr(t, 'held_object'):
- t = t.held_object
+ for t in listify(target, unholder=True):
if not t.is_linkable_target():
raise InvalidArguments('Link target {!r} is not linkable.'.format(t))
if isinstance(self, SharedLibrary) and isinstance(t, StaticLibrary) and not t.pic:
@@ -870,9 +868,7 @@ You probably should put it in link_with instead.''')
self.link_targets.append(t)
def link_whole(self, target):
- for t in flatten(target):
- if hasattr(t, 'held_object'):
- t = t.held_object
+ for t in listify(target, unholder=True):
if not isinstance(t, StaticLibrary):
raise InvalidArguments('{!r} is not a static library.'.format(t))
if isinstance(self, SharedLibrary) and not t.pic:
@@ -915,7 +911,7 @@ You probably should put it in link_with instead.''')
self.include_dirs += ids
def add_compiler_args(self, language, args):
- args = flatten(args)
+ args = listify(args)
for a in args:
if not isinstance(a, (str, File)):
raise InvalidArguments('A non-string passed to compiler args.')
@@ -1546,11 +1542,9 @@ class CustomTarget(Target):
return deps
def flatten_command(self, cmd):
- cmd = listify(cmd)
+ cmd = listify(cmd, unholder=True)
final_cmd = []
for c in cmd:
- if hasattr(c, 'held_object'):
- c = c.held_object
if isinstance(c, str):
final_cmd.append(c)
elif isinstance(c, File):
@@ -1573,12 +1567,7 @@ class CustomTarget(Target):
def process_kwargs(self, kwargs):
super().process_kwargs(kwargs)
- sources = flatten(kwargs.get('input', []))
- self.sources = []
- for s in sources:
- if hasattr(s, 'held_object'):
- s = s.held_object
- self.sources.append(s)
+ self.sources = extract_as_list(kwargs, 'input', unholder=True)
if 'output' not in kwargs:
raise InvalidArguments('Missing keyword argument "output".')
self.outputs = listify(kwargs['output'])
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
index 82b1ef0..3f9ba5c 100644
--- a/mesonbuild/compilers/c.py
+++ b/mesonbuild/compilers/c.py
@@ -172,6 +172,9 @@ class CCompiler(Compiler):
def get_linker_search_args(self, dirname):
return ['-L' + dirname]
+ def get_default_include_dirs(self):
+ return []
+
def gen_import_library_args(self, implibname):
"""
The name of the outputted import library
@@ -1056,3 +1059,34 @@ class VisualStudioCCompiler(CCompiler):
# and the can not be called.
return None
return vs32_instruction_set_args.get(instruction_set, None)
+
+ def get_toolset_version(self):
+ # See boost/config/compiler/visualc.cpp for up to date mapping
+ try:
+ version = int(''.join(self.version.split('.')[0:2]))
+ except:
+ return None
+ if version < 1310:
+ return '7.0'
+ elif version < 1400:
+ return '7.1' # (Visual Studio 2003)
+ elif version < 1500:
+ return '8.0' # (Visual Studio 2005)
+ elif version < 1600:
+ return '9.0' # (Visual Studio 2008)
+ elif version < 1700:
+ return '10.0' # (Visual Studio 2010)
+ elif version < 1800:
+ return '11.0' # (Visual Studio 2012)
+ elif version < 1900:
+ return '12.0' # (Visual Studio 2013)
+ elif version < 1910:
+ return '14.0' # (Visual Studio 2015)
+ elif version < 1920:
+ return '14.1' # (Visual Studio 2017)
+ return None
+
+ def get_default_include_dirs(self):
+ if 'INCLUDE' not in os.environ:
+ return []
+ return os.environ['INCLUDE'].split(os.pathsep)
diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
index 89208e0..3f088b0 100644
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -13,6 +13,7 @@
# limitations under the License.
import contextlib, os.path, re, tempfile
+import subprocess
from ..linkers import StaticLinker
from .. import coredata
@@ -917,6 +918,35 @@ def get_largefile_args(compiler):
# those features explicitly.
return []
+# TODO: The result from calling compiler should be cached. So that calling this
+# function multiple times don't add latency.
+def gnulike_default_include_dirs(compiler, lang):
+ if lang == 'cpp':
+ lang = 'c++'
+ p = subprocess.Popen(
+ compiler + ['-x{}'.format(lang), '-E', '-v', '-'],
+ stdin=subprocess.DEVNULL,
+ stderr=subprocess.PIPE,
+ stdout=subprocess.PIPE
+ )
+ stderr = p.stderr.read().decode('utf-8')
+ parse_state = 0
+ paths = []
+ for line in stderr.split('\n'):
+ if parse_state == 0:
+ if line == '#include "..." search starts here:':
+ parse_state = 1
+ elif parse_state == 1:
+ if line == '#include <...> search starts here:':
+ parse_state = 2
+ else:
+ paths.append(line[1:])
+ elif parse_state == 2:
+ if line == 'End of search list.':
+ break
+ else:
+ paths.append(line[1:])
+ return paths
class GnuCompiler:
# Functionality that is common to all GNU family compilers.
@@ -998,6 +1028,9 @@ class GnuCompiler:
def get_instruction_set_args(self, instruction_set):
return gnulike_instruction_set_args.get(instruction_set, None)
+ def get_default_include_dirs(self):
+ return gnulike_default_include_dirs(self.exelist, self.language)
+
class ClangCompiler:
def __init__(self, clang_type):
@@ -1082,6 +1115,9 @@ class ClangCompiler:
def get_instruction_set_args(self, instruction_set):
return gnulike_instruction_set_args.get(instruction_set, None)
+ def get_default_include_dirs(self):
+ return gnulike_default_include_dirs(self.exelist, self.language)
+
# Tested on linux for ICC 14.0.3, 15.0.6, 16.0.4, 17.0.1
class IntelCompiler:
@@ -1132,3 +1168,6 @@ class IntelCompiler:
# if self.icc_type == ICC_OSX:
# return ['-bundle']
return ['-shared']
+
+ def get_default_include_dirs(self):
+ return gnulike_default_include_dirs(self.exelist, self.language)
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py
index 7c7f986..0d9742d 100644
--- a/mesonbuild/dependencies/base.py
+++ b/mesonbuild/dependencies/base.py
@@ -23,7 +23,7 @@ from enum import Enum
from .. import mlog
from .. import mesonlib
-from ..mesonlib import MesonException, Popen_safe, flatten, version_compare_many, listify
+from ..mesonlib import MesonException, Popen_safe, version_compare_many, listify
# These must be defined in this file to avoid cyclical references.
@@ -586,7 +586,7 @@ class ExtraFrameworkDependency(ExternalDependency):
def get_dep_identifier(name, kwargs, want_cross):
# Need immutable objects since the identifier will be used as a dict key
- version_reqs = flatten(kwargs.get('version', []))
+ version_reqs = listify(kwargs.get('version', []))
if isinstance(version_reqs, list):
version_reqs = frozenset(version_reqs)
identifier = (name, version_reqs, want_cross)
@@ -599,7 +599,7 @@ def get_dep_identifier(name, kwargs, want_cross):
continue
# All keyword arguments are strings, ints, or lists (or lists of lists)
if isinstance(value, list):
- value = frozenset(flatten(value))
+ value = frozenset(listify(value))
identifier += (key, value)
return identifier
diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py
index d2dd107..387300a 100644
--- a/mesonbuild/dependencies/dev.py
+++ b/mesonbuild/dependencies/dev.py
@@ -21,7 +21,7 @@ import shutil
from .. import mlog
from .. import mesonlib
-from ..mesonlib import version_compare, Popen_safe
+from ..mesonlib import version_compare, Popen_safe, stringlistify, extract_as_list
from .base import DependencyException, ExternalDependency, PkgConfigDependency
class GTestDependency(ExternalDependency):
@@ -185,7 +185,7 @@ class LLVMDependency(ExternalDependency):
raise DependencyException('Could not generate modules for LLVM.')
self.modules = shlex.split(out)
- modules = mesonlib.stringlistify(mesonlib.flatten(kwargs.get('modules', [])))
+ modules = stringlistify(extract_as_list(kwargs, 'modules'))
for mod in sorted(set(modules)):
if mod not in self.modules:
mlog.log('LLVM module', mod, 'found:', mlog.red('NO'))
diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py
index c2b6dbd..a0d5e47 100644
--- a/mesonbuild/dependencies/misc.py
+++ b/mesonbuild/dependencies/misc.py
@@ -18,7 +18,6 @@ import glob
import os
import re
import shlex
-import stat
import shutil
import sysconfig
@@ -30,77 +29,153 @@ from ..environment import detect_cpu_family
from .base import DependencyException, DependencyMethods
from .base import ExternalDependency, ExternalProgram, ExtraFrameworkDependency, PkgConfigDependency
+# On windows 3 directory layouts are supported:
+# * The default layout (versioned) installed:
+# - $BOOST_ROOT/include/boost-x_x/boost/*.hpp
+# - $BOOST_ROOT/lib/*.lib
+# * The non-default layout (system) installed:
+# - $BOOST_ROOT/include/boost/*.hpp
+# - $BOOST_ROOT/lib/*.lib
+# * The pre-built binaries from sf.net:
+# - $BOOST_ROOT/boost/*.hpp
+# - $BOOST_ROOT/lib<arch>-<compiler>/*.lib where arch=32/64 and compiler=msvc-14.1
+#
+# Library names supported:
+# - libboost_<module>-<compiler>-mt-gd-x_x.lib (static)
+# - boost_<module>-<compiler>-mt-gd-x_x.lib|.dll (shared)
+# - libboost_<module>.lib (static)
+# - boost_<module>.lib|.dll (shared)
+# where compiler is vc141 for example.
+#
+# NOTE: -gb means runtime and build time debugging is on
+# -mt means threading=multi
+#
+# The `modules` argument accept library names. This is because every module that
+# has libraries to link against also has multiple options regarding how to
+# link. See for example:
+# * http://www.boost.org/doc/libs/1_65_1/libs/test/doc/html/boost_test/usage_variants.html
+# * http://www.boost.org/doc/libs/1_65_1/doc/html/stacktrace/configuration_and_build.html
+# * http://www.boost.org/doc/libs/1_65_1/libs/math/doc/html/math_toolkit/main_tr1.html
class BoostDependency(ExternalDependency):
- # Some boost libraries have different names for
- # their sources and libraries. This dict maps
- # between the two.
- name2lib = {'test': 'unit_test_framework'}
-
def __init__(self, environment, kwargs):
super().__init__('boost', environment, 'cpp', kwargs)
+ self.need_static_link = ['boost_exception', 'boost_test_exec_monitor']
+ self.is_debug = environment.cmd_line_options.buildtype.startswith('debug')
+ threading = kwargs.get("threading", "multi")
+ self.is_multithreading = threading == "multi"
+
+ self.requested_modules = self.get_requested(kwargs)
+
+ self.boost_root = None
+ self.boost_roots = []
+ self.incdir = None
self.libdir = None
- try:
+
+ if 'BOOST_ROOT' in os.environ:
self.boost_root = os.environ['BOOST_ROOT']
+ self.boost_roots = [self.boost_root]
if not os.path.isabs(self.boost_root):
raise DependencyException('BOOST_ROOT must be an absolute path.')
- except KeyError:
- self.boost_root = None
+ if 'BOOST_INCLUDEDIR' in os.environ:
+ self.incdir = os.environ['BOOST_INCLUDEDIR']
+ if 'BOOST_LIBRARYDIR' in os.environ:
+ self.libdir = os.environ['BOOST_LIBRARYDIR']
+
+ if self.want_cross and self.boost_root is None and self.incdir is None:
+ raise DependencyException('BOOST_ROOT or BOOST_INCLUDEDIR is needed while cross-compiling')
+
if self.boost_root is None:
- if self.want_cross:
- if 'BOOST_INCLUDEDIR' in os.environ:
- self.incdir = os.environ['BOOST_INCLUDEDIR']
- else:
- raise DependencyException('BOOST_ROOT or BOOST_INCLUDEDIR is needed while cross-compiling')
if mesonlib.is_windows():
- self.boost_root = self.detect_win_root()
- self.incdir = self.boost_root
+ self.boost_roots = self.detect_win_roots()
else:
- if 'BOOST_INCLUDEDIR' in os.environ:
- self.incdir = os.environ['BOOST_INCLUDEDIR']
- else:
- self.incdir = '/usr/include'
+ self.boost_roots = self.detect_nix_roots()
+
+ if self.boost_root is None and not self.boost_roots:
+ self.log_fail()
+ return
+
+ if self.incdir is None:
+ if mesonlib.is_windows():
+ self.incdir = self.detect_win_incdir()
+ else:
+ self.incdir = self.detect_nix_incdir()
+
+ if self.incdir is None:
+ 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))
- if 'BOOST_LIBRARYDIR' in os.environ:
- self.libdir = os.environ['BOOST_LIBRARYDIR']
- else:
- self.incdir = os.path.join(self.boost_root, 'include')
- self.boost_inc_subdir = os.path.join(self.incdir, 'boost')
- mlog.debug('Boost library root dir is', self.boost_root)
- self.src_modules = {}
self.lib_modules = {}
- self.lib_modules_mt = {}
self.detect_version()
- self.requested_modules = self.get_requested(kwargs)
- module_str = ', '.join(self.requested_modules)
if self.is_found:
- self.detect_src_modules()
self.detect_lib_modules()
+ mlog.debug('Boost library directory is', mlog.bold(self.libdir))
self.validate_requested()
- if self.boost_root is not None:
- info = self.version + ', ' + self.boost_root
- else:
- info = self.version
- mlog.log('Dependency Boost (%s) found:' % module_str, mlog.green('YES'), info)
+ self.log_success()
+ else:
+ self.log_fail()
+
+ def log_fail(self):
+ module_str = ', '.join(self.requested_modules)
+ mlog.log("Dependency Boost (%s) found:" % module_str, mlog.red('NO'))
+
+ def log_success(self):
+ module_str = ', '.join(self.requested_modules)
+ if self.boost_root:
+ info = self.version + ', ' + self.boost_root
else:
- mlog.log("Dependency Boost (%s) found:" % module_str, mlog.red('NO'))
+ info = self.version
+ mlog.log('Dependency Boost (%s) found:' % module_str, mlog.green('YES'), info)
+
+ def detect_nix_roots(self):
+ return ['/usr/local', '/usr']
- def detect_win_root(self):
- globtext = 'c:\\local\\boost_*'
+ def detect_win_roots(self):
+ res = []
+ # Where boost documentation says it should be
+ globtext = 'C:\\Program Files\\boost\\boost_*'
files = glob.glob(globtext)
- if len(files) > 0:
- return files[0]
- return 'C:\\'
+ res.extend(files)
+
+ # Where boost built from source actually installs it
+ if os.path.isdir('C:\\Boost'):
+ res.append('C:\\Boost')
+
+ # Where boost prebuilt binaries are
+ globtext = 'C:\\local\\boost_*'
+ files = glob.glob(globtext)
+ res.extend(files)
+ return res
+
+ def detect_nix_incdir(self):
+ for root in self.boost_roots:
+ incdir = os.path.join(root, 'include', 'boost')
+ if os.path.isdir(incdir):
+ return os.path.join(root, 'include')
+ return None
+
+ # FIXME: Should pick a version that matches the requested version
+ # Returns the folder that contains the boost folder.
+ def detect_win_incdir(self):
+ for root in self.boost_roots:
+ globtext = os.path.join(root, 'include', 'boost-*')
+ incdirs = glob.glob(globtext)
+ if len(incdirs) > 0:
+ return incdirs[0]
+ incboostdir = os.path.join(root, 'include', 'boost')
+ if os.path.isdir(incboostdir):
+ return os.path.join(root, 'include')
+ incboostdir = os.path.join(root, 'boost')
+ if os.path.isdir(incboostdir):
+ return root
+ return None
def get_compile_args(self):
args = []
- if self.boost_root is not None:
- if mesonlib.is_windows():
- include_dir = self.boost_root
- else:
- include_dir = os.path.join(self.boost_root, 'include')
- else:
- include_dir = self.incdir
+ include_dir = self.incdir
# Use "-isystem" when including boost headers instead of "-I"
# to avoid compiler warnings/failures when "-Werror" is used
@@ -116,18 +191,7 @@ class BoostDependency(ExternalDependency):
# and http://stackoverflow.com/questions/37218953/isystem-on-a-system-include-directory-causes-errors
# for more details
- # TODO: The correct solution would probably be to ask the
- # compiler for it's default include paths (ie: "gcc -xc++ -E
- # -v -") and avoid including those with -isystem
-
- # For now, use -isystem for all includes except for some
- # typical defaults (which don't need to be included at all
- # since they are in the default include paths). These typical
- # defaults include the usual directories at the root of the
- # filesystem, but also any path that ends with those directory
- # names in order to handle cases like cross-compiling where we
- # might have a different sysroot.
- if not include_dir.endswith(('/usr/include', '/usr/local/include')):
+ if include_dir and include_dir not in self.compiler.get_default_include_dirs():
args.append("".join(self.compiler.get_include_args(include_dir, True)))
return args
@@ -136,19 +200,23 @@ class BoostDependency(ExternalDependency):
for c in candidates:
if not isinstance(c, str):
raise DependencyException('Boost module argument is not a string.')
+ if 'boost_' + c not in BOOST_LIBS:
+ raise DependencyException('Dependency {} not found. It is not a valid boost library.'.format(c))
return candidates
def validate_requested(self):
for m in self.requested_modules:
- if m not in self.src_modules and m not in self.lib_modules and m + '-mt' not in self.lib_modules_mt:
- msg = 'Requested Boost module {!r} not found'
+ if 'boost_' + m not in self.lib_modules:
+ msg = 'Requested Boost library {!r} not found'
raise DependencyException(msg.format(m))
def detect_version(self):
try:
- ifile = open(os.path.join(self.boost_inc_subdir, 'version.hpp'))
+ ifile = open(os.path.join(self.incdir, 'boost', 'version.hpp'))
except FileNotFoundError:
return
+ except TypeError:
+ return
with ifile:
for line in ifile:
if line.startswith("#define") and 'BOOST_LIB_VERSION' in line:
@@ -158,12 +226,6 @@ class BoostDependency(ExternalDependency):
self.is_found = True
return
- def detect_src_modules(self):
- for entry in os.listdir(self.boost_inc_subdir):
- entry = os.path.join(self.boost_inc_subdir, entry)
- if stat.S_ISDIR(os.stat(entry).st_mode):
- self.src_modules[os.path.split(entry)[-1]] = True
-
def detect_lib_modules(self):
if mesonlib.is_windows():
return self.detect_lib_modules_win()
@@ -171,32 +233,79 @@ class BoostDependency(ExternalDependency):
def detect_lib_modules_win(self):
arch = detect_cpu_family(self.env.coredata.compilers)
- # Guess the libdir
- if arch == 'x86':
- gl = 'lib32*'
- elif arch == 'x86_64':
- gl = 'lib64*'
- else:
- # Does anyone do Boost cross-compiling to other archs on Windows?
- gl = None
- # See if the libdir is valid
- if gl:
- libdir = glob.glob(os.path.join(self.boost_root, gl))
- else:
- libdir = []
- # Can't find libdir, bail
- if not libdir:
+ compiler_ts = self.env.detect_cpp_compiler(self.want_cross).get_toolset_version().split('.')
+ compiler = 'vc{}{}'.format(compiler_ts[0], compiler_ts[1])
+ if not self.libdir:
+ # The libdirs in the distributed binaries
+ if arch == 'x86':
+ gl = 'lib32*'
+ elif arch == 'x86_64':
+ gl = 'lib64*'
+ else:
+ # Does anyone do Boost cross-compiling to other archs on Windows?
+ gl = None
+ if self.boost_root:
+ roots = [self.boost_root]
+ else:
+ roots = self.boost_roots
+ for root in roots:
+ # The default libdir when building
+ libdir = os.path.join(root, 'lib')
+ if os.path.isdir(libdir):
+ self.libdir = libdir
+ break
+ if gl:
+ tmp = glob.glob(os.path.join(root, gl))
+ if len(tmp) > 0:
+ # FIXME: Should pick the correct version
+ self.libdir = tmp[0]
+ break
+
+ if not self.libdir:
return
- libdir = libdir[0]
- # Don't override what was set in the environment
- if self.libdir:
- self.libdir = libdir
- globber = 'libboost_*-gd-*.lib' if self.static else 'boost_*-gd-*.lib' # FIXME
- for entry in glob.glob(os.path.join(libdir, globber)):
+
+ for name in self.need_static_link:
+ libname = "lib{}".format(name) + '-' + compiler
+ if self.is_multithreading:
+ libname = libname + '-mt'
+ if self.is_debug:
+ libname = libname + '-gd'
+ libname = libname + "-{}.lib".format(self.version.replace('.', '_'))
+ if os.path.isfile(os.path.join(self.libdir, libname)):
+ modname = libname.split('-', 1)[0][3:]
+ self.lib_modules[modname] = libname
+ else:
+ libname = "lib{}.lib".format(name)
+ if os.path.isfile(os.path.join(self.libdir, libname)):
+ self.lib_modules[name[3:]] = libname
+
+ # globber1 applies to a layout=system installation
+ # globber2 applies to a layout=versioned installation
+ globber1 = 'libboost_*' if self.static else 'boost_*'
+ globber2 = globber1 + '-' + compiler
+ if self.is_multithreading:
+ globber2 = globber2 + '-mt'
+ if self.is_debug:
+ globber2 = globber2 + '-gd'
+ globber2 = globber2 + '-{}'.format(self.version.replace('.', '_'))
+ globber2_matches = glob.glob(os.path.join(self.libdir, globber2 + '.lib'))
+ for entry in globber2_matches:
(_, fname) = os.path.split(entry)
- base = fname.split('_', 1)[1]
- modname = base.split('-', 1)[0]
- self.lib_modules_mt[modname] = fname
+ modname = fname.split('-', 1)
+ if len(modname) > 1:
+ modname = modname[0]
+ else:
+ modname = modname.split('.', 1)[0]
+ if self.static:
+ modname = modname[3:]
+ self.lib_modules[modname] = fname
+ if len(globber2_matches) == 0:
+ for entry in glob.glob(os.path.join(self.libdir, globber1 + '.lib')):
+ (_, fname) = os.path.split(entry)
+ modname = fname.split('.', 1)[0]
+ if self.static:
+ modname = modname[3:]
+ self.lib_modules[modname] = fname
def detect_lib_modules_nix(self):
if self.static:
@@ -214,25 +323,32 @@ class BoostDependency(ExternalDependency):
else:
libdirs = [os.path.join(self.boost_root, 'lib')]
for libdir in libdirs:
+ for name in self.need_static_link:
+ libname = 'lib{}.a'.format(name)
+ if os.path.isfile(os.path.join(libdir, libname)):
+ self.lib_modules[name] = libname
for entry in glob.glob(os.path.join(libdir, globber)):
lib = os.path.basename(entry)
- name = lib.split('.')[0].split('_', 1)[-1]
+ name = lib.split('.')[0][3:]
# I'm not 100% sure what to do here. Some distros
# have modules such as thread only as -mt versions.
- if entry.endswith('-mt.{}'.format(libsuffix)):
- self.lib_modules_mt[name] = True
- else:
- self.lib_modules[name] = True
+ # On debian all packages are built threading=multi
+ # but not suffixed with -mt.
+ # FIXME: implement detect_lib_modules_{debian, redhat, ...}
+ if self.is_multithreading and mesonlib.is_debianlike():
+ self.lib_modules[name] = lib
+ elif self.is_multithreading and entry.endswith('-mt.{}'.format(libsuffix)):
+ self.lib_modules[name] = lib
+ elif not entry.endswith('-mt.{}'.format(libsuffix)):
+ self.lib_modules[name] = lib
def get_win_link_args(self):
args = []
# TODO: should this check self.libdir?
- if self.boost_root:
+ if self.libdir:
args.append('-L' + self.libdir)
- for module in self.requested_modules:
- module = BoostDependency.name2lib.get(module, module)
- if module in self.lib_modules_mt:
- args.append(self.lib_modules_mt[module])
+ for lib in self.requested_modules:
+ args.append(self.lib_modules['boost_' + lib])
return args
def get_link_args(self):
@@ -243,33 +359,14 @@ class BoostDependency(ExternalDependency):
args.append('-L' + os.path.join(self.boost_root, 'lib'))
elif self.libdir:
args.append('-L' + self.libdir)
- for module in self.requested_modules:
- module = BoostDependency.name2lib.get(module, module)
- libname = 'boost_' + module
+ for lib in self.requested_modules:
# The compiler's library detector is the most reliable so use that first.
- default_detect = self.compiler.find_library(libname, self.env, [])
+ default_detect = self.compiler.find_library('boost_' + lib, self.env, [])
if default_detect is not None:
- if module == 'unit_testing_framework':
- emon_args = self.compiler.find_library('boost_test_exec_monitor')
- else:
- emon_args = None
args += default_detect
- if emon_args is not None:
- args += emon_args
- elif module in self.lib_modules or module in self.lib_modules_mt:
- linkcmd = '-l' + libname
- args.append(linkcmd)
- # FIXME a hack, but Boost's testing framework has a lot of
- # different options and it's hard to determine what to do
- # without feedback from actual users. Update this
- # as we get more bug reports.
- if module == 'unit_testing_framework':
- args.append('-lboost_test_exec_monitor')
- elif module + '-mt' in self.lib_modules_mt:
- linkcmd = '-lboost_' + module + '-mt'
+ elif lib in self.lib_modules:
+ linkcmd = '-l' + lib
args.append(linkcmd)
- if module == 'unit_testing_framework':
- args.append('-lboost_test_exec_monitor-mt')
return args
def get_sources(self):
@@ -664,3 +761,78 @@ class CupsDependency(ExternalDependency):
return [DependencyMethods.PKGCONFIG, DependencyMethods.CUPSCONFIG, DependencyMethods.EXTRAFRAMEWORK]
else:
return [DependencyMethods.PKGCONFIG, DependencyMethods.CUPSCONFIG]
+
+# Generated with boost_names.py
+BOOST_LIBS = [
+ 'boost_atomic',
+ 'boost_chrono',
+ 'boost_chrono',
+ 'boost_container',
+ 'boost_context',
+ 'boost_coroutine',
+ 'boost_date_time',
+ 'boost_exception',
+ 'boost_fiber',
+ 'boost_filesystem',
+ 'boost_graph',
+ 'boost_iostreams',
+ 'boost_locale',
+ 'boost_log',
+ 'boost_log_setup',
+ 'boost_math_tr1',
+ 'boost_math_tr1f',
+ 'boost_math_tr1l',
+ 'boost_math_c99',
+ 'boost_math_c99f',
+ 'boost_math_c99l',
+ 'boost_math_tr1',
+ 'boost_math_tr1f',
+ 'boost_math_tr1l',
+ 'boost_math_c99',
+ 'boost_math_c99f',
+ 'boost_math_c99l',
+ 'boost_math_tr1',
+ 'boost_math_tr1f',
+ 'boost_math_tr1l',
+ 'boost_math_c99',
+ 'boost_math_c99f',
+ 'boost_math_c99l',
+ 'boost_math_tr1',
+ 'boost_math_tr1f',
+ 'boost_math_tr1l',
+ 'boost_math_c99',
+ 'boost_math_c99f',
+ 'boost_math_c99l',
+ 'boost_math_tr1',
+ 'boost_math_tr1f',
+ 'boost_math_tr1l',
+ 'boost_math_c99',
+ 'boost_math_c99f',
+ 'boost_math_c99l',
+ 'boost_math_tr1',
+ 'boost_math_tr1f',
+ 'boost_math_tr1l',
+ 'boost_math_c99',
+ 'boost_math_c99f',
+ 'boost_math_c99l',
+ 'boost_mpi',
+ 'boost_random',
+ 'boost_regex',
+ 'boost_serialization',
+ 'boost_wserialization',
+ 'boost_signals',
+ 'boost_stacktrace_noop',
+ 'boost_stacktrace_backtrace',
+ 'boost_stacktrace_addr2line',
+ 'boost_stacktrace_basic',
+ 'boost_stacktrace_windbg',
+ 'boost_stacktrace_windbg_cached',
+ 'boost_system',
+ 'boost_prg_exec_monitor',
+ 'boost_test_exec_monitor',
+ 'boost_unit_test_framework',
+ 'boost_thread',
+ 'boost_timer',
+ 'boost_type_erasure',
+ 'boost_wave'
+]
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 8197b5e..b938080 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -48,6 +48,14 @@ def stringifyUserArguments(args):
raise InvalidArguments('Function accepts only strings, integers, lists and lists thereof.')
+class ObjectHolder:
+ def __init__(self, obj):
+ self.held_object = obj
+
+ def __repr__(self):
+ return '<Holder: {!r}>'.format(self.held_object)
+
+
class TryRunResultHolder(InterpreterObject):
def __init__(self, res):
super().__init__()
@@ -117,17 +125,18 @@ class RunProcess(InterpreterObject):
def stderr_method(self, args, kwargs):
return self.stderr
-class ConfigureFileHolder(InterpreterObject):
+class ConfigureFileHolder(InterpreterObject, ObjectHolder):
def __init__(self, subdir, sourcename, targetname, configuration_data):
InterpreterObject.__init__(self)
- self.held_object = build.ConfigureFile(subdir, sourcename, targetname, configuration_data)
+ ObjectHolder.__init__(self, build.ConfigureFile(subdir, sourcename,
+ targetname, configuration_data))
-class EnvironmentVariablesHolder(MutableInterpreterObject):
+class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder):
def __init__(self):
- super().__init__()
- self.held_object = build.EnvironmentVariables()
+ MutableInterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, build.EnvironmentVariables())
self.methods.update({'set': self.set_method,
'append': self.append_method,
'prepend': self.prepend_method,
@@ -158,11 +167,11 @@ class EnvironmentVariablesHolder(MutableInterpreterObject):
self.add_var(self.held_object.prepend, args, kwargs)
-class ConfigurationDataHolder(MutableInterpreterObject):
+class ConfigurationDataHolder(MutableInterpreterObject, ObjectHolder):
def __init__(self):
- super().__init__()
+ MutableInterpreterObject.__init__(self)
self.used = False # These objects become immutable after use in configure_file.
- self.held_object = build.ConfigurationData()
+ ObjectHolder.__init__(self, build.ConfigurationData())
self.methods.update({'set': self.set_method,
'set10': self.set10_method,
'set_quoted': self.set_quoted_method,
@@ -242,10 +251,10 @@ class ConfigurationDataHolder(MutableInterpreterObject):
# Interpreter objects can not be pickled so we must have
# these wrappers.
-class DependencyHolder(InterpreterObject):
+class DependencyHolder(InterpreterObject, ObjectHolder):
def __init__(self, dep):
InterpreterObject.__init__(self)
- self.held_object = dep
+ ObjectHolder.__init__(self, dep)
self.methods.update({'found': self.found_method,
'type_name': self.type_name_method,
'version': self.version_method,
@@ -272,10 +281,10 @@ class DependencyHolder(InterpreterObject):
raise InterpreterException('Variable name must be a string.')
return self.held_object.get_pkgconfig_variable(varname)
-class InternalDependencyHolder(InterpreterObject):
+class InternalDependencyHolder(InterpreterObject, ObjectHolder):
def __init__(self, dep):
InterpreterObject.__init__(self)
- self.held_object = dep
+ ObjectHolder.__init__(self, dep)
self.methods.update({'found': self.found_method,
'version': self.version_method,
})
@@ -286,10 +295,10 @@ class InternalDependencyHolder(InterpreterObject):
def version_method(self, args, kwargs):
return self.held_object.get_version()
-class ExternalProgramHolder(InterpreterObject):
+class ExternalProgramHolder(InterpreterObject, ObjectHolder):
def __init__(self, ep):
InterpreterObject.__init__(self)
- self.held_object = ep
+ ObjectHolder.__init__(self, ep)
self.methods.update({'found': self.found_method,
'path': self.path_method})
@@ -308,10 +317,10 @@ class ExternalProgramHolder(InterpreterObject):
def get_name(self):
return self.held_object.get_name()
-class ExternalLibraryHolder(InterpreterObject):
+class ExternalLibraryHolder(InterpreterObject, ObjectHolder):
def __init__(self, el):
InterpreterObject.__init__(self)
- self.held_object = el
+ ObjectHolder.__init__(self, el)
self.methods.update({'found': self.found_method})
def found(self):
@@ -332,11 +341,11 @@ class ExternalLibraryHolder(InterpreterObject):
def get_exe_args(self):
return self.held_object.get_exe_args()
-class GeneratorHolder(InterpreterObject):
+class GeneratorHolder(InterpreterObject, ObjectHolder):
def __init__(self, interpreter, args, kwargs):
- super().__init__()
+ InterpreterObject.__init__(self)
self.interpreter = interpreter
- self.held_object = build.Generator(args, kwargs)
+ ObjectHolder.__init__(self, build.Generator(args, kwargs))
self.methods.update({'process': self.process_method})
def process_method(self, args, kwargs):
@@ -345,13 +354,13 @@ class GeneratorHolder(InterpreterObject):
return GeneratedListHolder(gl)
-class GeneratedListHolder(InterpreterObject):
+class GeneratedListHolder(InterpreterObject, ObjectHolder):
def __init__(self, arg1, extra_args=[]):
- super().__init__()
+ InterpreterObject.__init__(self)
if isinstance(arg1, GeneratorHolder):
- self.held_object = build.GeneratedList(arg1.held_object, extra_args)
+ ObjectHolder.__init__(self, build.GeneratedList(arg1.held_object, extra_args))
else:
- self.held_object = arg1
+ ObjectHolder.__init__(self, arg1)
def __repr__(self):
r = '<{}: {!r}>'
@@ -360,14 +369,15 @@ class GeneratedListHolder(InterpreterObject):
def add_file(self, a):
self.held_object.add_file(a)
-class BuildMachine(InterpreterObject):
+class BuildMachine(InterpreterObject, ObjectHolder):
def __init__(self, compilers):
self.compilers = compilers
InterpreterObject.__init__(self)
- self.held_object = environment.MachineInfo(environment.detect_system(),
- environment.detect_cpu_family(self.compilers),
- environment.detect_cpu(self.compilers),
- sys.byteorder)
+ held_object = environment.MachineInfo(environment.detect_system(),
+ environment.detect_cpu_family(self.compilers),
+ environment.detect_cpu(self.compilers),
+ sys.byteorder)
+ ObjectHolder.__init__(self, held_object)
self.methods.update({'system': self.system_method,
'cpu_family': self.cpu_family_method,
'cpu': self.cpu_method,
@@ -388,7 +398,7 @@ class BuildMachine(InterpreterObject):
# This class will provide both host_machine and
# target_machine
-class CrossMachineInfo(InterpreterObject):
+class CrossMachineInfo(InterpreterObject, ObjectHolder):
def __init__(self, cross_info):
InterpreterObject.__init__(self)
minimum_cross_info = {'cpu', 'cpu_family', 'endian', 'system'}
@@ -397,10 +407,11 @@ class CrossMachineInfo(InterpreterObject):
'Machine info is currently {}\n'.format(cross_info) +
'but is missing {}.'.format(minimum_cross_info - set(cross_info)))
self.info = cross_info
- self.held_object = environment.MachineInfo(cross_info['system'],
- cross_info['cpu_family'],
- cross_info['cpu'],
- cross_info['endian'])
+ minfo = environment.MachineInfo(cross_info['system'],
+ cross_info['cpu_family'],
+ cross_info['cpu'],
+ cross_info['endian'])
+ ObjectHolder.__init__(self, minfo)
self.methods.update({'system': self.system_method,
'cpu': self.cpu_method,
'cpu_family': self.cpu_family_method,
@@ -419,10 +430,10 @@ class CrossMachineInfo(InterpreterObject):
def endian_method(self, args, kwargs):
return self.held_object.endian
-class IncludeDirsHolder(InterpreterObject):
+class IncludeDirsHolder(InterpreterObject, ObjectHolder):
def __init__(self, idobj):
- super().__init__()
- self.held_object = idobj
+ InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, idobj)
class Headers(InterpreterObject):
@@ -447,10 +458,10 @@ class Headers(InterpreterObject):
def get_custom_install_dir(self):
return self.custom_install_dir
-class DataHolder(InterpreterObject):
+class DataHolder(InterpreterObject, ObjectHolder):
def __init__(self, data):
- super().__init__()
- self.held_object = data
+ InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, data)
def get_source_subdir(self):
return self.held_object.source_subdir
@@ -495,20 +506,20 @@ class Man(InterpreterObject):
def get_sources(self):
return self.sources
-class GeneratedObjectsHolder(InterpreterObject):
+class GeneratedObjectsHolder(InterpreterObject, ObjectHolder):
def __init__(self, held_object):
- super().__init__()
- self.held_object = held_object
+ InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, held_object)
-class TargetHolder(InterpreterObject):
- def __init__(self):
- super().__init__()
+class TargetHolder(InterpreterObject, ObjectHolder):
+ def __init__(self, target, interp):
+ InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, target)
+ self.interpreter = interp
class BuildTargetHolder(TargetHolder):
def __init__(self, target, interp):
- super().__init__()
- self.held_object = target
- self.interpreter = interp
+ super().__init__(target, interp)
self.methods.update({'extract_objects': self.extract_objects_method,
'extract_all_objects': self.extract_all_objects_method,
'get_id': self.get_id_method,
@@ -566,16 +577,14 @@ class JarHolder(BuildTargetHolder):
def __init__(self, target, interp):
super().__init__(target, interp)
-class CustomTargetIndexHolder(InterpreterObject):
+class CustomTargetIndexHolder(InterpreterObject, ObjectHolder):
def __init__(self, object_to_hold):
- super().__init__()
- self.held_object = object_to_hold
+ InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, object_to_hold)
class CustomTargetHolder(TargetHolder):
- def __init__(self, object_to_hold, interp):
- super().__init__()
- self.held_object = object_to_hold
- self.interpreter = interp
+ def __init__(self, target, interp):
+ super().__init__(target, interp)
self.methods.update({'full_path': self.full_path_method,
})
@@ -596,10 +605,10 @@ class CustomTargetHolder(TargetHolder):
def __delitem__(self, index):
raise InterpreterException('Cannot delete a member of a CustomTarget')
-class RunTargetHolder(InterpreterObject):
+class RunTargetHolder(InterpreterObject, ObjectHolder):
def __init__(self, name, command, args, dependencies, subdir):
- super().__init__()
- self.held_object = build.RunTarget(name, command, args, dependencies, subdir)
+ InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, build.RunTarget(name, command, args, dependencies, subdir))
def __repr__(self):
r = '<{} {}: {}>'
@@ -625,11 +634,11 @@ class Test(InterpreterObject):
def get_name(self):
return self.name
-class SubprojectHolder(InterpreterObject):
+class SubprojectHolder(InterpreterObject, ObjectHolder):
def __init__(self, subinterpreter):
- super().__init__()
- self.held_object = subinterpreter
+ InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, subinterpreter)
self.methods.update({'get_variable': self.get_variable_method,
})
@@ -1056,11 +1065,11 @@ ModuleState = namedtuple('ModuleState', [
'man', 'global_args', 'project_args', 'build_machine', 'host_machine',
'target_machine'])
-class ModuleHolder(InterpreterObject):
+class ModuleHolder(InterpreterObject, ObjectHolder):
def __init__(self, modname, module, interpreter):
InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, module)
self.modname = modname
- self.held_object = module
self.interpreter = interpreter
def method_call(self, method_name, args, kwargs):
@@ -1551,12 +1560,11 @@ class Interpreter(InterpreterBase):
version = kwargs.get('version', self.project_version)
if not isinstance(version, str):
raise InterpreterException('Version must be a string.')
- incs = extract_as_list(kwargs, 'include_directories')
- libs = extract_as_list(kwargs, 'link_with')
+ incs = extract_as_list(kwargs, 'include_directories', unholder=True)
+ libs = extract_as_list(kwargs, 'link_with', unholder=True)
sources = extract_as_list(kwargs, 'sources')
- sources = self.source_strings_to_files(self.flatten(sources))
- deps = self.flatten(kwargs.get('dependencies', []))
- deps = listify(deps)
+ sources = listify(self.source_strings_to_files(sources), unholder=True)
+ deps = extract_as_list(kwargs, 'dependencies', unholder=True)
compile_args = mesonlib.stringlistify(kwargs.get('compile_args', []))
link_args = mesonlib.stringlistify(kwargs.get('link_args', []))
final_deps = []
@@ -1568,13 +1576,8 @@ class Interpreter(InterpreterBase):
if not isinstance(d, (dependencies.Dependency, dependencies.ExternalLibrary, dependencies.InternalDependency)):
raise InterpreterException('Dependencies must be external deps')
final_deps.append(d)
- dep = dependencies.InternalDependency(version,
- mesonlib.unholder_array(incs),
- compile_args,
- link_args,
- mesonlib.unholder_array(libs),
- mesonlib.unholder_array(sources),
- final_deps)
+ dep = dependencies.InternalDependency(version, incs, compile_args,
+ link_args, libs, sources, final_deps)
return DependencyHolder(dep)
@noKwargs
@@ -1629,7 +1632,7 @@ class Interpreter(InterpreterBase):
'or not executable'.format(cmd))
cmd = prog
expanded_args = []
- for a in mesonlib.flatten(cargs):
+ for a in listify(cargs):
if isinstance(a, str):
expanded_args.append(a)
elif isinstance(a, mesonlib.File):
@@ -1700,6 +1703,9 @@ class Interpreter(InterpreterBase):
if len(args) != 1:
raise InterpreterException('Argument required for get_option.')
optname = args[0]
+ if ':' in optname:
+ raise InterpreterException('''Having a colon in option name is forbidden, projects are not allowed
+to directly access options of other subprojects.''')
try:
return self.environment.get_coredata().base_options[optname].value
except KeyError:
@@ -2296,11 +2302,7 @@ class Interpreter(InterpreterBase):
raise InterpreterException('Run_target needs at least one positional argument.')
cleaned_args = []
- for i in mesonlib.flatten(all_args):
- try:
- i = i.held_object
- except AttributeError:
- pass
+ for i in listify(all_args, unholder=True):
if not isinstance(i, (str, build.BuildTarget, build.CustomTarget, dependencies.ExternalProgram, mesonlib.File)):
mlog.debug('Wrong type:', str(i))
raise InterpreterException('Invalid argument to run_target.')
@@ -2371,11 +2373,10 @@ class Interpreter(InterpreterBase):
par = kwargs.get('is_parallel', True)
if not isinstance(par, bool):
raise InterpreterException('Keyword argument is_parallel must be a boolean.')
- cmd_args = extract_as_list(kwargs, 'args')
+ cmd_args = extract_as_list(kwargs, 'args', unholder=True)
for i in cmd_args:
- if not isinstance(i, (str, mesonlib.File, TargetHolder)):
+ if not isinstance(i, (str, mesonlib.File, build.Target)):
raise InterpreterException('Command line arguments must be strings, files or targets.')
- cmd_args = mesonlib.unholder_array(cmd_args)
env = self.unpack_env_kwarg(kwargs)
should_fail = kwargs.get('should_fail', False)
if not isinstance(should_fail, bool):
@@ -2793,7 +2794,8 @@ different subdirectory.
elif isinstance(s, str):
s = mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, s)
else:
- raise InterpreterException("Source item is not string or File-type object.")
+ raise InterpreterException('Source item is {!r} instead of '
+ 'string or File-type object'.format(s))
results.append(s)
return results
@@ -2819,7 +2821,7 @@ different subdirectory.
if not args:
raise InterpreterException('Target does not have a name.')
name = args[0]
- sources = args[1:]
+ sources = listify(args[1:])
if self.environment.is_cross_build():
if kwargs.get('native', False):
is_cross = False
@@ -2827,19 +2829,14 @@ different subdirectory.
is_cross = True
else:
is_cross = False
- try:
- kw_src = self.flatten(kwargs['sources'])
- kw_src = listify(kw_src)
- except KeyError:
- kw_src = []
- sources += kw_src
+ if 'sources' in kwargs:
+ sources += listify(kwargs['sources'])
sources = self.source_strings_to_files(sources)
- objs = self.flatten(kwargs.get('objects', []))
- kwargs['dependencies'] = self.flatten(kwargs.get('dependencies', []))
+ objs = extract_as_list(kwargs, 'objects')
+ kwargs['dependencies'] = extract_as_list(kwargs, 'dependencies')
if 'extra_files' in kwargs:
ef = extract_as_list(kwargs, 'extra_files')
kwargs['extra_files'] = self.source_strings_to_files(ef)
- objs = listify(objs)
self.check_sources_exist(os.path.join(self.source_root, self.subdir), sources)
if targetholder is ExecutableHolder:
targetclass = build.Executable
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index 71134a8..5c4c374 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -199,17 +199,6 @@ def classify_unity_sources(compilers, sources):
compsrclist[comp].append(src)
return compsrclist
-def flatten(item):
- if not isinstance(item, list):
- return [item]
- result = []
- for i in item:
- if isinstance(i, list):
- result += flatten(i)
- else:
- result.append(i)
- return result
-
def is_osx():
return platform.system().lower() == 'darwin'
@@ -474,24 +463,45 @@ def replace_if_different(dst, dst_tmp):
else:
os.unlink(dst_tmp)
-
-def listify(*args):
+def listify(item, flatten=True, unholder=False):
'''
- Returns a list with all args embedded in a list if they are not of type list.
+ Returns a list with all args embedded in a list if they are not a list.
This function preserves order.
+ @flatten: Convert lists of lists to a flat list
+ @unholder: Replace each item with the object it holds, if required
+
+ Note: unholding only works recursively when flattening
'''
- if len(args) == 1: # Special case with one single arg
- return args[0] if type(args[0]) is list else [args[0]]
- return [item if type(item) is list else [item] for item in args]
+ if not isinstance(item, list):
+ if unholder and hasattr(item, 'held_object'):
+ item = item.held_object
+ return [item]
+ result = []
+ for i in item:
+ if unholder and hasattr(i, 'held_object'):
+ i = i.held_object
+ if flatten and isinstance(i, list):
+ result += listify(i, flatten=True, unholder=unholder)
+ else:
+ result.append(i)
+ return result
-def extract_as_list(dict_object, *keys, pop = False):
+def extract_as_list(dict_object, *keys, pop=False, **kwargs):
'''
Extracts all values from given dict_object and listifies them.
'''
+ result = []
+ fetch = dict_object.get
if pop:
- return listify(*[dict_object.pop(key, []) for key in keys])
- return listify(*[dict_object.get(key, []) for key in keys])
+ fetch = dict_object.pop
+ # If there's only one key, we don't return a list with one element
+ if len(keys) == 1:
+ return listify(fetch(keys[0], []), **kwargs)
+ # Return a list of values corresponding to *keys
+ for key in keys:
+ result.append(listify(fetch(key, []), **kwargs))
+ return result
def typeslistify(item, types):
@@ -750,15 +760,6 @@ def windows_proof_rmtree(f):
# Try one last time and throw if it fails.
shutil.rmtree(f)
-def unholder_array(entries):
- result = []
- entries = flatten(entries)
- for e in entries:
- if hasattr(e, 'held_object'):
- e = e.held_object
- result.append(e)
- return result
-
class OrderedSet(collections.MutableSet):
"""A set that preserves the order in which items are added, by first
insertion.
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index d1d7013..1f813da 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -20,7 +20,7 @@ import os
import copy
import subprocess
from . import ModuleReturnValue
-from ..mesonlib import MesonException, OrderedSet, Popen_safe
+from ..mesonlib import MesonException, OrderedSet, Popen_safe, extract_as_list
from ..dependencies import Dependency, PkgConfigDependency, InternalDependency
from .. import mlog
from .. import mesonlib
@@ -323,11 +323,9 @@ class GnomeModule(ExtensionModule):
cflags = OrderedSet()
ldflags = OrderedSet()
gi_includes = OrderedSet()
- deps = mesonlib.listify(deps)
+ deps = mesonlib.listify(deps, unholder=True)
for dep in deps:
- if hasattr(dep, 'held_object'):
- dep = dep.held_object
if isinstance(dep, InternalDependency):
cflags.update(get_include_args(dep.include_directories))
for lib in dep.libraries:
@@ -378,7 +376,7 @@ class GnomeModule(ExtensionModule):
elif isinstance(dep, (build.StaticLibrary, build.SharedLibrary)):
cflags.update(get_include_args(dep.get_include_dirs()))
else:
- mlog.log('dependency %s not handled to build gir files' % dep)
+ mlog.log('dependency {!r} not handled to build gir files'.format(dep))
continue
if gir_has_extra_lib_arg() and use_gir_args:
@@ -394,7 +392,7 @@ class GnomeModule(ExtensionModule):
@permittedKwargs({'sources', 'nsversion', 'namespace', 'symbol_prefix', 'identifier_prefix',
'export_packages', 'includes', 'dependencies', 'link_with', 'include_directories',
'install', 'install_dir_gir', 'install_dir_typelib', 'extra_args',
- 'packages', 'build_by_default'})
+ 'packages', 'header', 'build_by_default'})
def generate_gir(self, state, args, kwargs):
if len(args) != 1:
raise MesonException('Gir takes one argument')
@@ -417,7 +415,7 @@ class GnomeModule(ExtensionModule):
raise MesonException('gobject-introspection dependency was not found, gir cannot be generated.')
ns = kwargs.pop('namespace')
nsversion = kwargs.pop('nsversion')
- libsources = mesonlib.flatten(kwargs.pop('sources'))
+ libsources = mesonlib.extract_as_list(kwargs, 'sources', pop=True)
girfile = '%s-%s.gir' % (ns, nsversion)
srcdir = os.path.join(state.environment.get_source_dir(), state.subdir)
builddir = os.path.join(state.environment.get_build_dir(), state.subdir)
@@ -429,6 +427,12 @@ class GnomeModule(ExtensionModule):
scan_command += ['--no-libtool', '--namespace=' + ns, '--nsversion=' + nsversion, '--warn-all',
'--output', '@OUTPUT@']
+ header = kwargs.pop('header', None)
+ if header:
+ if not isinstance(header, str):
+ raise MesonException('header must be a string')
+ scan_command += ['--c-include=' + header]
+
extra_args = mesonlib.stringlistify(kwargs.pop('extra_args', []))
scan_command += extra_args
scan_command += ['-I' + srcdir,
@@ -525,9 +529,8 @@ class GnomeModule(ExtensionModule):
else:
raise MesonException('Gir export packages must be str or list')
- deps = mesonlib.extract_as_list(kwargs, 'dependencies', pop = True)
deps = (girtarget.get_all_link_deps() + girtarget.get_external_deps() +
- deps)
+ extract_as_list(kwargs, 'dependencies', pop=True, unholder=True))
# Need to recursively add deps on GirTarget sources from our
# dependencies and also find the include directories needed for the
# typelib generation custom target below.
@@ -794,7 +797,8 @@ This will become a hard error in the future.''')
def _get_build_args(self, kwargs, state):
args = []
- cflags, ldflags, gi_includes = self._get_dependencies_flags(kwargs.get('dependencies', []), state, include_rpath=True)
+ deps = extract_as_list(kwargs, 'dependencies', unholder=True)
+ cflags, ldflags, gi_includes = self._get_dependencies_flags(deps, state, include_rpath=True)
inc_dirs = mesonlib.extract_as_list(kwargs, 'include_directories')
for incd in inc_dirs:
if not isinstance(incd.held_object, (str, build.IncludeDirs)):
diff --git a/mesonbuild/modules/qt4.py b/mesonbuild/modules/qt4.py
index a63aff8..4ab07b9 100644
--- a/mesonbuild/modules/qt4.py
+++ b/mesonbuild/modules/qt4.py
@@ -21,6 +21,7 @@ from . import ExtensionModule
import xml.etree.ElementTree as ET
from . import ModuleReturnValue
from ..interpreterbase import permittedKwargs
+from . import get_include_args
class Qt4Module(ExtensionModule):
tools_detected = False
@@ -97,10 +98,10 @@ class Qt4Module(ExtensionModule):
except Exception:
return []
- @permittedKwargs({'moc_headers', 'moc_sources', 'ui_files', 'qresources', 'method'})
+ @permittedKwargs({'moc_headers', 'moc_sources', 'include_directories', 'ui_files', 'qresources', 'method'})
def preprocess(self, state, args, kwargs):
- rcc_files, ui_files, moc_headers, moc_sources, sources \
- = extract_as_list(kwargs, 'qresources', 'ui_files', 'moc_headers', 'moc_sources', 'sources', pop = True)
+ rcc_files, ui_files, moc_headers, moc_sources, sources, include_directories \
+ = extract_as_list(kwargs, 'qresources', 'ui_files', 'moc_headers', 'moc_sources', 'sources', 'include_directories', pop = True)
sources += args[1:]
method = kwargs.get('method', 'auto')
self._detect_tools(state.environment, method)
@@ -133,9 +134,10 @@ class Qt4Module(ExtensionModule):
ui_gen = build.Generator([self.uic], ui_kwargs)
ui_output = ui_gen.process_files('Qt4 ui', ui_files, state)
sources.append(ui_output)
+ inc = get_include_args(include_dirs=include_directories)
if len(moc_headers) > 0:
moc_kwargs = {'output': 'moc_@BASENAME@.cpp',
- 'arguments': ['@INPUT@', '-o', '@OUTPUT@']}
+ 'arguments': inc + ['@INPUT@', '-o', '@OUTPUT@']}
moc_gen = build.Generator([self.moc], moc_kwargs)
moc_output = moc_gen.process_files('Qt4 moc header', moc_headers, state)
sources.append(moc_output)
diff --git a/mesonbuild/modules/qt5.py b/mesonbuild/modules/qt5.py
index 08ce662..001caa7 100644
--- a/mesonbuild/modules/qt5.py
+++ b/mesonbuild/modules/qt5.py
@@ -21,6 +21,7 @@ from . import ExtensionModule
import xml.etree.ElementTree as ET
from . import ModuleReturnValue
from ..interpreterbase import permittedKwargs
+from . import get_include_args
class Qt5Module(ExtensionModule):
tools_detected = False
@@ -103,10 +104,10 @@ class Qt5Module(ExtensionModule):
except Exception:
return []
- @permittedKwargs({'moc_headers', 'moc_sources', 'ui_files', 'qresources', 'method'})
+ @permittedKwargs({'moc_headers', 'moc_sources', 'include_directories', 'ui_files', 'qresources', 'method'})
def preprocess(self, state, args, kwargs):
- rcc_files, ui_files, moc_headers, moc_sources, sources \
- = extract_as_list(kwargs, 'qresources', 'ui_files', 'moc_headers', 'moc_sources', 'sources', pop = True)
+ rcc_files, ui_files, moc_headers, moc_sources, sources, include_directories \
+ = extract_as_list(kwargs, 'qresources', 'ui_files', 'moc_headers', 'moc_sources', 'sources', 'include_directories', pop = True)
sources += args[1:]
method = kwargs.get('method', 'auto')
self._detect_tools(state.environment, method)
@@ -139,9 +140,10 @@ class Qt5Module(ExtensionModule):
ui_gen = build.Generator([self.uic], ui_kwargs)
ui_output = ui_gen.process_files('Qt5 ui', ui_files, state)
sources.append(ui_output)
+ inc = get_include_args(include_dirs=include_directories)
if len(moc_headers) > 0:
moc_kwargs = {'output': 'moc_@BASENAME@.cpp',
- 'arguments': ['@INPUT@', '-o', '@OUTPUT@']}
+ 'arguments': inc + ['@INPUT@', '-o', '@OUTPUT@']}
moc_gen = build.Generator([self.moc], moc_kwargs)
moc_output = moc_gen.process_files('Qt5 moc header', moc_headers, state)
sources.append(moc_output)
diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py
index 81a2f88..26a3489 100644
--- a/mesonbuild/wrap/wrap.py
+++ b/mesonbuild/wrap/wrap.py
@@ -19,6 +19,7 @@ import subprocess
import sys
from pathlib import Path
from . import WrapMode
+from ..mesonlib import Popen_safe
try:
import ssl
@@ -78,6 +79,8 @@ class PackageDefinition:
self.type = 'git'
elif first == '[wrap-hg]':
self.type = 'hg'
+ elif first == '[wrap-svn]':
+ self.type = 'svn'
else:
raise RuntimeError('Invalid format of package file')
for line in ifile:
@@ -145,6 +148,8 @@ class Resolver:
self.get_git(p)
elif p.type == "hg":
self.get_hg(p)
+ elif p.type == "svn":
+ self.get_svn(p)
else:
raise AssertionError('Unreachable code.')
return p.get('directory')
@@ -228,6 +233,27 @@ class Resolver:
subprocess.check_call(['hg', 'checkout', revno],
cwd=checkoutdir)
+ def get_svn(self, p):
+ checkoutdir = os.path.join(self.subdir_root, p.get('directory'))
+ revno = p.get('revision')
+ is_there = os.path.isdir(checkoutdir)
+ if is_there:
+ p, out = Popen_safe(['svn', 'info', '--show-item', 'revision', checkoutdir])
+ current_revno = out
+ if current_revno == revno:
+ return
+
+ if revno.lower() == 'head':
+ # Failure to do pull is not a fatal error,
+ # because otherwise you can't develop without
+ # a working net connection.
+ subprocess.call(['svn', 'update'], cwd=checkoutdir)
+ else:
+ subprocess.check_call(['svn', 'update', '-r', revno], cwd=checkoutdir)
+ else:
+ subprocess.check_call(['svn', 'checkout', '-r', revno, p.get('url'),
+ p.get('directory')], cwd=self.subdir_root)
+
def get_data(self, url):
blocksize = 10 * 1024
h = hashlib.sha256()
diff --git a/run_tests.py b/run_tests.py
index f74ff13..00c97ca 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -90,7 +90,7 @@ def get_backend_commands(backend, debug=False):
test_cmd = cmd + ['-target', 'RUN_TESTS']
elif backend is Backend.ninja:
# We need at least 1.6 because of -w dupbuild=err
- cmd = [detect_ninja('1.6'), '-w', 'dupbuild=err']
+ cmd = [detect_ninja('1.6'), '-w', 'dupbuild=err', '-d', 'explain']
if cmd[0] is None:
raise RuntimeError('Could not find Ninja v1.6 or newer')
if debug:
diff --git a/run_unittests.py b/run_unittests.py
index af1a5f2..fa8f049 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -31,6 +31,7 @@ import mesonbuild.compilers
import mesonbuild.environment
import mesonbuild.mesonlib
import mesonbuild.coredata
+from mesonbuild.interpreter import ObjectHolder
from mesonbuild.mesonlib import is_linux, is_windows, is_osx, is_cygwin, windows_proof_rmtree
from mesonbuild.environment import Environment
from mesonbuild.dependencies import DependencyException
@@ -62,7 +63,6 @@ def get_soname(fname):
def get_rpath(fname):
return get_dynamic_section_entry(fname, r'(?:rpath|runpath)')
-
class InternalTests(unittest.TestCase):
def test_version_number(self):
@@ -398,6 +398,49 @@ class InternalTests(unittest.TestCase):
self.assertEqual(forced_value, desired_value)
+ def test_listify(self):
+ listify = mesonbuild.mesonlib.listify
+ # Test sanity
+ self.assertEqual([1], listify(1))
+ self.assertEqual([], listify([]))
+ self.assertEqual([1], listify([1]))
+ # Test flattening
+ self.assertEqual([1, 2, 3], listify([1, [2, 3]]))
+ self.assertEqual([1, 2, 3], listify([1, [2, [3]]]))
+ self.assertEqual([1, [2, [3]]], listify([1, [2, [3]]], flatten=False))
+ # Test flattening and unholdering
+ holder1 = ObjectHolder(1)
+ holder3 = ObjectHolder(3)
+ self.assertEqual([holder1], listify(holder1))
+ self.assertEqual([holder1], listify([holder1]))
+ self.assertEqual([holder1, 2], listify([holder1, 2]))
+ self.assertEqual([holder1, 2, 3], listify([holder1, 2, [3]]))
+ self.assertEqual([1], listify(holder1, unholder=True))
+ self.assertEqual([1], listify([holder1], unholder=True))
+ self.assertEqual([1, 2], listify([holder1, 2], unholder=True))
+ self.assertEqual([1, 2, 3], listify([holder1, 2, [holder3]], unholder=True))
+ # Unholding doesn't work recursively when not flattening
+ self.assertEqual([1, [2], [holder3]], listify([holder1, [2], [holder3]], unholder=True, flatten=False))
+
+ def test_extract_as_list(self):
+ extract = mesonbuild.mesonlib.extract_as_list
+ # Test sanity
+ kwargs = {'sources': [1, 2, 3]}
+ self.assertEqual([1, 2, 3], extract(kwargs, 'sources'))
+ self.assertEqual(kwargs, {'sources': [1, 2, 3]})
+ self.assertEqual([1, 2, 3], extract(kwargs, 'sources', pop=True))
+ self.assertEqual(kwargs, {})
+ # Test unholding
+ holder3 = ObjectHolder(3)
+ kwargs = {'sources': [1, 2, holder3]}
+ self.assertEqual([1, 2, 3], extract(kwargs, 'sources', unholder=True))
+ self.assertEqual(kwargs, {'sources': [1, 2, holder3]})
+ self.assertEqual([1, 2, 3], extract(kwargs, 'sources', unholder=True, pop=True))
+ self.assertEqual(kwargs, {})
+ # Test listification
+ kwargs = {'sources': [1, 2, 3], 'pch_sources': [4, 5, 6]}
+ self.assertEqual([[1, 2, 3], [4, 5, 6]], extract(kwargs, 'sources', 'pch_sources'))
+
class BasePlatformTests(unittest.TestCase):
def setUp(self):
diff --git a/test cases/common/145 whole archive/allofme/meson.build b/test cases/common/145 whole archive/allofme/meson.build
new file mode 100644
index 0000000..f5c2027
--- /dev/null
+++ b/test cases/common/145 whole archive/allofme/meson.build
@@ -0,0 +1 @@
+stlib = static_library('allofme', '../libfile.c')
diff --git a/test cases/common/145 whole archive/exe/meson.build b/test cases/common/145 whole archive/exe/meson.build
new file mode 100644
index 0000000..f47a246
--- /dev/null
+++ b/test cases/common/145 whole archive/exe/meson.build
@@ -0,0 +1,2 @@
+exe = executable('prog', '../prog.c',
+ link_with : dylib)
diff --git a/test cases/common/145 whole archive/exe2/meson.build b/test cases/common/145 whole archive/exe2/meson.build
new file mode 100644
index 0000000..5365f03
--- /dev/null
+++ b/test cases/common/145 whole archive/exe2/meson.build
@@ -0,0 +1 @@
+exe2 = executable('prog2', '../prog.c', link_with : dylib2)
diff --git a/test cases/common/145 whole archive/meson.build b/test cases/common/145 whole archive/meson.build
index eadebf8..617ae03 100644
--- a/test cases/common/145 whole archive/meson.build
+++ b/test cases/common/145 whole archive/meson.build
@@ -1,5 +1,7 @@
project('whole archive', 'c')
+add_project_arguments('-I' + meson.source_root(), language : 'c')
+
cc = meson.get_compiler('c')
if cc.get_id() == 'msvc'
@@ -8,15 +10,15 @@ if cc.get_id() == 'msvc'
endif
endif
-stlib = static_library('allofme', 'libfile.c')
-
-# Nothing in dylib.c uses func1, so the linker would throw it
-# away and thus linking the exe would fail.
-dylib = shared_library('shlib', 'dylib.c',
- link_whole : stlib)
-
-exe = executable('prog', 'prog.c',
- link_with : dylib)
+subdir('allofme')
+subdir('shlib')
+subdir('exe')
test('prog', exe)
+# link_whole only
+subdir('stlib')
+subdir('wholeshlib')
+subdir('exe2')
+
+test('prog2', exe2)
diff --git a/test cases/common/145 whole archive/shlib/meson.build b/test cases/common/145 whole archive/shlib/meson.build
new file mode 100644
index 0000000..34a1b78
--- /dev/null
+++ b/test cases/common/145 whole archive/shlib/meson.build
@@ -0,0 +1,4 @@
+# Nothing in dylib.c uses func1, so the linker would throw it
+# away and thus linking the exe would fail.
+dylib = shared_library('shlib', '../dylib.c',
+ link_whole : stlib)
diff --git a/test cases/common/145 whole archive/stlib/meson.build b/test cases/common/145 whole archive/stlib/meson.build
new file mode 100644
index 0000000..07a434e
--- /dev/null
+++ b/test cases/common/145 whole archive/stlib/meson.build
@@ -0,0 +1 @@
+static = static_library('static', '../dylib.c')
diff --git a/test cases/common/145 whole archive/wholeshlib/meson.build b/test cases/common/145 whole archive/wholeshlib/meson.build
new file mode 100644
index 0000000..69a1995
--- /dev/null
+++ b/test cases/common/145 whole archive/wholeshlib/meson.build
@@ -0,0 +1 @@
+dylib2 = shared_library('link_whole', link_whole : [stlib, static])
diff --git a/test cases/failing/61 getoption prefix/meson.build b/test cases/failing/61 getoption prefix/meson.build
new file mode 100644
index 0000000..8f85cff
--- /dev/null
+++ b/test cases/failing/61 getoption prefix/meson.build
@@ -0,0 +1,5 @@
+project('getopt prefix')
+
+subproject('abc')
+
+get_option('abc:foo')
diff --git a/test cases/failing/61 getoption prefix/subprojects/abc/meson.build b/test cases/failing/61 getoption prefix/subprojects/abc/meson.build
new file mode 100644
index 0000000..aa9c3df
--- /dev/null
+++ b/test cases/failing/61 getoption prefix/subprojects/abc/meson.build
@@ -0,0 +1 @@
+project('abc', 'c')
diff --git a/test cases/failing/61 getoption prefix/subprojects/abc/meson_options.txt b/test cases/failing/61 getoption prefix/subprojects/abc/meson_options.txt
new file mode 100644
index 0000000..89e624e
--- /dev/null
+++ b/test cases/failing/61 getoption prefix/subprojects/abc/meson_options.txt
@@ -0,0 +1 @@
+option('foo', type : 'boolean')
diff --git a/test cases/frameworks/1 boost/meson.build b/test cases/frameworks/1 boost/meson.build
index 6f25f8b..b73f93a 100644
--- a/test cases/frameworks/1 boost/meson.build
+++ b/test cases/frameworks/1 boost/meson.build
@@ -9,21 +9,18 @@ add_project_arguments(['-DBOOST_LOG_DYN_LINK'],
# within one project. The need to be independent of each other.
# Use one without a library dependency and one with it.
-nolinkdep = dependency('boost', modules: 'utility')
linkdep = dependency('boost', modules : ['thread', 'system'])
staticdep = dependency('boost', modules : ['thread', 'system'], static : true)
-testdep = dependency('boost', modules : 'test')
+testdep = dependency('boost', modules : ['unit_test_framework'])
nomoddep = dependency('boost')
extralibdep = dependency('boost', modules : ['thread', 'system', 'log_setup', 'log'])
-nolinkexe = executable('nolinkedexe', 'nolinkexe.cc', dependencies : nolinkdep)
linkexe = executable('linkedexe', 'linkexe.cc', dependencies : linkdep)
staticexe = executable('staticlinkedexe', 'linkexe.cc', dependencies : staticdep)
unitexe = executable('utf', 'unit_test.cpp', dependencies: testdep)
nomodexe = executable('nomod', 'nomod.cpp', dependencies : nomoddep)
extralibexe = executable('extralibexe', 'extralib.cpp', dependencies : extralibdep)
-test('Boost nolinktest', nolinkexe)
test('Boost linktest', linkexe)
test('Boost statictest', staticexe)
test('Boost UTF test', unitexe)
diff --git a/test cases/frameworks/1 boost/nolinkexe.cc b/test cases/frameworks/1 boost/nolinkexe.cc
deleted file mode 100644
index 7b6c6d9..0000000
--- a/test cases/frameworks/1 boost/nolinkexe.cc
+++ /dev/null
@@ -1,12 +0,0 @@
-#include<boost/utility.hpp>
-
-class MyClass : boost::noncopyable {
-public:
- MyClass() {};
- ~MyClass() {};
-};
-
-int main(int argc, char **argv) {
- MyClass obj;
- return 0;
-}
diff --git a/test cases/frameworks/4 qt/meson.build b/test cases/frameworks/4 qt/meson.build
index d9cab6f..c6f108b 100644
--- a/test cases/frameworks/4 qt/meson.build
+++ b/test cases/frameworks/4 qt/meson.build
@@ -70,5 +70,15 @@ foreach qt : ['qt4', 'qt5']
dependencies : qtcore)
test(qt + 'maninclude', qtmaninclude)
+
+ # building Qt plugins implies to give include path to moc
+ plugin_includes = include_directories('pluginInterface', 'plugin')
+ pluginpreprocess = qtmodule.preprocess(
+ moc_headers : 'plugin/plugin.h',
+ include_directories : plugin_includes
+ )
+ plugin = library('plugin', 'plugin/plugin.cpp', pluginpreprocess,
+ include_directories : plugin_includes,
+ dependencies : qtcore)
endif
endforeach
diff --git a/test cases/frameworks/4 qt/plugin/plugin.cpp b/test cases/frameworks/4 qt/plugin/plugin.cpp
new file mode 100644
index 0000000..eeae98d
--- /dev/null
+++ b/test cases/frameworks/4 qt/plugin/plugin.cpp
@@ -0,0 +1,7 @@
+#include "plugin.h"
+#include <QFile>
+
+QString plugin1::getResource()
+{
+ return "hello world";
+}
diff --git a/test cases/frameworks/4 qt/plugin/plugin.h b/test cases/frameworks/4 qt/plugin/plugin.h
new file mode 100644
index 0000000..1138f41
--- /dev/null
+++ b/test cases/frameworks/4 qt/plugin/plugin.h
@@ -0,0 +1,11 @@
+#pragma once
+#include <plugin_if.h>
+
+class plugin1:public QObject,public PluginInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(PluginInterface)
+ Q_PLUGIN_METADATA(IID "demo.PluginInterface" FILE "plugin.json")
+public:
+ QString getResource() override;
+};
diff --git a/test cases/frameworks/4 qt/plugin/plugin.json b/test cases/frameworks/4 qt/plugin/plugin.json
new file mode 100644
index 0000000..6c6a011
--- /dev/null
+++ b/test cases/frameworks/4 qt/plugin/plugin.json
@@ -0,0 +1,3 @@
+{
+ "name" : "Plugin1"
+}
diff --git a/test cases/frameworks/4 qt/pluginInterface/plugin_if.h b/test cases/frameworks/4 qt/pluginInterface/plugin_if.h
new file mode 100644
index 0000000..97d2800
--- /dev/null
+++ b/test cases/frameworks/4 qt/pluginInterface/plugin_if.h
@@ -0,0 +1,21 @@
+#ifndef PLUGIN_IF_H
+#define PLUGIN_IF_H
+
+#include <QString>
+#include <QtPlugin>
+
+/**
+ * @brief Interface for a plugin
+ */
+class PluginInterface
+{
+public:
+ virtual ~PluginInterface() = default;
+
+ /// Initializes the plugin
+ virtual QString getResource() = 0;
+};
+
+Q_DECLARE_INTERFACE(PluginInterface, "demo.PluginInterface")
+
+#endif
diff --git a/test cases/frameworks/7 gnome/gir/dep1/meson.build b/test cases/frameworks/7 gnome/gir/dep1/meson.build
index 75dd731..baa0b1d 100644
--- a/test cases/frameworks/7 gnome/gir/dep1/meson.build
+++ b/test cases/frameworks/7 gnome/gir/dep1/meson.build
@@ -19,6 +19,7 @@ dep1gir = gnome.generate_gir(
namespace : 'MesonDep1',
symbol_prefix : 'meson',
identifier_prefix : 'Meson',
+ header: 'dep1.h',
includes : ['GObject-2.0', 'MesonDep2-1.0'],
dependencies : [dep2_dep],
install : true
diff --git a/test cases/frameworks/7 gnome/gir/meson.build b/test cases/frameworks/7 gnome/gir/meson.build
index 3598b66..a91cb97 100644
--- a/test cases/frameworks/7 gnome/gir/meson.build
+++ b/test cases/frameworks/7 gnome/gir/meson.build
@@ -27,7 +27,7 @@ gnome.generate_gir(
identifier_prefix : 'Meson',
includes : ['GObject-2.0', 'MesonDep1-1.0'],
# dep1_dep pulls in dep2_dep for us
- dependencies : [fake_dep, dep1_dep],
+ dependencies : [[fake_dep, dep1_dep]],
install : true,
build_by_default : true,
# Test that unknown kwargs do not crash the parser.
diff --git a/tools/boost_names.py b/tools/boost_names.py
new file mode 100755
index 0000000..9ac16bb
--- /dev/null
+++ b/tools/boost_names.py
@@ -0,0 +1,180 @@
+#!/usr/bin/env python3
+
+# Copyright 2017 Niklas Claesson
+
+# 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 is two implementations for how to get module names from the boost
+sources. One relies on json metadata files in the sources, the other relies on
+the folder names.
+
+Run the tool in the boost directory and append the stdout to the misc.py:
+
+boost/$ path/to/meson/tools/boost_names.py >> path/to/meson/dependencies/misc.py
+"""
+
+import sys
+import os
+import collections
+import pprint
+import json
+import re
+
+Module = collections.namedtuple('Module', ['dirname', 'name', 'libnames'])
+Module.__repr__ = lambda self: str((self.dirname, self.name, self.libnames))
+
+LIBS = 'libs'
+
+manual_map = {
+ 'callable_traits': 'Call Traits',
+ 'crc': 'CRC',
+ 'dll': 'DLL',
+ 'gil': 'GIL',
+ 'graph_parallel': 'GraphParallel',
+ 'icl': 'ICL',
+ 'io': 'IO State Savers',
+ 'msm': 'Meta State Machine',
+ 'mpi': 'MPI',
+ 'mpl': 'MPL',
+ 'multi_array': 'Multi-Array',
+ 'multi_index': 'Multi-Index',
+ 'numeric': 'Numeric Conversion',
+ 'ptr_container': 'Pointer Container',
+ 'poly_collection': 'PolyCollection',
+ 'qvm': 'QVM',
+ 'throw_exception': 'ThrowException',
+ 'tti': 'TTI',
+ 'vmd': 'VMD',
+}
+
+extra = [
+ Module('utility', 'Compressed Pair', []),
+ Module('core', 'Enable If', []),
+ Module('functional', 'Functional/Factory', []),
+ Module('functional', 'Functional/Forward', []),
+ Module('functional', 'Functional/Hash', []),
+ Module('functional', 'Functional/Overloaded Function', []),
+ Module('utility', 'Identity Type', []),
+ Module('utility', 'In Place Factory, Typed In Place Factory', []),
+ Module('numeric', 'Interval', []),
+ Module('math', 'Math Common Factor', []),
+ Module('math', 'Math Octonion', []),
+ Module('math', 'Math Quaternion', []),
+ Module('math', 'Math/Special Functions', []),
+ Module('math', 'Math/Statistical Distributions', []),
+ Module('bind', 'Member Function', []),
+ Module('algorithm', 'Min-Max', []),
+ Module('numeric', 'Odeint', []),
+ Module('utility', 'Operators', []),
+ Module('core', 'Ref', []),
+ Module('utility', 'Result Of', []),
+ Module('algorithm', 'String Algo', []),
+ Module('core', 'Swap', []),
+ Module('', 'Tribool', []),
+ Module('numeric', 'uBLAS', []),
+ Module('utility', 'Value Initialized', []),
+]
+
+# Cannot find the following modules in the documentation of boost
+not_modules = ['beast', 'logic', 'mp11', 'winapi']
+
+def eprint(message):
+ print(message, file=sys.stderr)
+
+def get_library_names(jamfile):
+ libs = []
+ with open(jamfile) as jamfh:
+ jam = jamfh.read()
+ res = re.finditer(r'^lib[\s]+([A-Za-z0-9_]+)([^;]*);', jam, re.MULTILINE | re.DOTALL)
+ for matches in res:
+ if ':' in matches.group(2):
+ libs.append(matches.group(1))
+ return libs
+
+def exists(modules, module):
+ return len([x for x in modules if x.dirname == module.dirname]) != 0
+
+def get_modules(init=extra):
+ modules = init
+ for directory in os.listdir(LIBS):
+ if not os.path.isdir(os.path.join(LIBS, directory)):
+ continue
+ if directory in not_modules:
+ continue
+ jamfile = os.path.join(LIBS, directory, 'build', 'Jamfile.v2')
+ if os.path.isfile(jamfile):
+ libs = get_library_names(jamfile)
+ else:
+ libs = []
+ if directory in manual_map.keys():
+ modname = manual_map[directory]
+ else:
+ modname = directory.replace('_', ' ').title()
+ modules.append(Module(directory, modname, libs))
+ return modules
+
+def get_modules_2():
+ modules = []
+ for (root, dirs, files) in os.walk(LIBS):
+ for f in files:
+ if f == "libraries.json":
+ projectdir = os.path.dirname(root)
+
+ jamfile = os.path.join(projectdir, 'build', 'Jamfile.v2')
+ if os.path.isfile(jamfile):
+ libs = get_library_names(jamfile)
+ else:
+ libs = []
+
+ # Get metadata for module
+ jsonfile = os.path.join(root, f)
+ with open(jsonfile) as jsonfh:
+ boost_modules = json.loads(jsonfh.read())
+ if(isinstance(boost_modules, dict)):
+ boost_modules = [boost_modules]
+ for boost_module in boost_modules:
+ modules.append(Module(boost_module['key'], boost_module['name'], libs))
+
+ # Some subprojects do not have meta directory with json file. Find those
+ jsonless_modules = [x for x in get_modules([]) if not exists(modules, x)]
+ for module in jsonless_modules:
+ eprint("WARNING: {} does not have meta/libraries.json. Will guess pretty name '{}'".format(module.dirname, module.name))
+ modules.extend(jsonless_modules)
+
+ return modules
+
+def main(args):
+ if not os.path.isdir(LIBS):
+ eprint("ERROR: script must be run in boost source directory")
+
+ # It will pick jsonless algorithm if 1 is given as argument
+ impl = 0
+ if len(args) > 1:
+ if args[1] == '1':
+ impl = 1
+
+ if impl == 1:
+ modules = get_modules()
+ else:
+ modules = get_modules_2()
+
+ sorted_modules = sorted(modules, key=lambda module: module.name.lower())
+ sorted_modules = [x[2] for x in sorted_modules if x[2]]
+ sorted_modules = sum(sorted_modules, [])
+ sorted_modules = [x for x in sorted_modules if x.startswith('boost')]
+
+ pp = pprint.PrettyPrinter()
+ pp.pprint(sorted_modules)
+
+if __name__ == '__main__':
+ main(sys.argv)