Merge pull request #3132 from mesonbuild/csc

Visual Studio C# compiler support and some fixes
pull/3137/head
Jussi Pakkanen 7 years ago committed by GitHub
commit 8a68dc0179
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      docs/markdown/snippets/csc.md
  2. 17
      mesonbuild/backend/ninjabackend.py
  3. 3
      mesonbuild/compilers/__init__.py
  4. 46
      mesonbuild/compilers/cs.py
  5. 29
      mesonbuild/environment.py
  6. 24
      run_project_tests.py
  7. 0
      test cases/common/178 preserve gendir/base.inp
  8. 0
      test cases/common/178 preserve gendir/com/mesonbuild/subbie.inp
  9. 0
      test cases/common/178 preserve gendir/genprog.py
  10. 0
      test cases/common/178 preserve gendir/meson.build
  11. 0
      test cases/common/178 preserve gendir/testprog.c
  12. 2
      test cases/csharp/1 basic/meson.build
  13. 3
      test cases/csharp/1 basic/prog.cs
  14. 7
      test cases/csharp/1 basic/text.cs
  15. 7
      test cases/csharp/4 external dep/meson.build
  16. 7
      test cases/csharp/4 pkgconfig/meson.build
  17. 11
      test cases/csharp/4 pkgconfig/test-lib.cs

@ -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.

@ -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()])
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)

@ -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,

@ -15,19 +15,26 @@
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'
def get_always_args(self):
return ['/nologo']
def get_linker_always_args(self):
return ['/nologo']
def get_output_args(self, fname):
return ['-out:' + fname]
@ -92,11 +99,14 @@ class MonoCompiler(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())
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 +117,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

@ -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']
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(exelist + ['--version'])
except OSError:
raise EnvironmentException('Could not execute C# compiler "%s"' % ' '.join(exelist))
p, out, err = Popen_safe(comp + ['--version'])
except OSError as e:
popen_exceptions[' '.join(comp + ['--version'])] = e
continue
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) + '"')
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:

@ -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')),
('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()),

@ -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)

@ -2,6 +2,7 @@ using System;
public class Prog {
static public void Main () {
Console.WriteLine("C# is working.");
TextGetter tg = new TextGetter();
Console.WriteLine(tg.getText());
}
}

@ -0,0 +1,7 @@
using System;
public class TextGetter {
public String getText() {
return "C# is working.";
}
}

@ -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')])

@ -1,7 +0,0 @@
project('C# pkg-config', 'cs')
nunit_dep = dependency('nunit')
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)

@ -1,11 +0,0 @@
using NUnit.Framework;
[TestFixture]
public class NUnitTest
{
[Test]
public void Test()
{
Assert.AreEqual(1 + 1, 2);
}
}
Loading…
Cancel
Save