From 90e43dbdfe06d062939803ec4ee261315096aee7 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 25 Jul 2019 08:37:30 -0700 Subject: re-architect the D compiler abstractions We support 3 D compilers, DMD, LDC, and GDC. DMD is the reference compiler, and LDC attempts to largely mirror it's command line usage. GDC does not, it instead acts like GCC (which makes sense). The current abstraction puts DMD behavior in the base D compiler and then overrides then in the GnuDCompiler class. This is messy, but it becomes more problematic when splitting the linker and compiler abstractions apart. I've opted to instead split the DCompiler class into two separate classes. The DCompiler implements core D functinoality, and DmdLikeCompilerMixin, which implements the DMD and LDC command line arguments. I've then mxed that into the DmdDCompiler and LLVMDCompiler classes, and mixed the GnuCompiler into the GnuDCompiler class to get Gnu command line behavior. --- mesonbuild/compilers/d.py | 412 ++++++++++++++++++++++------------------------ 1 file changed, 197 insertions(+), 215 deletions(-) (limited to 'mesonbuild/compilers/d.py') diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index 1a37c63..27ca6fc 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -27,7 +27,7 @@ from .compilers import ( Compiler, CompilerArgs, ) -from .mixins.gnu import get_gcc_soname_args, gnu_color_args, gnu_optimization_args +from .mixins.gnu import get_gcc_soname_args, GnuCompiler d_feature_args = {'gcc': {'unittest': '-funittest', 'debug': '-fdebug', @@ -62,44 +62,11 @@ dmd_optimization_args = {'0': [], 's': ['-O'], } -class DCompiler(Compiler): - mscrt_args = { - 'none': ['-mscrtlib='], - 'md': ['-mscrtlib=msvcrt'], - 'mdd': ['-mscrtlib=msvcrtd'], - 'mt': ['-mscrtlib=libcmt'], - 'mtd': ['-mscrtlib=libcmtd'], - } - def __init__(self, exelist, version, for_machine: MachineChoice, arch, **kwargs): - self.language = 'd' - super().__init__(exelist, version, for_machine, **kwargs) - self.id = 'unknown' - self.arch = arch - - def sanity_check(self, work_dir, environment): - source_name = os.path.join(work_dir, 'sanity.d') - output_name = os.path.join(work_dir, 'dtest') - with open(source_name, 'w') as ofile: - ofile.write('''void main() { }''') - pc = subprocess.Popen(self.exelist + self.get_output_args(output_name) + self.get_target_arch_args() + [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 +class DmdLikeCompilerMixin: def get_linker_exelist(self): - return self.exelist[:] + return self.get_exelist() def get_output_args(self, target): return ['-of=' + target] @@ -147,20 +114,6 @@ class DCompiler(Compiler): def get_compile_only_args(self): return ['-c'] - def depfile_for_object(self, objfile): - return objfile + '.' + self.get_depfile_suffix() - - def get_depfile_suffix(self): - return 'deps' - - def get_pic_args(self): - if is_windows(): - return [] - return ['-fPIC'] - - def get_std_shared_lib_link_args(self): - return ['-shared'] - def get_soname_args(self, *args): # FIXME: Make this work for cross-compiling if is_windows(): @@ -173,96 +126,6 @@ class DCompiler(Compiler): return get_gcc_soname_args(CompilerType.GCC_STANDARD, *args) - def get_feature_args(self, kwargs, build_to_src): - 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 'debug' in kwargs: - debug_level = -1 - debugs = kwargs.pop('debug') - if not isinstance(debugs, list): - debugs = [debugs] - - debug_arg = d_feature_args[self.id]['debug'] - if not debug_arg: - raise EnvironmentException('D compiler %s does not support conditional debug identifiers.' % self.name_string()) - - # Parse all debug identifiers and the largest debug level identifier - for d in debugs: - if isinstance(d, int): - if d > debug_level: - debug_level = d - elif isinstance(d, str) and d.isdigit(): - if int(d) > debug_level: - debug_level = int(d) - else: - res.append('{0}={1}'.format(debug_arg, d)) - - if debug_level >= 0: - res.append('{0}={1}'.format(debug_arg, debug_level)) - - if 'versions' in kwargs: - version_level = -1 - 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 conditional version identifiers.' % self.name_string()) - - # Parse all version identifiers and the largest version level identifier - for v in versions: - if isinstance(v, int): - if v > version_level: - version_level = v - elif isinstance(v, str) and v.isdigit(): - if int(v) > version_level: - version_level = int(v) - else: - res.append('{0}={1}'.format(version_arg, v)) - - if version_level >= 0: - res.append('{0}={1}'.format(version_arg, version_level)) - - 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 idir_obj in import_dirs: - basedir = idir_obj.get_curdir() - for idir in idir_obj.get_incdirs(): - # Avoid superfluous '/.' at the end of paths when d is '.' - if idir not in ('', '.'): - expdir = os.path.join(basedir, idir) - else: - expdir = basedir - srctreedir = os.path.join(build_to_src, expdir) - res.append('{0}{1}'.format(import_dir_arg, srctreedir)) - - if kwargs: - raise EnvironmentException('Unknown D compiler feature(s) selected: %s' % ', '.join(kwargs.keys())) - - return res - - def get_buildtype_linker_args(self, buildtype): - if buildtype != 'plain': - return self.get_target_arch_args() - return [] - - def get_std_exe_link_args(self): - return [] - def gen_import_library_args(self, implibname): return ['-Wl,--out-implib=' + implibname] @@ -285,54 +148,6 @@ class DCompiler(Compiler): paths = paths + ':' + padding return ['-Wl,-rpath,{}'.format(paths)] - def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'): - if callable(extra_args): - extra_args = extra_args(mode) - if extra_args is None: - extra_args = [] - elif isinstance(extra_args, str): - extra_args = [extra_args] - if dependencies is None: - dependencies = [] - elif not isinstance(dependencies, list): - dependencies = [dependencies] - # Collect compiler arguments - args = CompilerArgs(self) - for d in dependencies: - # Add compile flags needed by dependencies - args += d.get_compile_args() - if mode == 'link': - # Add link flags needed to find dependencies - args += d.get_link_args() - - if mode == 'compile': - # Add DFLAGS from the env - args += env.coredata.get_external_args(self.for_machine, self.language) - elif mode == 'link': - # Add LDFLAGS from the env - args += env.coredata.get_external_link_args(self.for_machine, self.language) - # extra_args must override all other arguments, so we add them last - args += extra_args - return args - - def compiles(self, code, env, *, extra_args=None, dependencies=None, mode='compile'): - args = self._get_compiler_check_args(env, extra_args, dependencies, mode) - - with self.cached_compile(code, env.coredata, extra_args=args, mode=mode) as p: - return p.returncode == 0, p.cached - - def has_multi_arguments(self, args, env): - return self.compiles('int i;\n', env, extra_args=args) - - def get_target_arch_args(self): - # LDC2 on Windows targets to current OS architecture, but - # it should follow the target specified by the MSVC toolchain. - if is_windows(): - if self.arch == 'x86_64': - return ['-m64'] - return ['-m32'] - return [] - @classmethod def translate_args_to_nongnu(cls, args): dcargs = [] @@ -480,6 +295,189 @@ class DCompiler(Compiler): assert(buildtype == 'custom') raise EnvironmentException('Requested C runtime based on buildtype, but buildtype is "custom".') + +class DCompiler(Compiler): + mscrt_args = { + 'none': ['-mscrtlib='], + 'md': ['-mscrtlib=msvcrt'], + 'mdd': ['-mscrtlib=msvcrtd'], + 'mt': ['-mscrtlib=libcmt'], + 'mtd': ['-mscrtlib=libcmtd'], + } + + def __init__(self, exelist, version, for_machine: MachineChoice, arch, **kwargs): + self.language = 'd' + super().__init__(exelist, version, for_machine, **kwargs) + self.id = 'unknown' + self.arch = arch + + def sanity_check(self, work_dir, environment): + source_name = os.path.join(work_dir, 'sanity.d') + output_name = os.path.join(work_dir, 'dtest') + with open(source_name, 'w') as ofile: + ofile.write('''void main() { }''') + pc = subprocess.Popen(self.exelist + self.get_output_args(output_name) + self.get_target_arch_args() + [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 depfile_for_object(self, objfile): + return objfile + '.' + self.get_depfile_suffix() + + def get_depfile_suffix(self): + return 'deps' + + def get_pic_args(self): + if is_windows(): + return [] + return ['-fPIC'] + + def get_std_shared_lib_link_args(self): + return ['-shared'] + + def get_feature_args(self, kwargs, build_to_src): + 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 'debug' in kwargs: + debug_level = -1 + debugs = kwargs.pop('debug') + if not isinstance(debugs, list): + debugs = [debugs] + + debug_arg = d_feature_args[self.id]['debug'] + if not debug_arg: + raise EnvironmentException('D compiler %s does not support conditional debug identifiers.' % self.name_string()) + + # Parse all debug identifiers and the largest debug level identifier + for d in debugs: + if isinstance(d, int): + if d > debug_level: + debug_level = d + elif isinstance(d, str) and d.isdigit(): + if int(d) > debug_level: + debug_level = int(d) + else: + res.append('{0}={1}'.format(debug_arg, d)) + + if debug_level >= 0: + res.append('{0}={1}'.format(debug_arg, debug_level)) + + if 'versions' in kwargs: + version_level = -1 + 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 conditional version identifiers.' % self.name_string()) + + # Parse all version identifiers and the largest version level identifier + for v in versions: + if isinstance(v, int): + if v > version_level: + version_level = v + elif isinstance(v, str) and v.isdigit(): + if int(v) > version_level: + version_level = int(v) + else: + res.append('{0}={1}'.format(version_arg, v)) + + if version_level >= 0: + res.append('{0}={1}'.format(version_arg, version_level)) + + 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 idir_obj in import_dirs: + basedir = idir_obj.get_curdir() + for idir in idir_obj.get_incdirs(): + # Avoid superfluous '/.' at the end of paths when d is '.' + if idir not in ('', '.'): + expdir = os.path.join(basedir, idir) + else: + expdir = basedir + srctreedir = os.path.join(build_to_src, expdir) + res.append('{0}{1}'.format(import_dir_arg, srctreedir)) + + if kwargs: + raise EnvironmentException('Unknown D compiler feature(s) selected: %s' % ', '.join(kwargs.keys())) + + return res + + def get_buildtype_linker_args(self, buildtype): + if buildtype != 'plain': + return self.get_target_arch_args() + return [] + + def get_std_exe_link_args(self): + return [] + + def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'): + if callable(extra_args): + extra_args = extra_args(mode) + if extra_args is None: + extra_args = [] + elif isinstance(extra_args, str): + extra_args = [extra_args] + if dependencies is None: + dependencies = [] + elif not isinstance(dependencies, list): + dependencies = [dependencies] + # Collect compiler arguments + args = CompilerArgs(self) + for d in dependencies: + # Add compile flags needed by dependencies + args += d.get_compile_args() + if mode == 'link': + # Add link flags needed to find dependencies + args += d.get_link_args() + + if mode == 'compile': + # Add DFLAGS from the env + args += env.coredata.get_external_args(self.for_machine, self.language) + elif mode == 'link': + # Add LDFLAGS from the env + args += env.coredata.get_external_link_args(self.for_machine, self.language) + # extra_args must override all other arguments, so we add them last + args += extra_args + return args + + def compiles(self, code, env, *, extra_args=None, dependencies=None, mode='compile'): + args = self._get_compiler_check_args(env, extra_args, dependencies, mode) + + with self.cached_compile(code, env.coredata, extra_args=args, mode=mode) as p: + return p.returncode == 0, p.cached + + def has_multi_arguments(self, args, env): + return self.compiles('int i;\n', env, extra_args=args) + + def get_target_arch_args(self): + # LDC2 on Windows targets to current OS architecture, but + # it should follow the target specified by the MSVC toolchain. + if is_windows(): + if self.arch == 'x86_64': + return ['-m64'] + return ['-m32'] + return [] + def get_crt_compile_args(self, crt_val, buildtype): return [] @@ -489,7 +487,11 @@ class DCompiler(Compiler): def thread_link_flags(self, env): return ['-pthread'] -class GnuDCompiler(DCompiler): + def name_string(self): + return ' '.join(self.exelist) + + +class GnuDCompiler(DCompiler, GnuCompiler): def __init__(self, exelist, version, for_machine: MachineChoice, arch, **kwargs): DCompiler.__init__(self, exelist, version, for_machine, arch, **kwargs) self.id = 'gcc' @@ -507,32 +509,17 @@ class GnuDCompiler(DCompiler): def get_colorout_args(self, colortype): if self._has_color_support: - return gnu_color_args[colortype][:] + super().get_colorout_args(colortype) return [] def get_dependency_gen_args(self, outtarget, outfile): - if not self._has_deps_support: - return [] - return ['-MD', '-MQ', outtarget, '-MF', outfile] - - def get_output_args(self, target): - return ['-o', target] - - def get_linker_output_args(self, target): - return ['-o', target] - - def get_include_args(self, path, is_system): - return ['-I' + path] + if self._has_deps_support: + return super().get_dependency_gen_args(outtarget, outfile) + return [] def get_warn_args(self, level): return self.warn_args[level] - def get_werror_args(self): - return ['-Werror'] - - def get_linker_search_args(self, dirname): - return ['-L' + dirname] - def get_coverage_args(self): return [] @@ -546,13 +533,8 @@ class GnuDCompiler(DCompiler): return parameter_list - 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_optimization_args(self, optimization_level): - return gnu_optimization_args[optimization_level] -class LLVMDCompiler(DCompiler): +class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler): def __init__(self, exelist, version, for_machine: MachineChoice, arch, **kwargs): DCompiler.__init__(self, exelist, version, for_machine, arch, **kwargs) self.id = 'llvm' @@ -590,7 +572,7 @@ class LLVMDCompiler(DCompiler): return ldc_optimization_args[optimization_level] -class DmdDCompiler(DCompiler): +class DmdDCompiler(DmdLikeCompilerMixin, DCompiler): def __init__(self, exelist, version, for_machine: MachineChoice, arch, **kwargs): DCompiler.__init__(self, exelist, version, for_machine, arch, **kwargs) self.id = 'dmd' -- cgit v1.1 From 06dcbd50eea47b3182081527ea1c0ada01d4d847 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 9 Aug 2019 13:46:35 -0700 Subject: compilers: Dispatch to dynamic linker class Most of the cuda code is from Olexa Bilaniuk. Most of the PGI code is from Michael Hirsc --- mesonbuild/compilers/d.py | 131 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 108 insertions(+), 23 deletions(-) (limited to 'mesonbuild/compilers/d.py') diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index 27ca6fc..18e3bf9 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -13,6 +13,7 @@ # limitations under the License. import os.path, subprocess +import typing from ..mesonlib import ( EnvironmentException, MachineChoice, version_compare, is_windows, is_osx @@ -27,7 +28,8 @@ from .compilers import ( Compiler, CompilerArgs, ) -from .mixins.gnu import get_gcc_soname_args, GnuCompiler +from .mixins.gnu import GnuCompiler +from .mixins.islinker import LinkerEnvVarsMixin, BasicLinkerIsCompilerMixin d_feature_args = {'gcc': {'unittest': '-funittest', 'debug': '-fdebug', @@ -65,9 +67,6 @@ dmd_optimization_args = {'0': [], class DmdLikeCompilerMixin: - def get_linker_exelist(self): - return self.get_exelist() - def get_output_args(self, target): return ['-of=' + target] @@ -100,11 +99,6 @@ class DmdLikeCompilerMixin: # DMD and LDC does not currently return Makefile-compatible dependency info. return [] - def get_linker_search_args(self, dirname): - # -L is recognized as "add this to the search path" by the linker, - # while the compiler recognizes it as "pass to linker". - return ['-Wl,-L' + dirname] - def get_coverage_args(self): return ['-cov'] @@ -114,22 +108,111 @@ class DmdLikeCompilerMixin: def get_compile_only_args(self): return ['-c'] - def get_soname_args(self, *args): - # FIXME: Make this work for cross-compiling + def depfile_for_object(self, objfile): + return objfile + '.' + self.get_depfile_suffix() + + def get_depfile_suffix(self): + return 'deps' + + def get_pic_args(self): if is_windows(): return [] - elif is_osx(): - soname_args = get_gcc_soname_args(CompilerType.GCC_OSX, *args) - if soname_args: - return ['-Wl,' + ','.join(soname_args)] - return [] + return ['-fPIC'] + + def get_feature_args(self, kwargs, build_to_src): + 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 'debug' in kwargs: + debug_level = -1 + debugs = kwargs.pop('debug') + if not isinstance(debugs, list): + debugs = [debugs] + + debug_arg = d_feature_args[self.id]['debug'] + if not debug_arg: + raise EnvironmentException('D compiler %s does not support conditional debug identifiers.' % self.name_string()) + + # Parse all debug identifiers and the largest debug level identifier + for d in debugs: + if isinstance(d, int): + if d > debug_level: + debug_level = d + elif isinstance(d, str) and d.isdigit(): + if int(d) > debug_level: + debug_level = int(d) + else: + res.append('{0}={1}'.format(debug_arg, d)) - return get_gcc_soname_args(CompilerType.GCC_STANDARD, *args) + if debug_level >= 0: + res.append('{0}={1}'.format(debug_arg, debug_level)) + + if 'versions' in kwargs: + version_level = -1 + 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 conditional version identifiers.' % self.name_string()) + + # Parse all version identifiers and the largest version level identifier + for v in versions: + if isinstance(v, int): + if v > version_level: + version_level = v + elif isinstance(v, str) and v.isdigit(): + if int(v) > version_level: + version_level = int(v) + else: + res.append('{0}={1}'.format(version_arg, v)) + + if version_level >= 0: + res.append('{0}={1}'.format(version_arg, version_level)) + + 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 idir_obj in import_dirs: + basedir = idir_obj.get_curdir() + for idir in idir_obj.get_incdirs(): + # Avoid superfluous '/.' at the end of paths when d is '.' + if idir not in ('', '.'): + expdir = os.path.join(basedir, idir) + else: + expdir = basedir + srctreedir = os.path.join(build_to_src, expdir) + res.append('{0}{1}'.format(import_dir_arg, srctreedir)) + + if kwargs: + raise EnvironmentException('Unknown D compiler feature(s) selected: %s' % ', '.join(kwargs.keys())) + + return res + + def get_buildtype_linker_args(self, buildtype): + if buildtype != 'plain': + return self.get_target_arch_args() + return [] + + def get_std_exe_link_args(self): + return [] def gen_import_library_args(self, implibname): return ['-Wl,--out-implib=' + implibname] - def build_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath): + def build_rpath_args(self, env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath): if is_windows(): return [] @@ -295,6 +378,11 @@ class DmdLikeCompilerMixin: assert(buildtype == 'custom') raise EnvironmentException('Requested C runtime based on buildtype, but buildtype is "custom".') + def get_soname_args(self, *args, **kwargs) -> typing.List[str]: + # LDC and DMD actually do use a linker, but they proxy all of that with + # their own arguments + return Compiler.get_soname_args(self, *args, **kwargs) + class DCompiler(Compiler): mscrt_args = { @@ -337,9 +425,6 @@ class DCompiler(Compiler): return [] return ['-fPIC'] - def get_std_shared_lib_link_args(self): - return ['-shared'] - def get_feature_args(self, kwargs, build_to_src): res = [] if 'unittest' in kwargs: @@ -534,7 +619,7 @@ class GnuDCompiler(DCompiler, GnuCompiler): return parameter_list -class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler): +class LLVMDCompiler(DmdLikeCompilerMixin, LinkerEnvVarsMixin, BasicLinkerIsCompilerMixin, DCompiler): def __init__(self, exelist, version, for_machine: MachineChoice, arch, **kwargs): DCompiler.__init__(self, exelist, version, for_machine, arch, **kwargs) self.id = 'llvm' @@ -572,7 +657,7 @@ class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler): return ldc_optimization_args[optimization_level] -class DmdDCompiler(DmdLikeCompilerMixin, DCompiler): +class DmdDCompiler(DmdLikeCompilerMixin, LinkerEnvVarsMixin, BasicLinkerIsCompilerMixin, DCompiler): def __init__(self, exelist, version, for_machine: MachineChoice, arch, **kwargs): DCompiler.__init__(self, exelist, version, for_machine, arch, **kwargs) self.id = 'dmd' -- cgit v1.1