Пакет іржі як з бібліотекою, так і з бінарним файлом?


190

Я хотів би зробити пакет Rust, який містить бібліотеку для багаторазового використання (де реалізована більшість програми), а також виконуваний файл, який його використовує.

Якщо припустити, що я не переплутав жодної семантики в системі модуля Rust, як повинен Cargo.tomlвиглядати мій файл?

Відповіді:


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 <me@gmail.com>"]

[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();
}

2
Дякую Дугу, я спробую! Чи додаткові примітки #! [Crate_name =] та #! [Crate_type]?
Ендрю Вагнер

4
Під час використання Cargo ці параметри не потрібні, оскільки Cargo передає їх як прапори компілятора. Якщо ви запустите cargo build --verbose, ви побачите їх у rustcкомандному рядку.
Володимир Матвєєв

33
Чи знаєте ви, чому [[bin]]це масив таблиць? Навіщо використовувати [[bin]]і ні [bin]? Здається, немає жодної документації на це.
CMCDragonkai

40
@CMCDragonkai Це специфікація формату toml [[x]] - це масив, який колись деріаріалізується; тобто. один ящик може створювати кілька бінарних файлів, але лише одна бібліотека (таким чином [lib], а не [[lib]]). Ви можете мати кілька секцій для сміття. (Я згоден, це виглядає дивно, але toml завжди був суперечливим вибором).
Дуг

1
Чи є спосіб запобігти його складенню двійкового файлу, коли все, що я хочу, - це lib? Бінарний файл має додаткові залежності, які я додаю через функцію під назвою "бінарний", коли я намагаюся компілювати її без цієї функції, вона не може побудувати. Він скаржиться, що не може знайти ящики, які bin.rs намагається імпортувати.
Особа93

150

Ви також можете просто вставити бінарні джерела src/binта інші джерела src. Приклад ви можете побачити в моєму проекті . Вам взагалі не потрібно змінювати свої Cargo.tomlфайли, і кожен вихідний файл буде скомпільований у двійковий файл з тим самим іменем.

Конфігурація іншої відповіді потім замінюється на:

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

Cargo.toml

[package]
name = "example"
version = "0.0.1"
authors = ["An Devloper <an.devloper@example.com>"]

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));
}

І виконати:

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

Крім того, ви можете просто створити файл, src/main.rsякий буде використовуватися як виконуваний файл за замовчуванням. На жаль, це суперечить cargo docкоманді:

Неможливо документувати пакет, у якому бібліотека та двійковий файл мають одне ім’я. Подумайте про перейменування одного або позначення цілі якdoc = false


13
добре поєднується з підходом іржі до конфігурації! обидва відповіді разом, і ви маєте велику зручність і гнучкість.
літаючі вівці

9
extern crate example;не потрібно від іржі 2018, ви можете безпосередньо записати use example::really_complicated_code;та використовувати функцію, не називаючи область застосування
sassman

47

Альтернативне рішення - насправді не намагатися втиснути обидві речі в один пакет. Для трохи більших проектів із дружньою версією, я вважаю дуже приємним використовувати робочу область

Ми створюємо бінарний проект, який включає бібліотеку всередині нього:

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

Cargo.toml

Для цього використовується [workspace]ключ і залежить від бібліотеки:

[package]
name = "the-binary"
version = "0.1.0"
authors = ["An Devloper <an.devloper@example.com>"]

[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)
}

І виконати:

$ 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)

У цієї схеми є дві великі переваги:

  1. Бінарний файл тепер може використовувати залежності, які стосуються лише цього. Наприклад, ви можете включити безліч ящиків для поліпшення роботи користувачів, таких як парсери командного рядка або форматування терміналів. Жодне з них не "заразить" бібліотеку.

  2. Робоча область запобігає надмірному нарощуванню кожного компонента. Якщо ми працюємо cargo buildяк в каталозі, так mylibraryі в the-binaryкаталозі, бібліотека не буде побудована обидва рази - вона поділяється між обома проектами.


Це здається набагато кращим шляхом. Очевидно минуло роки з того часу, як питання було задано, але люди все ще борються з організацією великих проектів. Чи є недолік використання робочої області проти обраної відповіді вище?
Jspies

4
@Jspies Найбільший недолік, про який я можу подумати, - це те, що є деякі інструменти, які не повністю знають, як працювати з робочими просторами. Вони начебто в дивному місці під час взаємодії з існуючими інструментами, які мають якусь концепцію "проекту". Я особисто схильний до континуумного підходу: я починаю з усього main.rs, що входить, потім розбиваю його на модулі, коли він стає більшим, остаточно розбиваючись на src/binколи він трохи більший, потім переходить на робочу область, коли я починаю сильно використовувати основну логіку.
Шепмайстер

дякую, я підкажу це. у мого поточного проекту є кілька лайків, які розроблені як частина проекту, але також використовуються зовні.
Jspies

Він створює і працює добре, але, cargo testздається, ігнорує одиничні тести в lib.rs
Штейн

3
@Stein Я думаю, що ти хочешcargo test --all
Shepmaster

18

Ви можете скласти папку lib.rsта main.rsджерела разом. Конфлікту немає і вантаж будує обидві речі.

Для вирішення конфлікту документації додайте до свого Cargo.toml:

[[bin]]
name = "main"
doc = false

3
Це буде охоплено " Додатково, ви можете просто створити src / main.rs, який буде використовуватися як виконуваний файл дефакто ". в іншій відповіді, ні? А конфлікт документації вирішується прийнятою відповіддю, правда? Можливо, вам потрібно буде уточнити свою відповідь, щоб показати, чому це унікально. Добре посилатися на інші відповіді, які спираються на них.
Шепмайстер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.