aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Klumpp <matthias@tenstral.net>2016-08-18 10:42:12 +0200
committerMatthias Klumpp <matthias@tenstral.net>2016-08-19 03:02:51 +0200
commit56823272ab96892e4f6c8dd86842d8c930281209 (patch)
tree84587d66fd89acfbb167d372dfdc206eabcdf3d7
parent58359c8fac09ecd06af1d389bfad8cd65e831e2a (diff)
downloadmeson-56823272ab96892e4f6c8dd86842d8c930281209.zip
meson-56823272ab96892e4f6c8dd86842d8c930281209.tar.gz
meson-56823272ab96892e4f6c8dd86842d8c930281209.tar.bz2
Implement D support
This patch adds support for the D programming language[1] to Meson. The following compilers are supported: * LDC * GDC * DMD [1]: http://dlang.org/
-rw-r--r--authors.txt1
-rw-r--r--mesonbuild/backend/backends.py9
-rw-r--r--mesonbuild/build.py5
-rw-r--r--mesonbuild/compilers.py281
-rw-r--r--mesonbuild/environment.py49
-rw-r--r--mesonbuild/interpreter.py4
-rw-r--r--mesonbuild/optinterpreter.py1
-rwxr-xr-xrun_tests.py12
-rw-r--r--test cases/d/1 simple/app.d8
-rw-r--r--test cases/d/1 simple/installed_files.txt1
-rw-r--r--test cases/d/1 simple/meson.build4
-rw-r--r--test cases/d/1 simple/utils.d8
-rw-r--r--test cases/d/2 library/app.d8
-rw-r--r--test cases/d/2 library/installed_files.txt4
-rw-r--r--test cases/d/2 library/libstuff.d9
-rw-r--r--test cases/d/2 library/meson.build14
16 files changed, 414 insertions, 4 deletions
diff --git a/authors.txt b/authors.txt
index 4244917..e9fceb0 100644
--- a/authors.txt
+++ b/authors.txt
@@ -40,3 +40,4 @@ Noam Meltzer
Vincent Szolnoky
Zhe Wang
Wim Taymans
+Matthias Klumpp
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 1f1c3ca..806a6f3 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -16,6 +16,7 @@ import os, pickle, re
from .. import build
from .. import dependencies
from .. import mesonlib
+from .. import compilers
import json
import subprocess
from ..mesonlib import MesonException
@@ -233,6 +234,9 @@ class Backend():
def has_swift(self, target):
return self.has_source_suffix(target, '.swift')
+ def has_d(self, target):
+ return self.has_source_suffix(target, '.d')
+
def determine_linker(self, target, src):
if isinstance(target, build.StaticLibrary):
if self.build.static_cross_linker is not None:
@@ -358,7 +362,10 @@ class Backend():
if not isinstance(d, build.StaticLibrary) and\
not isinstance(d, build.SharedLibrary):
raise RuntimeError('Tried to link with a non-library target "%s".' % d.get_basename())
- args.append(self.get_target_filename_for_linking(d))
+ if isinstance(compiler, compilers.LLVMDCompiler):
+ args.extend(['-L', self.get_target_filename_for_linking(d)])
+ else:
+ args.append(self.get_target_filename_for_linking(d))
# If you have executable e that links to shared lib s1 that links to shared library s2
# you have to specify s2 as well as s1 when linking e even if e does not directly use
# s2. Gcc handles this case fine but Clang does not for some reason. Thus we need to
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 61dace7..143cba9 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -27,6 +27,7 @@ known_basic_kwargs = {'install' : True,
'cpp_args' : True,
'cs_args' : True,
'vala_args' : True,
+ 'd_args' : True,
'link_args' : True,
'link_depends': True,
'link_with' : True,
@@ -385,6 +386,10 @@ class BuildTarget():
if not isinstance(valalist, list):
valalist = [valalist]
self.add_compiler_args('vala', valalist)
+ dlist = kwargs.get('d_args', [])
+ if not isinstance(dlist, list):
+ dlist = [dlist]
+ self.add_compiler_args('d', dlist)
self.link_args = kwargs.get('link_args', [])
if not isinstance(self.link_args, list):
self.link_args = [self.link_args]
diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py
index d020033..88184cb 100644
--- a/mesonbuild/compilers.py
+++ b/mesonbuild/compilers.py
@@ -23,7 +23,7 @@ from . import coredata
about. To support a new compiler, add its information below.
Also add corresponding autodetection code in environment.py."""
-header_suffixes = ['h', 'hh', 'hpp', 'hxx', 'H', 'ipp', 'moc', 'vapi']
+header_suffixes = ['h', 'hh', 'hpp', 'hxx', 'H', 'ipp', 'moc', 'vapi', 'di']
cpp_suffixes = ['cc', 'cpp', 'cxx', 'h', 'hh', 'hpp', 'ipp', 'hxx', 'c++']
c_suffixes = ['c']
clike_suffixes = c_suffixes + cpp_suffixes
@@ -108,6 +108,27 @@ rust_buildtype_args = {'plain' : [],
'minsize' : [],
}
+d_gdc_buildtype_args = {'plain' : [],
+ 'debug' : ['-g', '-O0'],
+ 'debugoptimized' : ['-g', '-O'],
+ 'release' : ['-O3', '-frelease'],
+ 'minsize' : [],
+ }
+
+d_ldc_buildtype_args = {'plain' : [],
+ 'debug' : ['-g', '-O0'],
+ 'debugoptimized' : ['-g', '-O'],
+ 'release' : ['-O3', '-release'],
+ 'minsize' : [],
+ }
+
+d_dmd_buildtype_args = {'plain' : [],
+ 'debug' : ['-g'],
+ 'debugoptimized' : ['-g', '-O'],
+ 'release' : ['-O', '-release'],
+ 'minsize' : [],
+ }
+
mono_buildtype_args = {'plain' : [],
'debug' : ['-debug'],
'debugoptimized': ['-debug', '-optimize+'],
@@ -1425,6 +1446,264 @@ class SwiftCompiler(Compiler):
suffix = filename.split('.')[-1]
return suffix in ('swift')
+class DCompiler(Compiler):
+ def __init__(self, exelist, version, is_cross):
+ super().__init__(exelist, version)
+ self.id = 'unknown'
+ self.language = 'd'
+ self.is_cross = is_cross
+
+ def sanity_check(self, work_dir, environment):
+ source_name = os.path.join(work_dir, 'sanity.d')
+ output_name = os.path.join(work_dir, 'dtest')
+ ofile = open(source_name, 'w')
+ ofile.write('''void main() {
+}
+''')
+ ofile.close()
+ pc = subprocess.Popen(self.exelist + self.get_output_args(output_name) + [source_name], cwd=work_dir)
+ pc.wait()
+ if pc.returncode != 0:
+ raise EnvironmentException('D compiler %s can not compile programs.' % self.name_string())
+ if subprocess.call(output_name) != 0:
+ raise EnvironmentException('Executables created by D compiler %s are not runnable.' % self.name_string())
+
+ def needs_static_linker(self):
+ return True
+
+ def name_string(self):
+ return ' '.join(self.exelist)
+
+ def get_exelist(self):
+ return self.exelist
+
+ def get_id(self):
+ return self.id
+
+ def get_language(self):
+ return self.language
+
+ def can_compile(self, fname):
+ suffix = fname.split('.')[-1]
+ return suffix in ('d', 'di')
+
+ def get_linker_exelist(self):
+ return self.exelist[:]
+
+ def depfile_for_object(self, objfile):
+ return objfile + '.' + self.get_depfile_suffix()
+
+ def get_depfile_suffix(self):
+ return 'dep'
+
+ def get_pic_args(self):
+ return ['-fPIC']
+
+ def get_std_shared_lib_link_args(self):
+ return ['-shared']
+
+ def get_soname_args(self, shlib_name, path, soversion):
+ return []
+
+ def build_rpath_args(self, build_dir, rpath_paths, install_rpath):
+ # This method is to be used by LDC and DMD.
+ # GDC can deal with the verbatim flags.
+ if len(rpath_paths) == 0 and len(install_rpath) == 0:
+ return []
+ paths = ':'.join([os.path.join(build_dir, p) for p in rpath_paths])
+ if len(paths) < len(install_rpath):
+ padding = 'X'*(len(install_rpath) - len(paths))
+ if len(paths) == 0:
+ paths = padding
+ else:
+ paths = paths + ':' + padding
+ return ['-L-rpath={}'.format(paths)]
+
+class GnuDCompiler(DCompiler):
+ def __init__(self, exelist, version, is_cross):
+ DCompiler.__init__(self, exelist, version, is_cross)
+ self.id = 'gcc'
+ self.warn_args = {'1': ['-Wall', '-Wdeprecated'],
+ '2': ['-Wall', '-Wextra', '-Wdeprecated'],
+ '3': ['-Wall', '-Wextra', '-Wdeprecated', '-Wpedantic']}
+
+ def get_dependency_gen_args(self, outtarget, outfile):
+ # FIXME: Passing -fmake-deps results in a file-not-found message.
+ # Investigate why.
+ return []
+
+ def get_output_args(self, target):
+ return ['-o', target]
+
+ def get_compile_only_args(self):
+ return ['-c']
+
+ def get_linker_output_args(self, target):
+ return ['-o', target]
+
+ def get_include_args(self, path, is_system):
+ return ['-I' + path]
+
+ def get_warn_args(self, level):
+ return self.warn_args[level]
+
+ def get_werror_args(self):
+ return ['-Werror']
+
+ def get_buildtype_linker_args(self, buildtype):
+ return []
+
+ def get_std_exe_link_args(self):
+ return []
+
+ def get_buildtype_args(self, buildtype):
+ return d_gdc_buildtype_args[buildtype]
+
+ def build_rpath_args(self, build_dir, rpath_paths, install_rpath):
+ return build_unix_rpath_args(build_dir, rpath_paths, install_rpath)
+
+class LLVMDCompiler(DCompiler):
+ def __init__(self, exelist, version, is_cross):
+ DCompiler.__init__(self, exelist, version, is_cross)
+ self.id = 'llvm'
+
+ def get_dependency_gen_args(self, outtarget, outfile):
+ # LDC using the -deps flag returns a non-Makefile dependency-info file, which
+ # the backends can not use. So we disable this feature for now.
+ return []
+
+ def get_output_args(self, target):
+ return ['-of', target]
+
+ def get_compile_only_args(self):
+ return ['-c']
+
+ def get_linker_output_args(self, target):
+ return ['-of', target]
+
+ def get_include_args(self, path, is_system):
+ return ['-I' + path]
+
+ def get_warn_args(self, level):
+ if level == '2':
+ return ['-wi']
+ else:
+ return ['-w']
+
+ def get_coverage_args(self):
+ return ['-cov']
+
+ def get_buildtype_linker_args(self, buildtype):
+ return []
+
+ def get_std_exe_link_args(self):
+ return []
+
+ def get_buildtype_args(self, buildtype):
+ return d_ldc_buildtype_args[buildtype]
+
+ def get_pic_args(self):
+ return ['-relocation-model=pic']
+
+ def _translate_args(self, args):
+ ldcargs = []
+ # Translate common arguments to flags this compiler can
+ # understand.
+ # The flags might have been added by pkg-config files,
+ # and are therefore out of the user's control.
+ for arg in args:
+ if arg == '-pthread':
+ continue
+ if arg.startswith('-Wl,'):
+ linkargs = arg[arg.index(',')+1:].split(',')
+ for la in linkargs:
+ ldcargs.append('-L' + la.strip())
+ continue
+ elif arg.startswith('-l'):
+ # translate library link flag
+ ldcargs.append('-L' + arg)
+ continue
+ ldcargs.append(arg)
+
+ return ldcargs
+
+ def unix_link_flags_to_native(self, args):
+ return self._translate_args(args)
+
+ def unix_compile_flags_to_native(self, args):
+ return self._translate_args(args)
+
+class DmdDCompiler(DCompiler):
+ def __init__(self, exelist, version, is_cross):
+ DCompiler.__init__(self, exelist, version, is_cross)
+ self.id = 'dmd'
+
+ def get_dependency_gen_args(self, outtarget, outfile):
+ # LDC using the -deps flag returns a non-Makefile dependency-info file, which
+ # the backends can not use. So we disable this feature for now.
+ return []
+
+ def get_output_args(self, target):
+ return ['-of' + target]
+
+ def get_werror_args(self):
+ return ['-w']
+
+ def get_compile_only_args(self):
+ return ['-c']
+
+ def get_linker_output_args(self, target):
+ return ['-of' + target]
+
+ def get_include_args(self, path, is_system):
+ return ['-I' + path]
+
+ def get_warn_args(self, level):
+ return []
+
+ def get_coverage_args(self):
+ return ['-cov']
+
+ def get_buildtype_linker_args(self, buildtype):
+ return []
+
+ def get_std_exe_link_args(self):
+ return []
+
+ def get_buildtype_args(self, buildtype):
+ return d_dmd_buildtype_args[buildtype]
+
+ def get_std_shared_lib_link_args(self):
+ return ['-shared', '-defaultlib=libphobos2.so']
+
+ def _translate_args(self, args):
+ dmdargs = []
+ # Translate common arguments to flags this compiler can
+ # understand.
+ # The flags might have been added by pkg-config files,
+ # and are therefore out of the user's control.
+ for arg in args:
+ if arg == '-pthread':
+ continue
+ if arg.startswith('-Wl,'):
+ linkargs = arg[arg.index(',')+1:].split(',')
+ for la in linkargs:
+ dmdargs.append('-L' + la.strip())
+ continue
+ elif arg.startswith('-l'):
+ # translate library link flag
+ dmdargs.append('-L' + arg)
+ continue
+ dmdargs.append(arg)
+
+ return dmdargs
+
+ def unix_link_flags_to_native(self, args):
+ return self._translate_args(args)
+
+ def unix_compile_flags_to_native(self, args):
+ return self._translate_args(args)
+
class VisualStudioCCompiler(CCompiler):
std_warn_args = ['/W3']
std_opt_args= ['/O2']
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index 16af0d1..a4d053e 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -18,6 +18,7 @@ from . import mesonlib
from . import mlog
from .compilers import *
import configparser
+import shutil
build_filename = 'meson.build'
@@ -580,6 +581,49 @@ class Environment():
return RustCompiler(exelist, version)
raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
+ def detect_d_compiler(self):
+ exelist = None
+ is_cross = False
+ # Search for a D compiler.
+ # We prefer LDC over GDC unless overridden with the DC
+ # environment variable because LDC has a much more
+ # up to date language version at time (2016).
+ if 'DC' in os.environ:
+ exelist = os.environ['DC'].split()
+ elif self.is_cross_build() and want_cross:
+ exelist = [self.cross_info.config['binaries']['d']]
+ ccache = []
+ is_cross = True
+ elif shutil.which("ldc2"):
+ exelist = ['ldc2']
+ elif shutil.which("ldc"):
+ exelist = ['ldc']
+ elif shutil.which("gdc"):
+ exelist = ['gdc']
+ elif shutil.which("dmd"):
+ exelist = ['dmd']
+ else:
+ raise EnvironmentException('Could not find any supported D compiler.')
+
+ try:
+ p = subprocess.Popen(exelist + ['--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except OSError:
+ raise EnvironmentException('Could not execute D compiler "%s"' % ' '.join(exelist))
+ (out, _) = p.communicate()
+ out = out.decode(errors='ignore')
+ vmatch = re.search(Environment.version_regex, out)
+ if vmatch:
+ version = vmatch.group(0)
+ else:
+ version = 'unknown version'
+ if 'LLVM D compiler' in out:
+ return LLVMDCompiler(exelist, version, is_cross)
+ elif 'gdc' in out:
+ return GnuDCompiler(exelist, version, is_cross)
+ elif 'Digital Mars' in out:
+ return DmdDCompiler(exelist, version, is_cross)
+ raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
+
def detect_swift_compiler(self):
exelist = ['swiftc']
try:
@@ -712,13 +756,14 @@ def get_args_from_envvars(lang, compiler_is_linker):
if val:
mlog.log('Appending {} from environment: {!r}'.format(var, val))
- if lang not in ('c', 'cpp', 'objc', 'objcpp', 'fortran'):
+ if lang not in ('c', 'cpp', 'objc', 'objcpp', 'fortran', 'd'):
return ([], [])
# Compile flags
cflags_mapping = {'c': 'CFLAGS', 'cpp': 'CXXFLAGS',
'objc': 'OBJCFLAGS', 'objcpp': 'OBJCXXFLAGS',
- 'fortran': 'FFLAGS'}
+ 'fortran': 'FFLAGS',
+ 'd': 'DFLAGS'}
compile_flags = os.environ.get(cflags_mapping[lang], '')
log_var(cflags_mapping[lang], compile_flags)
compile_flags = compile_flags.split()
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 8645b68..5a6d702 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -1566,6 +1566,10 @@ class Interpreter():
comp = self.environment.detect_vala_compiler()
if need_cross_compiler:
cross_comp = comp # Vala is too (I think).
+ elif lang == 'd':
+ comp = self.environment.detect_d_compiler()
+ if need_cross_compiler:
+ cross_comp = comp # D as well (AFAIK).
elif lang == 'rust':
comp = self.environment.detect_rust_compiler()
if need_cross_compiler:
diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py
index 409f9dc..e38bdb9 100644
--- a/mesonbuild/optinterpreter.py
+++ b/mesonbuild/optinterpreter.py
@@ -20,6 +20,7 @@ import os, re
forbidden_option_names = coredata.get_builtin_options()
forbidden_prefixes = {'c_': True,
'cpp_': True,
+ 'd_': True,
'rust_': True,
'fortran_': True,
'objc_': True,
diff --git a/run_tests.py b/run_tests.py
index 34258d8..3e49662 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -304,6 +304,17 @@ def gather_tests(testdir):
tests = [os.path.join(testdir, t[1]) for t in testlist]
return tests
+def have_d_compiler():
+ if shutil.which("ldc2"):
+ return True
+ elif shutil.which("ldc"):
+ return True
+ elif shutil.which("gdc"):
+ return True
+ elif shutil.which("dmd"):
+ return True
+ return False
+
def detect_tests_to_run():
all_tests = []
all_tests.append(('common', gather_tests('test cases/common'), False))
@@ -318,6 +329,7 @@ def detect_tests_to_run():
all_tests.append(('C#', gather_tests('test cases/csharp'), False if shutil.which('mcs') else True))
all_tests.append(('vala', gather_tests('test cases/vala'), False if shutil.which('valac') else True))
all_tests.append(('rust', gather_tests('test cases/rust'), False if shutil.which('rustc') else True))
+ all_tests.append(('d', gather_tests('test cases/d'), False if have_d_compiler() else True))
all_tests.append(('objective c', gather_tests('test cases/objc'), False if not mesonlib.is_windows() else True))
all_tests.append(('fortran', gather_tests('test cases/fortran'), False if shutil.which('gfortran') else True))
all_tests.append(('swift', gather_tests('test cases/swift'), False if shutil.which('swiftc') else True))
diff --git a/test cases/d/1 simple/app.d b/test cases/d/1 simple/app.d
new file mode 100644
index 0000000..0be1d2c
--- /dev/null
+++ b/test cases/d/1 simple/app.d
@@ -0,0 +1,8 @@
+
+import std.stdio;
+import utils;
+
+void main ()
+{
+ printGreeting ("a Meson D test");
+}
diff --git a/test cases/d/1 simple/installed_files.txt b/test cases/d/1 simple/installed_files.txt
new file mode 100644
index 0000000..9374c54
--- /dev/null
+++ b/test cases/d/1 simple/installed_files.txt
@@ -0,0 +1 @@
+usr/bin/dsimpleapp?exe
diff --git a/test cases/d/1 simple/meson.build b/test cases/d/1 simple/meson.build
new file mode 100644
index 0000000..a10b67b
--- /dev/null
+++ b/test cases/d/1 simple/meson.build
@@ -0,0 +1,4 @@
+project('D Simple Test', 'd')
+
+e = executable('dsimpleapp', ['app.d', 'utils.d'], install : true)
+test('apptest', e)
diff --git a/test cases/d/1 simple/utils.d b/test cases/d/1 simple/utils.d
new file mode 100644
index 0000000..8645548
--- /dev/null
+++ b/test cases/d/1 simple/utils.d
@@ -0,0 +1,8 @@
+
+import std.stdio;
+import std.string : format;
+
+void printGreeting (string name)
+{
+ writeln ("Hello, I am %s.".format (name));
+}
diff --git a/test cases/d/2 library/app.d b/test cases/d/2 library/app.d
new file mode 100644
index 0000000..5d84a69
--- /dev/null
+++ b/test cases/d/2 library/app.d
@@ -0,0 +1,8 @@
+
+import libstuff;
+
+void main ()
+{
+ immutable ret = printLibraryString ("foo");
+ assert (ret == 4);
+}
diff --git a/test cases/d/2 library/installed_files.txt b/test cases/d/2 library/installed_files.txt
new file mode 100644
index 0000000..f062075
--- /dev/null
+++ b/test cases/d/2 library/installed_files.txt
@@ -0,0 +1,4 @@
+usr/bin/app_d?exe
+?usr/lib/libstuff.so
+usr/bin/app_s?exe
+usr/lib/libstuff.a
diff --git a/test cases/d/2 library/libstuff.d b/test cases/d/2 library/libstuff.d
new file mode 100644
index 0000000..676a643
--- /dev/null
+++ b/test cases/d/2 library/libstuff.d
@@ -0,0 +1,9 @@
+
+import std.stdio;
+import std.string : format;
+
+int printLibraryString (string str)
+{
+ writeln ("Library says: %s".format (str));
+ return 4;
+}
diff --git a/test cases/d/2 library/meson.build b/test cases/d/2 library/meson.build
new file mode 100644
index 0000000..811131c
--- /dev/null
+++ b/test cases/d/2 library/meson.build
@@ -0,0 +1,14 @@
+project('D Library', 'd')
+
+if meson.get_compiler('d').get_id() != 'gcc'
+ ldyn = shared_library('stuff', 'libstuff.d', install : true)
+ ed = executable('app_d', 'app.d', link_with : ldyn, install : true)
+ test('linktest_dyn', ed)
+
+else
+ message('GDC can not build shared libraries. Build skipped.')
+endif
+
+lstatic = static_library('stuff', 'libstuff.d', install : true)
+es = executable('app_s', 'app.d', link_with : lstatic, install : true)
+test('linktest_static', es)