cargo-equip
A Cargo subcommand to bundle your code into one .rs
file for competitive programming.
Example
[]
= "solve"
= "0.0.0"
= "2018"
[]
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "/path/to/input" }
= { = "/path/to/output" }
= { = "/path/to/tonelli_shanks" }
# ...
// verify-helper: PROBLEM https://judge.yosupo.jp/problem/sqrt_mod
// Uncomment this line if you don't use your libraries. (`--check` still works)
//#![cfg_attr(cargo_equip, cargo_equip::skip)]
extern crate input as _;
use ModInt;
use Write as _;
use ModIntBaseExt as _;
↓
❯ cargo equip --resolve-cfgs --remove docs --minify libs --rustfmt --check -o ./bundled.rs
Running `/home/ryo/.rustup/toolchains/1.43.0-x86_64-unknown-linux-gnu/bin/cargo check --message-format json -p -p 'serde_derive:1.0.113' -p 'proc-macro2:1.0.10' -p 'serde:1.0.113' -p 'ryu:1.0.5' -p 'syn:1.0.17' -p 'ac-library-rs-parted-internal-math:0.1.0' -p 'num-traits:0.2.14' -p 'typenum:1.12.0' -p 'serde_json:1.0.59' -p 'ac-library-rs-parted-build:0.1.0' -p 'byteorder:1.3.4' -p 'ac-library-rs-parted-modint:0.1.0' -p 'anyhow:1.0.34'`
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
Running `/home/ryo/.cargo/bin/rustup run nightly cargo udeps --output json -p library-checker --bin sqrt-mod-test`
Checking library-checker v0.0.0 (/home/ryo/src/github.com/qryxip/oj-verify-playground/verification/library-checker)
Finished dev [unoptimized + debuginfo] target(s) in 0.17s
info: Loading save analysis from "/home/ryo/src/github.com/qryxip/oj-verify-playground/verification/library-checker/target/debug/deps/save-analysis/sqrt_mod_test-79cb22d236541548.json"
Bundling the code
Checking cargo-equip-check-output-54zdzn1al70b1izv v0.1.0 (/tmp/cargo-equip-check-output-54zdzn1al70b1izv)
Finished dev [unoptimized + debuginfo] target(s) in 0.39s
Submit Info #29645 - Library-Checker
Installation
Install a nightly
toolchain and cargo-udeps first.
❯ rustup update nightly
❯ cargo install --git https://github.com/est31/cargo-udeps # for est31/cargo-udeps#80
Crates.io
❯ cargo install cargo-equip
master
❯ cargo install --git https://github.com/qryxip/cargo-equip
GitHub Releases
Usage
Follow these constrants when you writing libraries to bundle.
-
Do not put items with the name names of
#[macro_export]
ed macros in each crate root.cargo-equip inserts
pub use crate::{ these_names };
just below eachmod lib_name
. Use#[macro_use]
to import macros in abin
.// in main source code extern crate input as _;
extern crate
items inbin
s are commented-out.// in main source code /*#[macro_use] extern crate input as _;*/ // `use crate::$name;` is inserted if the rename is not `_`
-
To make compatible with Rust 2015, do not resolve names of crates to bundle directly from extern prelude.
Mount them in some module except the root one with a
extern crate
item and refer them with relative paths.cargo-equip replaces
extern crate
items withuse crate::extern_crate_name_in_main_crate;
except for crates available on AtCoder or CodinGame (e.g.itertools
). Rename the libraries not to use directly.mod extern_crates { - pub(super) extern crate __another_lib as another_lib; + pub(super) use crate::another_lib; } use self::extern_crates::another_lib::foo::Foo; // Prepend `self::` to make compatible with Rust 2015
If you don't use website where Rust 2018 is unavailable (e.g. AIZU ONLINE JUDGE, yukicoder), you don't have to do this.
mod __pseudo_extern_prelude
like this is created in each library as a substitute for extern prelude. Thismod __pseudo_extern_prelude
itself is valid in Rust 2015 but unfortunately Rust 2015 cannot resolve theuse another_lib::A;
.+mod __pseudo_extern_prelude { + pub(super) use crate::{another_lib1, another_lib2}; +} +use self::__pseudo_extern_prelude::*; + use another_lib1::A; use another_lib2::B;
-
Use
$crate
instead ofcrate
in macros.cargo-equip replaces
$crate
inmacro_rules!
with$crate::extern_crate_name_in_main_crate
.crate
identifiers inmacro_rules!
are not modified. -
Do not use absolute path as possible.
cargo-equip replaces
crate
withcrate::extern_crate_name_in_main_crate
andpub(crate)
withpub(in crate::extern_crate_name_in_main_crate)
.However I cannot ensure this works well. Use
self::
andsuper::
instead ofcrate::
.-use crate::foo::Foo; +use super::foo::Foo;
-
Split into small separate crates as possible.
cargo-equip does not search "dependencies among items".
On a website other except AtCoder, Split your library into small crates to fit in 64KiB.
. ├── input │ ├── Cargo.toml │ └── src │ └── lib.rs ├── output │ ├── Cargo.toml │ └── src │ └── lib.rs ⋮
When you finish preparing your library crates, add them to [dependencies]
of the bin
.
If you generate packages automatically with a tool, add them to its template.
If you want to use rust-lang-ja/ac-library-rs, use qryxip/ac-library-rs-parted instead.
ac-library-rs-parted is a collection of 17 crates that process the real ac-library-rs in a custom-build
.
The custom-build
is written with syn 1.0.17
and proc-macro2 1.0.10
in order not to break lockfiles for AtCoder.
[]
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
= { = "https://github.com/qryxip/ac-library-rs-parted" }
The constraints for bin
s are:
- Do not import macros with
use
. Use them with#[macro_use]
or with qualified paths. - If you create
mod
s, inside them do not resolve names of crates to bundle directly from extern prelude.
// Uncomment this line if you don't use your libraries. (`--check` still works)
//#![cfg_attr(cargo_equip, cargo_equip::skip)]
extern crate input as _;
use Write as _;
Then execute cargo-equip
.
❯ cargo equip --bin "$name"
cargo-equip outputs code like this.
It gives tentative extern_crate_name
s like __package_name_0_1_0
to dependencies of the dependencies.
+//! # Bundled libraries
+//!
+//! ## `input` (private)
+//!
+//! ### `extern_crate_name`
+//!
+//! `input`
+//!
+//! ## `output` (private)
+//!
+//! ### `extern_crate_name`
+//!
+//! `output`
// Uncomment this line if you don't use your libraries. (`--check` still works)
//#![cfg_attr(cargo_equip, cargo_equip::skip)]
-#[macro_use]
-extern crate input as _;
+/*#[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),
}
}
});
}
+
+// The following code was expanded by `cargo-equip`.
+
+#[allow(dead_code)]
+mod input {
+ // ...
+}
+
+#[allow(dead_code)]
+mod output {
+ // ...
+}
cargo-equip does the following modification.
bin
- If a
#![cfg_attr(cargo_equip, cargo_equip::skip)]
was found, skips the remaining modification, doescargo check
if--check
is specified, and outputs the source code as-is. - If any, expands
mod $name;
s recursively indenting them except those containing multi-line literals. - Replaces some of the
extern crate
items. - Prepends a doc comment.
- Appends the expanded libraries.
- If a
lib
s- Expands
mod $name;
recursively. - Replaces some of the
crate
paths. - Replaces some of the
extern crate
items. - Modifies
macro_rules!
. - Inserts
mod __pseudo_extern_prelude { .. }
anduse (self::|$(super::)*)__pseudo_extern_prelude::*;
. - Removes
#[cfg(..)]
attributes or their targets if--resolve-cfg
is specified. - Removes doc comments if
--remove docs
is specified. - Removes comments if
--remove comments
is specified.
- Expands
- Whole
- Minifies the whole output f
--minify all
is specified. - Formats the output if
--rustfmt
is specified.
- Minifies the whole output f
Options
--resolve-cfgs
- Removes
#[cfg(always_true_predicate)]
(e.g.cfg(feature = "enabled-feature")
). - Removes items with
#[cfg(always_false_preducate)]
(e.g.cfg(test)
,cfg(feature = "disable-feature")
).
Predicates are evaluated according to this rule.
test
:false
proc_macro
:false
cargo_equip
:true
feature
:true
for those enabled- Otherwise: unknown
↓
--remove <REMOVE>...
Removes
- doc comments (
//! ..
,/// ..
,/** .. */
,#[doc = ".."]
) with--remove docs
. - comments (
// ..
,/* .. */
) with--remove comments
.
↓
--minify <MINIFY>
Minifies
- each expaned library with
--minify lib
. - the whole code with
--minify all
.
Not that the minification function is incomplete. Unnecessary spaces may be inserted.
--rustfmt
Formats the output with Rustfmt.
--check
Creates a temporary package that shares the current target directory and execute cargo check
before outputting.
This flag works even if bundling was skipped by #![cfg_attr(cargo_equip, cargo_equip::skip)]
.
❯ 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
License
Dual-licensed under MIT or Apache-2.0.