aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml8
-rw-r--r--docs/markdown/Compiler-properties.md12
-rw-r--r--docs/markdown/D.md89
-rw-r--r--docs/markdown/Getting-meson.md2
-rw-r--r--docs/markdown/Pkgconfig-module.md2
-rw-r--r--docs/markdown/Reference-manual.md27
-rw-r--r--docs/markdown/index.md2
-rw-r--r--docs/sitemap.txt1
-rwxr-xr-xghwt.py5
-rwxr-xr-xmeson.py2
-rw-r--r--mesonbuild/backend/ninjabackend.py4
-rw-r--r--mesonbuild/build.py171
-rw-r--r--mesonbuild/compilers/c.py5
-rw-r--r--mesonbuild/compilers/d.py55
-rw-r--r--mesonbuild/dependencies/base.py7
-rw-r--r--mesonbuild/dependencies/misc.py6
-rw-r--r--mesonbuild/dependencies/ui.py9
-rw-r--r--mesonbuild/interpreter.py97
-rw-r--r--mesonbuild/mesonlib.py42
-rw-r--r--mesonbuild/modules/gnome.py65
-rw-r--r--mesonbuild/modules/pkgconfig.py11
-rw-r--r--mesonbuild/modules/qt4.py19
-rw-r--r--mesonbuild/modules/qt5.py19
-rw-r--r--mesonbuild/modules/unstable_simd.py4
-rw-r--r--mesonbuild/modules/windows.py6
-rw-r--r--[-rwxr-xr-x]mesonbuild/rewriter.py0
-rw-r--r--[-rwxr-xr-x]msi/License.rtf0
-rwxr-xr-xrun_unittests.py5
-rw-r--r--test cases/common/146 C and CPP link/dummy.c0
-rw-r--r--test cases/common/146 C and CPP link/meson.build36
-rw-r--r--test cases/common/146 C and CPP link/sub.c19
-rw-r--r--test cases/common/146 C and CPP link/sub.h16
-rw-r--r--test cases/common/47 options/meson.build1
-rw-r--r--test cases/d/9 features/app.d51
-rw-r--r--test cases/d/9 features/data/food.txt6
-rw-r--r--test cases/d/9 features/data/people.txt5
-rw-r--r--test cases/d/9 features/meson.build29
-rw-r--r--test cases/unit/13 reconfigure/meson.build5
-rw-r--r--test cases/vala/12 custom output/bar.vala0
-rw-r--r--test cases/vala/12 custom output/meson.build10
40 files changed, 573 insertions, 280 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index ac69dd4..60a6fd3 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -49,6 +49,14 @@ branches:
only:
- master
+init:
+ - ps: |
+ If($Env:compiler -like 'msvc2010') {
+ Set-WinSystemLocale de-DE
+ Start-Sleep -s 5
+ Restart-Computer
+ }
+
install:
- cmd: set "ORIG_PATH=%PATH%"
# Boost 1.56.0: https://www.appveyor.com/docs/build-environment/#boost
diff --git a/docs/markdown/Compiler-properties.md b/docs/markdown/Compiler-properties.md
index 06efff3..4def628 100644
--- a/docs/markdown/Compiler-properties.md
+++ b/docs/markdown/Compiler-properties.md
@@ -103,12 +103,12 @@ The `result` variable encapsulates the state of the test, which can be
extracted with the following methods. The `name` keyword argument
works the same as with `compiles`.
-| Method | Return value
-| ------ | ------------
-| compiled | `True` if compilation succeeded. If `false` then all other methods return undefined values.
-| returncode | The return code of the application as an integer
-| stdout | Program's standard out as text.
-| stderr | Program's standard error as text.
+| Method | Return value |
+| ------ | ------------ |
+| compiled | `True` if compilation succeeded. If `false` then all other methods return undefined values. |
+| returncode | The return code of the application as an integer |
+| stdout | Program's standard out as text. |
+| stderr | Program's standard error as text. |
Here is an example usage:
diff --git a/docs/markdown/D.md b/docs/markdown/D.md
new file mode 100644
index 0000000..7b0d485
--- /dev/null
+++ b/docs/markdown/D.md
@@ -0,0 +1,89 @@
+---
+title: D
+short-description: Compiling D sources
+...
+
+# Compiling D applications
+
+Meson has support for compiling D programs. A minimal `meson.build` file for D looks like this:
+
+```meson
+project('myapp', 'd')
+
+executable('myapp', 'app.d')
+```
+
+## Compiling different versions
+
+If you are using the [version()](https://dlang.org/spec/version.html) feature for conditional compilation, you can use it using the `d_module_versions`
+target property:
+```meson
+project('myapp', 'd')
+executable('myapp', 'app.d', d_module_versions: ['Demo', 'FeatureA'])
+```
+
+## Using embedded unittests
+
+If you are using embedded [unittest functions](https://dlang.org/spec/unittest.html), your source code needs to be compiled twice, once in regular
+mode, and once with unittests active. This is done by setting the `d_unittest` target property to `true`.
+Meson will only ever pass the respective compiler's `-unittest` flag, and never have the compiler generate an empty main function.
+If you need that feature in a portable way, create an empty `main()` function for unittests yourself, since the GNU D compiler
+does not have this feature.
+
+This is an example for using D unittests with Meson:
+```meson
+project('myapp_tested', 'd')
+
+myapp_src = ['app.d', 'alpha.d', 'beta.d']
+executable('myapp', myapp_src)
+
+test_exe = executable('myapp_test', myapp_src, d_unittest: true)
+test('myapptest', test_exe)
+```
+
+# Compiling D libraries and installing them
+
+Building D libraries is a straightforward process, not different from how C libraries are built in Meson. You should generate a pkg-config file
+and install it, in order to make other software on the system find the dependency once it is installed.
+
+This is an example on how to build a D shared library:
+```meson
+project('mylib', 'd', version: '1.2.0')
+
+project_soversion = 0
+glib_dep = dependency('glib-2.0')
+
+my_lib = library('mylib',
+ ['src/mylib/libfunctions.d'],
+ dependencies: [glib_dep],
+ install: true,
+ version: meson.project_version(),
+ soversion: project_soversion,
+ d_module_versions: ['FeatureA', 'featureB']
+)
+pkgc.generate(name: 'mylib',
+ libraries: my_lib,
+ subdirs: 'd/mylib',
+ version: meson.project_version(),
+ description: 'A simple example D library.',
+ d_module_versions: ['FeatureA']
+)
+install_subdir('src/mylib/', install_dir: 'include/d/mylib/')
+```
+
+It is important to make the D sources install in a subdirectory in the include path, in this case `/usr/include/d/mylib/mylib`.
+All D compilers include the `/usr/include/d` directory by default, and if your library would be installed into `/usr/include/d/mylib`, there
+is a high chance that, when you compile your project again on a machine where you installed it, the compiler will prefer the old installed include over
+the new version in the source tree, leading to very confusing errors.
+
+This is an example of how to use the D library we just built and installed in an application:
+```meson
+project('myapp', 'd')
+
+mylib_dep = dependency('mylib', version: '>= 1.2.0')
+myapp_src = ['app.d', 'alpha.d', 'beta.d']
+executable('myapp', myapp_src, dependencies: [mylib_dep])
+```
+
+Please keep in mind that the library and executable would both need to be built with the exact same D compiler and D compiler version. The D ABI is not
+stable across compilers and their versions, and mixing compilers will lead to problems.
diff --git a/docs/markdown/Getting-meson.md b/docs/markdown/Getting-meson.md
index d654ff3..8664d61 100644
--- a/docs/markdown/Getting-meson.md
+++ b/docs/markdown/Getting-meson.md
@@ -5,7 +5,7 @@
Meson releases can be downloaded from the [GitHub release page].
Meson is also available in the [Python Package Index] and can be
-installed with <tt>pip3 install meson</tt>.
+installed with `pip3 install meson`.
The newest development code can be obtained directly from [Git]
diff --git a/docs/markdown/Pkgconfig-module.md b/docs/markdown/Pkgconfig-module.md
index 5a660fd..7f767f1 100644
--- a/docs/markdown/Pkgconfig-module.md
+++ b/docs/markdown/Pkgconfig-module.md
@@ -41,3 +41,5 @@ keyword arguments.
e.g. `datadir=${prefix}/share`. The names `prefix`, `libdir` and
`installdir` are reserved and may not be used.
- `version` a string describing the version of this library
+- `d_module_versions` a list of module version flags used when compiling
+ D sources referred to by this pkg-config file
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index 1bed44d..de51479 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -201,10 +201,15 @@ following.
to the target file. Note that your command argument list may not
contain `@OUTPUT@` when capture mode is active.
- `command` command to run to create outputs from inputs. The command
- may be strings or the return of `find_program()` or `executable()`
- (note: always specify commands in array form `['commandname',
+ may be strings or the return value of functions that return file-like
+ objects such as [`find_program()`](#find_program),
+ [`executable()`](#executable), [`configure_file()`](#configure_file),
+ [`files()`](#files), [`custom_target()`](#custom_target), etc.
+ Meson will automatically insert the appropriate dependencies on
+ targets and files listed in this keyword argument.
+ Note: always specify commands in array form `['commandname',
'-arg1', '-arg2']` rather than as a string `'commandname -arg1
- -arg2'` as the latter will *not* work)
+ -arg2'` as the latter will *not* work.
- `depend_files` files ([`string`](#string-object),
[`files()`](#files), or [`configure_file()`](#configure_file)) that
this target depends on but are not listed in the `command` keyword
@@ -424,6 +429,10 @@ be passed to [shared and static libraries](#library).
- `override_options` takes an array of strings in the same format as
`project`'s `default_options` overriding the values of these options
for this target only, since 0.40.0
+- `d_import_dirs` list of directories to look in for string imports used
+ in the D programmling language
+- `d_unittest`, when set to true, the D modules are compiled in debug mode
+- `d_module_versions` list of module versions set when compiling D sources
The list of `sources`, `objects`, and `dependencies` is always
flattened, which means you can freely nest and add lists while
@@ -835,7 +844,7 @@ This function prints its argument to stdout.
The first argument to this function must be a string defining the name
of this project. It is followed by programming languages that the
project uses. Supported values for languages are `c`, `cpp` (for
-`C++`), `objc`, `objcpp`, `fortran`, `java`, `cs` (for `C#`) and
+`C++`), `d`, `objc`, `objcpp`, `fortran`, `java`, `cs` (for `C#`) and
`vala`. In versions before `0.40.0` you must have at least one
language listed.
@@ -1502,7 +1511,10 @@ A build target is either an [executable](#executable),
files with custom flags. To use the object file(s) in another build
target, use the `objects:` keyword argument.
-- `full_path()` returns a full path pointing to the result target file
+- `full_path()` returns a full path pointing to the result target file.
+ NOTE: In most cases using the object itself will do the same job as
+ this and will also allow Meson to setup inter-target dependencies
+ correctly. Please file a bug if that doesn't work for you.
- `private_dir_include()` returns a opaque value that works like
`include_directories` but points to the private directory of this
@@ -1548,7 +1560,10 @@ cause a syntax error.
This object is returned by [`custom_target`](#custom_target) and
contains a target with the following methods:
-- `full_path()` returns a full path pointing to the result target file
+- `full_path()` returns a full path pointing to the result target file
+ NOTE: In most cases using the object itself will do the same job as
+ this and will also allow Meson to setup inter-target dependencies
+ correctly. Please file a bug if that doesn't work for you.
### `dependency` object
diff --git a/docs/markdown/index.md b/docs/markdown/index.md
index 15831d7..81c17ff 100644
--- a/docs/markdown/index.md
+++ b/docs/markdown/index.md
@@ -13,7 +13,7 @@ The main design point of Meson is that every moment a developer spends writing o
## Features
* multiplatform support for Linux, OSX, Windows, GCC, Clang, Visual Studio and others
-* supported languages include C, C++, Fortran, Java, Rust
+* supported languages include C, C++, D, Fortran, Java, Rust
* build definitions in a very readable and user friendly non-Turing complete DSL
* cross compilation for many operating systems as well as bare metal
* optimized for extremely fast full and incremental builds without sacrificing correctness
diff --git a/docs/sitemap.txt b/docs/sitemap.txt
index 755daac..89dfdbd 100644
--- a/docs/sitemap.txt
+++ b/docs/sitemap.txt
@@ -38,6 +38,7 @@ index.md
Windows-module.md
Java.md
Vala.md
+ D.md
IDE-integration.md
Custom-build-targets.md
Build-system-converters.md
diff --git a/ghwt.py b/ghwt.py
index d04cd3f..bf06e19 100755
--- a/ghwt.py
+++ b/ghwt.py
@@ -14,6 +14,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+# ghwt - GitHub WrapTool
+#
+# An emergency wraptool(1) replacement downloader that downloads
+# directly from GitHub in case wrapdb.mesonbuild.com is down.
+
import urllib.request, json, sys, os, shutil, subprocess
import configparser, hashlib
diff --git a/meson.py b/meson.py
index d1b629d..57cc6dc 100755
--- a/meson.py
+++ b/meson.py
@@ -24,7 +24,7 @@ def main():
# encoding, so we can just warn about it.
e = locale.getpreferredencoding()
if e.upper() != 'UTF-8' and not mesonlib.is_windows():
- print('Warning: You are using {!r} which is not a a Unicode-compatible '
+ print('Warning: You are using {!r} which is not a Unicode-compatible '
'locale.'.format(e), file=sys.stderr)
print('You might see errors if you use UTF-8 strings as '
'filenames, as strings, or as file contents.', file=sys.stderr)
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 2188074..f67e412 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -1048,7 +1048,7 @@ int dummy;
if hasattr(i, 'fname'):
i = i.fname
if i.endswith('vala'):
- vapiname = dep.name + '.vapi'
+ vapiname = dep.vala_vapi
fullname = os.path.join(self.get_target_dir(dep), vapiname)
result.add(fullname)
break
@@ -1818,7 +1818,7 @@ rule FORTRAN_DEP_HACK
elem.add_item('DEPFILE', depfile)
if len(extra_dependencies) > 0:
elem.add_dep(extra_dependencies)
- elem.add_item('DESC', 'Generating $out')
+ elem.add_item('DESC', 'Generating {!r}.'.format(sole_output))
if isinstance(exe, build.BuildTarget):
elem.add_dep(self.get_target_filename(exe))
elem.add_item('COMMAND', cmdlist)
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 39c900e..c54abbd 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -19,7 +19,7 @@ import itertools
from . import environment
from . import dependencies
from . import mlog
-from .mesonlib import File, MesonException
+from .mesonlib import File, MesonException, listify, extract_as_list
from .mesonlib import flatten, typeslistify, stringlistify, classify_unity_sources
from .mesonlib import get_filenames_templates_dict, substitute_values
from .environment import for_windows, for_darwin, for_cygwin
@@ -36,6 +36,9 @@ known_basic_kwargs = {'install': True,
'vala_args': True,
'fortran_args': True,
'd_args': True,
+ 'd_import_dirs': True,
+ 'd_unittest': True,
+ 'd_module_versions': True,
'java_args': True,
'rust_args': True,
'link_args': True,
@@ -351,11 +354,12 @@ class BuildTarget(Target):
# 1. Pre-existing objects provided by the user with the `objects:` kwarg
# 2. Compiled objects created by and extracted from another target
self.process_objectlist(objects)
+ 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:
raise InvalidArguments('Build target %s has no sources.' % name)
- self.process_compilers()
+ self.process_compilers_late()
self.validate_sources()
self.validate_cross_install(environment)
@@ -411,8 +415,7 @@ class BuildTarget(Target):
raise InvalidArguments(msg)
def process_sourcelist(self, sources):
- if not isinstance(sources, list):
- sources = [sources]
+ sources = listify(sources)
added_sources = {} # If the same source is defined multiple times, use it only once.
for s in sources:
# Holder unpacking. Ugly.
@@ -437,6 +440,39 @@ class BuildTarget(Target):
removed = True
return removed
+ def process_compilers_late(self):
+ """Processes additional compilers after kwargs have been evaluated.
+
+ This can add extra compilers that might be required by keyword
+ arguments, such as link_with or dependencies. It will also try to guess
+ which compiler to use if one hasn't been selected already.
+ """
+ # Populate list of compilers
+ if self.is_cross:
+ compilers = self.environment.coredata.cross_compilers
+ else:
+ compilers = self.environment.coredata.compilers
+
+ # If this library is linked against another library we need to consider
+ # the languages of those libraries as well.
+ if self.link_targets or self.link_whole_targets:
+ extra = set()
+ for t in itertools.chain(self.link_targets, self.link_whole_targets):
+ for name, compiler in t.compilers.items():
+ if name in clike_langs:
+ extra.add((name, compiler))
+ for name, compiler in sorted(extra, key=lambda p: sort_clike(p[0])):
+ self.compilers[name] = compiler
+
+ if not self.compilers:
+ # No source files or parent targets, target consists of only object
+ # files of unknown origin. Just add the first clike compiler
+ # that we have and hope that it can link these objects
+ for lang in clike_langs:
+ if lang in compilers:
+ self.compilers[lang] = compilers[lang]
+ break
+
def process_compilers(self):
'''
Populate self.compilers, which is the list of compilers that this
@@ -485,14 +521,7 @@ class BuildTarget(Target):
# Re-sort according to clike_langs
self.compilers = OrderedDict(sorted(self.compilers.items(),
key=lambda t: sort_clike(t[0])))
- else:
- # No source files, target consists of only object files of unknown
- # origin. Just add the first clike compiler that we have and hope
- # that it can link these objects
- for lang in clike_langs:
- if lang in compilers:
- self.compilers[lang] = compilers[lang]
- break
+
# If all our sources are Vala, our target also needs the C compiler but
# it won't get added above.
if 'vala' in self.compilers and 'c' not in self.compilers:
@@ -525,8 +554,7 @@ class BuildTarget(Target):
generated twice, since the output needs to be passed to the ld_args and
link_depends.
"""
- if not isinstance(sources, list):
- sources = [sources]
+ sources = listify(sources)
for s in sources:
if hasattr(s, 'held_object'):
s = s.held_object
@@ -548,8 +576,7 @@ class BuildTarget(Target):
return self.kwargs
def unpack_holder(self, d):
- if not isinstance(d, list):
- d = [d]
+ d = listify(d)
newd = []
for i in d:
if isinstance(i, list):
@@ -607,70 +634,54 @@ class BuildTarget(Target):
self.copy_kwargs(kwargs)
kwargs.get('modules', [])
self.need_install = kwargs.get('install', self.need_install)
- llist = kwargs.get('link_with', [])
- if not isinstance(llist, list):
- llist = [llist]
+ llist = extract_as_list(kwargs, 'link_with')
for linktarget in llist:
# Sorry for this hack. Keyword targets are kept in holders
# in kwargs. Unpack here without looking at the exact type.
if hasattr(linktarget, "held_object"):
linktarget = linktarget.held_object
self.link(linktarget)
- lwhole = kwargs.get('link_whole', [])
- if not isinstance(lwhole, list):
- lwhole = [lwhole]
+ lwhole = extract_as_list(kwargs, 'link_whole')
for linktarget in lwhole:
# Sorry for this hack. Keyword targets are kept in holders
# in kwargs. Unpack here without looking at the exact type.
if hasattr(linktarget, "held_object"):
linktarget = linktarget.held_object
self.link_whole(linktarget)
- c_pchlist = kwargs.get('c_pch', [])
- if not isinstance(c_pchlist, list):
- c_pchlist = [c_pchlist]
+
+ c_pchlist, cpp_pchlist, clist, cpplist, cslist, valalist, objclist, objcpplist, fortranlist, rustlist \
+ = extract_as_list(kwargs, 'c_pch', 'cpp_pch', 'c_args', 'cpp_args', 'cs_args', 'vala_args', 'objc_args',
+ 'objcpp_args', 'fortran_args', 'rust_args')
+
self.add_pch('c', c_pchlist)
- cpp_pchlist = kwargs.get('cpp_pch', [])
- if not isinstance(cpp_pchlist, list):
- cpp_pchlist = [cpp_pchlist]
self.add_pch('cpp', cpp_pchlist)
- clist = kwargs.get('c_args', [])
- if not isinstance(clist, list):
- clist = [clist]
- self.add_compiler_args('c', clist)
- cpplist = kwargs.get('cpp_args', [])
- if not isinstance(cpplist, list):
- cpplist = [cpplist]
- self.add_compiler_args('cpp', cpplist)
- cslist = kwargs.get('cs_args', [])
- if not isinstance(cslist, list):
- cslist = [cslist]
- self.add_compiler_args('cs', cslist)
- valalist = kwargs.get('vala_args', [])
- if not isinstance(valalist, list):
- valalist = [valalist]
- self.add_compiler_args('vala', valalist)
- objclist = kwargs.get('objc_args', [])
- if not isinstance(objclist, list):
- objclist = [objclist]
- self.add_compiler_args('objc', objclist)
- objcpplist = kwargs.get('objcpp_args', [])
- if not isinstance(objcpplist, list):
- objcpplist = [objcpplist]
- self.add_compiler_args('objcpp', objcpplist)
- fortranlist = kwargs.get('fortran_args', [])
- if not isinstance(fortranlist, list):
- fortranlist = [fortranlist]
- self.add_compiler_args('fortran', fortranlist)
- rustlist = kwargs.get('rust_args', [])
- if not isinstance(rustlist, list):
- rustlist = [rustlist]
- self.add_compiler_args('rust', rustlist)
+ compiler_args = {'c': clist, 'cpp': cpplist, 'cs': cslist, 'vala': valalist, 'objc': objclist, 'objcpp': objcpplist,
+ 'fortran': fortranlist, 'rust': rustlist
+ }
+ for key, value in compiler_args.items():
+ self.add_compiler_args(key, value)
+
if not isinstance(self, Executable):
self.vala_header = kwargs.get('vala_header', self.name + '.h')
self.vala_vapi = kwargs.get('vala_vapi', self.name + '.vapi')
self.vala_gir = kwargs.get('vala_gir', None)
+
dlist = stringlistify(kwargs.get('d_args', []))
self.add_compiler_args('d', dlist)
+ dfeatures = dict()
+ dfeature_unittest = kwargs.get('d_unittest', False)
+ if dfeature_unittest:
+ dfeatures['unittest'] = dfeature_unittest
+ dfeature_versions = kwargs.get('d_module_versions', None)
+ if dfeature_versions:
+ dfeatures['versions'] = dfeature_versions
+ dfeature_import_dirs = kwargs.get('d_import_dirs', None)
+ if dfeature_import_dirs:
+ dfeatures['import_dirs'] = dfeature_import_dirs
+ if dfeatures:
+ 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', []))
for i in self.link_args:
if not isinstance(i, str):
@@ -682,14 +693,10 @@ This will become a hard error in a future Meson release.''')
self.process_link_depends(kwargs.get('link_depends', []), environment)
# Target-specific include dirs must be added BEFORE include dirs from
# internal deps (added inside self.add_deps()) to override them.
- inclist = kwargs.get('include_directories', [])
- if not isinstance(inclist, list):
- inclist = [inclist]
+ inclist = extract_as_list(kwargs, 'include_directories')
self.add_include_dirs(inclist)
# Add dependencies (which also have include_directories)
- deplist = kwargs.get('dependencies', [])
- if not isinstance(deplist, list):
- deplist = [deplist]
+ deplist = extract_as_list(kwargs, 'dependencies')
self.add_deps(deplist)
# If an item in this list is False, the output corresponding to
# the list index of that item will not be installed
@@ -705,9 +712,7 @@ This will become a hard error in a future Meson release.''')
raise InvalidArguments('Argument gui_app must be boolean.')
elif 'gui_app' in kwargs:
raise InvalidArguments('Argument gui_app can only be used on executables.')
- extra_files = kwargs.get('extra_files', [])
- if not isinstance(extra_files, list):
- extra_files = [extra_files]
+ extra_files = extract_as_list(kwargs, 'extra_files')
for i in extra_files:
assert(isinstance(i, File))
trial = os.path.join(environment.get_source_dir(), i.subdir, i.fname)
@@ -720,9 +725,7 @@ This will become a hard error in a future Meson release.''')
self.build_rpath = kwargs.get('build_rpath', '')
if not isinstance(self.build_rpath, str):
raise InvalidArguments('Build_rpath is not a string.')
- resources = kwargs.get('resources', [])
- if not isinstance(resources, list):
- resources = [resources]
+ resources = extract_as_list(kwargs, 'resources')
for r in resources:
if not isinstance(r, str):
raise InvalidArguments('Resource argument is not a string.')
@@ -811,8 +814,7 @@ This will become a hard error in a future Meson release.''')
return self.include_dirs
def add_deps(self, deps):
- if not isinstance(deps, list):
- deps = [deps]
+ deps = listify(deps)
for dep in deps:
if hasattr(dep, 'held_object'):
dep = dep.held_object
@@ -1040,9 +1042,7 @@ class Generator:
self.arglist = args
if 'output' not in kwargs:
raise InvalidArguments('Generator must have "output" keyword argument.')
- outputs = kwargs['output']
- if not isinstance(outputs, list):
- outputs = [outputs]
+ outputs = listify(kwargs['output'])
for rule in outputs:
if not isinstance(rule, str):
raise InvalidArguments('"output" may only contain strings.')
@@ -1540,8 +1540,7 @@ class CustomTarget(Target):
return deps
def flatten_command(self, cmd):
- if not isinstance(cmd, list):
- cmd = [cmd]
+ cmd = listify(cmd)
final_cmd = []
for c in cmd:
if hasattr(c, 'held_object'):
@@ -1576,9 +1575,7 @@ class CustomTarget(Target):
self.sources.append(s)
if 'output' not in kwargs:
raise InvalidArguments('Missing keyword argument "output".')
- self.outputs = kwargs['output']
- if not isinstance(self.outputs, list):
- self.outputs = [self.outputs]
+ self.outputs = listify(kwargs['output'])
# This will substitute values from the input into output and return it.
inputs = get_sources_string_names(self.sources)
values = get_filenames_templates_dict(inputs, [])
@@ -1632,18 +1629,13 @@ class CustomTarget(Target):
self.build_always = kwargs.get('build_always', False)
if not isinstance(self.build_always, bool):
raise InvalidArguments('Argument build_always must be a boolean.')
- extra_deps = kwargs.get('depends', [])
- if not isinstance(extra_deps, list):
- extra_deps = [extra_deps]
+ extra_deps, depend_files = extract_as_list(kwargs, 'depends', 'depend_files', pop = False)
for ed in extra_deps:
while hasattr(ed, 'held_object'):
ed = ed.held_object
if not isinstance(ed, (CustomTarget, BuildTarget)):
raise InvalidArguments('Can only depend on toplevel targets: custom_target or build_target (executable or a library)')
self.extra_depends.append(ed)
- depend_files = kwargs.get('depend_files', [])
- if not isinstance(depend_files, list):
- depend_files = [depend_files]
for i in depend_files:
if isinstance(i, (File, str)):
self.depend_files.append(i)
@@ -1793,8 +1785,7 @@ class Data:
self.sources = sources
self.install_dir = install_dir
self.install_mode = install_mode
- if not isinstance(self.sources, list):
- self.sources = [self.sources]
+ self.sources = listify(self.sources)
for s in self.sources:
assert(isinstance(s, File))
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
index ec16134..255a506 100644
--- a/mesonbuild/compilers/c.py
+++ b/mesonbuild/compilers/c.py
@@ -16,7 +16,7 @@ import subprocess, os.path, tempfile
from .. import mlog
from .. import coredata
-from ..mesonlib import EnvironmentException, version_compare, Popen_safe
+from ..mesonlib import EnvironmentException, version_compare, Popen_safe, listify
from .compilers import (
GCC_MINGW,
@@ -1013,8 +1013,7 @@ class VisualStudioCCompiler(CCompiler):
def get_link_whole_for(self, args):
# Only since VS2015
- if not isinstance(args, list):
- args = [args]
+ args = listify(args)
return ['/WHOLEARCHIVE:' + x for x in args]
def get_instruction_set_args(self, instruction_set):
diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py
index a989704..9739f28 100644
--- a/mesonbuild/compilers/d.py
+++ b/mesonbuild/compilers/d.py
@@ -27,6 +27,20 @@ from .compilers import (
CompilerArgs,
)
+d_feature_args = {'gcc': {'unittest': '-funittest',
+ 'version': '-fversion',
+ 'import_dir': '-J'
+ },
+ 'llvm': {'unittest': '-unittest',
+ 'version': '-d-version',
+ 'import_dir': '-J'
+ },
+ 'dmd': {'unittest': '-unittest',
+ 'version': '-version',
+ 'import_dir': '-J'
+ }
+ }
+
class DCompiler(Compiler):
def __init__(self, exelist, version, is_cross):
self.language = 'd'
@@ -79,8 +93,42 @@ class DCompiler(Compiler):
# FIXME: Make this work for Windows, MacOS and cross-compiling
return get_gcc_soname_args(GCC_STANDARD, prefix, shlib_name, suffix, path, soversion, is_shared_module)
- def get_unittest_args(self):
- return ['-unittest']
+ def get_feature_args(self, kwargs):
+ res = []
+ if 'unittest' in kwargs:
+ unittest = kwargs.pop('unittest')
+ unittest_arg = d_feature_args[self.id]['unittest']
+ if not unittest_arg:
+ raise EnvironmentException('D compiler %s does not support the "unittest" feature.' % self.name_string())
+ if unittest:
+ res.append(unittest_arg)
+
+ if 'versions' in kwargs:
+ versions = kwargs.pop('versions')
+ if not isinstance(versions, list):
+ versions = [versions]
+
+ version_arg = d_feature_args[self.id]['version']
+ if not version_arg:
+ raise EnvironmentException('D compiler %s does not support the "feature versions" feature.' % self.name_string())
+ for v in versions:
+ res.append('{0}={1}'.format(version_arg, v))
+
+ if 'import_dirs' in kwargs:
+ import_dirs = kwargs.pop('import_dirs')
+ if not isinstance(import_dirs, list):
+ import_dirs = [import_dirs]
+
+ import_dir_arg = d_feature_args[self.id]['import_dir']
+ if not import_dir_arg:
+ raise EnvironmentException('D compiler %s does not support the "string import directories" feature.' % self.name_string())
+ for d in import_dirs:
+ res.append('{0}{1}'.format(import_dir_arg, d))
+
+ if kwargs:
+ raise EnvironmentException('Unknown D compiler feature(s) selected: %s' % ', '.join(kwargs.keys()))
+
+ return res
def get_buildtype_linker_args(self, buildtype):
return []
@@ -217,9 +265,6 @@ class GnuDCompiler(DCompiler):
def build_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath):
return self.build_unix_rpath_args(build_dir, from_dir, rpath_paths, build_rpath, install_rpath)
- def get_unittest_args(self):
- return ['-funittest']
-
class LLVMDCompiler(DCompiler):
def __init__(self, exelist, version, is_cross):
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py
index cc4837a..7c7f986 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
+from ..mesonlib import MesonException, Popen_safe, flatten, version_compare_many, listify
# These must be defined in this file to avoid cyclical references.
@@ -374,10 +374,7 @@ class ExternalProgram:
def __init__(self, name, command=None, silent=False, search_dir=None):
self.name = name
if command is not None:
- if not isinstance(command, list):
- self.command = [command]
- else:
- self.command = command
+ self.command = listify(command)
else:
self.command = self._search(name, search_dir)
if not silent:
diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py
index 12e0239..c0ac5a8 100644
--- a/mesonbuild/dependencies/misc.py
+++ b/mesonbuild/dependencies/misc.py
@@ -24,7 +24,7 @@ import sysconfig
from .. import mlog
from .. import mesonlib
-from ..mesonlib import Popen_safe
+from ..mesonlib import Popen_safe, extract_as_list
from ..environment import detect_cpu_family
from .base import DependencyException, DependencyMethods
@@ -132,9 +132,7 @@ class BoostDependency(ExternalDependency):
return args
def get_requested(self, kwargs):
- candidates = kwargs.get('modules', [])
- if not isinstance(candidates, list):
- candidates = [candidates]
+ candidates = extract_as_list(kwargs, 'modules')
for c in candidates:
if not isinstance(c, str):
raise DependencyException('Boost module argument is not a string.')
diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py
index 99e017b..8f183e5 100644
--- a/mesonbuild/dependencies/ui.py
+++ b/mesonbuild/dependencies/ui.py
@@ -23,7 +23,7 @@ from collections import OrderedDict
from .. import mlog
from .. import mesonlib
-from ..mesonlib import MesonException, Popen_safe, version_compare
+from ..mesonlib import MesonException, Popen_safe, version_compare, extract_as_list
from ..environment import for_windows, detect_cpu
from .base import DependencyException, DependencyMethods
@@ -468,12 +468,9 @@ class WxDependency(ExternalDependency):
self.link_args = out.split()
def get_requested(self, kwargs):
- modules = 'modules'
- if modules not in kwargs:
+ if 'modules' not in kwargs:
return []
- candidates = kwargs[modules]
- if not isinstance(candidates, list):
- candidates = [candidates]
+ candidates = extract_as_list(kwargs, 'modules')
for c in candidates:
if not isinstance(c, str):
raise DependencyException('wxwidgets module argument is not a string')
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 2bcf198..6658c26 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -21,7 +21,7 @@ from . import optinterpreter
from . import compilers
from .wrap import wrap, WrapMode
from . import mesonlib
-from .mesonlib import FileMode, Popen_safe
+from .mesonlib import FileMode, Popen_safe, listify, extract_as_list
from .dependencies import ExternalProgram
from .dependencies import InternalDependency, Dependency, DependencyException
from .interpreterbase import InterpreterBase
@@ -264,8 +264,7 @@ class DependencyHolder(InterpreterObject):
return self.held_object.get_version()
def pkgconfig_method(self, args, kwargs):
- if not isinstance(args, list):
- args = [args]
+ args = listify(args)
if len(args) != 1:
raise InterpreterException('get_pkgconfig_variable takes exactly one argument.')
varname = args[0]
@@ -669,9 +668,7 @@ class CompilerHolder(InterpreterObject):
if not isinstance(nobuiltins, bool):
raise InterpreterException('Type of no_builtin_args not a boolean.')
args = []
- incdirs = kwargs.get('include_directories', [])
- if not isinstance(incdirs, list):
- incdirs = [incdirs]
+ incdirs = extract_as_list(kwargs, 'include_directories')
for i in incdirs:
if not isinstance(i, IncludeDirsHolder):
raise InterpreterException('Include directories argument must be an include_directories object.')
@@ -688,8 +685,7 @@ class CompilerHolder(InterpreterObject):
def determine_dependencies(self, kwargs):
deps = kwargs.get('dependencies', None)
if deps is not None:
- if not isinstance(deps, list):
- deps = [deps]
+ deps = listify(deps)
final_deps = []
for d in deps:
try:
@@ -752,10 +748,13 @@ class CompilerHolder(InterpreterObject):
return self.compiler.symbols_have_underscore_prefix(self.environment)
def unittest_args_method(self, args, kwargs):
- # At time, only D compilers have this feature.
- if not hasattr(self.compiler, 'get_unittest_args'):
- raise InterpreterException('This {} compiler has no unittest arguments.'.format(self.compiler.get_display_language()))
- return self.compiler.get_unittest_args()
+ '''
+ This function is deprecated and should not be used.
+ It can be removed in a future version of Meson.
+ '''
+ if not hasattr(self.compiler, 'get_feature_args'):
+ raise InterpreterException('This {} compiler has no feature arguments.'.format(self.compiler.get_display_language()))
+ return self.compiler.get_feature_args({'unittest': 'true'})
def has_member_method(self, args, kwargs):
if len(args) != 2:
@@ -1249,6 +1248,9 @@ pch_kwargs = set(['c_pch', 'cpp_pch'])
lang_arg_kwargs = set(['c_args',
'cpp_args',
'd_args',
+ 'd_import_dirs',
+ 'd_unittest',
+ 'd_module_versions',
'fortran_args',
'java_args',
'objc_args',
@@ -1260,7 +1262,7 @@ lang_arg_kwargs = set(['c_args',
vala_kwargs = set(['vala_header', 'vala_gir', 'vala_vapi'])
rust_kwargs = set(['rust_crate_type'])
-cs_kwargs = set(['resources'])
+cs_kwargs = set(['resources', 'cs_args'])
buildtarget_kwargs = set(['build_by_default',
'build_rpath',
@@ -1454,8 +1456,7 @@ class Interpreter(InterpreterBase):
raise InterpreterException('Module returned a value of unknown type.')
def process_new_values(self, invalues):
- if not isinstance(invalues, list):
- invalues = [invalues]
+ invalues = listify(invalues)
for v in invalues:
if isinstance(v, (build.BuildTarget, build.CustomTarget, build.RunTarget)):
self.add_target(v.name, v)
@@ -1535,19 +1536,12 @@ class Interpreter(InterpreterBase):
version = kwargs.get('version', self.project_version)
if not isinstance(version, str):
raise InterpreterException('Version must be a string.')
- incs = kwargs.get('include_directories', [])
- if not isinstance(incs, list):
- incs = [incs]
- libs = kwargs.get('link_with', [])
- if not isinstance(libs, list):
- libs = [libs]
- sources = kwargs.get('sources', [])
- if not isinstance(sources, list):
- sources = [sources]
+ incs = extract_as_list(kwargs, 'include_directories')
+ libs = extract_as_list(kwargs, 'link_with')
+ sources = extract_as_list(kwargs, 'sources')
sources = self.source_strings_to_files(self.flatten(sources))
deps = self.flatten(kwargs.get('dependencies', []))
- if not isinstance(deps, list):
- deps = [deps]
+ deps = listify(deps)
compile_args = mesonlib.stringlistify(kwargs.get('compile_args', []))
link_args = mesonlib.stringlistify(kwargs.get('link_args', []))
final_deps = []
@@ -1692,11 +1686,11 @@ class Interpreter(InterpreterBase):
raise InterpreterException('Argument required for get_option.')
optname = args[0]
try:
- return compilers.base_options[optname].value
+ return self.environment.get_coredata().base_options[optname].value
except KeyError:
pass
try:
- return self.environment.get_coredata().get_builtin_option(optname)
+ return self.environment.coredata.get_builtin_option(optname)
except RuntimeError:
pass
try:
@@ -1721,6 +1715,11 @@ class Interpreter(InterpreterBase):
return self.coredata.external_args[lang]
except KeyError:
pass
+ # Some base options are not defined in some environments, return the default value.
+ try:
+ return compilers.base_options[optname].value
+ except KeyError:
+ pass
raise InterpreterException('Tried to access unknown option "%s".' % optname)
@noKwargs
@@ -1730,8 +1729,7 @@ class Interpreter(InterpreterBase):
return ConfigurationDataHolder()
def parse_default_options(self, default_options):
- if not isinstance(default_options, list):
- default_options = [default_options]
+ default_options = listify(default_options)
for option in default_options:
if not isinstance(option, str):
mlog.debug(option)
@@ -2019,7 +2017,7 @@ class Interpreter(InterpreterBase):
if progobj is None:
progobj = self.program_from_system(args)
if required and (progobj is None or not progobj.found()):
- raise InvalidArguments('Program "%s" not found or not executable' % exename)
+ raise InvalidArguments('Program "%s" not found or not executable' % args[0])
if progobj is None:
return ExternalProgramHolder(dependencies.ExternalProgram('nonexistingprogram'))
return progobj
@@ -2277,12 +2275,8 @@ class Interpreter(InterpreterBase):
elif len(args) == 1:
if 'command' not in kwargs:
raise InterpreterException('Missing "command" keyword argument')
- all_args = kwargs['command']
- if not isinstance(all_args, list):
- all_args = [all_args]
- deps = kwargs.get('depends', [])
- if not isinstance(deps, list):
- deps = [deps]
+ all_args = extract_as_list(kwargs, 'command')
+ deps = extract_as_list(kwargs, 'depends')
else:
raise InterpreterException('Run_target needs at least one positional argument.')
@@ -2333,8 +2327,7 @@ class Interpreter(InterpreterBase):
if isinstance(envlist, EnvironmentVariablesHolder):
env = envlist.held_object
else:
- if not isinstance(envlist, list):
- envlist = [envlist]
+ envlist = listify(envlist)
# Convert from array to environment object
env = EnvironmentVariablesHolder()
for e in envlist:
@@ -2363,9 +2356,7 @@ 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 = kwargs.get('args', [])
- if not isinstance(cmd_args, list):
- cmd_args = [cmd_args]
+ cmd_args = extract_as_list(kwargs, 'args')
for i in cmd_args:
if not isinstance(i, (str, mesonlib.File, TargetHolder)):
raise InterpreterException('Command line arguments must be strings, files or targets.')
@@ -2504,9 +2495,7 @@ class Interpreter(InterpreterBase):
if not isinstance(install_dir, str):
raise InvalidArguments('Keyword argument install_dir not a string.')
if 'exclude_files' in kwargs:
- exclude = kwargs['exclude_files']
- if not isinstance(exclude, list):
- exclude = [exclude]
+ exclude = extract_as_list(kwargs, 'exclude_files')
for f in exclude:
if not isinstance(f, str):
raise InvalidArguments('Exclude argument not a string.')
@@ -2516,9 +2505,7 @@ class Interpreter(InterpreterBase):
else:
exclude_files = set()
if 'exclude_directories' in kwargs:
- exclude = kwargs['exclude_directories']
- if not isinstance(exclude, list):
- exclude = [exclude]
+ exclude = extract_as_list(kwargs, 'exclude_directories')
for d in exclude:
if not isinstance(d, str):
raise InvalidArguments('Exclude argument not a string.')
@@ -2681,9 +2668,7 @@ different subdirectory.
if re.fullmatch('[_a-zA-Z][_0-9a-zA-Z]*', setup_name) is None:
raise InterpreterException('Setup name may only contain alphanumeric characters.')
try:
- inp = kwargs.get('exe_wrapper', [])
- if not isinstance(inp, list):
- inp = [inp]
+ inp = extract_as_list(kwargs, 'exe_wrapper')
exe_wrapper = []
for i in inp:
if hasattr(i, 'held_object'):
@@ -2825,8 +2810,7 @@ different subdirectory.
is_cross = False
try:
kw_src = self.flatten(kwargs['sources'])
- if not isinstance(kw_src, list):
- kw_src = [kw_src]
+ kw_src = listify(kw_src)
except KeyError:
kw_src = []
sources += kw_src
@@ -2834,12 +2818,9 @@ different subdirectory.
objs = self.flatten(kwargs.get('objects', []))
kwargs['dependencies'] = self.flatten(kwargs.get('dependencies', []))
if 'extra_files' in kwargs:
- ef = kwargs['extra_files']
- if not isinstance(ef, list):
- ef = [ef]
+ ef = extract_as_list(kwargs, 'extra_files')
kwargs['extra_files'] = self.source_strings_to_files(ef)
- if not isinstance(objs, list):
- objs = [objs]
+ 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 d03e5a2..71134a8 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -14,6 +14,7 @@
"""A library of random helper functionality."""
+import sys
import stat
import time
import platform, subprocess, operator, os, shutil, re
@@ -473,6 +474,26 @@ def replace_if_different(dst, dst_tmp):
else:
os.unlink(dst_tmp)
+
+def listify(*args):
+ '''
+ Returns a list with all args embedded in a list if they are not of type list.
+ This function preserves order.
+ '''
+ 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]
+
+
+def extract_as_list(dict_object, *keys, pop = False):
+ '''
+ Extracts all values from given dict_object and listifies them.
+ '''
+ if pop:
+ return listify(*[dict_object.pop(key, []) for key in keys])
+ return listify(*[dict_object.get(key, []) for key in keys])
+
+
def typeslistify(item, types):
'''
Ensure that type(@item) is one of @types or a
@@ -509,6 +530,8 @@ def expand_arguments(args):
return expended_args
def Popen_safe(args, write=None, stderr=subprocess.PIPE, **kwargs):
+ if sys.version_info < (3, 6) or not sys.stdout.encoding:
+ return Popen_safe_legacy(args, write=write, stderr=stderr, **kwargs)
p = subprocess.Popen(args, universal_newlines=True,
close_fds=False,
stdout=subprocess.PIPE,
@@ -516,6 +539,25 @@ def Popen_safe(args, write=None, stderr=subprocess.PIPE, **kwargs):
o, e = p.communicate(write)
return p, o, e
+def Popen_safe_legacy(args, write=None, stderr=subprocess.PIPE, **kwargs):
+ p = subprocess.Popen(args, universal_newlines=False,
+ stdout=subprocess.PIPE,
+ stderr=stderr, **kwargs)
+ if write is not None:
+ write = write.encode('utf-8')
+ o, e = p.communicate(write)
+ if o is not None:
+ if sys.stdout.encoding:
+ o = o.decode(encoding=sys.stdout.encoding, errors='replace').replace('\r\n', '\n')
+ else:
+ o = o.decode(errors='replace').replace('\r\n', '\n')
+ if e is not None:
+ if sys.stderr.encoding:
+ e = e.decode(encoding=sys.stderr.encoding, errors='replace').replace('\r\n', '\n')
+ else:
+ e = e.decode(errors='replace').replace('\r\n', '\n')
+ return p, o, e
+
def commonpath(paths):
'''
For use on Python 3.4 where os.path.commonpath is not available.
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index fcdd193..137d380 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -98,17 +98,12 @@ class GnomeModule(ExtensionModule):
cmd = ['glib-compile-resources', '@INPUT@']
- source_dirs = kwargs.pop('source_dir', [])
- if not isinstance(source_dirs, list):
- source_dirs = [source_dirs]
+ source_dirs, dependencies = mesonlib.extract_as_list(kwargs, 'source_dir', 'dependencies', pop=True)
if len(args) < 2:
raise MesonException('Not enough arguments; the name of the resource '
'and the path to the XML file are required')
- dependencies = kwargs.pop('dependencies', [])
- if not isinstance(dependencies, list):
- dependencies = [dependencies]
# Validate dependencies
for (ii, dep) in enumerate(dependencies):
if hasattr(dep, 'held_object'):
@@ -328,8 +323,7 @@ class GnomeModule(ExtensionModule):
cflags = OrderedSet()
ldflags = OrderedSet()
gi_includes = OrderedSet()
- if not isinstance(deps, list):
- deps = [deps]
+ deps = mesonlib.listify(deps)
for dep in deps:
if hasattr(dep, 'held_object'):
@@ -464,17 +458,14 @@ class GnomeModule(ExtensionModule):
scan_command += ['--filelist=' + gir_filelist_filename]
if 'link_with' in kwargs:
- link_with = kwargs.pop('link_with')
- if not isinstance(link_with, list):
- link_with = [link_with]
+ link_with = mesonlib.extract_as_list(kwargs, 'link_with', pop = True)
+
for link in link_with:
scan_command += self._get_link_args(state, link.held_object, depends,
use_gir_args=True)
if 'includes' in kwargs:
- includes = kwargs.pop('includes')
- if not isinstance(includes, list):
- includes = [includes]
+ includes = mesonlib.extract_as_list(kwargs, 'includes', pop = True)
for inc in includes:
if hasattr(inc, 'held_object'):
inc = inc.held_object
@@ -515,17 +506,17 @@ class GnomeModule(ExtensionModule):
# FIXME: Linking directly to libasan is not recommended but g-ir-scanner
# does not understand -f LDFLAGS. https://bugzilla.gnome.org/show_bug.cgi?id=783892
# ldflags += compilers.sanitizer_link_args(sanitize)
- if kwargs.get('symbol_prefix'):
+ if 'symbol_prefix' in kwargs:
sym_prefix = kwargs.pop('symbol_prefix')
if not isinstance(sym_prefix, str):
raise MesonException('Gir symbol prefix must be str')
scan_command += ['--symbol-prefix=%s' % sym_prefix]
- if kwargs.get('identifier_prefix'):
+ if 'identifier_prefix' in kwargs:
identifier_prefix = kwargs.pop('identifier_prefix')
if not isinstance(identifier_prefix, str):
raise MesonException('Gir identifier prefix must be str')
scan_command += ['--identifier-prefix=%s' % identifier_prefix]
- if kwargs.get('export_packages'):
+ if 'export_packages' in kwargs:
pkgs = kwargs.pop('export_packages')
if isinstance(pkgs, str):
scan_command += ['--pkg-export=%s' % pkgs]
@@ -534,9 +525,7 @@ class GnomeModule(ExtensionModule):
else:
raise MesonException('Gir export packages must be str or list')
- deps = kwargs.pop('dependencies', [])
- if not isinstance(deps, list):
- deps = [deps]
+ deps = mesonlib.extract_as_list(kwargs, 'dependencies', pop = True)
deps = (girtarget.get_all_link_deps() + girtarget.get_external_deps() +
deps)
# Need to recursively add deps on GirTarget sources from our
@@ -593,9 +582,7 @@ class GnomeModule(ExtensionModule):
for i in gi_includes:
scan_command += ['--add-include-path=%s' % i]
- inc_dirs = kwargs.pop('include_directories', [])
- if not isinstance(inc_dirs, list):
- inc_dirs = [inc_dirs]
+ inc_dirs = mesonlib.extract_as_list(kwargs, 'include_directories', pop = True)
for incd in inc_dirs:
if not isinstance(incd.held_object, (str, build.IncludeDirs)):
raise MesonException(
@@ -618,7 +605,7 @@ class GnomeModule(ExtensionModule):
scankwargs = {'output': girfile,
'command': scan_command,
'depends': depends}
- if kwargs.get('install'):
+ if 'install' in kwargs:
scankwargs['install'] = kwargs['install']
scankwargs['install_dir'] = kwargs.get('install_dir_gir',
os.path.join(state.environment.get_datadir(), 'gir-1.0'))
@@ -636,7 +623,7 @@ class GnomeModule(ExtensionModule):
'output': typelib_output,
'command': typelib_cmd,
}
- if kwargs.get('install'):
+ if 'install' in kwargs:
typelib_kwargs['install'] = kwargs['install']
typelib_kwargs['install_dir'] = kwargs.get('install_dir_typelib',
os.path.join(state.environment.get_libdir(), 'girepository-1.0'))
@@ -759,9 +746,7 @@ This will become a hard error in the future.''')
if mode not in VALID_MODES:
raise MesonException('gtkdoc: Mode {} is not a valid mode: {}'.format(mode, VALID_MODES))
- src_dirs = kwargs['src_dir']
- if not isinstance(src_dirs, list):
- src_dirs = [src_dirs]
+ src_dirs = mesonlib.extract_as_list(kwargs, 'src_dir')
header_dirs = []
for src_dir in src_dirs:
if hasattr(src_dir, 'held_object'):
@@ -806,9 +791,7 @@ 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)
- inc_dirs = kwargs.get('include_directories', [])
- if not isinstance(inc_dirs, list):
- inc_dirs = [inc_dirs]
+ inc_dirs = mesonlib.extract_as_list(kwargs, 'include_directories')
for incd in inc_dirs:
if not isinstance(incd.held_object, (str, build.IncludeDirs)):
raise MesonException(
@@ -839,9 +822,7 @@ This will become a hard error in the future.''')
if kwarg_name not in kwargs:
return []
- new_args = kwargs[kwarg_name]
- if not isinstance(new_args, list):
- new_args = [new_args]
+ new_args = mesonlib.extract_as_list(kwargs, kwarg_name)
args = []
for i in new_args:
if expend_file_state and isinstance(i, mesonlib.File):
@@ -1200,12 +1181,8 @@ G_END_DECLS'''
@staticmethod
def _vapi_args_to_command(prefix, variable, kwargs, accept_vapi=False):
- arg_list = kwargs.get(variable)
- if not arg_list:
- return []
+ arg_list = mesonlib.extract_as_list(kwargs, variable)
ret = []
- if not isinstance(arg_list, list):
- arg_list = [arg_list]
for arg in arg_list:
if not isinstance(arg, str):
types = 'strings' + ' or InternalDependencys' if accept_vapi else ''
@@ -1224,9 +1201,7 @@ G_END_DECLS'''
arg_list = kwargs.get('packages')
if not arg_list:
return [], [], [], []
- if not isinstance(arg_list, list):
- arg_list = [arg_list]
-
+ arg_list = mesonlib.listify(arg_list)
vapi_depends = []
vapi_packages = []
vapi_includes = []
@@ -1300,12 +1275,10 @@ G_END_DECLS'''
cmd += pkg_cmd
cmd += ['--metadatadir=' + source_dir]
- inputs = kwargs.get('sources')
- if not inputs:
+ if 'sources' not in kwargs:
raise MesonException('sources are required to generate the vapi file')
- if not isinstance(inputs, list):
- inputs = [inputs]
+ inputs = mesonlib.extract_as_list(kwargs, 'sources')
link_with = []
for i in inputs:
diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py
index 0a0498c..824ba78 100644
--- a/mesonbuild/modules/pkgconfig.py
+++ b/mesonbuild/modules/pkgconfig.py
@@ -108,8 +108,7 @@ class PkgConfigModule(ExtensionModule):
ofile.write('\n')
def process_libs(self, libs):
- if not isinstance(libs, list):
- libs = [libs]
+ libs = mesonlib.listify(libs)
processed_libs = []
for l in libs:
if hasattr(l, 'held_object'):
@@ -121,7 +120,7 @@ class PkgConfigModule(ExtensionModule):
@permittedKwargs({'libraries', 'version', 'name', 'description', 'filebase',
'subdirs', 'requires', 'requires_private', 'libraries_private',
- 'install_dir', 'extra_cflags', 'variables', 'url'})
+ 'install_dir', 'extra_cflags', 'variables', 'url', 'd_module_versions'})
def generate(self, state, args, kwargs):
if len(args) > 0:
raise mesonlib.MesonException('Pkgconfig_gen takes no positional arguments.')
@@ -148,6 +147,12 @@ class PkgConfigModule(ExtensionModule):
conflicts = mesonlib.stringlistify(kwargs.get('conflicts', []))
extra_cflags = mesonlib.stringlistify(kwargs.get('extra_cflags', []))
+ dversions = kwargs.get('d_module_versions', None)
+ if dversions:
+ compiler = state.environment.coredata.compilers.get('d')
+ if compiler:
+ extra_cflags.extend(compiler.get_feature_args({'versions': dversions}))
+
def parse_variable_list(stringlist):
reserved = ['prefix', 'libdir', 'includedir']
variables = []
diff --git a/mesonbuild/modules/qt4.py b/mesonbuild/modules/qt4.py
index 4056b6d..37e630b 100644
--- a/mesonbuild/modules/qt4.py
+++ b/mesonbuild/modules/qt4.py
@@ -15,7 +15,7 @@
import os
from .. import mlog
from .. import build
-from ..mesonlib import MesonException, Popen_safe
+from ..mesonlib import MesonException, Popen_safe, extract_as_list
from ..dependencies import Qt4Dependency
from . import ExtensionModule
import xml.etree.ElementTree as ET
@@ -99,21 +99,8 @@ class Qt4Module(ExtensionModule):
@permittedKwargs({'moc_headers', 'moc_sources', 'ui_files', 'qresources', 'method'})
def preprocess(self, state, args, kwargs):
- rcc_files = kwargs.pop('qresources', [])
- if not isinstance(rcc_files, list):
- rcc_files = [rcc_files]
- ui_files = kwargs.pop('ui_files', [])
- if not isinstance(ui_files, list):
- ui_files = [ui_files]
- moc_headers = kwargs.pop('moc_headers', [])
- if not isinstance(moc_headers, list):
- moc_headers = [moc_headers]
- moc_sources = kwargs.pop('moc_sources', [])
- if not isinstance(moc_sources, list):
- moc_sources = [moc_sources]
- sources = kwargs.pop('sources', [])
- if not isinstance(sources, list):
- sources = [sources]
+ rcc_files, ui_files, moc_headers, moc_sources, sources \
+ = extract_as_list(kwargs, 'qresources', 'ui_files', 'moc_headers', 'moc_sources', 'sources', pop = True)
sources += args[1:]
method = kwargs.get('method', 'auto')
self._detect_tools(state.environment, method)
diff --git a/mesonbuild/modules/qt5.py b/mesonbuild/modules/qt5.py
index 6194a23..ef3d52f 100644
--- a/mesonbuild/modules/qt5.py
+++ b/mesonbuild/modules/qt5.py
@@ -15,7 +15,7 @@
import os
from .. import mlog
from .. import build
-from ..mesonlib import MesonException, Popen_safe
+from ..mesonlib import MesonException, Popen_safe, extract_as_list
from ..dependencies import Qt5Dependency
from . import ExtensionModule
import xml.etree.ElementTree as ET
@@ -105,21 +105,8 @@ class Qt5Module(ExtensionModule):
@permittedKwargs({'moc_headers', 'moc_sources', 'ui_files', 'qresources', 'method'})
def preprocess(self, state, args, kwargs):
- rcc_files = kwargs.pop('qresources', [])
- if not isinstance(rcc_files, list):
- rcc_files = [rcc_files]
- ui_files = kwargs.pop('ui_files', [])
- if not isinstance(ui_files, list):
- ui_files = [ui_files]
- moc_headers = kwargs.pop('moc_headers', [])
- if not isinstance(moc_headers, list):
- moc_headers = [moc_headers]
- moc_sources = kwargs.pop('moc_sources', [])
- if not isinstance(moc_sources, list):
- moc_sources = [moc_sources]
- sources = kwargs.pop('sources', [])
- if not isinstance(sources, list):
- sources = [sources]
+ rcc_files, ui_files, moc_headers, moc_sources, sources \
+ = extract_as_list(kwargs, 'qresources', 'ui_files', 'moc_headers', 'moc_sources', 'sources', pop = True)
sources += args[1:]
method = kwargs.get('method', 'auto')
self._detect_tools(state.environment, method)
diff --git a/mesonbuild/modules/unstable_simd.py b/mesonbuild/modules/unstable_simd.py
index 828afec..b774cff 100644
--- a/mesonbuild/modules/unstable_simd.py
+++ b/mesonbuild/modules/unstable_simd.py
@@ -73,9 +73,7 @@ class SimdModule(ExtensionModule):
}
lib_kwargs.update(basic_kwargs)
langarg_key = compiler.get_language() + '_args'
- old_lang_args = lib_kwargs.get(langarg_key, [])
- if not isinstance(old_lang_args, list):
- old_lang_args = [old_lang_args]
+ old_lang_args = mesonlib.extract_as_list(lib_kwargs, langarg_key)
all_lang_args = old_lang_args + args
lib_kwargs[langarg_key] = all_lang_args
result.append(interpreter.func_static_lib(None, [libname], lib_kwargs))
diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py
index 6fef5bb..ab215dc 100644
--- a/mesonbuild/modules/windows.py
+++ b/mesonbuild/modules/windows.py
@@ -16,7 +16,7 @@ import os
from .. import mlog
from .. import mesonlib, dependencies, build
-from ..mesonlib import MesonException
+from ..mesonlib import MesonException, extract_as_list
from . import get_include_args
from . import ModuleReturnValue
from . import ExtensionModule
@@ -35,9 +35,7 @@ class WindowsModule(ExtensionModule):
comp = self.detect_compiler(state.compilers)
extra_args = mesonlib.stringlistify(kwargs.get('args', []))
- inc_dirs = kwargs.pop('include_directories', [])
- if not isinstance(inc_dirs, list):
- inc_dirs = [inc_dirs]
+ inc_dirs = extract_as_list(kwargs, 'include_directories', pop = True)
for incd in inc_dirs:
if not isinstance(incd.held_object, (str, build.IncludeDirs)):
raise MesonException('Resource include dirs should be include_directories().')
diff --git a/mesonbuild/rewriter.py b/mesonbuild/rewriter.py
index b88c5ef..b88c5ef 100755..100644
--- a/mesonbuild/rewriter.py
+++ b/mesonbuild/rewriter.py
diff --git a/msi/License.rtf b/msi/License.rtf
index 9b58df9..9b58df9 100755..100644
--- a/msi/License.rtf
+++ b/msi/License.rtf
diff --git a/run_unittests.py b/run_unittests.py
index 6487496..2626931 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -1948,6 +1948,11 @@ endian = 'little'
crossfile.flush()
self.init(testdir, ['--cross-file='+crossfile.name])
+ def test_reconfigure(self):
+ testdir = os.path.join(self.unit_test_dir, '13 reconfigure')
+ self.init(testdir, ['-Db_lto=true'], default_args=False)
+ self.build('reconfigure')
+
class LinuxArmCrossCompileTests(BasePlatformTests):
'''
diff --git a/test cases/common/146 C and CPP link/dummy.c b/test cases/common/146 C and CPP link/dummy.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test cases/common/146 C and CPP link/dummy.c
diff --git a/test cases/common/146 C and CPP link/meson.build b/test cases/common/146 C and CPP link/meson.build
index db84445..2dd3364 100644
--- a/test cases/common/146 C and CPP link/meson.build
+++ b/test cases/common/146 C and CPP link/meson.build
@@ -65,10 +65,44 @@ libfoo = shared_library(
#
# VS2010 lacks the /WHOLEARCHIVE option that later versions of MSVC support, so
# don't run this tests on that backend.
-if meson.backend() != 'vs2010'
+if not (cxx.get_id() == 'msvc' and cxx.version().version_compare('<19'))
libfoowhole = shared_library(
'foowhole',
['foobar.c', 'foobar.h'],
link_whole : [libc, libcpp],
)
endif
+
+# Test sublinking (linking C and C++, then linking that to C)
+libfoo_static = static_library(
+ 'foo_static',
+ ['foobar.c', 'foobar.h'],
+ link_with : [libc, libcpp],
+)
+
+libsub = shared_library(
+ 'sub',
+ ['sub.c', 'sub.h'],
+ link_with : libfoo_static,
+)
+
+if not (cxx.get_id() == 'msvc' and cxx.version().version_compare('<19'))
+ libsubwhole = shared_library(
+ 'subwhole',
+ ['sub.c', 'sub.h'],
+ link_whole : libfoo_static,
+ )
+endif
+
+# Test that it really is recursive
+libsub_static = static_library(
+ 'sub_static',
+ ['sub.c', 'sub.h'],
+ link_with : libfoo_static,
+)
+
+libsubsub = shared_library(
+ 'subsub',
+ ['dummy.c'],
+ link_with : libsub_static,
+)
diff --git a/test cases/common/146 C and CPP link/sub.c b/test cases/common/146 C and CPP link/sub.c
new file mode 100644
index 0000000..7c078f8
--- /dev/null
+++ b/test cases/common/146 C and CPP link/sub.c
@@ -0,0 +1,19 @@
+/* Copyright © 2017 Dylan Baker
+ *
+ * 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.
+ */
+#include "sub.h"
+
+float a_half(void) {
+ return .5;
+}
diff --git a/test cases/common/146 C and CPP link/sub.h b/test cases/common/146 C and CPP link/sub.h
new file mode 100644
index 0000000..5b02e17
--- /dev/null
+++ b/test cases/common/146 C and CPP link/sub.h
@@ -0,0 +1,16 @@
+/* Copyright © 2017 Dylan Baker
+ *
+ * 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.
+ */
+
+float a_half(void);
diff --git a/test cases/common/47 options/meson.build b/test cases/common/47 options/meson.build
index 4058748..2a764f0 100644
--- a/test cases/common/47 options/meson.build
+++ b/test cases/common/47 options/meson.build
@@ -12,6 +12,7 @@ if get_option('combo_opt') != 'combo'
error('Incorrect value to combo option.')
endif
+# If the default changes, update test cases/unit/13 reconfigure
if get_option('b_lto') != false
error('Incorrect value in base option.')
endif
diff --git a/test cases/d/9 features/app.d b/test cases/d/9 features/app.d
new file mode 100644
index 0000000..37cc1dd
--- /dev/null
+++ b/test cases/d/9 features/app.d
@@ -0,0 +1,51 @@
+
+import std.stdio;
+import std.array : split;
+import std.string : strip;
+
+auto getMenu ()
+{
+ auto foods = import ("food.txt").strip.split ("\n");
+ return foods;
+}
+
+auto getPeople ()
+{
+ return import ("people.txt").strip.split ("\n");
+}
+
+void main (string[] args)
+{
+ import std.array : join;
+ import core.stdc.stdlib : exit;
+
+ immutable request = args[1];
+ if (request == "menu") {
+ version (No_Menu) {
+ } else {
+ writeln ("On the menu: ", getMenu.join (", "));
+ exit (0);
+ }
+ }
+
+ version (With_People) {
+ if (request == "people") {
+ writeln ("People: ", getPeople.join (", "));
+ exit (0);
+ }
+ }
+
+ // we fail here
+ exit (1);
+}
+
+unittest
+{
+ writeln ("TEST");
+ import core.stdc.stdlib : exit;
+
+ writeln(getMenu);
+ assert (getMenu () == ["Spam", "Eggs", "Spam", "Baked Beans", "Spam", "Spam"]);
+
+ exit (0);
+}
diff --git a/test cases/d/9 features/data/food.txt b/test cases/d/9 features/data/food.txt
new file mode 100644
index 0000000..8275dd0
--- /dev/null
+++ b/test cases/d/9 features/data/food.txt
@@ -0,0 +1,6 @@
+Spam
+Eggs
+Spam
+Baked Beans
+Spam
+Spam
diff --git a/test cases/d/9 features/data/people.txt b/test cases/d/9 features/data/people.txt
new file mode 100644
index 0000000..abbae06
--- /dev/null
+++ b/test cases/d/9 features/data/people.txt
@@ -0,0 +1,5 @@
+Rick
+Morty
+Summer
+Beth
+Jerry
diff --git a/test cases/d/9 features/meson.build b/test cases/d/9 features/meson.build
new file mode 100644
index 0000000..9e63710
--- /dev/null
+++ b/test cases/d/9 features/meson.build
@@ -0,0 +1,29 @@
+project('D Features', 'd')
+
+# directory for data
+data_dir = join_paths(meson.current_source_dir(), 'data')
+
+e_plain = executable('dapp_menu',
+ 'app.d',
+ d_import_dirs: [data_dir]
+)
+test('dapp_menu_t_fail', e_plain, should_fail: true)
+test('dapp_menu_t', e_plain, args: ['menu'])
+
+# test feature versions and string imports
+e_versions = executable('dapp_versions',
+ 'app.d',
+ d_import_dirs: [data_dir],
+ d_module_versions: ['No_Menu', 'With_People']
+)
+test('dapp_versions_t_fail', e_versions, args: ['menu'], should_fail: true)
+test('dapp_versions_t', e_versions, args: ['people'])
+
+# test everything and unittests
+e_test = executable('dapp_test',
+ 'app.d',
+ d_import_dirs: [data_dir],
+ d_module_versions: ['No_Menu', 'With_People'],
+ d_unittest: true
+)
+test('dapp_test', e_test)
diff --git a/test cases/unit/13 reconfigure/meson.build b/test cases/unit/13 reconfigure/meson.build
new file mode 100644
index 0000000..102180e
--- /dev/null
+++ b/test cases/unit/13 reconfigure/meson.build
@@ -0,0 +1,5 @@
+project('reconfigure test', ['c'])
+
+if get_option('b_lto') != true
+ error('b_lto not set')
+endif
diff --git a/test cases/vala/12 custom output/bar.vala b/test cases/vala/12 custom output/bar.vala
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test cases/vala/12 custom output/bar.vala
diff --git a/test cases/vala/12 custom output/meson.build b/test cases/vala/12 custom output/meson.build
index ef6dbb5..c328959 100644
--- a/test cases/vala/12 custom output/meson.build
+++ b/test cases/vala/12 custom output/meson.build
@@ -3,7 +3,11 @@ project('valatest', 'c', 'vala')
glib = dependency('glib-2.0')
gobject = dependency('gobject-2.0')
-library('foo-1.0', 'foo.vala',
- vala_header: 'foo.h',
- vala_vapi: 'foo.vapi',
+foo_lib = library('foo-1.0', 'foo.vala',
+ vala_header: 'foo.h',
+ vala_vapi: 'foo.vapi',
+ dependencies: [glib, gobject])
+
+library('bar', 'bar.vala',
+ link_with: [foo_lib],
dependencies: [glib, gobject])