Skip to content

Commit f7a4121

Browse files
authored
Add language-level config (#233)
* 言語別configを追加 * 整形 * testを追加 * set[ConfigType]はtest通らないらしい
1 parent d0e1d10 commit f7a4121

File tree

12 files changed

+145
-53
lines changed

12 files changed

+145
-53
lines changed

README.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,15 +218,18 @@ optional arguments:
218218
- `code_generator_file="~/custom_code_generator.py"` カスタムコードジェネレーター `~/custom_code_generator.py`を指定する
219219
- `exec_on_each_problem_dir='clang-format -i ./*.cpp'` `exec_on_contest_dir='touch CMakeLists.txt'`
220220
- 問題用ディレクトリ内で毎回`clang-format`を実行して、最後に`CMakeLists.txt`(空)をコンテスト用ディレクトリに生成する
221+
222+
- `compile_before_testing` テスト前にコンパイルを実行するか否かをTrue/Falseで指定。何も指定しないとFalseとなります。
223+
- `compile_only_when_diff_detected` テスト前のコンパイルの際、元のソースに変更があった場合のみ実行するかをTrue/Falseで指定。何も指定しないとFalseとなります。
224+
225+
なお、`compile_before_testing`, `compile_only_when_diff_detected`はいずれもtesterの引数で指定することも可能で、指定した場合はそちらが優先されます。
226+
221227
- `download_without_login=false` AtCoderにログインせずにダウンロードを行う機能を使わない (公開コンテストに対してのみ可能)
222228
- `parallel_download=false` データの並列ダウンロードを無効にする
223229
- `save_no_session_cache=false` ログイン情報のクッキーを保存する
224230
- `skip_existing_problems=false` ディレクトリが既に存在する問題の処理をスキップする
225231
- `in_example_format="in_{}.txt"` テストケース(input)のフォーマットを`in_1.txt, in_2.txt, ...`とする
226232
- `out_example_format="out_{}.txt"` テストケース(output)のフォーマットを`out_1.txt, out_2.txt, ...`とする
227-
- `compile_before_testing` テスト前にコンパイルを実行するか否かをTrue/Falseで指定。何も指定しないとFalseとなります。
228-
- `compile_only_when_diff_detected` テスト前のコンパイルの際、元のソースに変更があった場合のみ実行するかをTrue/Falseで指定。何も指定しないとFalseとなります。
229-
230233

231234
```toml
232235
[codestyle]
@@ -239,7 +242,9 @@ code_generator_file="~/custom_code_generator.py"
239242
[postprocess]
240243
exec_on_each_problem_dir='clang-format -i ./*.cpp'
241244
exec_on_contest_dir='touch CMakeLists.txt'
242-
245+
[tester]
246+
compile_before_testing=true
247+
compile_only_when_diff_detected=true
243248
[etc]
244249
download_without_login=false
245250
parallel_download=false

atcodertools/atcoder_tools.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ def main():
4242
if len(sys.argv) < 2 or sys.argv[1] not in ("gen", "test", "submit", "codegen", "compile", "version"):
4343
print("Usage:")
4444
print("{} gen -- to generate workspace".format(sys.argv[0]))
45+
print(
46+
"{} compile -- to compile codes in your workspace".format(sys.argv[0]))
4547
print("{} test -- to test codes in your workspace".format(sys.argv[0]))
4648
print(
4749
"{} submit -- to submit a code to the contest system".format(sys.argv[0]))

atcodertools/common/language.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,10 @@ def source_code_name(self, name_without_extension: str) -> str:
5151
# put extension to the name
5252
return "{}.{}".format(name_without_extension, self.extension)
5353

54-
def get_compile_command(self, filename: str):
55-
return self.compile_command.format(filename=filename)
54+
def get_compile_command(self, filename: str, base_command: str = None):
55+
if base_command is None:
56+
base_command = self.compile_command
57+
return base_command.format(filename=filename)
5658

5759
def get_code_filename(self, filename: str):
5860
return self.code_filename.format(filename=filename)

atcodertools/config/config.py

Lines changed: 69 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
from argparse import Namespace
22
from typing import TextIO, Dict, Any, Optional
3+
from enum import Enum
34

45
import toml
56

67
from atcodertools.codegen.code_style_config import CodeStyleConfig
78
from atcodertools.config.etc_config import EtcConfig
89
from atcodertools.config.postprocess_config import PostprocessConfig
10+
from atcodertools.config.tester_config import TesterConfig
11+
12+
13+
class ConfigType(Enum):
14+
CODESTYLE = "codestyle"
15+
POSTPROCESS = "postprocess"
16+
TESTER = "tester"
17+
ETC = "etc"
918

1019

1120
def _update_config_dict(target_dic: Dict[str, Any], update_dic: Dict[str, Any]):
@@ -15,45 +24,82 @@ def _update_config_dict(target_dic: Dict[str, Any], update_dic: Dict[str, Any]):
1524
}
1625

1726

27+
def get_config_dic(config_dic, config_type: ConfigType, lang=None):
28+
result = dict()
29+
d = config_dic.get(config_type.value, {})
30+
lang_dic = {}
31+
for k, v in d.items():
32+
if type(v) is dict:
33+
if k == lang:
34+
lang_dic = v
35+
else:
36+
result[k] = v
37+
result = _update_config_dict(result, lang_dic)
38+
return result
39+
40+
1841
class Config:
1942

2043
def __init__(self,
2144
code_style_config: CodeStyleConfig = CodeStyleConfig(),
2245
postprocess_config: PostprocessConfig = PostprocessConfig(),
46+
tester_config: TesterConfig = TesterConfig(),
2347
etc_config: EtcConfig = EtcConfig()
2448
):
2549
self.code_style_config = code_style_config
2650
self.postprocess_config = postprocess_config
51+
self.tester_config = tester_config
2752
self.etc_config = etc_config
2853

2954
@classmethod
30-
def load(cls, fp: TextIO, args: Optional[Namespace] = None):
55+
def load(cls, fp: TextIO, get_config_type, args: Optional[Namespace] = None, lang=None):
3156
"""
3257
:param fp: .toml file's file pointer
3358
:param args: command line arguments
3459
:return: Config instance
3560
"""
3661
config_dic = toml.load(fp)
3762

38-
code_style_config_dic = config_dic.get('codestyle', {})
39-
postprocess_config_dic = config_dic.get('postprocess', {})
40-
etc_config_dic = config_dic.get('etc', {})
41-
42-
if args:
43-
code_style_config_dic = _update_config_dict(code_style_config_dic,
44-
dict(
45-
template_file=args.template,
46-
workspace_dir=args.workspace,
47-
lang=args.lang))
48-
etc_config_dic = _update_config_dict(etc_config_dic,
49-
dict(
50-
download_without_login=args.without_login,
51-
parallel_download=args.parallel,
52-
save_no_session_cache=args.save_no_session_cache,
53-
skip_existing_problems=args.skip_existing_problems))
54-
55-
return Config(
56-
code_style_config=CodeStyleConfig(**code_style_config_dic),
57-
postprocess_config=PostprocessConfig(**postprocess_config_dic),
58-
etc_config=EtcConfig(**etc_config_dic)
59-
)
63+
result = Config()
64+
if not lang:
65+
if args and args.lang:
66+
lang = args.lang
67+
elif "codestyle" in config_dic:
68+
lang = config_dic["codestyle"].get("lang", None)
69+
70+
if ConfigType.CODESTYLE in get_config_type:
71+
code_style_config_dic = get_config_dic(
72+
config_dic, ConfigType.CODESTYLE, lang)
73+
if args:
74+
code_style_config_dic = _update_config_dict(code_style_config_dic,
75+
dict(
76+
template_file=args.template,
77+
workspace_dir=args.workspace,
78+
lang=lang))
79+
result.code_style_config = CodeStyleConfig(**code_style_config_dic)
80+
if ConfigType.POSTPROCESS in get_config_type:
81+
postprocess_config_dic = get_config_dic(
82+
config_dic, ConfigType.POSTPROCESS)
83+
result.postprocess_config = PostprocessConfig(
84+
**postprocess_config_dic)
85+
if ConfigType.TESTER in get_config_type:
86+
tester_config_dic = get_config_dic(
87+
config_dic, ConfigType.TESTER, lang)
88+
if args:
89+
tester_config_dic = _update_config_dict(tester_config_dic,
90+
dict(compile_before_testing=args.compile_before_testing,
91+
compile_only_when_diff_detected=args.compile_only_when_diff_detected,
92+
compile_command=args.compile_command))
93+
result.tester_config = TesterConfig(**tester_config_dic)
94+
if ConfigType.ETC in get_config_type:
95+
etc_config_dic = get_config_dic(config_dic, ConfigType.ETC)
96+
if args:
97+
etc_config_dic = _update_config_dict(etc_config_dic,
98+
dict(
99+
download_without_login=args.without_login,
100+
parallel_download=args.parallel,
101+
save_no_session_cache=args.save_no_session_cache,
102+
skip_existing_problems=args.skip_existing_problems))
103+
result.etc_config = EtcConfig(**etc_config_dic)
104+
105+
return result

atcodertools/config/etc_config.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,11 @@ def __init__(self,
66
save_no_session_cache: bool = False,
77
skip_existing_problems: bool = False,
88
in_example_format: str = "in_{}.txt",
9-
out_example_format: str = "out_{}.txt",
10-
compile_before_testing: bool = False,
11-
compile_only_when_diff_detected: bool = False
9+
out_example_format: str = "out_{}.txt"
1210
):
1311
self.download_without_login = download_without_login
1412
self.parallel_download = parallel_download
1513
self.save_no_session_cache = save_no_session_cache
1614
self.skip_existing_problems = skip_existing_problems
1715
self.in_example_format = in_example_format
1816
self.out_example_format = out_example_format
19-
self.compile_before_testing = compile_before_testing
20-
self.compile_only_when_diff_detected = compile_only_when_diff_detected
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class TesterConfig:
2+
3+
def __init__(self,
4+
compile_before_testing: bool = False,
5+
compile_only_when_diff_detected: bool = False,
6+
compile_command: str = None,
7+
run_command: str = None
8+
):
9+
self.compile_before_testing = compile_before_testing
10+
self.compile_only_when_diff_detected = compile_only_when_diff_detected
11+
self.compile_command = compile_command
12+
self.run_command = run_command

atcodertools/tools/compiler.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def compile_main_and_judge_programs(lang: Language, cwd="./", force_compile=Fals
3232
print("[Main Program]")
3333
if compile_command is None:
3434
compile_command = lang.get_compile_command('main')
35+
print("compile command: ", compile_command)
3536
code_filename = lang.get_code_filename('main')
3637
exec_filename = lang.get_exec_filename('main')
3738

atcodertools/tools/envgen.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from atcodertools.tools import get_default_config_path
2929
from atcodertools.tools.models.metadata import Metadata
3030
from atcodertools.tools.utils import with_color
31+
from atcodertools.config.config import ConfigType
3132

3233

3334
class BannedFileDetectedError(Exception):
@@ -225,7 +226,7 @@ def get_config(args: argparse.Namespace) -> Config:
225226
def _load(path: str) -> Config:
226227
logger.info("Going to load {} as config".format(path))
227228
with open(path, 'r') as f:
228-
return Config.load(f, args)
229+
return Config.load(f, {ConfigType.CODESTYLE, ConfigType.POSTPROCESS, ConfigType.ETC}, args)
229230

230231
if args.config:
231232
return _load(args.config)

atcodertools/tools/tester.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from atcodertools.tools.models.metadata import Metadata, DEFAULT_METADATA
2020
from atcodertools.tools.utils import with_color
2121
from atcodertools.tools.compiler import compile_main_and_judge_programs, BadStatusCodeException
22-
from atcodertools.config.config import Config
22+
from atcodertools.config.config import Config, ConfigType
2323
from atcodertools.tools import get_default_config_path
2424

2525
DEFAULT_EPS = 0.000000001
@@ -337,13 +337,15 @@ def main(prog, args) -> bool:
337337

338338
parser.add_argument('--compile-before-testing',
339339
help='compile source before testing: '
340-
' [Default]: disable',
341-
action='store_true')
340+
' [Default]: None',
341+
action='store_true',
342+
default=None)
342343

343344
parser.add_argument('--compile-only-when-diff-detected',
344345
help='compile only when diff detected'
345-
' [Default]: disable',
346-
action='store_true')
346+
' [Default]: None',
347+
action='store_true',
348+
default=None)
347349

348350
parser.add_argument('--compile-command',
349351
help='set compile command'
@@ -374,11 +376,7 @@ def main(prog, args) -> bool:
374376
# TODO: https://github.com/kyuridenamida/atcoder-tools/issues/177
375377

376378
with open(args.config, "r") as f:
377-
config = Config.load(f)
378-
if args.compile_before_testing:
379-
config.etc_config.compile_before_testing = True
380-
if args.compile_only_when_diff_detected:
381-
config.etc_config.compile_only_when_diff_detected = True
379+
config = Config.load(f, {ConfigType.TESTER}, args, lang.name)
382380

383381
in_sample_file_list = sorted(
384382
glob.glob(os.path.join(args.dir, metadata.sample_in_pattern)))
@@ -393,15 +391,18 @@ def main(prog, args) -> bool:
393391

394392
if args.exec is not None:
395393
exec_file = args.exec
396-
elif config.etc_config.compile_before_testing:
394+
elif config.tester_config.compile_before_testing:
397395
# Use atcoder-tools's functionality to compile source code
398-
force_compile = not config.etc_config.compile_only_when_diff_detected
396+
force_compile = not config.tester_config.compile_only_when_diff_detected
397+
compile_command = config.tester_config.compile_command
398+
if compile_command:
399+
compile_command = lang.get_compile_command("main", compile_command)
399400
try:
400401
compile_main_and_judge_programs(
401402
metadata.lang,
402403
args.dir,
403404
force_compile=force_compile,
404-
compile_command=args.compile_command
405+
compile_command=compile_command
405406
)
406407
except BadStatusCodeException as e:
407408
raise e
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[codestyle]
2+
lang = 'cpp'
3+
indent_width = 3141
4+
[codestyle.cpp]
5+
indent_width = 4

0 commit comments

Comments
 (0)