11
11
from typing import *
12
12
13
13
import onlinejudge_verify .languages .special_comments as special_comments
14
+ from onlinejudge_verify .config import get_config
14
15
from onlinejudge_verify .languages .models import Language , LanguageEnvironment
15
16
16
17
logger = getLogger (__name__ )
17
18
18
19
20
+ @overload
21
+ def check_output (command : List [str ], * , text : Literal [True ]) -> str :
22
+ ...
23
+
24
+
25
+ @overload
26
+ def check_output (command : List [str ], * , text : Literal [False ]) -> bytes :
27
+ ...
28
+
29
+
30
+ @overload
31
+ def check_output (command : List [str ]) -> bytes :
32
+ ...
33
+
34
+
35
+ def check_output (command : List [str ], * , text : bool = False ) -> Union [bytes , str ]:
36
+ try :
37
+ return subprocess .check_output (command , text = text )
38
+ except (subprocess .CalledProcessError ) as e :
39
+ logger .error ('raise subprocess.CalledProcessError' )
40
+ logger .info (' stdout: %s' , e .stdout )
41
+ logger .info (' stderr: %s' , e .stderr )
42
+ raise
43
+
44
+
19
45
@functools .lru_cache (maxsize = 1 )
20
46
def _check_dotnet_version () -> None :
21
47
if not shutil .which ('dotnet' ):
22
48
raise RuntimeError ('`dotnet` not in $PATH' )
23
49
command = ['dotnet' , '--version' ]
24
50
logger .info ('$ %s' , ' ' .join (command ))
25
- res = subprocess . check_output (command , text = True ).strip ()
51
+ res = check_output (command , text = True ).strip ()
26
52
logger .info ('dotnet version: %s' , res )
27
53
if distutils .version .LooseVersion (res ) <= distutils .version .LooseVersion ("6" ):
28
54
raise RuntimeError ("oj-verify needs .NET 6 SDK or newer" )
@@ -34,7 +60,7 @@ def _check_expander_console() -> None:
34
60
raise RuntimeError ('`dotnet-source-expand` not in $PATH. Run `dotnet tool install -g SourceExpander.Console`' )
35
61
command = ['dotnet-source-expand' , 'version' ]
36
62
logger .info ('$ %s' , ' ' .join (command ))
37
- res = subprocess . check_output (command , text = True ).strip ()
63
+ res = check_output (command , text = True ).strip ()
38
64
logger .info ('dotnet-source-expand version: %s' , res )
39
65
if distutils .version .LooseVersion (res ) < distutils .version .LooseVersion ("5" ):
40
66
raise RuntimeError ('`dotnet-source-expand` version must be 5.0.0 or newer. Update SourceExpander.Console. `dotnet tool update -g SourceExpander.Console`' )
@@ -63,7 +89,7 @@ def enumerate_library(lines: List[str]):
63
89
if len (sp ) >= 2 :
64
90
yield EmbeddedLibrary (sp [0 ], sp [1 ])
65
91
66
- res = list (enumerate_library (subprocess . check_output (command , encoding = 'utf-8' ).strip ().splitlines ()))
92
+ res = list (enumerate_library (check_output (command , text = True ).strip ().splitlines ()))
67
93
logger .debug ('libraries: %s' , res )
68
94
return res
69
95
@@ -72,7 +98,7 @@ def enumerate_library(lines: List[str]):
72
98
def _check_embedded_existing (csproj_path : pathlib .Path ) -> None :
73
99
command = ['dotnet' , 'build' , str (csproj_path )]
74
100
logger .info ('$ %s' , ' ' .join (command ))
75
- subprocess . check_output (command )
101
+ check_output (command )
76
102
l = _list_embedded (csproj_path )
77
103
if len (l ) == 0 :
78
104
raise RuntimeError ('Library needs SourceExpander.Embedder' )
@@ -84,13 +110,6 @@ def _check_env(path: pathlib.Path):
84
110
_check_embedded_existing (_resolve_csproj (path ))
85
111
86
112
87
- @functools .lru_cache (maxsize = None )
88
- def _check_no_embedder (csproj_path : pathlib .Path ) -> None :
89
- root = ET .parse (csproj_path ).getroot ()
90
- if root .find ('.//PackageReference[@Include="SourceExpander.Embedder"]' ):
91
- logger .error (" Test project(%s) has `SourceExpander.Embedder` reference. Libraries and tests should not be in same project." , str (csproj_path ))
92
-
93
-
94
113
@functools .lru_cache (maxsize = None )
95
114
def _resolve_csproj (path : pathlib .Path ) -> Optional [pathlib .Path ]:
96
115
path = path .resolve ()
@@ -106,24 +125,6 @@ def _resolve_csproj(path: pathlib.Path) -> Optional[pathlib.Path]:
106
125
return _resolve_csproj (path .parent )
107
126
108
127
109
- @functools .lru_cache (maxsize = None )
110
- def _expand_code_dict (csproj_path : pathlib .Path ) -> Dict [pathlib .Path , str ]:
111
- _check_expander_console ()
112
- command = ['dotnet-source-expand' , 'expand-all' , str (csproj_path )]
113
- logger .info ('$ %s' , ' ' .join (command ))
114
- json_res = subprocess .check_output (command )
115
- return {pathlib .Path (t ['FilePath' ]): t ['ExpandedCode' ] for t in json .loads (json_res )}
116
-
117
-
118
- @functools .lru_cache (maxsize = None )
119
- def _expand_code (path : pathlib .Path ) -> bytes :
120
- _check_expander_console ()
121
- csproj_path = _resolve_csproj (path )
122
- _check_no_embedder (csproj_path )
123
- d = _expand_code_dict (csproj_path )
124
- return d [path ].encode ('utf-8' )
125
-
126
-
127
128
class DependencyInfo :
128
129
def __init__ (self , filename : str , dependencies : List [str ], typenames : Set [str ]) -> None :
129
130
self .filename = filename
@@ -143,7 +144,7 @@ def _dependency_info_list(csproj_path: pathlib.Path) -> List[DependencyInfo]:
143
144
144
145
command = ['dotnet-source-expand' , 'dependency' , '-p' , str (csproj_path )]
145
146
logger .info ('$ %s' , ' ' .join (command ))
146
- res = subprocess . check_output (command )
147
+ res = check_output (command )
147
148
return json .loads (res , object_hook = lambda d : DependencyInfo (d ['FileName' ], d ['Dependencies' ], set (d ['TypeNames' ])))
148
149
149
150
@@ -173,7 +174,17 @@ def _get_target_framework(csproj_path: pathlib.Path) -> str:
173
174
return target
174
175
175
176
177
+ class CSharpConfig :
178
+ def __init__ (self , config : Dict [str , Any ]) -> None :
179
+ root = config .get ('languages' , {}).get ('csharp' , {})
180
+ self .static_embedding : Optional [str ] = root .get ('static_embedding' , None )
181
+
182
+
176
183
class CSharpLanguageEnvironment (LanguageEnvironment ):
184
+ def __init__ (self , config : CSharpConfig ) -> None :
185
+ super ().__init__ ()
186
+ self .config = config
187
+
177
188
@staticmethod
178
189
def _create_runner_project (code : bytes , target_framework : str , output_dir ):
179
190
os .makedirs (str (output_dir ), exist_ok = True )
@@ -194,11 +205,11 @@ def compile(self, path: pathlib.Path, *, basedir: pathlib.Path, tempdir: pathlib
194
205
_check_env (path )
195
206
target_framework = _get_target_framework (_resolve_csproj (path ))
196
207
logger .info ('build: TargetFramework = %s' , target_framework )
197
- self ._create_runner_project (_expand_code (path ), target_framework , output_dir )
208
+ self ._create_runner_project (self . _expand_code (path ), target_framework , output_dir )
198
209
199
210
command = ['dotnet' , 'build' , str (output_dir / 'runner.csproj' ), '-c' , 'Release' , '-o' , str (output_dir / 'bin' )]
200
211
logger .info ('$ %s' , ' ' .join (command ))
201
- subprocess . check_output (command )
212
+ check_output (command )
202
213
203
214
def get_execute_command (self , path : pathlib .Path , * , basedir : pathlib .Path , tempdir : pathlib .Path ) -> List [str ]:
204
215
path = path .resolve ()
@@ -207,8 +218,30 @@ def get_execute_command(self, path: pathlib.Path, *, basedir: pathlib.Path, temp
207
218
_check_env (path )
208
219
return [str (output_dir / 'bin' / 'runner' )]
209
220
221
+ @functools .lru_cache (maxsize = None )
222
+ def _expand_code_dict (self , csproj_path : pathlib .Path ) -> Dict [pathlib .Path , str ]:
223
+ _check_expander_console ()
224
+ command = ['dotnet-source-expand' , 'expand-all' , str (csproj_path )]
225
+ if self .config .static_embedding :
226
+ command .extend (['--static-embedding' , self .config .static_embedding ])
227
+ logger .info ('$ %s' , ' ' .join (command ))
228
+ json_res = check_output (command )
229
+ return {pathlib .Path (t ['FilePath' ]): t ['ExpandedCode' ] for t in json .loads (json_res )}
230
+
231
+ @functools .lru_cache (maxsize = None )
232
+ def _expand_code (self , path : pathlib .Path ) -> bytes :
233
+ _check_expander_console ()
234
+ csproj_path = _resolve_csproj (path )
235
+ d = self ._expand_code_dict (csproj_path )
236
+ return d [path ].encode ('utf-8' )
237
+
210
238
211
239
class CSharpLanguage (Language ):
240
+ def __init__ (self ) -> None :
241
+ super ().__init__ ()
242
+ self .config = CSharpConfig (get_config ())
243
+ self .environment = CSharpLanguageEnvironment (self .config )
244
+
212
245
def list_attributes (self , path : pathlib .Path , * , basedir : pathlib .Path ) -> Dict [str , Any ]:
213
246
path = path .resolve ()
214
247
attributes : Dict [str , Any ] = special_comments .list_special_comments (path )
@@ -224,7 +257,7 @@ def list_dependencies(self, path: pathlib.Path, *, basedir: pathlib.Path) -> Lis
224
257
def bundle (self , path : pathlib .Path , * , basedir : pathlib .Path , options : Dict [str , Any ]) -> bytes :
225
258
path = path .resolve ()
226
259
_check_env (path )
227
- return _expand_code (path )
260
+ return self . environment . _expand_code (path )
228
261
229
262
def list_environments (self , path : pathlib .Path , * , basedir : pathlib .Path ) -> Sequence [CSharpLanguageEnvironment ]:
230
- return [CSharpLanguageEnvironment () ]
263
+ return [self . environment ]
0 commit comments