競技プログラミング用にRustコードを一つの.rs
ファイルにバンドルするCargoサブコマンドです。
更新情報はCHANGELOG.mdにあります。 また同一の内容がGitHubのリリースページにあります。
--exclude-{atcoder, codingame}-crates
),bin
内のみ)#[macro_export]
のスコープを保持#[cfg(..)]
の解決--remove
)--minify
)```toml [package] name = "library-checker" version = "0.0.0" edition = "2018"
[dependencies] ac-library-rs-parted-modint = { git = "https://github.com/qryxip/ac-library-rs-parted" } proconio = { version = "0.4.3", features = ["derive"] } qryxip-competitive-tonelli-shanks = { git = "https://github.com/qryxip/competitive-programming-library" }
```
```rust use aclmodint::ModInt; use proconio::{fastout, input}; use tonellishanks::ModIntBaseExt as _;
fn main() { input! { yps: [(u32, u32)], }
for (y, p) in yps {
ModInt::set_modulus(p);
if let Some(x) = ModInt::new(y).sqrt() {
println!("{}", x);
} else {
println!("-1");
}
}
}
mod sub {
// You can also use
the crate in submodules.
#[allow(unused_imports)]
use proconio::input as _;
} ```
↓
```console ❯ cargo equip \
--remove docs `# doc commentを除去` \ --minify libs `# ライブラリをそれぞれ一行にminify` \ --bin sqrt_mod `# binクレートを指定` | xsel -b
```
Submit Info #50014 - Library-Checker
nightly
ツールチェインとcargo-udepsもインストールしてください。
console
❯ rustup update nightly
console
❯ cargo install cargo-udeps
console
❯ cargo install cargo-equip
master
ブランチからconsole
❯ cargo install --git https://github.com/qryxip/cargo-equip
バイナリでの提供もしています。
cargo-equip
で展開できるライブラリには以下の制約があります。
edition
は"2018"
にする。
"2015"
はサポートしません。
lib
クレートからは手続き型マクロを利用しない。
lib
クレートからの手続き型マクロの利用は今のところサポートしていません。pub use
することは問題ありません。
#[macro_export]
しないマクロの中ではcrate
ではなく$crate
を使う。
macro_rules!
内の$crate
は$crate::extern_crate_name_in_main_crate
に置き換えられます。
macro_rules!
内のcrate
は置き換えられません。
3.以外の場合も可能な限り絶対パスを使わない。
cargo-equipはpathのcrate
はcrate::extern_crate_name_in_main_crate
に、pub(crate)
はpub(in crate::extern_crate_name_in_main_crate)
に置き換えます。
ただしこの置き換えは必ず上手くいくかどうかがわかりません。
できる限りcrate::
よりもself::
とsuper::
を使ってください。
diff
-use crate::foo::Foo;
+use super::foo::Foo;
可能な限りglob importを使わない。
cargo-equipはextern preludeや#[macro_use]
を再現するためにglob importを挿入します。
glob importを使うとこれと衝突する可能性があります。
可能な限りライブラリを小さなクレートに分割する。
cargo-equipは「クレート内のアイテムの依存関係」を調べることはしません。 AtCoder以外に参加する場合は、出力結果を制限内(たいてい64KiB程度)に収めるためにできるだけ小さなクレートに分割してください。
console
.
├── a
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
├── b
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
⋮
ライブラリが用意できたら、それらをbin
/example
側のCargo.toml
の[dependencies]
に加えてください。
コンテスト毎にツールでパッケージを自動生成しているならそれのテンプレートに加えてください。
rust-lang-ja/ac-library-rsを使いたい場合、qryxip/ac-library-rs-partedを使ってください。 本物のac-library-rsを加工したクレートです。
toml
[dependencies]
ac-library-rs-parted = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-convolution = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-dsu = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-fenwicktree = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-lazysegtree = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-math = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-maxflow = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-mincostflow = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-modint = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-scc = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-segtree = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-string = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-twosat = { git = "https://github.com/qryxip/ac-library-rs-parted" }
準備ができたらコードを書いてください。
bin
/example
側の制約は以下の2つです。
手続き型マクロを利用する場合、マクロ名が被らないようにproc-macro
クレートを選択する。
Rustのモジュールグラフを解析することは困難極まるため、手続き型マクロについてはプレフィックス抜きのマクロ名のみを頼りに展開します。
proc-macro
クレートの跡地にはダミーのアイテムを展開するため、通常のプログラミングのように手続き型マクロをuse
しても問題はありません。
もしuse
することで問題が起きるなら、#[macro_use] extern crate crate_name as _;
でインポートすることで、
これはuse crate::crate_name::__macros::*;
に置き換えられます。
cargo-equipはextern preludeや#[macro_use]
を再現するためにglob importを挿入します。
glob importを使うとこれと衝突する可能性があります。
可能な限りglob importを使わない。
ライブラリ同様にglob importを挿入します。
```rust _preludeformaincrate!();
mod sub { crate::_preludeformaincrate!(); }
macrorules! preludeformaincrate(() => (pub use crate::bundled::*;)); ```
```rust use input::input; use mic::answer; use partition_point::RangeBoundsExt as _;
fn main() -> _ { input! { a: [u64], } a.intoiter() .map(|a| (1u64..1000000000).partition_point(|ans| ans.pow(2) < a)) } ```
コードが書けたらcargo equip
で展開します。
--bin {binの名前}
か--example {exampleの名前}
、または--src {binのファイルパス}
でbin
/example
を指定してください。
パッケージ内のbin
/example
が一つの場合は省略できます。
ただしdefault-run
には未対応です。
console
❯ cargo equip --bin "$name"
コードはこのように展開されます。
extern_crate_name
がbin
/example
側から与えられていないクレートは__package_name_0_1_0
のような名前が与えられます。
``rust
//! # Bundled libraries
//!
//! -
mic 0.0.0 (path+███████████████████████████████████████████)published in https://github.com/qryxip/mic licensed under
CC0-1.0as
crate::bundled::mic
//! -
qryxip-competitive-input 0.0.0 (git+https://github.com/qryxip/competitive-programming-library#dadeb6e4685a86f25b4e5c8079f56337321aa12e)licensed under
CC0-1.0as
crate::bundled::input
//! -
qryxip-competitive-partition-point 0.0.0 (git+https://github.com/qryxip/competitive-programming-library#dadeb6e4685a86f25b4e5c8079f56337321aa12e)licensed under
CC0-1.0as
crate::_bundled::partitionpoint
//!
//! # Procedural macros
//!
//! -
mic_impl 0.0.0 (path+████████████████████████████████████████████████████)published in https://github.com/qryxip/mic licensed under
CC0-1.0`
_preludeformaincrate!();
use input::input; use mic::answer; use partition_point::RangeBoundsExt as _;
/#[answer(join("\n"))] fn main() -> _ { input! { a: [u64], } a.into_iter() .map(|a| (1u64..1_000_000_000).partition_point(|ans| ans.pow(2) < a)) }/ fn main() { #[allow(unusedimports)] use crate::bundled::mic::YouCannotRecurseIfTheOutputTypeIsInferred as main; let micans = (move || -> _ { input! {a:[u64],} a.intoiter() .map(|a| (1u64..1000000000).partitionpoint(|ans| ans.pow(2) < a)) })(); let _micans = {#[allow(unusedimports)]use/::/crate::bundled::mic::functions::*;(join("\n"))(_micans)}; ::std::println!("{}", _micans); }
// The following code was expanded by cargo-equip
.
macrorules! preludeformaincrate(() => (pub use crate::bundled::*;));
const : () = {
#[macroexport]macrorules!macrodefmicimpl000answer{($(:tt)*)=>(::std::compileerror!("answer
from mic_impl 0.0.0
should have been expanded");)}
#[macroexport]macrorules!macrodefmicimpl000solve{($(:tt))=>(::std::compile_error!("solve
from mic_impl 0.0.0
should have been expanded");)}
#[macro_export]macro_rules!macro_def_input_input_inner{/ … /}
#[macro_export]macro_rules!macro_def_input_read{/ … /}
#[macro_export]macro_rules!__macro_def_input_input{/ … */}
};
pub mod _bundled { #[allow(unused)] pub mod mic { pub mod _macros {} // ⋮ }
#[allow(unused)]
pub mod __mic_impl_0_0_0 {
pub mod __macros {
pub use crate::{
__macro_def___mic_impl_0_0_0_answer as answer,
__macro_def___mic_impl_0_0_0_solve as solve,
};
}
pub use self::__macros::*;
}
#[allow(unused)]
pub mod input {
pub mod __macros {
pub use crate::{
__macro_def_input___input_inner as __input_inner, __macro_def_input___read as __read,
__macro_def_input_input as input,
};
}
pub use self::__macros::*;
// ⋮
}
#[allow(unused)]
pub mod partition_point {
pub mod __macros {}
// ⋮
}
} ```
#[cfg(…)]
の解決cargo-equipはデフォルトで
#[cfg(恒真)]
(e.g. cfg(feature = "enabled-feature")
)のアトリビュートを消去します。#[cfg(恒偽)]
(e.g. cfg(test)
, cfg(feature = "disable-feature")
)のアトリビュートが付いたアイテムを消去します。これは次の割り当てで判定されます。
test
: false
proc_macro
: false
cargo_equip
: true
feature
: bin
/example
側から見て有効化されているもののみtrue
```rust
pub mod a { pub struct A;
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}
} ```
↓
```rust
pub mod a { pub struct A; } ```
cargo check
cargo-equipはデフォルトでバンドルしたコードを出力する前にtarget directoryを共有した一時パッケージを作り、それの上でcargo check
します。
console
Checking cargo-equip-check-output-6j2i3j3tgtugeaqm v0.1.0 (/tmp/cargo-equip-check-output-6j2i3j3tgtugeaqm)
Finished dev [unoptimized + debuginfo] target(s) in 0.11s
cargo-equipは手続き型マクロを展開する機能を持っています。
```rust use memoise::memoise; use proconio_derive::fastout;
fn main() { for i in 0..=10 { println!("{}", fib(i)); } }
fn fib(n: i64) -> i64 { if n == 0 || n == 1 { return n; } fib(n - 1) + fib(n - 2) } ```
↓
Output
``rust
//! # Procedural macros
//!
//! -
memoise 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)licensed under
BSD-3-Clause
//! -
proconio-derive 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)licensed under
MIT OR Apache-2.0`
_preludeformaincrate!();
use memoise::memoise; use proconio_derive::fastout;
/#[fastout] fn main() { for i in 0..=10 { println!("{}", fib(i)); } }/ fn main() { let proconiostdout = ::std::io::stdout(); let mut _proconiostdout = ::std::io::BufWriter::new(proconiostdout.lock()); #[allow(unusedmacros)] macrorules!print{($($tt:tt))=>{{use std::io::Write as _;::std::write!(proconio_stdout,$($tt)).unwrap();}};} #[allow(unusedmacros)] macrorules!println{($($tt:tt))=>{{use std::io::Write as _;::std::writeln!(proconio_stdout,$($tt)).unwrap();}};} let _proconiores = { for i in 0..=10 { println!("{}", fib(i)); } }; <::std::io::BufWriter<::std::io::StdoutLock> as ::std::io::Write>::flush( &mut _proconiostdout, ) .unwrap(); return _proconiores; }
/#[memoise(n <= 10)]
fn fib(n: i64) -> i64 {
if n == 0 || n == 1 {
return n;
}
fib(n - 1) + fib(n - 2)
}/
threadlocal!(static FIB:std::cell::RefCell
// The following code was expanded by cargo-equip
.
macrorules! preludeformaincrate(() => (pub use crate::bundled::*;));
const : () = {
#[macroexport]macrorules!macrodefmemoisememoise{($(:tt)*)=>(::std::compileerror!("memoise
from memoise 0.3.2
should have been expanded");)}
#[macroexport]macrorules!macrodefmemoisememoisemap{($(:tt)*)=>(::std::compileerror!("memoise_map
from memoise 0.3.2
should have been expanded");)}
#[macroexport]macrorules!macrodefproconioderivederivereadable{($(:tt))=>(::std::compile_error!("derive_readable
from proconio-derive 0.2.1
should have been expanded");)}
#[macro_export]macro_rules!macro_def_proconio_derive_fastout{($(_:tt))=>(::std::compile_error!("fastout
from proconio-derive 0.2.1
should have been expanded");)}
};
pub mod bundled { pub mod memoise { pub mod _macros { pub use crate::{ _macrodefmemoisememoise as memoise, _macrodefmemoisememoisemap as memoise_map, }; } pub use self::macros::*; }
pub mod proconio_derive {
pub mod __macros {
pub use crate::{
__macro_def_proconio_derive_derive_readable as derive_readable,
__macro_def_proconio_derive_fastout as fastout,
};
}
pub use self::__macros::*;
}
} ```
rust-analyzer(.exe)
は自動でダウンロードされます。proc-macro
クレートは1.47.0以上のRustでコンパイルされる必要があります。
現在のツールチェインが1.47.0未満である場合、1.47.0以上のツールチェインを探してそれでコンパイルします。pub use $name::*;
でre-exportedされた手続き型マクロも展開することができます。--no-resolve-cfgs
#[cfg(…)]
を解決しません。
--remove <REMOVE>...
--remove docs
でDoc comment (//! ..
, /// ..
, /** .. */
, #[doc = ".."]
)を--remove comments
でコメント (// ..
, /* .. */
)を除去します。
```rust
pub mod a { //! A.
/// A.
pub struct A; // aaaaa
} ```
↓
```rust
pub mod a { pub struct A; } ```
--minify <MINIFY>
--minify lib
で展開後のライブラリをそれぞれ一行に折り畳みます。
--minify all
でコード全体を最小化します。
ただ現段階では実装が適当なのでいくつか余計なスペースが挟まる場合があります。
--no-rustfmt
出力をRustfmtでフォーマットするのをスキップします。
--no-check
出力をcargo check
にかけるのをスキップします。
MIT or Apache-2.0のデュアルライセンスです。