cargo-equip

CI codecov dependency status Crates.io Crates.io

English

競技プログラミング用にRustコードを一つの.rsファイルにバンドルするCargoサブコマンドです。

更新情報

更新情報はCHANGELOG.mdにあります。 また同一の内容がGitHubのリリースページにあります。

機能

Sqrt Mod - Library-Cheker

```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 _;

[fastout]

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

Crates.ioから

console ❯ cargo install cargo-equip

masterブランチから

console ❯ cargo install --git https://github.com/qryxip/cargo-equip

GitHub Releases

バイナリでの提供もしています。

使い方

cargo-equipで展開できるライブラリには以下の制約があります。

  1. edition"2018"にする。

    "2015"はサポートしません。

  2. libクレートからは手続き型マクロを利用しない。

    libクレートからの手続き型マクロの利用は今のところサポートしていません。pub useすることは問題ありません。

  3. #[macro_export]しないマクロの中ではcrateではなく$crateを使う。

    macro_rules!内の$crate$crate::extern_crate_name_in_main_crateに置き換えられます。 macro_rules!内のcrateは置き換えられません。

  4. 3.以外の場合も可能な限り絶対パスを使わない。

    cargo-equipはpathのcratecrate::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;

  5. 可能な限りglob importを使わない。

    cargo-equipはextern prelude#[macro_use]を再現するためにglob importを挿入します。 glob importを使うとこれと衝突する可能性があります。

  6. 可能な限りライブラリを小さなクレートに分割する。

    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つです。

  1. 手続き型マクロを利用する場合、マクロ名が被らないように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を使うとこれと衝突する可能性があります。

  2. 可能な限りglob importを使わない。

    ライブラリ同様にglob importを挿入します。

    ```rust _preludeformaincrate!();

    mod sub { crate::_preludeformaincrate!(); }

    [macro_export]

    macrorules! preludeformaincrate(() => (pub use crate::bundled::*;)); ```

```rust use input::input; use mic::answer; use partition_point::RangeBoundsExt as _;

[answer(join("\n"))]

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_namebin/example側から与えられていないクレートは__package_name_0_1_0のような名前が与えられます。

``rust //! # Bundled libraries //! //! -mic 0.0.0 (path+███████████████████████████████████████████)published in https://github.com/qryxip/mic licensed underCC0-1.0ascrate::bundled::mic //! -qryxip-competitive-input 0.0.0 (git+https://github.com/qryxip/competitive-programming-library#dadeb6e4685a86f25b4e5c8079f56337321aa12e)licensed underCC0-1.0ascrate::bundled::input //! -qryxip-competitive-partition-point 0.0.0 (git+https://github.com/qryxip/competitive-programming-library#dadeb6e4685a86f25b4e5c8079f56337321aa12e)licensed underCC0-1.0ascrate::_bundled::partitionpoint //! //! # Procedural macros //! //! -mic_impl 0.0.0 (path+████████████████████████████████████████████████████)published in https://github.com/qryxip/mic licensed underCC0-1.0`

![allow(unused_imports)]

_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.

[macro_export]

macrorules! preludeformaincrate(() => (pub use crate::bundled::*;));

[cfg_attr(any(), rustfmt::skip)]

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{/ … */} };

[allow(unused)]

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はデフォルトで

  1. #[cfg(恒真)] (e.g. cfg(feature = "enabled-feature"))のアトリビュートを消去します。
  2. #[cfg(恒偽)] (e.g. cfg(test), cfg(feature = "disable-feature"))のアトリビュートが付いたアイテムを消去します。

これは次の割り当てで判定されます。

```rust

[allow(dead_code)]

pub mod a { pub struct A;

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}

} ```

```rust

[allow(dead_code)]

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;

[fastout]

fn main() { for i in 0..=10 { println!("{}", fib(i)); } }

[memoise(n <= 10)]

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 underBSD-3-Clause //! -proconio-derive 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)licensed underMIT OR Apache-2.0`

![allow(unused_imports)]

_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 > > =std::cell::RefCell::new(vec![None;11usize])); fn fibreset() { FIB.with(|cache| { let mut r = cache.borrowmut(); for r in r.itermut() { *r = None } }); } fn fib(n: i64) -> i64 { if let Some(ret) = FIB.with(|cache| { let mut bm = cache.borrowmut(); bm[(n) as usize].clone() }) { return ret; } let ret: i64 = (|| { if n == 0 || n == 1 { return n; } fib(n - 1) + fib(n - 2) })(); FIB.with(|cache| { let mut bm = cache.borrowmut(); bm[(n) as usize] = Some(ret.clone()); }); ret }

// The following code was expanded by cargo-equip.

[macro_export]

macrorules! preludeformaincrate(() => (pub use crate::bundled::*;));

[cfg_attr(any(), rustfmt::skip)]

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");)} };

[allow(unused)]

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::*;
}

} ```

オプション

--no-resolve-cfgs

#[cfg(…)]を解決しません。

--remove <REMOVE>...

  1. --remove docsでDoc comment (//! .., /// .., /** .. */, #[doc = ".."])を
  2. --remove commentsでコメント (// .., /* .. */)を

除去します。

```rust

[allow(dead_code)]

pub mod a { //! A.

/// A.
pub struct A; // aaaaa

} ```

```rust

[allow(dead_code)]

pub mod a { pub struct A; } ```

--minify <MINIFY>

--minify libで展開後のライブラリをそれぞれ一行に折り畳みます。 --minify allでコード全体を最小化します。

ただ現段階では実装が適当なのでいくつか余計なスペースが挟まる場合があります。

--no-rustfmt

出力をRustfmtでフォーマットするのをスキップします。

--no-check

出力をcargo checkにかけるのをスキップします。

ライセンス

MIT or Apache-2.0のデュアルライセンスです。