Paket karat dengan pustaka dan biner?

190

Saya ingin membuat paket Rust yang berisi pustaka yang dapat digunakan kembali (di mana sebagian besar program diimplementasikan), dan juga sebuah executable yang menggunakannya.

Dengan asumsi saya tidak bingung semantik dalam sistem modul Rust, seperti apa Cargo.tomlfile saya ?

Andrew Wagner
sumber

Jawaban:

205
Tok:tmp doug$ du -a

8   ./Cargo.toml
8   ./src/bin.rs
8   ./src/lib.rs
16  ./src

Cargo.toml:

[package]
name = "mything"
version = "0.0.1"
authors = ["me <[email protected]>"]

[lib]
name = "mylib"
path = "src/lib.rs"

[[bin]]
name = "mybin"
path = "src/bin.rs"

src / lib.rs:

pub fn test() {
    println!("Test");
}

src / bin.rs:

extern crate mylib; // not needed since Rust edition 2018

use mylib::test;

pub fn main() {
    test();
}
Doug
sumber
2
Terima kasih Doug, saya akan mencobanya! Apakah anotasi #! [Crate_name =] dan #! [Crate_type] opsional?
Andrew Wagner
4
Saat Anda menggunakan Cargo, opsi ini tidak perlu karena Cargo melewatinya sebagai flag compiler. Jika Anda menjalankan cargo build --verbose, Anda akan melihatnya di rustcbaris perintah.
Vladimir Matveev
33
Apakah Anda tahu mengapa [[bin]]array tabel? Mengapa menggunakan [[bin]]dan tidak [bin]? Sepertinya tidak ada dokumentasi tentang ini.
CMCDragonkai
40
@ CMCDragonkai Ini adalah spesifikasi format toml [[x]] adalah sebuah array yang pernah di-deserialisasi; yaitu. satu peti dapat menghasilkan banyak binari, tetapi hanya satu perpustakaan (dengan demikian [lib], bukan [[lib]]). Anda dapat memiliki beberapa bagian tempat sampah. (Saya setuju, ini terlihat aneh, tetapi toml selalu menjadi pilihan yang kontroversial).
Doug
1
Apakah ada cara untuk mencegahnya dari kompilasi biner ketika semua yang saya inginkan adalah lib? Biner memiliki dependensi tambahan yang saya tambahkan melalui fitur yang disebut "biner", ketika saya mencoba mengompilasinya tanpa fitur itu, ia gagal membangun. Ia mengeluh bahwa ia tidak dapat menemukan peti yang berusaha diimpor oleh bin.rs.
Person93
150

Anda juga bisa memasukkan sumber biner src/bindan sisa sumber Anda src. Anda dapat melihat contoh di proyek saya . Anda tidak perlu memodifikasi Cargo.tomlsama sekali, dan setiap file sumber akan dikompilasi ke biner dengan nama yang sama.

Konfigurasi jawaban lain kemudian diganti dengan:

$ tree
.
├── Cargo.toml
└── src
    ├── bin
    │   └── mybin.rs
    └── lib.rs

Cargo.toml

[package]
name = "example"
version = "0.0.1"
authors = ["An Devloper <[email protected]>"]

src / lib.rs

use std::error::Error;

pub fn really_complicated_code(a: u8, b: u8) -> Result<u8, Box<Error>> {
    Ok(a + b)
}

src / bin / mybin.rs

extern crate example; // Optional in Rust 2018

fn main() {
    println!("I'm using the library: {:?}", example::really_complicated_code(1, 2));
}

Dan jalankan:

$ cargo run --bin mybin
I'm using the library: Ok(3)

Selain itu, Anda bisa membuat src/main.rsyang akan digunakan sebagai executable defacto. Sayangnya, ini bertentangan dengan cargo docperintah:

Tidak dapat mendokumentasikan paket di mana perpustakaan dan biner memiliki nama yang sama. Pertimbangkan untuk mengganti nama atau menandai target sebagaidoc = false

Shepmaster
sumber
13
sangat cocok dengan pendekatan konvensi-konfigurasi-karat! keduanya menjawab bersama dan Anda memiliki kenyamanan dan fleksibilitas yang luar biasa.
domba terbang
9
extern crate example;tidak diperlukan pada karat 2018, Anda dapat langsung menulis use example::really_complicated_code;dan menggunakan fungsi tanpa menyebutkan ruang lingkup
sassman
47

Solusi alternatif adalah tidak benar-benar mencoba menjejalkan kedua hal ke dalam satu paket. Untuk proyek yang sedikit lebih besar dengan eksekusi yang ramah, saya merasa senang menggunakan ruang kerja

Kami membuat proyek biner yang menyertakan perpustakaan di dalamnya:

the-binary
├── Cargo.lock
├── Cargo.toml
├── mylibrary
│   ├── Cargo.toml
│   └── src
│       └── lib.rs
└── src
    └── main.rs

Cargo.toml

Ini menggunakan [workspace]kunci dan tergantung pada perpustakaan:

[package]
name = "the-binary"
version = "0.1.0"
authors = ["An Devloper <[email protected]>"]

[workspace]

[dependencies]
mylibrary = { path = "mylibrary" }

src / main.rs

extern crate mylibrary;

fn main() {
    println!("I'm using the library: {:?}", mylibrary::really_complicated_code(1, 2));
}

mylibrary / src / lib.rs

use std::error::Error;

pub fn really_complicated_code(a: u8, b: u8) -> Result<u8, Box<Error>> {
    Ok(a + b)
}

Dan jalankan:

$ cargo run
   Compiling mylibrary v0.1.0 (file:///private/tmp/the-binary/mylibrary)
   Compiling the-binary v0.1.0 (file:///private/tmp/the-binary)
    Finished dev [unoptimized + debuginfo] target(s) in 0.73 secs
     Running `target/debug/the-binary`
I'm using the library: Ok(3)

Ada dua manfaat besar untuk skema ini:

  1. Biner sekarang dapat menggunakan dependensi yang hanya berlaku untuknya. Misalnya, Anda dapat memasukkan banyak peti untuk meningkatkan pengalaman pengguna, seperti parser baris perintah atau pemformatan terminal. Tidak satu pun dari ini akan "menginfeksi" perpustakaan.

  2. Ruang kerja mencegah membangun berlebihan dari setiap komponen. Jika kita menjalankan cargo buildkeduanya pada direktori mylibrarydan the-binary, perpustakaan tidak akan dibangun dua kali - itu dibagi antara kedua proyek.

Shepmaster
sumber
Sepertinya ini cara yang lebih baik. Jelas sudah bertahun-tahun sejak pertanyaan itu diajukan tetapi orang masih berjuang dengan mengatur proyek-proyek besar. Apakah ada kerugian menggunakan ruang kerja versus jawaban yang dipilih di atas?
Jspies
4
@Jspies kelemahan terbesar yang bisa saya pikirkan dari atas kepala saya adalah bahwa ada beberapa alat yang tidak sepenuhnya tahu bagaimana menghadapi ruang kerja. Mereka agak aneh ketika berinteraksi dengan alat yang ada yang memiliki semacam konsep "proyek". Saya pribadi cenderung mengambil pendekatan kontinum: Saya mulai dengan segalanya main.rs, kemudian memecahnya menjadi modul-modul karena semakin besar, akhirnya terbelah src/binketika itu hanya sedikit lebih besar, kemudian pindah ke ruang kerja ketika saya mulai menggunakan kembali logika inti.
Shepmaster
terima kasih saya akan mencobanya. proyek saya saat ini memiliki beberapa lib yang dikembangkan sebagai bagian dari proyek tetapi juga digunakan secara eksternal.
Jspies
Itu membangun dan bekerja dengan baik, tetapi cargo testtampaknya mengabaikan unit test di lib.rs
Stein
3
@ Sein, saya pikir Anda ingincargo test --all
Shepmaster
18

Anda dapat menempatkan lib.rsdan main.rske folder sumber secara bersamaan. Tidak ada konflik dan kargo akan membangun keduanya.

Untuk menyelesaikan konflik documentaion, tambahkan ke Cargo.toml:

[[bin]]
name = "main"
doc = false
DenisKolodin
sumber
3
Itu akan dibahas oleh " Selain itu, Anda bisa membuat src / main.rs yang akan digunakan sebagai executable defacto ". di jawaban yang lain, bukan? Dan konflik dokumentasi diselesaikan dengan jawaban yang diterima, kan? Anda mungkin perlu mengklarifikasi jawaban Anda untuk menunjukkan mengapa ini unik. Tidak apa-apa untuk referensi jawaban lain untuk membangun di atas mereka.
Shepmaster