Skip to content

Latest commit

 

History

History
635 lines (495 loc) · 20.9 KB

README-ja.md

File metadata and controls

635 lines (495 loc) · 20.9 KB

cargo-equip

CI codecov dependency status Crates.io Crates.io

English

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

更新情報

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

機能

  • 複数のクレートのバンドル
  • cargo-udepsにより使っているライブラリだけバンドル
  • 一部のクレートを除外 (--exclude-{atcoder, codingame}-crates),
  • 手続き型マクロの展開 (bin内のみ)
  • #[macro_export]のスコープを保持
  • #[cfg(..)]の解決
  • コメントおよびdocコメントの削除 (--remove)
  • minify機能 (--minify)
  • 生成物をコンパイルが通るかチェック

Sqrt Mod - Library-Cheker

[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" }
# ...
use acl_modint::ModInt;
use proconio::{fastout, input};
use tonelli_shanks::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 _;
}

cargo equip \
>       --remove docs `# doc commentを除去` \
>       --minify libs `# ライブラリをそれぞれ一行にminify` \
>       --bin sqrt_mod `# binクレートを指定` | xsel -b

Submit Info #59239 - Library-Checker

動作するクレート

インストール

nightlyツールチェインとcargo-udepsもインストールしてください。

rustup update nightly
cargo install cargo-udeps

Crates.ioから

cargo install cargo-equip

masterブランチから

cargo install cargo-equip --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::を使ってください。

    -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程度)に収めるためにできるだけ小さなクレートに分割してください。

    .
    ├── 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を加工したクレートです。

[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を挿入します。

    pub use __cargo_equip::prelude::*;
    
    // ︙
    
    pub mod __cargo_equip {
        pub mod crates {
            // ︙
        }
        // ︙
    
        pub(crate) prelude {
            pub use crate::__cargo_equip::crates::*;
        }
    }
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))
}

コードが書けたらcargo equipで展開します。 --bin {binの名前}--example {exampleの名前}、または--src {binのファイルパス}bin/exampleを指定してください。 パッケージ内のbin/exampleが一つの場合は省略できます。 ただしdefault-runには未対応です。

cargo equip --bin "$name"

#[cfg(…)]の解決

cargo-equipはデフォルトで

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

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

  • test: false
  • proc_macro: false
  • cargo_equip: true
  • feature: bin/example側から見て有効化されているもののみtrue
  • それ以外: 不明
#[allow(dead_code)]
pub mod a {
    pub struct A;

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

#[allow(dead_code)]
pub mod a {
    pub struct A;
}

出力をcargo check

cargo-equipはデフォルトでバンドルしたコードを出力する前にtarget directoryを共有した一時パッケージを作り、それの上でcargo checkします。

    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は手続き型マクロを展開する機能を持っています。

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)
}
  • proc-macroクレートは1.48.0以上のRustでコンパイルされる必要があります。 現在のツールチェインが1.48.0未満である場合、1.48.0以上のツールチェインを探してそれでコンパイルします。
  • pub use $name::*;でre-exportedされた手続き型マクロも展開することができます。

オプション

--remove <REMOVE>...

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

除去します。

#[allow(dead_code)]
pub mod a {
    //! A.

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

#[allow(dead_code)]
pub mod a {
    pub struct A;
}

--minify <MINIFY>

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

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

--no-resolve-cfgs

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

--no-rustfmt

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

--no-check

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

ライセンス

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