diff --git a/backends.py b/backends.py index 869842845..0e5f8f7be 100644 --- a/backends.py +++ b/backends.py @@ -191,6 +191,12 @@ class Backend(): return True return False + def has_cs(self, target): + for s in target.get_sources(): + if s.endswith('.cs'): + return True + return False + def generate_target(self, target, outfile): if isinstance(target, build.CustomTarget): self.generate_custom_target(target, outfile) @@ -206,6 +212,9 @@ class Backend(): if 'rust' in self.environment.coredata.compilers.keys() and self.has_rust(target): self.generate_rust_target(target, outfile) return + if 'cs' in self.environment.coredata.compilers.keys() and self.has_cs(target): + self.generate_cs_target(target, outfile) + return if 'vala' in self.environment.coredata.compilers.keys() and self.has_vala(target): gen_src_deps += self.generate_vala_compile(target, outfile) # The following deals with C/C++ compilation. diff --git a/environment.py b/environment.py index 10329748e..07c1c2832 100644 --- a/environment.py +++ b/environment.py @@ -445,6 +445,143 @@ class ObjCPPCompiler(CPPCompiler): if pe.returncode != 0: raise EnvironmentException('Executables created by ObjC++ compiler %s are not runnable.' % self.name_string()) +class MonoCompiler(): + def __init__(self, exelist, version): + if type(exelist) == type(''): + self.exelist = [exelist] + elif type(exelist) == type([]): + self.exelist = exelist + else: + raise TypeError('Unknown argument to Mono compiler') + self.version = version + self.language = 'cs' + self.default_suffix = 'cs' + self.id = 'mono' + self.monorunner = 'mono' + + def get_always_args(self): + return [] + + def get_output_args(self, fname): + return ['-out:' + fname] + + def get_linker_always_args(self): + return [] + + def get_soname_args(self, shlib_name, path): + return [] + + def get_werror_args(self): + return ['-warnaserror'] + + def split_shlib_to_parts(self, fname): + return (None, fname) + + def build_rpath_args(self, build_dir, rpath_paths, install_rpath): + return [] + + def get_id(self): + return self.id + + def get_dependency_gen_args(self, outtarget, outfile): + return [] + + def get_language(self): + return self.language + + def get_default_suffix(self): + return self.default_suffix + + def get_exelist(self): + return self.exelist[:] + + def get_linker_exelist(self): + return self.exelist[:] + + def get_compile_only_args(self): + return [] + + def get_linker_output_args(self, outputname): + return [] + + def get_debug_args(self): + return ['-g'] + + def get_coverage_args(self): + return [] + + def get_coverage_link_args(self): + return [] + + def get_std_exe_link_args(self): + return [] + + def get_include_arg(self, path): + return '' + + def get_std_shared_lib_link_args(self): + return [] + + def can_compile(self, filename): + suffix = filename.split('.')[-1] + if suffix == 'cs': + return True + return False + + def get_pic_args(self): + return [] + + def name_string(self): + return ' '.join(self.exelist) + + def get_pch_use_args(self, pch_dir, header): + return [] + + def get_pch_name(self, header_name): + return '' + + def sanity_check(self, work_dir): + src = 'sanity.cs' + obj = 'sanity.exe' + source_name = os.path.join(work_dir, src) + ofile = open(source_name, 'w') + ofile.write('''public class Sanity { + static public void Main () { + } +} +''') + ofile.close() + pc = subprocess.Popen(self.exelist + [src], cwd=work_dir) + pc.wait() + if pc.returncode != 0: + raise EnvironmentException('Mono compiler %s can not compile programs.' % self.name_string()) + cmdlist = [self.monorunner, obj] + pe = subprocess.Popen(cmdlist, cwd=work_dir) + pe.wait() + if pe.returncode != 0: + raise EnvironmentException('Executables created by Mono compiler %s are not runnable.' % self.name_string()) + + def needs_static_linker(self): + return False + + def has_header(self, hname): + raise EnvironmentException('Mono does not support header checks.') + + def compiles(self, code): + raise EnvironmentException('Mono does not support compile checks.') + + def run(self, code): + raise EnvironmentException('Mono does not support run checks.') + + def sizeof(self, element, prefix, env): + raise EnvironmentException('Mono does not support sizeof checks.') + + def alignment(self, typename, env): + raise EnvironmentException('Mono does not support alignment checks.') + + def has_function(self, funcname, prefix, env): + raise EnvironmentException('Mono does not support function checks.') + class JavaCompiler(): def __init__(self, exelist, version): if type(exelist) == type(''): @@ -468,6 +605,9 @@ class JavaCompiler(): def get_soname_args(self, shlib_name, path): return [] + def get_werror_args(self): + return ['-Werror'] + def split_shlib_to_parts(self, fname): return (None, fname) @@ -1369,6 +1509,24 @@ class Environment(): return JavaCompiler(exelist, version) raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') + def detect_cs_compiler(self): + exelist = ['mcs'] + try: + p = subprocess.Popen(exelist + ['--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except OSError: + raise EnvironmentException('Could not execute C# compiler "%s"' % ' '.join(exelist)) + (out, err) = p.communicate() + out = out.decode() + err = err.decode() + vmatch = re.search(Environment.version_regex, out) + if vmatch: + version = vmatch.group(0) + else: + version = 'unknown version' + if 'Mono' in out: + return MonoCompiler(exelist, version) + raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') + def detect_vala_compiler(self): exelist = ['valac'] try: diff --git a/interpreter.py b/interpreter.py index d8e20deca..a3b5e6faf 100644 --- a/interpreter.py +++ b/interpreter.py @@ -963,6 +963,10 @@ class Interpreter(): comp = self.environment.detect_java_compiler() if is_cross: cross_comp = comp # Java is platform independent. + elif lang == 'cs': + comp = self.environment.detect_cs_compiler() + if is_cross: + cross_comp = comp # C# is platform independent. elif lang == 'vala': comp = self.environment.detect_vala_compiler() if is_cross: diff --git a/ninjabackend.py b/ninjabackend.py index d019ae7d1..a58f6c3aa 100644 --- a/ninjabackend.py +++ b/ninjabackend.py @@ -433,6 +433,21 @@ class NinjaBackend(backends.Backend): elem.add_item('ARGS', commands) elem.write(outfile) + def generate_cs_target(self, target, outfile): + fname = target.get_filename() + subdir = target.get_subdir() + outname_rel = os.path.join(subdir, fname) + src_list = target.get_sources() + class_list = [] + compiler = self.get_compiler_for_source(src_list[0]) + assert(compiler.get_language() == 'cs') + rel_srcs = [os.path.join(self.build_to_src, s) for s in src_list] + commands = [] + commands += compiler.get_output_args(outname_rel) + elem = NinjaBuildElement(outname_rel, 'cs_COMPILER', rel_srcs) + elem.add_item('ARGS', commands) + elem.write(outfile) + def generate_single_java_compile(self, subdir, src, target, compiler, outfile): buildtype = self.environment.coredata.buildtype args = [] @@ -569,7 +584,8 @@ class NinjaBackend(backends.Backend): for (complist, is_cross) in ctypes: for compiler in complist: langname = compiler.get_language() - if langname == 'java' or langname == 'vala' or langname == 'rust': + if langname == 'java' or langname == 'vala' or\ + langname == 'rust' or langname == 'cs': continue crstr = '' if is_cross: @@ -608,6 +624,16 @@ class NinjaBackend(backends.Backend): outfile.write(description) outfile.write('\n') + def generate_cs_compile_rule(self, compiler, outfile): + rule = 'rule %s_COMPILER\n' % compiler.get_language() + invoc = ' '.join([ninja_quote(i) for i in compiler.get_exelist()]) + command = ' command = %s $ARGS $in\n' % invoc + description = ' description = Compiling cs target $out.\n' + outfile.write(rule) + outfile.write(command) + outfile.write(description) + outfile.write('\n') + def generate_vala_compile_rules(self, compiler, outfile): rule = 'rule %s_COMPILER\n' % compiler.get_language() invoc = ' '.join([ninja_quote(i) for i in compiler.get_exelist()]) @@ -647,6 +673,10 @@ class NinjaBackend(backends.Backend): if not is_cross: self.generate_java_compile_rule(compiler, outfile) return + if langname == 'cs': + if not is_cross: + self.generate_cs_compile_rule(compiler, outfile) + return if langname == 'vala': if not is_cross: self.generate_vala_compile_rules(compiler, outfile) diff --git a/test cases/csharp/1 basic/meson.build b/test cases/csharp/1 basic/meson.build new file mode 100644 index 000000000..5a1b7b3ee --- /dev/null +++ b/test cases/csharp/1 basic/meson.build @@ -0,0 +1,4 @@ +project('simple c#', 'cs') + +e = executable('prog', 'prog.cs') +test('basic', e) diff --git a/test cases/csharp/1 basic/prog.cs b/test cases/csharp/1 basic/prog.cs new file mode 100644 index 000000000..dfb24002b --- /dev/null +++ b/test cases/csharp/1 basic/prog.cs @@ -0,0 +1,7 @@ +using System; + +public class Prog { + static public void Main () { + Console.WriteLine("C# is working."); + } +}