kuretchi / cargo-simple-bundler

Packs only necessary modules of the crate into a single file automatically.
Apache License 2.0
5 stars 1 forks source link

Improve the interface #3

Open kuretchi opened 4 years ago

kuretchi commented 4 years ago

今までは「ライブラリクレートに対して実行し,必要なコードを抜粋する」コマンドだったのを,「(ライブラリに依存する) バイナリクレートに対して実行し,提出可能なコードを生成する」コマンドにしたい.

Before:

cargo simple-bundler --manifest-path /path/to/lib/one/Cargo.toml -e main.rs >one.rs
cargo simple-bundler --manifest-path /path/to/lib/two/Cargo.toml -e main.rs >two.rs
cat one.rs two.rs main.rs >bundled.rs

After:

cargo simple-bundler --crates one,two >bundled.rs

qryxip commented 4 years ago

私が知っていることを書いておきます。

まず依存グラフおよび依存先のパッケージの情報はcargo metadataで簡単に取れます。(この"kind": nullcargo_metadata::DependencyKind::Normalにあたります)

❯ cargo metadata --format-version 1 | jq '.resolve.root as $r | .resolve.nodes[] | select(.id == $r) | .deps[0]'
{
  "name": "anyhow",
  "pkg": "anyhow 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
  "dep_kinds": [
    {
      "kind": null,
      "target": null
    }
  ]
}
  • ライブラリ同士の依存関係を考えるか

少なくとも依存先の方はクレート全体が十分に小さいと考えて、無条件でroot moduleに依存するとみなすという手があると思います。

また展開方法ですが、cargo-equipではこのようにlib間のextern_crate_nameを解決しています。

+// このような"pseudo extern prelude"を挿入。
+mod __pseudo_extern_prelude {
+    // `extern_crate_name`を翻訳
+    pub(super) use crate::{another_lib1, another_lib2};
+}
+use self::__pseudo_extern_prelude::*;
+
 use another_lib1::A;
 use another_lib2::B;

ただこれはRust 2015ではコンパイルできない(継ぎ木が無理)ので、ライブラリを2015-compatibleにしたい場合ユーザーがマーカーとしてextern crateを書くことになります。

 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

ただバージョンが1.31+なのにエディションが2015なサイトに片っぱしから要望を出す方が早いかもしれません。私はこの前Library-CheckerにPRを出し、yukicoderにも昨日要望を出しました。

  • ライブラリのソースコードをどのように持ってくるか

cargo metadataを打った時点で、というより依存グラフが構築された時点で依存クレートはすべてローカルにダウンロードされています。

❯ cargo metadata --format-version 1 | jq '.packages[] | select(.name == "anyhow") | .manifest_path'
"/home/ryo/.cargo/registry/src/github.com-1ecc6299db9ec823/anyhow-1.0.33/Cargo.toml"

ソースコードを編集したやつを保存したいとなればどこかにcp -rする必要がありますが。(私が考えているやつ。permutohedronとかrustc-hashとかを適切なcopyright notice付きで使えないかなーと)

「このライブラリについては依存関係の解析をスキップ」ができるようにする?

関係あるかわかりませんがAtCoderで使えるクレートについては私はハードコードしてしまいました。ただpermutohedronとか今のままでも普通に展開できそうなんですよね...

kuretchi commented 4 years ago

ありがとうございます.助かります.

とりあえずの要求として考えていたのは

だったので,(すべての依存先を展開するのではなく) ユーザーが明示的に (依存先のうち) どのクレートを展開するか選択するようにしようというつもりでした.なんですが,これだとクレートを細かく分割するスタイルのライブラリに対応しようとなった場合には困ったことになりますね…

cargo metadataを打った時点で、というより依存グラフが構築された時点で依存クレートはすべてローカルにダウンロードされています。

これは知りませんでした.これなら簡単ですね.

qryxip commented 4 years ago

なんですが,これだとクレートを細かく分割するスタイルのライブラリに対応しようとなった場合には困ったことになりますね…

cargo-equip(と今online-judge-tools/verification-helperに出している途中のpr)では私がcollaboratorをやっているcargo-udepsを使って「深さ1」のクレートを絞り込んでます。

なんですがcargo-udeps(1)に加えてnightlyツールチェインが必要な上に吐くJSONの形式はドキュメント化してないんですよね。

{
  "success": false,
  "unused_deps": {
    "solve 0.0.0 (path+file:///home/ryo/src/local/play-cargo-equip/solve)": {
      "manifest_path": "/home/ryo/src/local/play-cargo-equip/solve/Cargo.toml",
      "normal": [
        "ac-library-rs-parted",
        "ac-library-rs-parted-convolution",
        "ac-library-rs-parted-dsu",
        "ac-library-rs-parted-fenwicktree",
        "ac-library-rs-parted-lazysegtree",
        "ac-library-rs-parted-math",
        "ac-library-rs-parted-maxflow",
        "ac-library-rs-parted-mincostflow",
        "ac-library-rs-parted-scc",
        "ac-library-rs-parted-segtree",
        "ac-library-rs-parted-string",
        "ac-library-rs-parted-twosat",
        "im-rc",
        "permutohedron"
      ],
      "development": [],
      "build": []
    }
  },
  "note": "Note: These dependencies might be used by other targets.\n      To find dependencies that are not used by any target, enable `--all-targets`.\nNote: They might be false-positive.\n      For example, `cargo-udeps` cannot detect usage of crates that are only used in doc-tests.\n      To ignore some dependencies, write `package.metadata.cargo-udeps.ignore` in Cargo.toml.\n"
}

JSON自体も必要最低限の情報しか詰めてないのでちょっと面倒かも。注意点としてはこのあたり?

あとパッケージ/クレートの「名前」については、このように解決されると私は理解しています。Cargoに詳しいわけではないので誤っているかもしれませんが。

qryxip commented 4 years ago

「このライブラリについては依存関係の解析をスキップ」ができるようにする?

関係あるかわかりませんがAtCoderで使えるクレートについては私はハードコードしてしまいました。ただpermutohedronとか今のままでも普通に展開できそうなんですよね...

あと一応これについてですが、明示的に--exclude-atcoder-cratesを付ける形式に先程変えてしまいました

        --exclude <SPEC>...           Exclude library crates from bundling
        --exclude-atcoder-crates      Alias for `--exclude https://github.com/rust-lang/crates.io-index#alga:0.9.3 ..`
        --exclude-codingame-crates    Alias for `--exclude https://github.com/rust-lang/crates.io-index#chrono:0.4.9 ..`

破壊的変更なのでエラーメッセージも追加した上で。

note: attempted to bundle with the following crate(s), which are available on AtCoder. to exclude them from bundling, run with `--exclude-atcoder-crates`

- `im-rc 14.3.0 (registry+https://github.com/rust-lang/crates.io-index)`
- `rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)`

「CLIオプションで指定するパッケージ」を扱うとしたら krates::PkgSpecpackage ID specificationsを取るのがいいと思います。Cargoコマンドらしくなるし。