# Copyright 2012-2017 The Meson development team # 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. import os.path, subprocess from ..mesonlib import EnvironmentException, version_compare from .compilers import ( GCC_STANDARD, d_dmd_buildtype_args, d_gdc_buildtype_args, d_ldc_buildtype_args, get_gcc_soname_args, gnu_color_args, Compiler, CompilerArgs, ) class DCompiler(Compiler): def __init__(self, exelist, version, is_cross): self.language = 'd' super().__init__(exelist, version) self.id = 'unknown' 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') with open(source_name, 'w') as ofile: ofile.write('''void main() { } ''') 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_linker_exelist(self): return self.exelist[:] def get_preprocess_only_args(self): return ['-E'] 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 'dep' def get_pic_args(self): return ['-fPIC'] def get_std_shared_lib_link_args(self): return ['-shared'] def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): # 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_buildtype_linker_args(self, buildtype): return [] def get_std_exe_link_args(self): return [] def build_rpath_args(self, build_dir, from_dir, rpath_paths, install_rpath): # This method is to be used by LDC and DMD. # GDC can deal with the verbatim flags. if not rpath_paths and not install_rpath: 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 not paths: paths = padding else: paths = paths + ':' + padding return ['-L-rpath={}'.format(paths)] def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'): 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.external_args[self.language] elif mode == 'link': # Add LDFLAGS from the env args += env.coredata.external_link_args[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.compile(code, args, mode) as p: return p.returncode == 0 def has_multi_arguments(self, args, env): return self.compiles('int i;\n', env, extra_args=args) @classmethod def translate_args_to_nongnu(cls, args): dcargs = [] # Translate common arguments to flags the LDC/DMD compilers # 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: dcargs.append('-L' + la.strip()) continue elif arg.startswith('-l'): # translate library link flag dcargs.append('-L' + arg) continue elif arg.startswith('-L/') or arg.startswith('-L./'): # we need to handle cases where -L is set by e.g. a pkg-config # setting to select a linker search path. We can however not # unconditionally prefix '-L' with '-L' because the user might # have set this flag too to do what it is intended to for this # compiler (pass flag through to the linker) # Hence, we guess here whether the flag was intended to pass # a linker search path. dcargs.append('-L' + arg) continue dcargs.append(arg) return dcargs class GnuDCompiler(DCompiler): def __init__(self, exelist, version, is_cross): DCompiler.__init__(self, exelist, version, is_cross) self.id = 'gcc' default_warn_args = ['-Wall', '-Wdeprecated'] self.warn_args = {'1': default_warn_args, '2': default_warn_args + ['-Wextra'], '3': default_warn_args + ['-Wextra', '-Wpedantic']} self.base_options = ['b_colorout', 'b_sanitize', 'b_staticpic'] def get_colorout_args(self, colortype): if version_compare(self.version, '>=4.9.0'): return gnu_color_args[colortype][:] return [] def get_dependency_gen_args(self, outtarget, outfile): return ['-fmake-deps=' + 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] 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_buildtype_args(self, buildtype): return d_gdc_buildtype_args[buildtype] def build_rpath_args(self, build_dir, from_dir, rpath_paths, install_rpath): return self.build_unix_rpath_args(build_dir, from_dir, rpath_paths, install_rpath) def get_unittest_args(self): return ['-funittest'] class LLVMDCompiler(DCompiler): def __init__(self, exelist, version, is_cross): DCompiler.__init__(self, exelist, version, is_cross) self.id = 'llvm' self.base_options = ['b_coverage', 'b_colorout'] def get_colorout_args(self, colortype): if colortype == 'always': return ['-enable-color'] return [] 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_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' or level == '3': return ['-wi', '-dw'] else: return ['-wi'] def get_werror_args(self): return ['-w'] def get_coverage_args(self): return ['-cov'] def get_buildtype_args(self, buildtype): return d_ldc_buildtype_args[buildtype] def get_pic_args(self): return ['-relocation-model=pic'] 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". So, the first # -L is for the compiler, telling it to pass the second -L to the linker. return ['-L-L' + dirname] @classmethod def unix_args_to_native(cls, args): return cls.translate_args_to_nongnu(args) class DmdDCompiler(DCompiler): def __init__(self, exelist, version, is_cross): DCompiler.__init__(self, exelist, version, is_cross) self.id = 'dmd' self.base_options = ['b_coverage', 'b_colorout'] def get_colorout_args(self, colortype): if colortype == 'always': return ['-color=on'] return [] 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_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 ['-wi'] def get_coverage_args(self): return ['-cov'] 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". So, the first # -L is for the compiler, telling it to pass the second -L to the linker. return ['-L-L' + dirname] 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'] @classmethod def unix_args_to_native(cls, args): return cls.translate_args_to_nongnu(args)