Skip to content

Commit fe63882

Browse files
committed
CSharpLanguage
1 parent 2ac9807 commit fe63882

File tree

10 files changed

+255
-11
lines changed

10 files changed

+255
-11
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using System;
2+
3+
namespace Library
4+
{
5+
public static partial class HelloWorld
6+
{
7+
private const string Text = "Hello World";
8+
}
9+
}

examples/csharpsx/Library/HelloWorld.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
namespace Library
44
{
5-
public static class HelloWorld
5+
public static partial class HelloWorld
66
{
7-
public static void Hello() => Console.WriteLine("Hello World");
7+
public static void Hello() => Console.WriteLine(Text);
88
}
99
}

examples/csharpsx/Library/Library.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
</PropertyGroup>
66

77
<ItemGroup>
8-
<PackageReference Include="SourceExpander.Embedder" Version="4.2.0-beta1">
8+
<PackageReference Include="SourceExpander.Embedder" Version="5.0.0">
99
<PrivateAssets>all</PrivateAssets>
1010
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1111
</PackageReference>

examples/csharpsx/Verifier/Verifier.csproj

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@
88

99
<ItemGroup>
1010
<ProjectReference Include="..\Library\Library.csproj" />
11-
<PackageReference Include="SourceExpander.Generator" Version="4.2.0-beta1">
12-
<PrivateAssets>all</PrivateAssets>
13-
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
14-
</PackageReference>
1511
</ItemGroup>
1612

1713
</Project>

examples/csharpsx/Verifier/helloworld.test.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// PROBLEM https://onlinejudge.u-aizu.ac.jp/courses/lesson/2/ITP1/1/ITP1_1_A
1+
// verification-helper: PROBLEM https://onlinejudge.u-aizu.ac.jp/courses/lesson/2/ITP1/1/ITP1_1_A
22

33
internal class helloworld
44
{

examples/csharpsx/Verifier/segment_tree.point_set_range_composite.test.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// PROBLEM https://judge.yosupo.jp/problem/point_set_range_composite
1+
// verification-helper: PROBLEM https://judge.yosupo.jp/problem/point_set_range_composite
22
using System;
33
using System.Linq;
44
using Library;

examples/csharpsx/Verifier/segment_tree.range_minimum_query.test.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using System.Linq;
33
using Library;
44

5-
// PROBLEM https://onlinejudge.u-aizu.ac.jp/courses/library/3/DSL/all/DSL_2_A
5+
// verification-helper: PROBLEM https://onlinejudge.u-aizu.ac.jp/courses/library/3/DSL/all/DSL_2_A
66
class range_minimum_query
77
{
88
static void Main()

examples/csharpsx/Verifier/segment_tree.range_sum_query.test.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using System.Linq;
33
using Library;
44

5-
// PROBLEM https://onlinejudge.u-aizu.ac.jp/courses/library/3/DSL/all/DSL_2_B
5+
// verification-helper: PROBLEM https://onlinejudge.u-aizu.ac.jp/courses/library/3/DSL/all/DSL_2_B
66
class range_sum_query
77
{
88
static void Main()
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
# Python Version: 3.x
2+
import functools
3+
import json
4+
import os
5+
import pathlib
6+
import subprocess
7+
from logging import getLogger
8+
from typing import *
9+
import distutils.version
10+
import shutil
11+
import xml.etree.ElementTree as ET
12+
13+
import onlinejudge_verify.languages.special_comments as special_comments
14+
from onlinejudge_verify.languages.models import Language, LanguageEnvironment
15+
16+
logger = getLogger(__name__)
17+
18+
dotnet_dll_caches_dir = pathlib.Path('.verify-helper/cache') / 'dotnet'
19+
20+
21+
@functools.lru_cache(maxsize=1)
22+
def _check_dotnet_version() -> None:
23+
if not shutil.which('dotnet'):
24+
raise RuntimeError('`dotnet` not in $PATH')
25+
command = ['dotnet', '--version']
26+
logger.info('$ %s', ' '.join(command))
27+
res = subprocess.check_output(command).decode().strip()
28+
logger.info('dotnet version: %s', res)
29+
if distutils.version.LooseVersion(res) <= distutils.version.LooseVersion("6"):
30+
raise RuntimeError("needs .NET 6 or newer SDK")
31+
32+
33+
@functools.lru_cache(maxsize=1)
34+
def _check_expander_console() -> None:
35+
if not shutil.which('dotnet-source-expand'):
36+
raise RuntimeError(
37+
'`dotnet-source-expand` not in $PATH. You needs SourceExpander.Console. `dotnet tool install -g SourceExpander.Console`')
38+
command = ['dotnet-source-expand', 'version']
39+
logger.info('$ %s', ' '.join(command))
40+
res = subprocess.check_output(command).decode().strip()
41+
logger.info('dotnet-source-expand version: %s', res)
42+
if distutils.version.LooseVersion(res) < distutils.version.LooseVersion("5"):
43+
raise RuntimeError(
44+
'`dotnet-source-expand` version must be 5.0.0 or newer. Update SourceExpander.Console. `dotnet tool update -g SourceExpander.Console`')
45+
46+
47+
class EmbeddedLibrary:
48+
def __init__(self, name: str, version: str) -> None:
49+
self.name = name
50+
self.version = version
51+
52+
def __repr__(self) -> str:
53+
return 'EmbeddedLibrary("%s", "%s")' % (self.name, self.version)
54+
55+
56+
@functools.lru_cache(maxsize=None)
57+
def _list_embedded(csproj_path: pathlib.Path) -> List[EmbeddedLibrary]:
58+
_check_expander_console()
59+
if csproj_path is None or csproj_path.suffix != ".csproj":
60+
raise None
61+
command = ['dotnet-source-expand', 'library-list', str(csproj_path)]
62+
logger.info('$ %s', ' '.join(command))
63+
64+
def enumerate_library(lines: List[str]):
65+
for line in lines:
66+
sp = line.split(',')
67+
if len(sp) >= 2:
68+
yield EmbeddedLibrary(sp[0], sp[1])
69+
70+
res = list(enumerate_library(
71+
subprocess.check_output(
72+
command, encoding='utf-8'
73+
).strip().splitlines()))
74+
logger.debug('libraries: %s', res)
75+
return res
76+
77+
78+
@functools.lru_cache(maxsize=None)
79+
def _check_embedded_existing(csproj_path: pathlib.Path) -> None:
80+
command = ['dotnet', 'build', str(csproj_path)]
81+
logger.info('$ %s', ' '.join(command))
82+
subprocess.check_output(command)
83+
l = _list_embedded(csproj_path)
84+
if len(l) == 0:
85+
raise RuntimeError(
86+
'Library needs SourceExpander.Embedder')
87+
88+
89+
def _check_env(path: pathlib.Path):
90+
_check_dotnet_version()
91+
_check_expander_console()
92+
_check_embedded_existing(_resolve_csproj(path))
93+
94+
95+
@functools.lru_cache(maxsize=None)
96+
def _resolve_csproj(path: pathlib.Path) -> Optional[pathlib.Path]:
97+
path = path.resolve()
98+
if path.suffix == ".csproj":
99+
return path
100+
101+
proj = next(path.glob("*.csproj"), None)
102+
if proj is not None:
103+
return proj
104+
105+
if path == path.parent:
106+
return None
107+
return _resolve_csproj(path.parent)
108+
109+
110+
@functools.lru_cache(maxsize=None)
111+
def _expand_code_dict(csproj_path: pathlib.Path) -> Dict[pathlib.Path, str]:
112+
_check_expander_console()
113+
command = ['dotnet-source-expand', 'expand-all', str(csproj_path)]
114+
logger.info('$ %s', ' '.join(command))
115+
json_res = subprocess.check_output(command)
116+
return {pathlib.Path(t['FilePath']): t['ExpandedCode']
117+
for t in json.loads(json_res)}
118+
119+
120+
@functools.lru_cache(maxsize=None)
121+
def _expand_code(path: pathlib.Path) -> bytes:
122+
_check_expander_console()
123+
d = _expand_code_dict(_resolve_csproj(path))
124+
return d[path].encode('utf-8')
125+
126+
127+
class DependencyInfo:
128+
def __init__(self, filename: str, dependencies: List[str], typenames: Set[str]) -> None:
129+
self.filename = filename
130+
self.dependencies = dependencies
131+
self.defined_types = typenames
132+
133+
def __repr__(self) -> str:
134+
return 'DependencyInfo("%s", %s, %s)' % (self.filename, self.dependencies, self.defined_types)
135+
136+
137+
@functools.lru_cache(maxsize=None)
138+
def _dependency_info_list(csproj_path: pathlib.Path) -> List[DependencyInfo]:
139+
_check_expander_console()
140+
_check_embedded_existing(csproj_path)
141+
if csproj_path is None or csproj_path.suffix != ".csproj":
142+
raise None
143+
144+
command = ['dotnet-source-expand', 'dependency', '-p', str(csproj_path)]
145+
logger.info('$ %s', ' '.join(command))
146+
res = subprocess.check_output(command)
147+
return json.loads(
148+
res,
149+
object_hook=lambda d: DependencyInfo(d['FileName'], d['Dependencies'], set(d['TypeNames'])))
150+
151+
152+
@functools.lru_cache(maxsize=None)
153+
def _dependency_info_dict(csproj_path: pathlib.Path) -> Dict[pathlib.Path, DependencyInfo]:
154+
deps: Dict[pathlib.Path, DependencyInfo] = dict()
155+
for d in _dependency_info_list(csproj_path):
156+
p = pathlib.Path(d.filename)
157+
if p.exists():
158+
deps[p] = d
159+
return deps
160+
161+
162+
@functools.lru_cache(maxsize=None)
163+
def _list_dependencies(path: pathlib.Path) -> List[pathlib.Path]:
164+
path = path.resolve()
165+
depinfo = _dependency_info_dict(_resolve_csproj(path))
166+
return [p
167+
for p in (
168+
pathlib.Path(dep)
169+
for dep in depinfo[path].dependencies
170+
) if p.exists()]
171+
172+
173+
@functools.lru_cache(maxsize=None)
174+
def _get_target_framework(csproj_path: pathlib.Path) -> bytes:
175+
root = ET.parse(csproj_path).getroot()
176+
target = root.findtext('.//TargetFramework')
177+
return target
178+
179+
180+
class CSharpLanguageEnvironment(LanguageEnvironment):
181+
@staticmethod
182+
def _create_runner_project(code: bytes, target_framework: str, output_dir):
183+
os.makedirs(str(output_dir), exist_ok=True)
184+
with open(output_dir/'runner.csproj', 'w') as f:
185+
f.write('''<Project Sdk="Microsoft.NET.Sdk">
186+
<PropertyGroup>
187+
<OutputType>Exe</OutputType>
188+
<TargetFramework>{}</TargetFramework>
189+
</PropertyGroup>
190+
</Project>'''.format(target_framework))
191+
192+
with open(output_dir/'main.cs', 'wb') as f:
193+
f.write(code)
194+
195+
def compile(self, path: pathlib.Path, *, basedir: pathlib.Path, tempdir: pathlib.Path) -> None:
196+
path = path.resolve()
197+
output_dir = tempdir/'dotnet'
198+
_check_env(path)
199+
target_framework = _get_target_framework(_resolve_csproj(path))
200+
logger.info('build: TargetFramework = %s', target_framework)
201+
self._create_runner_project(
202+
_expand_code(path), target_framework, output_dir)
203+
204+
command = ['dotnet', 'build', str(output_dir/'runner.csproj'),
205+
'-c', 'Release',
206+
'-o', str(output_dir/'bin')]
207+
logger.info('$ %s', ' '.join(command))
208+
subprocess.check_output(command)
209+
210+
def get_execute_command(self, path: pathlib.Path, *, basedir: pathlib.Path, tempdir: pathlib.Path) -> List[str]:
211+
path = path.resolve()
212+
output_dir = tempdir/'dotnet'
213+
path = path.resolve()
214+
_check_env(path)
215+
return [str(output_dir/'bin'/'runner')]
216+
217+
218+
class CSharpLanguage(Language):
219+
def list_attributes(self, path: pathlib.Path, *, basedir: pathlib.Path) -> Dict[str, Any]:
220+
path = path.resolve()
221+
attributes = special_comments.list_special_comments(path)
222+
attributes.setdefault('links', [])
223+
attributes['links'].extend(special_comments.list_embedded_urls(path))
224+
return attributes
225+
226+
def list_dependencies(self, path: pathlib.Path, *, basedir: pathlib.Path) -> List[pathlib.Path]:
227+
path = path.resolve()
228+
_check_env(path)
229+
return _list_dependencies(path)
230+
231+
def bundle(self, path: pathlib.Path, *, basedir: pathlib.Path, options: Dict[str, Any]) -> bytes:
232+
path = path.resolve()
233+
_check_env(path)
234+
return _expand_code(path)
235+
236+
def list_environments(self, path: pathlib.Path, *, basedir: pathlib.Path) -> Sequence[CSharpLanguageEnvironment]:
237+
return [CSharpLanguageEnvironment()]

onlinejudge_verify/languages/list.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from onlinejudge_verify.config import get_config
66
from onlinejudge_verify.languages.cplusplus import CPlusPlusLanguage
7+
from onlinejudge_verify.languages.csharp import CSharpLanguage
78
from onlinejudge_verify.languages.csharpscript import CSharpScriptLanguage
89
from onlinejudge_verify.languages.go import GoLanguage
910
from onlinejudge_verify.languages.haskell import HaskellLanguage
@@ -29,6 +30,7 @@ def _get_dict() -> Dict[str, Language]:
2930
_dict['.cc'] = _dict['.cpp']
3031
_dict['.h'] = _dict['.cpp']
3132
_dict['.csx'] = CSharpScriptLanguage()
33+
_dict['.cs'] = CSharpLanguage()
3234
_dict['.nim'] = NimLanguage()
3335
_dict['.py'] = PythonLanguage()
3436
_dict['.hs'] = HaskellLanguage()

0 commit comments

Comments
 (0)