Эта глава рассказывает об основах, но вы можете найти полное описание по адресу The Cargo Book.
Зависимости
Большинство программ зависят от нескольких библиотек. Если вам приходилось когда-либо управлять зависимостями вручную, вы знаете, сколько боли это может доставить. К счастью экосистема языка Rust содержит такой инструмент как cargo! cargo может управлять зависимостями проекта.
Создание нового проекта на языке Rust:
# Исполняемый проект (проект с программой)
cargo new foo
# ИЛИ библиотека
cargo new --lib foo
Предположим, что для оставшейся части главы мы создали исполняемый проект, а не библиотеку, хотя обе концепции одинаковы.
После выполнения следующих команд вы увидите примерно следующую иерархию файлов:
foo
├── Cargo.toml
└── src
└── main.rs
main.rs - это корневой файл вашего нового проекта. Cargo.toml - это конфигурационный файл этого проекта (foo) для cargo. Если посмотрите внутрь файла, вы должны увидеть что-то вроде этого:
[package]
name = "foo"
version = "0.1.0"
authors = ["mark"]
[dependencies]
Поле name под [package] определяет имя проекта. Оно используется если Вы будете его публиковать на crates.io (более подробно позже). Также это имя выходного файла при компиляции.
Поле version - это версия пакета, записанное с использованием системы семантического версионирования.
Поле authors содержит список авторов пакета и используется при публикации.
В секции [dependencies] вы можете указывать зависимости вашего проекта.
Предположим, что вы хотите, чтобы ваша программа имела отличный CLI. Вы можете найти много хороших пакетов на crates.io (официальный реестр пакетов языка Rust). Один из популярных вариантов - clap. На момент написания этой статьи самой последней опубликованной версией clap является 2.27.1. Для добавления зависимости в ваш проект, вы можете просто добавить соответствующую запись в Ваш Cargo.toml под [dependencies]: clap = "2.27.1". И конечно, extern crate clap в main.rs. И это все! Вы можете начать использовать clap в вашей программе.
cargo также поддерживает другие типы зависимостей. Здесь только небольшие примеры:
[package]
name = "foo"
version = "0.1.0"
authors = ["mark"]
[dependencies]
clap = "2.27.1" # из crates.io
rand = { git = "https://github.com/rust-lang-nursery/rand" } # из онлайн репозитория
bar = { path = "../bar" } # из локальной файловой системы
cargo больше чем менеджер зависимостей. Все поддерживаемые возможности доступны в спецификации формата Cargo.toml.
Для сборки проекта Вы можете выполнить команду cargo build в любой директории проекта (включая поддиректории!). Также Вы можете выполнить cargo run для сборки и запуска. Обратите внимание, что эти команды разрешат все зависимости, скачают пакеты если нужно, и соберут все, включая ваш пакет. (Обратите внимание, что он собирает только то, что ещё не собрал, подобно make).
Вот и все!
Соглашения
В предыдущей главе мы видели следующую иерархию каталогов:
foo
├── Cargo.toml
└── src
└── main.rs
Предположим, что мы хотим иметь два двоичных файла в одном проекте. Что тогда?
Оказывается, cargo это поддерживает. Двоичный файл по умолчанию называется main.rs, это мы видели раньше, но вы можете добавить дополнительные файлы, поместив их в каталог bin/:
foo
├── Cargo.toml
└── src
├── main.rs
└── bin
└── my_other_bin.rs
Чтобы сказать cargo скомпилировать или запустить этот двоичный файл, мы просто передаём cargo флаг --bin my_other_bin, где my_other_bin это имя двоичного файла, с которым мы хотим работать.
Помимо дополнительных двоичных файлов, в cargo есть встроенная поддержка примеров, модульных тестов, интеграционных тестов и тестов на производительность.
В следующей главе мы более подробно рассмотрим тесты.
Тестирование
Как мы знаем, тестирование является неотъемлемой частью любого программного обеспечения! Rust имеет первоклассную поддержку модульного и интеграционного тестирования (см. главу о тестировании в TRPL).
Из разделов тестирования, приведённых выше, мы знаем, как писать модульные и интеграционные тесты. Организационно, мы можем расположить модульные тесты в модулях, которые они тестируют, а интеграционные - в собственном каталоге tests/:
foo
├── Cargo.toml
├── src
│ └── main.rs
└── tests
├── my_test.rs
└── my_other_test.rs
Каждый файл в каталоге tests - это отдельный интеграционный тест.
cargo естественно, обеспечивает простой способ запуска всех ваших тестов!
$ cargo test
Вы должны увидеть примерно такой результат:
$ cargo test
Compiling blah v0.1.0 (file:///nobackup/blah)
Finished dev [unoptimized + debuginfo] target(s) in 0.89 secs
Running target/debug/deps/blah-d3b32b97275ec472
running 3 tests
test test_bar ... ok
test test_baz ... ok
test test_foo_bar ... ok
test test_foo ... ok
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Вы также можете запустить тесты, чьё имя соответствует шаблону:
$ cargo test test_foo
$ cargo test test_foo
Compiling blah v0.1.0 (file:///nobackup/blah)
Finished dev [unoptimized + debuginfo] target(s) in 0.35 secs
Running target/debug/deps/blah-d3b32b97275ec472
running 2 tests
test test_foo ... ok
test test_foo_bar ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out
Одно слово предостережения: Cargo может выполнять несколько тестов одновременно, поэтому убедитесь, что они не участвуют в гонках друг с другом. Например, если они все выводят в файл, вы должны заставить их записывать в разные файлы.
Скрипты сборки
Иногда обычной сборки, предоставляемой cargo, недостаточно. Возможно вашему крейту нужны некоторые предварительные условия, прежде чем он успешно скомпилируется, например кодогенерация или предварительно должен скомпилироваться какой-то нативный код. Для решения этой проблемы, мы имеем скрипты сборки, которые cargo может запустить.
Для добавления скрипта сборки в ваш пакет, вы можете указать его в Cargo.toml следующим образом:
[package]
...
build = "build.rs"
Иначе по умолчанию cargo будет искать файл build.rs в директории проекта.
Как использовать скрипт сборки
Скрипт сборки - это просто другой файл на Rust, который будет скомпилирован и вызван до компиляции чего-либо другого в пакете. Следовательно он может быть использовать для выполнения предварительных условий вашего крейта.
Через переменные окружения cargo предоставляет скрипту входные параметры