tanakh / cargo-atcoder Goto Github PK
View Code? Open in Web Editor NEWCargo subcommand for AtCoder
License: BSD 3-Clause "New" or "Revised" License
Cargo subcommand for AtCoder
License: BSD 3-Clause "New" or "Revised" License
#18 で言った「多数のパッケージを一つのワークスペースで管理することでwarmupを不要にする」という話です。
Rustのワークスペースは同名のbin
を含むことは非推奨でcargo
コマンドの度にwarningが出ます。 無視するにしてもrust-analyzer等のツールが将来にわたって動いてくれるとは限りません。 ゆえにbin
のリネームが必要だと考えます。
bin
をリネームするにあたってproblem-id
とbin
名の対応をどうするかですが、ユーザーの既存の環境の互換性を考えると単純に<bin> = <contest-id>-<problem-id>
等と仮定するよりはリネームするタイミング(new
時)でしっかりとメモするのが良いと思います。 Cargo.tomlにはpackage.metadata
といううってつけのフィールドがあります。
この考えの下で以下の方法を提案したいと思います。 どうでしょうか?
cargo atcoder new
時にbin
の名前は<contest-id>-<problem-id>
と、ファイル名は従来のまま<problem-id>.rs
とする。その際package.metadata
にproblem-id
→ bin
の対応をメモする。
cargo atcoder new
時に既存のworkspace下に追加されるなら、(cargo new
する前に)そのworkspaceのmembers
に追加する。 そうでないなら、従来通り1パッケージからなるworkspaceを新たに作り、そこにprofile.release
を書く。
cargo atcoder new
以外のコマンドでは、1.でメモした表があるならそれでproblem-id
→ bin
を得る。 そうでないなら、従来通りproblem_id
= bin
とする。
# <workspace-root>/Cargo.toml
[workspace]
members = ["./abc126"]
# packageの方ではなくこちらに書く
[profile.release]
lto = true
panic = 'abort'
# <workspace-root>/abc126/Cargo.toml
[package]
name = "abc126"
version = "0.1.0"
authors = ["Ryo Yamashita <[email protected]>"]
edition = "2018"
[package.metadata.cargo-atcoder.problems]
a = { bin = "abc126-a" }
b = { bin = "abc126-b" }
c = { bin = "abc126-c" }
d = { bin = "abc126-d" }
e = { bin = "abc126-e" }
f = { bin = "abc126-f" }
[[bin]]
name = "abc126-a"
path = "./src/bin/a.rs"
[[bin]]
name = "abc126-b"
path = "./src/bin/b.rs"
[[bin]]
name = "abc126-c"
path = "./src/bin/c.rs"
[[bin]]
name = "abc126-d"
path = "./src/bin/d.rs"
[[bin]]
name = "abc126-e"
path = "./src/bin/e.rs"
[[bin]]
name = "abc126-f"
path = "./src/bin/f.rs"
# こっちではなくroot manifestに書く
#[profile.release]
#lto = true
#panic = 'abort'
[dependencies]
proconio = { version = "0.4.1", features = ["derive"] }
(edit) 互換性を保とうとする理由ですが、Codeforces等にも参加している人なんかはstd
しか使わないのでこの機能による恩恵がほぼゼロに等しく、そういう人にとっては煩わしさの方が勝ると思われるからです。 実際Rustの提出をスクレイピングしてみると、2020年環境であってもproconio
を含めクレートを使用している人はあまり多くはありません。
私が作っているcargo-competeでは
cargo compete new --open
cargo compete open
で
.rs
とテストファイルの.yml
を任意のエディタで開けるようにしています。思いの他体験が良いのでcargo-atcoderでもどうでしょうか。
ただ1.はCargoでも使われているopener
で良いとして、2.を任意のエディタに対して提供しようとすると設定がちょっと面倒ですね。
こういうのを思いついたのでcargo-competeはそうしたんですが
# Open files with the command (`jq` command)
#
# Emacs:
open = '["emacsclient", "-n"] + (.paths | map([.src, .test_suite]) | flatten)'
VSCodeがこうなってしまい後悔しています。(-a
を一緒にまとめるとrust-analyzer拡張が変なエラーを出したりして体験が悪い。VSCodeを常用していないので気づきませんでした)
# VSCode:
open = '["bash", "-c"] + ["code -a " + .manifest_dir + " && code " + (.paths | map([.src, .test_suite]) | flatten | join(" "))]'
(edit) こうしたら普通に解決しました (qryxip/cargo-compete#71)
# Open files with the command (`jq` command that outputs `string[] | string[][]`)
open = '[["code", "-a", .manifest_dir], ["code"] + (.paths | map([.src, .test_suite]) | flatten)]'
ABC078のA問題のようにテストケースにHTMLのCharacter Referenceが混ざっている場合に元の文字へ変換されていない不具合があるようです
running 3 tests
test sample 1 ... FAILED
test sample 2 ... FAILED
test sample 3 ... ok
---- sample 1 ----
input:
1 | A B
expected output:
1 | <
your output:
1 | <
---- sample 2 ----
input:
1 | E C
expected output:
1 | >
your output:
1 | >
test result: FAILED. 1 passed; 2 failed
Gitterにも書きましたが現在gen_binary_source
内のcargo build
には--quiet
が指定されています。
Lines 669 to 683 in 1e66dee
しかしTODO.mdを見ると
Line 51 in 1e66dee
とあります。 --quiet
の対象はBuilding ...
等の進捗メッセージを含みます。 この--quiet
は誤って追加されたものではないでしょうか?
面白い感じでしたので、実験させていただきました。
が、残念なことに、submitがうまく出来ません。
環境:
$ cat /etc/os-release
NAME="Ubuntu"
VERSION="19.10 (Eoan Ermine)"
ID=ubuntu
$ rustup show
Default host: x86_64-unknown-linux-gnu
rustup home: /home/mito/.rustup
installed toolchains
--------------------
stable-x86_64-unknown-linux-gnu (default)
nightly-x86_64-unknown-linux-gnu
installed targets for active toolchain
--------------------------------------
thumbv7em-none-eabihf
x86_64-unknown-linux-gnu
x86_64-unknown-linux-musl
active toolchain
----------------
stable-x86_64-unknown-linux-gnu (default)
rustc 1.43.0 (4fb7144ed 2020-04-20)
この環境で、試しに、
$cargo atcoder new abc164
を実行。
abc164/src/bin/a.rsに次のソースを入力
use proconio::{input, fastout};
#[fastout]
fn main() {
input! {
r: f64,
}
println!("{}", 2.0 * r * 3.14159265358979);
}
続いてtest
atcoder/abc163$ cargo atcoder test a
Finished dev [unoptimized + debuginfo] target(s) in 0.03s
running 2 tests
test sample 1 ... ok (abs error: 6.217e-15 , rel error: 9.895e-16 )
test sample 2 ... ok (abs error: 4.547e-13 , rel error: 9.914e-16 )
test_result: ok
これは、順調。
次に、submitすると・・・(参考データにRUST_BACKTRACEをつけておきます。つけなくても同じ結果です。)
atcoder/abc163$ RUST_BACKTRACE=1 cargo atcoder submit a
Finished dev [unoptimized + debuginfo] target(s) in 0.04s
running 2 tests
test sample 1 ... ok (abs error: 6.217e-15 , rel error: 9.895e-16 )
test sample 2 ... ok (abs error: 4.547e-13 , rel error: 9.914e-16 )
test_result: ok
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', /home/****/.cargo/git/checkouts/cargo-atcoder-22f22504501ff75c/e51f21f/src/atcoder.rs:536:24
stack backtrace:
0: backtrace::backtrace::libunwind::trace
at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.40/src/backtrace/libunwind.rs:88
1: backtrace::backtrace::trace_unsynchronized
at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.40/src/backtrace/mod.rs:66
2: std::sys_common::backtrace::_print_fmt
at src/libstd/sys_common/backtrace.rs:77
3: <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt
at src/libstd/sys_common/backtrace.rs:59
4: core::fmt::write
at src/libcore/fmt/mod.rs:1052
5: std::io::Write::write_fmt
at src/libstd/io/mod.rs:1426
6: std::sys_common::backtrace::_print
at src/libstd/sys_common/backtrace.rs:62
7: std::sys_common::backtrace::print
at src/libstd/sys_common/backtrace.rs:49
8: std::panicking::default_hook::{{closure}}
at src/libstd/panicking.rs:204
9: std::panicking::default_hook
at src/libstd/panicking.rs:224
10: std::panicking::rust_panic_with_hook
at src/libstd/panicking.rs:472
11: rust_begin_unwind
at src/libstd/panicking.rs:380
12: core::panicking::panic_fmt
at src/libcore/panicking.rs:85
13: core::panicking::panic
at src/libcore/panicking.rs:52
14: std::future::poll_with_tls_context
15: cargo_atcoder::main::{{closure}}
16: <std::future::GenFuture<T> as core::future::future::Future>::poll
17: std::thread::local::LocalKey<T>::with
18: tokio::runtime::basic_scheduler::BasicScheduler<P>::block_on
19: tokio::runtime::context::enter
20: tokio::runtime::handle::Handle::enter
21: cargo_atcoder::main
22: std::rt::lang_start::{{closure}}
23: std::rt::lang_start_internal::{{closure}}
at src/libstd/rt.rs:52
24: std::panicking::try::do_call
at src/libstd/panicking.rs:305
25: __rust_maybe_catch_panic
at src/libpanic_unwind/lib.rs:86
26: std::panicking::try
at src/libstd/panicking.rs:281
27: std::panic::catch_unwind
at src/libstd/panic.rs:394
28: std::rt::lang_start_internal
at src/libstd/rt.rs:51
29: main
30: __libc_start_main
31: _start
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
となります。
便利そうだったので、ちょっと残念。
治ると嬉しいのですが。
How to reproduce:
$ cargo atcoder new dwacon5th-prelims
$ cd dwacon5th-prelims
$ cargo atcoder submit c
Error:
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `3`,
right: `5`', /Users/xxx/.cargo/git/checkouts/cargo-atcoder-22f22504501ff75c/3c72a19/src/atcoder.rs:469:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
例えば A - 2 倍チェック / Is It a Number? でこの現象が起きます.
以下の行 で最初の 20 bytes を取ろうとしているのが原因のようです.
pb.set_prefix(&format!(
"{} | {:20} |",
DateTime::<Local>::from(result.date).format("%Y-%m-%d %H:%M:%S"),
&result.problem_name[0..min(20, result.problem_name.len())],
));
Fetching submission result...
thread 'main' panicked at 'byte index 20 is not a char boundary; it is inside 'ク' (bytes 18..21) of `A - 2 倍チェック / Is It a Number?`', src/libcore/str/mod.rs:2068:5
stack backtrace:
0: backtrace::backtrace::libunwind::trace
at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.40/src/backtrace/libunwind.rs:88
1: backtrace::backtrace::trace_unsynchronized
at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.40/src/backtrace/mod.rs:66
2: std::sys_common::backtrace::_print_fmt
at src/libstd/sys_common/backtrace.rs:77
3: <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt
at src/libstd/sys_common/backtrace.rs:61
4: core::fmt::write
at src/libcore/fmt/mod.rs:1028
5: std::io::Write::write_fmt
at src/libstd/io/mod.rs:1412
6: std::sys_common::backtrace::_print
at src/libstd/sys_common/backtrace.rs:65
7: std::sys_common::backtrace::print
at src/libstd/sys_common/backtrace.rs:50
8: std::panicking::default_hook::{{closure}}
at src/libstd/panicking.rs:188
9: std::panicking::default_hook
at src/libstd/panicking.rs:205
10: std::panicking::rust_panic_with_hook
at src/libstd/panicking.rs:464
11: std::panicking::continue_panic_fmt
at src/libstd/panicking.rs:373
12: rust_begin_unwind
at src/libstd/panicking.rs:302
13: core::panicking::panic_fmt
at src/libcore/panicking.rs:139
14: core::str::slice_error_fail
at src/libcore/str/mod.rs:0
15: core::str::traits::<impl core::slice::SliceIndex<str> for core::ops::range::Range<usize>>::index::{{closure}}
16: alloc::collections::btree::map::Entry<K,V>::or_insert_with
17: cargo_atcoder::watch_submission_status::{{closure}}::{{closure}}
18: tokio::task::core::Core<T>::poll
19: std::panicking::try::do_call
20: __rust_maybe_catch_panic
at src/libpanic_unwind/lib.rs:78
21: tokio::task::harness::Harness<T,S>::poll
22: tokio::runtime::basic_scheduler::BasicScheduler<P>::block_on
23: tokio::runtime::context::enter
24: tokio::runtime::handle::Handle::enter
25: cargo_atcoder::main
26: std::rt::lang_start::{{closure}}
27: std::rt::lang_start_internal::{{closure}}
at src/libstd/rt.rs:48
28: std::panicking::try::do_call
at src/libstd/panicking.rs:287
29: __rust_maybe_catch_panic
at src/libpanic_unwind/lib.rs:78
30: std::panicking::try
at src/libstd/panicking.rs:265
31: std::panic::catch_unwind
at src/libstd/panic.rs:396
32: std::rt::lang_start_internal
at src/libstd/rt.rs:47
33: main
34: __libc_start_main
35: _start
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
^C
[1] 29112 interrupt cargo atcoder submit a
#34 の修正の際にabc058 C問題のようにテストケースに空文字列が存在する場合にpanicするバグが混入してしまったみたいです
私は rustup でデフォルトの toolchain を stable-x86_64-unknown-linux-gnu
としているのですが、AtCoder の Rust は 1.42.0 であるため、~/.config/cargo-atcoder.toml
内で rust-toolchain
を生成するように指定しています。
[project]
# to create `rust-toolchain` file, uncomment this line.
rustc_version = "1.42.0"
先日 Rust 1.56.0 が stable としてリリースされ、 2021 edition が始まりました。これを反映させるため rustup の update を行いましたが、その影響で cargo new によって生成されるクレートは edition="2021"
の指定が入るようになりました。
以上の理由から、cargo atcoder new ...
で生成されるプロジェクトを動かすためには Cargo.toml
を手で書き換えなければなりません。
プロジェクト生成部
Line 101 in 8382265
でオプションとして --edition 2018
を指定すればこれを解決できると思うのですが、
cargo-atcoder.toml
で指定された rustc_version
から指定するべき edition を自動で認識し、オプションとして付加するrustc_version
と同様に edition
も cargo-atcoder.toml
で指定できるようにするcargo atcoder new
に対して cargo new
で使用可能なコマンドを受け付けるようにして、実行時にそのまま渡すようにするといった解決策が考えられると思います。
この点について PR を送りたいと思っているのですが、どのようにするべきかご意見いただけると幸いです。
素晴らしいツールをありがとうございます!いつも使わせていただいてます!
cargo atcoder submit a
などで回答を提出した際にfailed to parse result
とエラーが表示され、正常に動作しません
cargo atcoder submit b
Finished dev [unoptimized + debuginfo] target(s) in 0.24s
running 3 tests
test sample 1 ... ok
test sample 2 ... ok
test sample 3 ... ok
test_result: ok
Submitted to problem `abc178_b`, using language `Rust (1.42.0)`
Fetching submission result...
thread 'main' panicked at 'failed to parse result:
<tr>
<td class="no-break"><time class="fixtime fixtime-second">2020-09-19 16:49:53+0900</time></td>
<td><a href="/contests/abc178/tasks/abc178_b">B - Product Max</a></td>
<td><a href="/users/DuGlaser">DuGlaser</a> <a href="/contests/abc178/submissions?f.User=DuGlaser"><span title="view DuGlaser" submissions'="" data-toggle="tooltip" s="" class="glyphicon glyphicon-search black" aria-hidden="true"></span></a></td>
<td><a href="/contests/abc178/submissions/me?f.Language=4050">Rust (1.42.0)</a></td>
<td data-id="16840943" class="text-right submission-score">0</td>
<td class="text-right">543 Byte</td>
<td class="text-center waiting-judge" data-id="16840943" colspan="3"><span title="Waiting for Judging" aria-hidden="true" data-toggle="tooltip" class="label label-default" data-placement="top">WJ</span></td>
<td class="text-center">
<a href="/contests/abc178/submissions/16840943">Detail</a>
</td>
</tr>', /Users/DuGlaser/.cargo/registry/src/github.com-1ecc6299db9ec823/cargo-atcoder-0.2.0/src/atcoder.rs:667:17
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
RUST_BACKTRACE=1 cargo atcoder submit b
Finished dev [unoptimized + debuginfo] target(s) in 0.04s
running 3 tests
test sample 1 ... ok
test sample 2 ... ok
test sample 3 ... ok
test_result: ok
Submitted to problem `abc178_b`, using language `Rust (1.42.0)`
Fetching submission result...
thread 'main' panicked at 'failed to parse result:
<tr>
<td class="no-break"><time class="fixtime fixtime-second">2020-09-19 17:00:45+0900</time></td>
<td><a href="/contests/abc178/tasks/abc178_b">B - Product Max</a></td>
<td><a href="/users/DuGlaser">DuGlaser</a> <a href="/contests/abc178/submissions?f.User=DuGlaser"><span data-toggle="tooltip" title="view DuGlaser" s="" submissions'="" aria-hidden="true" class="glyphicon glyphicon-search black"></span></a></td>
<td><a href="/contests/abc178/submissions/me?f.Language=4050">Rust (1.42.0)</a></td>
<td class="text-right submission-score" data-id="16841177">0</td>
<td class="text-right">543 Byte</td>
<td class="text-center waiting-judge" colspan="3" data-id="16841177"><span aria-hidden="true" title="Waiting for Judging" class="label label-default" data-toggle="tooltip" data-placement="top">WJ</span></td>
<td class="text-center">
<a href="/contests/abc178/submissions/16841177">Detail</a>
</td>
</tr>', /Users/DuGlaser/.cargo/registry/src/github.com-1ecc6299db9ec823/cargo-atcoder-0.2.0/src/atcoder.rs:667:17
stack backtrace:
0: <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt
1: core::fmt::write
2: std::io::Write::write_fmt
3: std::panicking::default_hook::{{closure}}
4: std::panicking::default_hook
5: std::panicking::rust_panic_with_hook
6: rust_begin_unwind
7: std::panicking::begin_panic_fmt
8: cargo_atcoder::atcoder::AtCoder::submission_status
9: <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll
10: tokio::runtime::task::core::Core<T,S>::poll
11: <std::panic::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
12: tokio::runtime::task::raw::poll
13: std::thread::local::LocalKey<T>::with
14: tokio::runtime::basic_scheduler::BasicScheduler<P>::block_on
15: tokio::runtime::context::enter
16: tokio::runtime::handle::Handle::enter
17: cargo_atcoder::main
18: std::rt::lang_start::{{closure}}
19: std::rt::lang_start_internal
20: main
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
macOS 環境における設定ファイルのパスが
~/Library/Preferences/cargo-atcoder.toml
から
~/Library/Application Support/cargo-atcoder.toml
に変更されたようです
macOS 環境でsubmitしてもソースコードで提出されたり、crossが使われなくなったりして不思議に思ったのですが上記が原因でした。
dirsのバージョンが2.0.2から3.0.1に更新されるに伴いconfig_dir()が返す値が変更された事が原因のようです。
https://docs.rs/dirs/2.0.2/dirs/fn.config_dir.html
https://docs.rs/dirs/3.0.1/dirs/fn.config_dir.html
macOS Catalina ver 10.15.4, fish 環境にて、インストール直後にログインしようとしたところ、下記エラーが出ます。
> cargo atcoder login
Username: ***
Password: [hidden]
Login succeeded.
An error occurred while saving the session: failed to open `/Users/***/Library/Caches/cargo-atcoder/session.json`: No such file or directory (os error 2)
~/Library/Cachesを見るとcargo-atcoderが無かったので、作成してみたところログインできるようになりました。
~/Library/Caches
❯ mkdir cargo-atcoder
~/Library/Caches
❯ cargo atcoder login
Username: ***
Password: [hidden]
Login succeeded.
ファイルに保存して閲覧、編集できたほうが便利だと思うので。 方針としては以下の2つがあります。
tests/in/test1.txt
とtests/out/test1.txt
、あるいはtests/test1.in
とtests/test2.in
をペアにするような保存形式です。
oj(とそれに依存するatcoder-cli等のツール)をはじめとしたほぼすべての競プロツールがこれのはずです。
competitive-companionとかがこれです。あと私が作ったのも。
一応、大きいデータに対応するため一部をテキストファイルに替えることができます。
私のツールだとこんなのです。
---
type: batch # "batch", "interactive", or "unsubmittable"
timelimit: 2000ms # optional
match: exact # "any", "exact", or "float"
cases:
- name: Sample 1
in: |
1
2 3
test
out: |
6 test
- name: Sample 2
in: |
72
128 256
myonmyon
out: |
456 myonmyon
# "name" and "out" are optional
- in: |
1000
1000 1000
oooooooooooooo
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.