1111from typing import *
1212
1313import onlinejudge_verify .languages .special_comments as special_comments
14+ from onlinejudge_verify .config import get_config
1415from onlinejudge_verify .languages .models import Language , LanguageEnvironment
1516
1617logger = getLogger (__name__ )
1718
1819
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+
1945@functools .lru_cache (maxsize = 1 )
2046def _check_dotnet_version () -> None :
2147 if not shutil .which ('dotnet' ):
2248 raise RuntimeError ('`dotnet` not in $PATH' )
2349 command = ['dotnet' , '--version' ]
2450 logger .info ('$ %s' , ' ' .join (command ))
25- res = subprocess . check_output (command , text = True ).strip ()
51+ res = check_output (command , text = True ).strip ()
2652 logger .info ('dotnet version: %s' , res )
2753 if distutils .version .LooseVersion (res ) <= distutils .version .LooseVersion ("6" ):
2854 raise RuntimeError ("oj-verify needs .NET 6 SDK or newer" )
@@ -34,7 +60,7 @@ def _check_expander_console() -> None:
3460 raise RuntimeError ('`dotnet-source-expand` not in $PATH. Run `dotnet tool install -g SourceExpander.Console`' )
3561 command = ['dotnet-source-expand' , 'version' ]
3662 logger .info ('$ %s' , ' ' .join (command ))
37- res = subprocess . check_output (command , text = True ).strip ()
63+ res = check_output (command , text = True ).strip ()
3864 logger .info ('dotnet-source-expand version: %s' , res )
3965 if distutils .version .LooseVersion (res ) < distutils .version .LooseVersion ("5" ):
4066 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]):
6389 if len (sp ) >= 2 :
6490 yield EmbeddedLibrary (sp [0 ], sp [1 ])
6591
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 ()))
6793 logger .debug ('libraries: %s' , res )
6894 return res
6995
@@ -72,7 +98,7 @@ def enumerate_library(lines: List[str]):
7298def _check_embedded_existing (csproj_path : pathlib .Path ) -> None :
7399 command = ['dotnet' , 'build' , str (csproj_path )]
74100 logger .info ('$ %s' , ' ' .join (command ))
75- subprocess . check_output (command )
101+ check_output (command )
76102 l = _list_embedded (csproj_path )
77103 if len (l ) == 0 :
78104 raise RuntimeError ('Library needs SourceExpander.Embedder' )
@@ -84,13 +110,6 @@ def _check_env(path: pathlib.Path):
84110 _check_embedded_existing (_resolve_csproj (path ))
85111
86112
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-
94113@functools .lru_cache (maxsize = None )
95114def _resolve_csproj (path : pathlib .Path ) -> Optional [pathlib .Path ]:
96115 path = path .resolve ()
@@ -106,24 +125,6 @@ def _resolve_csproj(path: pathlib.Path) -> Optional[pathlib.Path]:
106125 return _resolve_csproj (path .parent )
107126
108127
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-
127128class DependencyInfo :
128129 def __init__ (self , filename : str , dependencies : List [str ], typenames : Set [str ]) -> None :
129130 self .filename = filename
@@ -143,7 +144,7 @@ def _dependency_info_list(csproj_path: pathlib.Path) -> List[DependencyInfo]:
143144
144145 command = ['dotnet-source-expand' , 'dependency' , '-p' , str (csproj_path )]
145146 logger .info ('$ %s' , ' ' .join (command ))
146- res = subprocess . check_output (command )
147+ res = check_output (command )
147148 return json .loads (res , object_hook = lambda d : DependencyInfo (d ['FileName' ], d ['Dependencies' ], set (d ['TypeNames' ])))
148149
149150
@@ -173,7 +174,17 @@ def _get_target_framework(csproj_path: pathlib.Path) -> str:
173174 return target
174175
175176
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+
176183class CSharpLanguageEnvironment (LanguageEnvironment ):
184+ def __init__ (self , config : CSharpConfig ) -> None :
185+ super ().__init__ ()
186+ self .config = config
187+
177188 @staticmethod
178189 def _create_runner_project (code : bytes , target_framework : str , output_dir ):
179190 os .makedirs (str (output_dir ), exist_ok = True )
@@ -194,11 +205,11 @@ def compile(self, path: pathlib.Path, *, basedir: pathlib.Path, tempdir: pathlib
194205 _check_env (path )
195206 target_framework = _get_target_framework (_resolve_csproj (path ))
196207 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 )
198209
199210 command = ['dotnet' , 'build' , str (output_dir / 'runner.csproj' ), '-c' , 'Release' , '-o' , str (output_dir / 'bin' )]
200211 logger .info ('$ %s' , ' ' .join (command ))
201- subprocess . check_output (command )
212+ check_output (command )
202213
203214 def get_execute_command (self , path : pathlib .Path , * , basedir : pathlib .Path , tempdir : pathlib .Path ) -> List [str ]:
204215 path = path .resolve ()
@@ -207,8 +218,30 @@ def get_execute_command(self, path: pathlib.Path, *, basedir: pathlib.Path, temp
207218 _check_env (path )
208219 return [str (output_dir / 'bin' / 'runner' )]
209220
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+
210238
211239class CSharpLanguage (Language ):
240+ def __init__ (self ) -> None :
241+ super ().__init__ ()
242+ self .config = CSharpConfig (get_config ())
243+ self .environment = CSharpLanguageEnvironment (self .config )
244+
212245 def list_attributes (self , path : pathlib .Path , * , basedir : pathlib .Path ) -> Dict [str , Any ]:
213246 path = path .resolve ()
214247 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
224257 def bundle (self , path : pathlib .Path , * , basedir : pathlib .Path , options : Dict [str , Any ]) -> bytes :
225258 path = path .resolve ()
226259 _check_env (path )
227- return _expand_code (path )
260+ return self . environment . _expand_code (path )
228261
229262 def list_environments (self , path : pathlib .Path , * , basedir : pathlib .Path ) -> Sequence [CSharpLanguageEnvironment ]:
230- return [CSharpLanguageEnvironment () ]
263+ return [self . environment ]
0 commit comments