aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2017-09-18 20:23:56 +0300
committerGitHub <noreply@github.com>2017-09-18 20:23:56 +0300
commit076f3c53bd4d959448507eb904ed9d5890a6f121 (patch)
tree68ef3357b7cd46c4d5c6b94d0c3fdf2f3079c4b1
parent0b0448f8f2b062355242c86bb59c2596789fd90f (diff)
parentc3c37fac38dcea1f00cb6750722b39704ef8fc10 (diff)
downloadmeson-076f3c53bd4d959448507eb904ed9d5890a6f121.zip
meson-076f3c53bd4d959448507eb904ed9d5890a6f121.tar.gz
meson-076f3c53bd4d959448507eb904ed9d5890a6f121.tar.bz2
Merge pull request #2263 from ximion/dlang
d: Add an easy way to use D-specific features
-rw-r--r--docs/markdown/D.md89
-rw-r--r--docs/markdown/Pkgconfig-module.md2
-rw-r--r--docs/markdown/Reference-manual.md6
-rw-r--r--docs/markdown/index.md2
-rw-r--r--docs/sitemap.txt1
-rw-r--r--mesonbuild/build.py20
-rw-r--r--mesonbuild/compilers/d.py55
-rw-r--r--mesonbuild/interpreter.py14
-rw-r--r--mesonbuild/modules/pkgconfig.py8
-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
13 files changed, 275 insertions, 13 deletions
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/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..2025ab5 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -424,6 +424,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 +839,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.
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/mesonbuild/build.py b/mesonbuild/build.py
index 89689d7..5bf2874 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -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,11 @@ 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.validate_sources()
self.validate_cross_install(environment)
@@ -669,8 +672,23 @@ class BuildTarget(Target):
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):
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/interpreter.py b/mesonbuild/interpreter.py
index 2bcf198..9a1bb84 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -752,10 +752,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 +1252,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',
diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py
index 0a0498c..a7a35d5 100644
--- a/mesonbuild/modules/pkgconfig.py
+++ b/mesonbuild/modules/pkgconfig.py
@@ -121,7 +121,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 +148,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/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)