From c48b9594ff1ffc9dacdcb0f4fca6f459b95989f5 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Fri, 25 Aug 2017 15:16:17 -0300 Subject: [PATCH 1/8] Add support for Visual Studio csc c# compiler --- mesonbuild/backend/ninjabackend.py | 19 +++++++++++---- mesonbuild/compilers/__init__.py | 3 ++- mesonbuild/compilers/cs.py | 38 +++++++++++++++++++++++++----- mesonbuild/environment.py | 35 ++++++++++++++++++--------- run_project_tests.py | 2 +- 5 files changed, 73 insertions(+), 24 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index e8c8b39ba..8b616a60e 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -103,7 +103,8 @@ class NinjaBuildElement: # This is the only way I could find to make this work on all # platforms including Windows command shell. Slash is a dir separator # on Windows, too, so all characters are unambiguous and, more importantly, - # do not require quoting. + # do not require quoting, unless explicitely specified, which is necessary for + # the csc compiler. line = line.replace('\\', '/') outfile.write(line) @@ -988,7 +989,7 @@ int dummy; outname_rel = os.path.join(self.get_target_dir(target), fname) src_list = target.get_sources() compiler = target.compilers['cs'] - rel_srcs = [s.rel_to_builddir(self.build_to_src) for s in src_list] + rel_srcs = [os.path.normpath(s.rel_to_builddir(self.build_to_src)) for s in src_list] deps = [] commands = CompilerArgs(compiler, target.extra_args.get('cs', [])) commands += compiler.get_buildtype_args(buildtype) @@ -1014,8 +1015,8 @@ int dummy; for rel_src in generated_sources.keys(): dirpart, fnamepart = os.path.split(rel_src) if rel_src.lower().endswith('.cs'): - rel_srcs.append(rel_src) - deps.append(rel_src) + rel_srcs.append(os.path.normpath(rel_src)) + deps.append(os.path.normpath(rel_src)) for dep in target.get_external_deps(): commands.extend_direct(dep.get_link_args()) @@ -1588,7 +1589,15 @@ int dummy; 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 + + if mesonlib.is_windows(): + command = ''' command = {executable} @$out.rsp + rspfile = $out.rsp + rspfile_content = $ARGS $in +'''.format(executable=invoc) + else: + command = ' command = %s $ARGS $in\n' % invoc + description = ' description = Compiling C Sharp target $out.\n' outfile.write(rule) outfile.write(command) diff --git a/mesonbuild/compilers/__init__.py b/mesonbuild/compilers/__init__.py index f09f25277..84c87fb09 100644 --- a/mesonbuild/compilers/__init__.py +++ b/mesonbuild/compilers/__init__.py @@ -67,6 +67,7 @@ __all__ = [ 'JavaCompiler', 'LLVMDCompiler', 'MonoCompiler', + 'VisualStudioCsCompiler', 'NAGFortranCompiler', 'ObjCCompiler', 'ObjCPPCompiler', @@ -127,7 +128,7 @@ from .cpp import ( IntelCPPCompiler, VisualStudioCPPCompiler, ) -from .cs import MonoCompiler +from .cs import MonoCompiler, VisualStudioCsCompiler from .d import ( DCompiler, DmdDCompiler, diff --git a/mesonbuild/compilers/cs.py b/mesonbuild/compilers/cs.py index dd7a433d9..6d4811b38 100644 --- a/mesonbuild/compilers/cs.py +++ b/mesonbuild/compilers/cs.py @@ -15,15 +15,16 @@ import os.path, subprocess from ..mesonlib import EnvironmentException +from ..mesonlib import is_windows from .compilers import Compiler, mono_buildtype_args -class MonoCompiler(Compiler): - def __init__(self, exelist, version, **kwargs): +class CsCompiler(Compiler): + def __init__(self, exelist, version, id, runner=None): self.language = 'cs' - super().__init__(exelist, version, **kwargs) - self.id = 'mono' - self.monorunner = 'mono' + super().__init__(exelist, version) + self.id = id + self.runner = runner def get_display_language(self): return 'C sharp' @@ -96,7 +97,10 @@ class MonoCompiler(Compiler): pc.wait() if pc.returncode != 0: raise EnvironmentException('Mono compiler %s can not compile programs.' % self.name_string()) - cmdlist = [self.monorunner, obj] + if self.runner: + cmdlist = [self.runner, obj] + else: + cmdlist = [os.path.join(work_dir, obj)] pe = subprocess.Popen(cmdlist, cwd=work_dir) pe.wait() if pe.returncode != 0: @@ -107,3 +111,25 @@ class MonoCompiler(Compiler): def get_buildtype_args(self, buildtype): return mono_buildtype_args[buildtype] + + +class MonoCompiler(CsCompiler): + def __init__(self, exelist, version): + super().__init__(exelist, version, 'mono', + 'mono') + + +class VisualStudioCsCompiler(CsCompiler): + def __init__(self, exelist, version): + super().__init__(exelist, version, 'csc') + + def get_buildtype_args(self, buildtype): + res = mono_buildtype_args[buildtype] + if not is_windows(): + tmp = [] + for flag in res: + if flag == '-debug': + flag = '-debug:portable' + tmp.append(flag) + res = tmp + return res diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index e553423c2..ccd85d53e 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -54,6 +54,7 @@ from .compilers import ( IntelFortranCompiler, JavaCompiler, MonoCompiler, + VisualStudioCsCompiler, NAGFortranCompiler, Open64FortranCompiler, PathScaleFortranCompiler, @@ -275,6 +276,10 @@ class Environment: else: self.default_c = ['cc', 'gcc', 'clang'] self.default_cpp = ['c++', 'g++', 'clang++'] + if mesonlib.is_windows(): + self.default_cs = ['csc', 'mcs'] + else: + self.default_cs = ['mcs', 'csc'] self.default_objc = ['cc'] self.default_objcpp = ['c++'] self.default_fortran = ['gfortran', 'g95', 'f95', 'f90', 'f77', 'ifort'] @@ -419,7 +424,7 @@ class Environment: def _get_compilers(self, lang, evar, want_cross): ''' The list of compilers is detected in the exact same way for - C, C++, ObjC, ObjC++, Fortran so consolidate it here. + C, C++, ObjC, ObjC++, Fortran, CS so consolidate it here. ''' if self.is_cross_build() and want_cross: compilers = mesonlib.stringlistify(self.cross_info.config['binaries'][lang]) @@ -664,16 +669,24 @@ class Environment: raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') def detect_cs_compiler(self): - exelist = ['mcs'] - try: - p, out, err = Popen_safe(exelist + ['--version']) - except OSError: - raise EnvironmentException('Could not execute C# compiler "%s"' % ' '.join(exelist)) - version = search_version(out) - full_version = out.split('\n', 1)[0] - if 'Mono' in out: - return MonoCompiler(exelist, version, full_version=full_version) - raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') + compilers, ccache, is_cross, exe_wrap = self._get_compilers('cs', 'CSC', False) + popen_exceptions = {} + for comp in compilers: + if not isinstance(comp, list): + comp = [comp] + try: + p, out, err = Popen_safe(comp + ['--version']) + except OSError as e: + popen_exceptions[' '.join(comp + ['--version'])] = e + continue + + version = search_version(out) + if 'Mono' in out: + return MonoCompiler(comp, version) + elif "Visual C#" in out: + return VisualStudioCsCompiler(comp, version) + + self._handle_exceptions(popen_exceptions, compilers) def detect_vala_compiler(self): if 'VALAC' in os.environ: diff --git a/run_project_tests.py b/run_project_tests.py index c2c3efe76..6e03f6c9d 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -478,7 +478,7 @@ def detect_tests_to_run(): ('platform-linux', 'linuxlike', mesonlib.is_osx() or mesonlib.is_windows()), ('java', 'java', backend is not Backend.ninja or mesonlib.is_osx() or not have_java()), - ('C#', 'csharp', backend is not Backend.ninja or not shutil.which('mcs')), + ('C#', 'csharp', backend is not Backend.ninja or not (shutil.which('mcs') or shutil.which('csc')) or not shutil.which('resgen')), ('vala', 'vala', backend is not Backend.ninja or not shutil.which('valac')), ('rust', 'rust', backend is not Backend.ninja or not shutil.which('rustc')), ('d', 'd', backend is not Backend.ninja or not have_d_compiler()), From 2488ccfa1c6fbb27623ed09238b8b5ea2be5ed10 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 25 Feb 2018 01:09:18 +0200 Subject: [PATCH 2/8] Skip tests if deps are not found. --- test cases/csharp/4 external dep/meson.build | 7 ++++++- test cases/csharp/4 pkgconfig/meson.build | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/test cases/csharp/4 external dep/meson.build b/test cases/csharp/4 external dep/meson.build index 004d25ffc..019d618b7 100644 --- a/test cases/csharp/4 external dep/meson.build +++ b/test cases/csharp/4 external dep/meson.build @@ -1,4 +1,9 @@ project('C# external library', 'cs') -glib_sharp_2 = dependency('glib-sharp-2.0') +glib_sharp_2 = dependency('glib-sharp-2.0', required : false) + +if not glib_sharp_2.found() + error('MESON_SKIP_TEST glib# not found.') +endif + e = executable('prog', 'prog.cs', dependencies: glib_sharp_2, install : true) test('libtest', e, args: [join_paths(meson.current_source_dir(), 'hello.txt')]) diff --git a/test cases/csharp/4 pkgconfig/meson.build b/test cases/csharp/4 pkgconfig/meson.build index e2ba03578..3062fca60 100644 --- a/test cases/csharp/4 pkgconfig/meson.build +++ b/test cases/csharp/4 pkgconfig/meson.build @@ -1,6 +1,11 @@ project('C# pkg-config', 'cs') -nunit_dep = dependency('nunit') +nunit_dep = dependency('nunit', required : false) + +if not nunit_dep.found() + error('MESON_SKIP_TEST nunit not found.') +endif + nunit_runner = find_program('nunit-console') test_lib = library('test_lib', 'test-lib.cs', dependencies: nunit_dep) From 0c3af38c3ef90f27302aeaca1124bc2f4123ef12 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 25 Feb 2018 01:12:42 +0200 Subject: [PATCH 3/8] Renamed test dir with duplicate number. --- .../common/{174 preserve gendir => 178 preserve gendir}/base.inp | 0 .../com/mesonbuild/subbie.inp | 0 .../{174 preserve gendir => 178 preserve gendir}/genprog.py | 0 .../{174 preserve gendir => 178 preserve gendir}/meson.build | 0 .../{174 preserve gendir => 178 preserve gendir}/testprog.c | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename test cases/common/{174 preserve gendir => 178 preserve gendir}/base.inp (100%) rename test cases/common/{174 preserve gendir => 178 preserve gendir}/com/mesonbuild/subbie.inp (100%) rename test cases/common/{174 preserve gendir => 178 preserve gendir}/genprog.py (100%) rename test cases/common/{174 preserve gendir => 178 preserve gendir}/meson.build (100%) rename test cases/common/{174 preserve gendir => 178 preserve gendir}/testprog.c (100%) diff --git a/test cases/common/174 preserve gendir/base.inp b/test cases/common/178 preserve gendir/base.inp similarity index 100% rename from test cases/common/174 preserve gendir/base.inp rename to test cases/common/178 preserve gendir/base.inp diff --git a/test cases/common/174 preserve gendir/com/mesonbuild/subbie.inp b/test cases/common/178 preserve gendir/com/mesonbuild/subbie.inp similarity index 100% rename from test cases/common/174 preserve gendir/com/mesonbuild/subbie.inp rename to test cases/common/178 preserve gendir/com/mesonbuild/subbie.inp diff --git a/test cases/common/174 preserve gendir/genprog.py b/test cases/common/178 preserve gendir/genprog.py similarity index 100% rename from test cases/common/174 preserve gendir/genprog.py rename to test cases/common/178 preserve gendir/genprog.py diff --git a/test cases/common/174 preserve gendir/meson.build b/test cases/common/178 preserve gendir/meson.build similarity index 100% rename from test cases/common/174 preserve gendir/meson.build rename to test cases/common/178 preserve gendir/meson.build diff --git a/test cases/common/174 preserve gendir/testprog.c b/test cases/common/178 preserve gendir/testprog.c similarity index 100% rename from test cases/common/174 preserve gendir/testprog.c rename to test cases/common/178 preserve gendir/testprog.c From 26010807db3540c95deabc7444c20239de95ae76 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 25 Feb 2018 01:14:01 +0200 Subject: [PATCH 4/8] Removed test dir 4 which was doing the same thing as the other test dir number 4. --- test cases/csharp/4 pkgconfig/meson.build | 12 ------------ test cases/csharp/4 pkgconfig/test-lib.cs | 11 ----------- 2 files changed, 23 deletions(-) delete mode 100644 test cases/csharp/4 pkgconfig/meson.build delete mode 100644 test cases/csharp/4 pkgconfig/test-lib.cs diff --git a/test cases/csharp/4 pkgconfig/meson.build b/test cases/csharp/4 pkgconfig/meson.build deleted file mode 100644 index 3062fca60..000000000 --- a/test cases/csharp/4 pkgconfig/meson.build +++ /dev/null @@ -1,12 +0,0 @@ -project('C# pkg-config', 'cs') - -nunit_dep = dependency('nunit', required : false) - -if not nunit_dep.found() - error('MESON_SKIP_TEST nunit not found.') -endif - -nunit_runner = find_program('nunit-console') - -test_lib = library('test_lib', 'test-lib.cs', dependencies: nunit_dep) -test('nunit test', nunit_runner, args: test_lib) diff --git a/test cases/csharp/4 pkgconfig/test-lib.cs b/test cases/csharp/4 pkgconfig/test-lib.cs deleted file mode 100644 index 29f679505..000000000 --- a/test cases/csharp/4 pkgconfig/test-lib.cs +++ /dev/null @@ -1,11 +0,0 @@ -using NUnit.Framework; - -[TestFixture] -public class NUnitTest -{ - [Test] - public void Test() - { - Assert.AreEqual(1 + 1, 2); - } -} From 0b6f90caf7a61e3cc62ce507552a74af0de2061f Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 25 Feb 2018 01:16:06 +0200 Subject: [PATCH 5/8] Added release note snippet for csc support. --- docs/markdown/snippets/csc.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 docs/markdown/snippets/csc.md diff --git a/docs/markdown/snippets/csc.md b/docs/markdown/snippets/csc.md new file mode 100644 index 000000000..90a6f7f94 --- /dev/null +++ b/docs/markdown/snippets/csc.md @@ -0,0 +1,4 @@ +## Visual Studio C# compiler support + +In addition to the Mono C# compiler we also support Visual Studio's C# +compiler. Currently this is only supported on the Ninja backend. From f223aa56a7b73a3224790642b5f2f68e1872b820 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 25 Feb 2018 01:21:35 +0200 Subject: [PATCH 6/8] Made test program consist of two source files. --- test cases/csharp/1 basic/meson.build | 2 +- test cases/csharp/1 basic/prog.cs | 5 +++-- test cases/csharp/1 basic/text.cs | 7 +++++++ 3 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 test cases/csharp/1 basic/text.cs diff --git a/test cases/csharp/1 basic/meson.build b/test cases/csharp/1 basic/meson.build index 2ee6a4a93..09e46c293 100644 --- a/test cases/csharp/1 basic/meson.build +++ b/test cases/csharp/1 basic/meson.build @@ -1,4 +1,4 @@ project('simple c#', 'cs') -e = executable('prog', 'prog.cs', install : true) +e = executable('prog', 'prog.cs', 'text.cs', install : true) test('basic', e) diff --git a/test cases/csharp/1 basic/prog.cs b/test cases/csharp/1 basic/prog.cs index dfb24002b..6ee47b074 100644 --- a/test cases/csharp/1 basic/prog.cs +++ b/test cases/csharp/1 basic/prog.cs @@ -1,7 +1,8 @@ using System; - + public class Prog { static public void Main () { - Console.WriteLine("C# is working."); + TextGetter tg = new TextGetter(); + Console.WriteLine(tg.getText()); } } diff --git a/test cases/csharp/1 basic/text.cs b/test cases/csharp/1 basic/text.cs new file mode 100644 index 000000000..c83c424c8 --- /dev/null +++ b/test cases/csharp/1 basic/text.cs @@ -0,0 +1,7 @@ +using System; + +public class TextGetter { + public String getText() { + return "C# is working."; + } +} From dd74f3fb55e14e766ce465d6b98d06628b7f5d00 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 25 Feb 2018 02:48:39 +0200 Subject: [PATCH 7/8] Hide banner when invoking csc. --- mesonbuild/compilers/cs.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mesonbuild/compilers/cs.py b/mesonbuild/compilers/cs.py index 6d4811b38..f78e364b3 100644 --- a/mesonbuild/compilers/cs.py +++ b/mesonbuild/compilers/cs.py @@ -29,6 +29,12 @@ class CsCompiler(Compiler): def get_display_language(self): return 'C sharp' + def get_always_args(self): + return ['/nologo'] + + def get_linker_always_args(self): + return ['/nologo'] + def get_output_args(self, fname): return ['-out:' + fname] @@ -93,7 +99,7 @@ class CsCompiler(Compiler): } } ''') - pc = subprocess.Popen(self.exelist + [src], cwd=work_dir) + pc = subprocess.Popen(self.exelist + self.get_always_args() + [src], cwd=work_dir) pc.wait() if pc.returncode != 0: raise EnvironmentException('Mono compiler %s can not compile programs.' % self.name_string()) From 67f3f803620cdf5cbabd2757211cb4c969ccf41f Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 25 Feb 2018 03:13:11 +0200 Subject: [PATCH 8/8] Determining whether to run C# tests is so difficult it deserves its own function. --- run_project_tests.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/run_project_tests.py b/run_project_tests.py index 6e03f6c9d..f9d4dbd04 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -465,6 +465,28 @@ def skippable(suite, test): # Other framework tests are allowed to be skipped on other platforms return True +def skip_csharp(backend): + if backend is not Backend.ninja: + return True + if not shutil.which('resgen'): + return True + if shutil.which('mcs'): + return False + if shutil.which('csc'): + # Only support VS2017 for now. Earlier versions fail + # under CI in mysterious ways. + try: + stdo = subprocess.check_output(['csc', '/version']) + except subprocess.CalledProcessError: + return True + # Having incrementing version numbers would be too easy. + # Microsoft reset the versioning back to 1.0 (from 4.x) + # when they got the Roslyn based compiler. Thus there + # is NO WAY to reliably do version number comparisons. + # Only support the version that ships with VS2017. + return not stdo.startswith(b'2.') + return True + def detect_tests_to_run(): # Name, subdirectory, skip condition. all_tests = [ @@ -478,7 +500,7 @@ def detect_tests_to_run(): ('platform-linux', 'linuxlike', mesonlib.is_osx() or mesonlib.is_windows()), ('java', 'java', backend is not Backend.ninja or mesonlib.is_osx() or not have_java()), - ('C#', 'csharp', backend is not Backend.ninja or not (shutil.which('mcs') or shutil.which('csc')) or not shutil.which('resgen')), + ('C#', 'csharp', skip_csharp(backend)), ('vala', 'vala', backend is not Backend.ninja or not shutil.which('valac')), ('rust', 'rust', backend is not Backend.ninja or not shutil.which('rustc')), ('d', 'd', backend is not Backend.ninja or not have_d_compiler()),