cargo-equip

CI codecov dependency status Crates.io Crates.io

English

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

Sqrt Mod - Library-Cheker

```toml [package] name = "solve" version = "0.0.0" edition = "2018"

[dependencies] 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" } input = { path = "/path/to/input" } output = { path = "/path/to/output" } tonellishanks = { path = "/path/to/tonellishanks" }

...

```

``rust // Uncomment this line if you don't use your libraries. (--check` still works) //#![cfgattr(cargoequip, cargo_equip::skip)]

[macro_use]

extern crate input as _;

use aclmodint::ModInt; use std::io::Write as _; use tonellishanks::ModIntBaseExt as _;

use permutohedron as _;

fn main() { input! { yps: [(u32, u32)], }

output::buf_print(|out| {
    macro_rules! println(($($tt:tt)*) => (writeln!(out, $($tt)*).unwrap()));
    for (y, p) in yps {
        ModInt::set_modulus(p);
        if let Some(sqrt) = ModInt::new(y).sqrt() {
            println!("{}", sqrt);
        } else {
            println!("-1");
        }
    }
});

} ```

console ❯ cargo equip --resolve-cfgs --remove docs --minify libs --rustfmt --check -o ./bundled.rs Running `/home/ryo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/bin/cargo check --message-format json -p -p 'ac-library-rs-parted-build:0.1.0' -p 'ac-library-rs-parted-convolution:0.1.0' -p 'ac-library-rs-parted-dsu:0.1.0' -p 'ac-library-rs-parted-fenwicktree:0.1.0' -p 'ac-library-rs-parted-internal-bit:0.1.0' -p 'ac-library-rs-parted-internal-math:0.1.0' -p 'ac-library-rs-parted-internal-queue:0.1.0' -p 'ac-library-rs-parted-internal-scc:0.1.0' -p 'ac-library-rs-parted-internal-type-traits:0.1.0' -p 'ac-library-rs-parted-lazysegtree:0.1.0' -p 'ac-library-rs-parted-math:0.1.0' -p 'ac-library-rs-parted-maxflow:0.1.0' -p 'ac-library-rs-parted-mincostflow:0.1.0' -p 'ac-library-rs-parted-modint:0.1.0' -p 'ac-library-rs-parted-scc:0.1.0' -p 'ac-library-rs-parted-segtree:0.1.0' -p 'ac-library-rs-parted-string:0.1.0' -p 'ac-library-rs-parted-twosat:0.1.0' -p 'anyhow:1.0.34' -p 'byteorder:1.3.4' -p 'num-traits:0.2.14' -p 'proc-macro2:1.0.10' -p 'ryu:1.0.5' -p 'serde:1.0.113' -p 'serde_derive:1.0.113' -p 'serde_json:1.0.59' -p 'syn:1.0.17' -p 'typenum:1.12.0'` Finished dev [unoptimized + debuginfo] target(s) in 0.03s Running `/home/ryo/.cargo/bin/rustup run nightly cargo udeps --output json -p solve --bin solve` Checking solve v0.0.0 (/home/ryo/src/local/a/solve) Finished dev [unoptimized + debuginfo] target(s) in 0.16s info: Loading save analysis from "/home/ryo/src/local/a/solve/target/debug/deps/save-analysis/solve-2970d6e10b9c0877.json" Bundling the code Checking cargo-equip-check-output-nq4nm7zkj9vtgbd9 v0.1.0 (/tmp/cargo-equip-check-output-nq4nm7zkj9vtgbd9) Finished dev [unoptimized + debuginfo] target(s) in 0.35s

Submit Info #29083 - Library-Checker

インストール

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

console ❯ rustup update nightly

console ❯ cargo install --git https://github.com/est31/cargo-udeps # for est31/cargo-udeps#80

Crates.io

console ❯ cargo install cargo-equip

master

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

GitHub Releases

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

使い方

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

  1. libには#[macro_export]したマクロと同名なアイテムが存在しないようにする。

    cargo-equipはmod lib_name直下にpub use crate::{ それらの名前 }を挿入するため、展開後のuseで壊れます。 マクロは#[macro_use]で使ってください。

    ```rust // in main source code

    [macro_use]

    extern crate input as _; ```

    展開時にはコメントアウトされます。

    ```rust // in main source code

    /#[macro_use] extern crate input as _;/ // as _でなければuse crate::$name;が挿入される ```

  2. 共に展開する予定のクレートを使う場合、extern crateを宣言してそれを適当な場所にマウントし、そこを相対パスで参照する。

    cargo-equipはitertools等のAtCoderやCodinGameで使えるクレートを除いて、 extern crateuse crate::extern_crate_name_in_main_crate;に置き換えます。

    誤って直接使わないようにliblibの依存においては対象の名前はリネームしておくことを強く推奨します。

    rust extern crate __another_lib as another_lib;

    注意点として、バンドル後のコードが2015 editionで実行される場合(AOJ, yukicoder, ~~Library-Checker~~)、相対パスで参照するときはself::を付けてください。

    rust use self::another_lib::foo::Foo;

  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;

    またマクロ内では$crateを使ってください。 macro_rules!内の$crate$crate::extern_crate_name_in_main_crateに置き換えられます。

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

    cargo-equipは「クレート内のアイテムの依存関係」を調べることはしません。 出力結果を64KiBに収めるためにはできるだけ小さなクレートに分割してください。

    console . ├── input │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ └── lib.rs ├── output │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ └── lib.rs ⋮

ライブラリが用意できたら、それらをbin側のCargo.toml[dependencies]に加えてください。 コンテスト毎にツールでパッケージを自動生成しているならそれのテンプレートに加えてください。

ac-library-rsを使いたい場合、ac-library-rs-partedを使ってください。

toml [dependencies] 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側の制約は以下の2つです。

  1. マクロはuseしない。qualified pathで使うか#[macro_use]で使う。
  2. bin内にmodを作る場合、その中ではExtern Preludeから名前を解決しない。

``rust // Uncomment this line if you don't use your libraries. (--check` still works) //#![cfgattr(cargoequip, cargo_equip::skip)]

[macro_use]

extern crate input as _;

use std::io::Write as _;

fn main() { input! { n: usize, }

output::buf_print(|out| {
    macro_rules! println(($($tt:tt)*) => (writeln!(out, $($tt)*).unwrap()));
    for i in 1..=n {
        match i % 15 {
            0 => println!("Fizz Buzz"),
            3 | 6 | 9 | 12 => println!("Fizz"),
            5 | 10 => println!("Buzz"),
            _ => println!("{}", i),
        }
    }
});

} ```

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

console ❯ cargo equip --bin "$name"

コードはこのように展開されます。 extern_crate_namebin側から与えられていないクレートは"__internal_lib_0_1_0" + &"_".repeat(n)のような名前が与えられます。

``diff +//! # Bundled libraries +//! +//! ##input(private) +//! +//! ###externcratename +//! +//!input +//! +//! ##output(private) +//! +//! ###externcratename +//! +//!output`

// Uncomment this line if you don't use your libraries. (--check still works) //#![cfgattr(cargoequip, cargo_equip::skip)]

-#[macrouse] -extern crate input as _; +/*#[macrouse] +extern crate input as _;*/

use std::io::Write as _;

fn main() { input! { n: usize, }

 output::buf_print(|out| {
     macro_rules! println(($($tt:tt)*) => (writeln!(out, $($tt)*).unwrap()));
     for i in 1..=n {
         match i % 15 {
             0 => println!("Fizz Buzz"),
             3 | 6 | 9 | 12 => println!("Fizz"),
             5 | 10 => println!("Buzz"),
             _ => println!("{}", i),
         }
     }
 });

} + +// The following code was expanded by cargo-equip. + +#[allow(deadcode)] +mod input { + // ... +} + +#[allow(deadcode)] +mod output { + // ... +} ```

cargo-equipがやる操作は以下の通りです。

オプション

--resolve-cfgs

  1. #[cfg(常にTRUEのように見える式)] (e.g. cfg(feature = "enabled-feature"))のアトリビュートを消去します。
  2. #[cfg(常にFALSEのように見える式)] (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; } ```

--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でコード全体を最小化します。

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

--rustfmt

出力をRustfmtでフォーマットします。

--check

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

#![cfg_attr(cargo_equip, cargo_equip::skip)]でスキップした場合もチェックします。

console ❯ cargo equip --check -o /dev/null Running `/home/ryo/.cargo/bin/rustup run nightly cargo udeps --output json -p solve --bin solve` Checking solve v0.0.0 (/home/ryo/src/local/a/solve) Finished dev [unoptimized + debuginfo] target(s) in 0.13s info: Loading save analysis from "/home/ryo/src/local/a/solve/target/debug/deps/save-analysis/solve-4eea33c8603d6001.json" Bundling the code Checking cargo-equip-check-output-6j2i3j3tgtugeaqm v0.1.0 (/tmp/cargo-equip-check-output-6j2i3j3tgtugeaqm) Finished dev [unoptimized + debuginfo] target(s) in 0.11s

ライセンス

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