From 9cc63f6e795c0c24582142a1297e17ee38b7cca8 Mon Sep 17 00:00:00 2001 From: kzrnm Date: Thu, 17 Mar 2022 22:51:08 +0900 Subject: [PATCH] Add config --- .verify-helper/docs/static/document.ja.md | 11 ++- .verify-helper/docs/static/document.md | 11 ++- onlinejudge_verify/languages/csharp.py | 95 ++++++++++++++++------- 3 files changed, 86 insertions(+), 31 deletions(-) diff --git a/.verify-helper/docs/static/document.ja.md b/.verify-helper/docs/static/document.ja.md index 8e48a261..4267ae94 100644 --- a/.verify-helper/docs/static/document.ja.md +++ b/.verify-helper/docs/static/document.ja.md @@ -43,8 +43,6 @@ CXXFLAGS = ["-std=c++17", "-Wall", "-g", "-fsanitize=undefined", "-D_GLIBCXX_DEB ### C# の設定 -設定項目はありません。 - [SourceExpander](https://github.com/kzrnm/SourceExpander) を使用して各種機能を実現します。 必須設定 @@ -54,6 +52,15 @@ CXXFLAGS = ["-std=c++17", "-Wall", "-g", "-fsanitize=undefined", "-D_GLIBCXX_DEB 具体的な設定は [examples/csharpsx](https://github.com/online-judge-tools/verification-helper/tree/master/examples/csharpsx) を参照。 + +`.verify-helper/config.toml` というファイルを作って以下のように設定を書くと各種設定ができます。 + +- static_embedding: `dotnet-source-expand` の `--static-embedding` オプション + +``` toml +[[languages.csharp]] +static_embedding = "// embed" +``` ### Nim の設定 `.verify-helper/config.toml` というファイルを作って以下のように設定を書くと、コンパイルの際に変換する言語 (例: `c`, `cpp`) やそのオプションを指定できます。 diff --git a/.verify-helper/docs/static/document.md b/.verify-helper/docs/static/document.md index a9916cf4..55b41955 100644 --- a/.verify-helper/docs/static/document.md +++ b/.verify-helper/docs/static/document.md @@ -43,8 +43,6 @@ There is no config now. ### Settings for C# -There is no config now. - `oj-verify` and `oj-bundle` work with [SourceExpander](https://github.com/kzrnm/SourceExpander). Requied settings @@ -54,6 +52,15 @@ Requied settings For the details refer to [examples/csharpsx](https://github.com/online-judge-tools/verification-helper/tree/master/examples/csharpsx). +You can specify compilers and options with writing `.verify-helper/config.toml` as below. + +- static_embedding: `dotnet-source-expand` with `--static-embedding` option + +``` toml +[[languages.csharp]] +static_embedding = "// embed" +``` + ### Settings for Nim You can specify options and targets (e.g. `c` `cpp`) with writing `.verify-helper/config.toml` as below. diff --git a/onlinejudge_verify/languages/csharp.py b/onlinejudge_verify/languages/csharp.py index 9e1e5794..c2018a46 100644 --- a/onlinejudge_verify/languages/csharp.py +++ b/onlinejudge_verify/languages/csharp.py @@ -11,18 +11,44 @@ from typing import * import onlinejudge_verify.languages.special_comments as special_comments +from onlinejudge_verify.config import get_config from onlinejudge_verify.languages.models import Language, LanguageEnvironment logger = getLogger(__name__) +@overload +def check_output(command: List[str], *, text: Literal[True]) -> str: + ... + + +@overload +def check_output(command: List[str], *, text: Literal[False]) -> bytes: + ... + + +@overload +def check_output(command: List[str]) -> bytes: + ... + + +def check_output(command: List[str], *, text: bool = False) -> Union[bytes, str]: + try: + return subprocess.check_output(command, text=text) + except (subprocess.CalledProcessError) as e: + logger.error('raise subprocess.CalledProcessError') + logger.info(' stdout: %s', e.stdout) + logger.info(' stderr: %s', e.stderr) + raise + + @functools.lru_cache(maxsize=1) def _check_dotnet_version() -> None: if not shutil.which('dotnet'): raise RuntimeError('`dotnet` not in $PATH') command = ['dotnet', '--version'] logger.info('$ %s', ' '.join(command)) - res = subprocess.check_output(command, text=True).strip() + res = check_output(command, text=True).strip() logger.info('dotnet version: %s', res) if distutils.version.LooseVersion(res) <= distutils.version.LooseVersion("6"): raise RuntimeError("oj-verify needs .NET 6 SDK or newer") @@ -34,7 +60,7 @@ def _check_expander_console() -> None: raise RuntimeError('`dotnet-source-expand` not in $PATH. Run `dotnet tool install -g SourceExpander.Console`') command = ['dotnet-source-expand', 'version'] logger.info('$ %s', ' '.join(command)) - res = subprocess.check_output(command, text=True).strip() + res = check_output(command, text=True).strip() logger.info('dotnet-source-expand version: %s', res) if distutils.version.LooseVersion(res) < distutils.version.LooseVersion("5"): 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]): if len(sp) >= 2: yield EmbeddedLibrary(sp[0], sp[1]) - res = list(enumerate_library(subprocess.check_output(command, encoding='utf-8').strip().splitlines())) + res = list(enumerate_library(check_output(command, text=True).strip().splitlines())) logger.debug('libraries: %s', res) return res @@ -72,7 +98,7 @@ def enumerate_library(lines: List[str]): def _check_embedded_existing(csproj_path: pathlib.Path) -> None: command = ['dotnet', 'build', str(csproj_path)] logger.info('$ %s', ' '.join(command)) - subprocess.check_output(command) + check_output(command) l = _list_embedded(csproj_path) if len(l) == 0: raise RuntimeError('Library needs SourceExpander.Embedder') @@ -106,24 +132,6 @@ def _resolve_csproj(path: pathlib.Path) -> Optional[pathlib.Path]: return _resolve_csproj(path.parent) -@functools.lru_cache(maxsize=None) -def _expand_code_dict(csproj_path: pathlib.Path) -> Dict[pathlib.Path, str]: - _check_expander_console() - command = ['dotnet-source-expand', 'expand-all', str(csproj_path)] - logger.info('$ %s', ' '.join(command)) - json_res = subprocess.check_output(command) - return {pathlib.Path(t['FilePath']): t['ExpandedCode'] for t in json.loads(json_res)} - - -@functools.lru_cache(maxsize=None) -def _expand_code(path: pathlib.Path) -> bytes: - _check_expander_console() - csproj_path = _resolve_csproj(path) - _check_no_embedder(csproj_path) - d = _expand_code_dict(csproj_path) - return d[path].encode('utf-8') - - class DependencyInfo: def __init__(self, filename: str, dependencies: List[str], typenames: Set[str]) -> None: self.filename = filename @@ -143,7 +151,7 @@ def _dependency_info_list(csproj_path: pathlib.Path) -> List[DependencyInfo]: command = ['dotnet-source-expand', 'dependency', '-p', str(csproj_path)] logger.info('$ %s', ' '.join(command)) - res = subprocess.check_output(command) + res = check_output(command) return json.loads(res, object_hook=lambda d: DependencyInfo(d['FileName'], d['Dependencies'], set(d['TypeNames']))) @@ -173,7 +181,17 @@ def _get_target_framework(csproj_path: pathlib.Path) -> str: return target +class CSharpConfig: + def __init__(self, config: Dict[str, Any]) -> None: + root = config.get('languages', {}).get('csharp', {}) + self.static_embedding: Optional[str] = root.get('static_embedding', None) + + class CSharpLanguageEnvironment(LanguageEnvironment): + def __init__(self, config: CSharpConfig) -> None: + super().__init__() + self.config = config + @staticmethod def _create_runner_project(code: bytes, target_framework: str, output_dir): os.makedirs(str(output_dir), exist_ok=True) @@ -194,11 +212,11 @@ def compile(self, path: pathlib.Path, *, basedir: pathlib.Path, tempdir: pathlib _check_env(path) target_framework = _get_target_framework(_resolve_csproj(path)) logger.info('build: TargetFramework = %s', target_framework) - self._create_runner_project(_expand_code(path), target_framework, output_dir) + self._create_runner_project(self._expand_code(path), target_framework, output_dir) command = ['dotnet', 'build', str(output_dir / 'runner.csproj'), '-c', 'Release', '-o', str(output_dir / 'bin')] logger.info('$ %s', ' '.join(command)) - subprocess.check_output(command) + check_output(command) def get_execute_command(self, path: pathlib.Path, *, basedir: pathlib.Path, tempdir: pathlib.Path) -> List[str]: path = path.resolve() @@ -207,8 +225,31 @@ def get_execute_command(self, path: pathlib.Path, *, basedir: pathlib.Path, temp _check_env(path) return [str(output_dir / 'bin' / 'runner')] + @functools.lru_cache(maxsize=None) + def _expand_code_dict(self, csproj_path: pathlib.Path) -> Dict[pathlib.Path, str]: + _check_expander_console() + command = ['dotnet-source-expand', 'expand-all', str(csproj_path)] + if self.config.static_embedding: + command.extend(['--static-embedding', self.config.static_embedding]) + logger.info('$ %s', ' '.join(command)) + json_res = check_output(command) + return {pathlib.Path(t['FilePath']): t['ExpandedCode'] for t in json.loads(json_res)} + + @functools.lru_cache(maxsize=None) + def _expand_code(self, path: pathlib.Path) -> bytes: + _check_expander_console() + csproj_path = _resolve_csproj(path) + _check_no_embedder(csproj_path) + d = self._expand_code_dict(csproj_path) + return d[path].encode('utf-8') + class CSharpLanguage(Language): + def __init__(self) -> None: + super().__init__() + self.config = CSharpConfig(get_config()) + self.environment = CSharpLanguageEnvironment(self.config) + def list_attributes(self, path: pathlib.Path, *, basedir: pathlib.Path) -> Dict[str, Any]: path = path.resolve() attributes: Dict[str, Any] = special_comments.list_special_comments(path) @@ -224,7 +265,7 @@ def list_dependencies(self, path: pathlib.Path, *, basedir: pathlib.Path) -> Lis def bundle(self, path: pathlib.Path, *, basedir: pathlib.Path, options: Dict[str, Any]) -> bytes: path = path.resolve() _check_env(path) - return _expand_code(path) + return self.environment._expands_code(path, self.config.static_embedding) def list_environments(self, path: pathlib.Path, *, basedir: pathlib.Path) -> Sequence[CSharpLanguageEnvironment]: - return [CSharpLanguageEnvironment()] + return [self.environment]