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


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.