Способы указания зависимостей
Ваши контейнеры (crates) могут зависеть от других библиотек с crates.io, git
репозиториев, или поддиректорий в локальной файловой системе. Вы также можете
временно перезаписать расположение зависимости, например, чтобы проверить
исправление в пакете, с которым вы работаете локально. Вы можете указать
зависимости для различных платформ, и зависимости, используемые только при
разработке. Давайте посмотрим, как этим управлять.
Указание зависимостей с crates.io
Cargo по-умолчанию настроен на поиск зависимостей по crates.io. В этом случае
требуется только название и строка, содержащая номер версии.
В руководстве по Cargo, мы указали зависимость от
контейнера time:
[dependencies]
time = "0.1.12"
Строка "0.1.12" является строкой указания семантической версии, также
известной как SemVer. Поскольку эта строка не содержит каких-либо операторов,
то эта строка интерпретируется также, как если бы мы указали строку "^0.1.12",
которую называют мажорным ограничением.
Подробнее о семантическом версионировании можно прочитать на сайте
semver.org.
Мажорные ограничения
Мажорное ограничение разрешает совместимые обновления указанной версии.
Обновление допускается, если в номере новой версии не изменяется самая левая
ненулевая цифра в группе мажорная.минорная.патч.
В этом случае, если мы запустим cargo update -p time, cargo обновит пакет
до 0.1.13, если он доступен, но не обновит до 0.2.0. Иначе, если указать
строку версии как ^1.0, cargo обновит до 1.1, но не 2.0. 0.0.x
считается несовместимым с любой другой версией.
Вот несколько примеров мажорных ограничений и версий, которые оно разрешает:
^1.2.3 := >=1.2.3 <2.0.0
^1.2 := >=1.2.0 <2.0.0
^1 := >=1.0.0 <2.0.0
^0.2.3 := >=0.2.3 <0.3.0
^0.0.3 := >=0.0.3 <0.0.4
^0.0 := >=0.0.0 <0.1.0
^0 := >=0.0.0 <1.0.0
Хотя семантическое версионирование подразумевает, что нет совместимости до
1.0.0, многие программисты относятся к релизам 0.x.y также, как к 1.x.y
релизам: то есть y увеличивается для обратно совместимых баг-фиксов, а x
увеличивается при добавлении новых возможностей. Таким образом, Cargo считает
0.x.y и 0.x.z версии совместимыми, еслиz > y.
Минорные ограничения
Минорное ограничение указывает минимальную версию с некоторой возможностью обновления. Если вы указываете мажорную версию, минорную и номер патча или только мажорную и минорную версию, то допускаются изменения только на уровне версии патча. Если вы указываете только мажорный номер версии, то разрешается изменение на уровне минорной версии и версии патча.
~1.2.3 является примером минорного ограничения.
~1.2.3 := >=1.2.3 <1.3.0
~1.2 := >=1.2.0 <1.3.0
~1 := >=1.0.0 <2.0.0
Позиционные ограничения
Позиционное ограничение разрешает любую версию в соответствии с шаблоном.
*, 1.* и 1.2.* являются примерами позиционного ограничения.
* := >=0.0.0
1.* := >=1.0.0 <2.0.0
1.2.* := >=1.2.0 <1.3.0
Ограничения неравенством
Ограничение неравенством позволяет вручную указывать допустимый диапазон версий или конкретную версию, которая требуется.
Ниже несколько примеров ограничения неравенством:
>= 1.2.0
> 1
< 2
= 1.2.3
Несколько ограничений
Несколько ограничений версии могут быть разделены через запятую,
например, >= 1.2, < 1.5.
Указание зависимостей из git репозиториев
Для указания зависимости от библиотеки, расположенной в git репозитории,
вы должны, как минимум, указать расположение репозитория с помощью ключа git:
[dependencies]
rand = { git = "https://github.com/rust-lang-nursery/rand" }
Cargo загрузит git репозиторий, а затем
будет искать Cargo.toml для запрошенного контейнера
в любом месте git репозитория (не обязательно в корне).
Поскольку мы не указали никакой другой информации, Cargo предполагает,
что мы намерены использовать последний коммит в ветке master
для сборки нашего проекта.
Вы можете комбинировать ключ git с ключами rev, tag или branch,
чтобы указать что-то ещё. Вот пример указания того, что вы хотите
использовать последний коммит из ветки next:
[dependencies]
rand = { git = "https://github.com/rust-lang-nursery/rand", branch = "next" }
Указание локальных зависимостей
Со временем наш проект hello_world из руководства значительно
вырос! Понятно, что мы, вероятно, хотим разделить контейнер на несколько других.
Для этого Cargo поддерживает пути до зависимостей, которые обычно являются
под-контейнерами, которые живут в одном репозитории. Давайте начнем с создания
нового контейнера внутри нашего проекта hello_world:
# в папке hello_world/
$ cargo new hello_utils
Это создаст новую папку hello_utils, внутри которой Cargo.toml и папка src
будут готовы для настройки. Чтобы сообщить Cargo об этом, откройте
hello_world/Cargo.toml и добавьте hello_utils в ваши зависимости:
[dependencies]
hello_utils = { path = "hello_utils" }
Это укажет Cargo, что наш проект зависит от контейнера с именем hello_utils,
который находится в папке hello_utils (относительно Cargo.toml,
в котором это указано).
Вот и всё! Следующий cargo build автоматически соберёт hello_utils и все
свои зависимости, и другие контейнеры могут использовать под-контейнеры тем же
образом. Однако, контейнеры, которые используют зависимости, указанные по
локальному пути, не допускаются на crates.io.
Если мы хотим опубликовать наш контейнер hello_world, мы должны сперва
опубликовать hello_utils на crates.io
(или указать адрес git репозитория) и указать версию в строке зависимости:
[dependencies]
hello_utils = { path = "hello_utils", version = "0.1.0" }
Переопределение зависимостей
Иногда вам может понадобиться переопределить одну из Cargo зависимостей.
Допустим, вы работаете над проектом, используя контейнер
uuid, который зависит от
rand. Вы обнаружили ошибку в rand, и она
уже исправлена, по пока не опубликована. Вы хотите проверить это исправление,
поэтому давайте сначала посмотрим, как будет выглядеть ваш Cargo.toml:
[package]
name = "my-awesome-crate"
version = "0.2.0"
authors = ["The Rust Project Developers"]
[dependencies]
uuid = "0.2"
Чтобы переопределить зависимость rand контейнера uuid, мы будем
использовать [секцию [replace]] replace-section в Cargo.toml,
добавив это в конце:
[replace]
"rand:0.3.14" = { git = 'https://github.com/rust-lang-nursery/rand' }
Это означает, что rand версии 0.3.14, которую мы сейчас используем, будет
заменена веткой master репозитория rand на GitHub. В следующий раз, когда вы
выполните cargo build, Cargo возьмёт на себя проверку этого репозитория и
сборку uuid с обновлённой версией.
Обратите внимание, что в секции [replace] переопределяемый контейнер должен
иметь не только такое же имя, но и ту же версию, что и оригинальный. Это
означает, что если в master ветке версия rand была обновлена до, скажем,
0.4.3, то вам необходимо выполнить несколько дополнительных шагов для
тестирования контейнера:
- Создайте форк оригинального репозитория
- Создайте ветку, начинающуюся с релиза версии 0.3.14 (вероятно, отмечена тегом 0.3.14)
- Найдите исправление и отправьте его в вашу ветку
- В секции
[replace]укажите ваш git репозиторий и ветку
Этот метод также может быть полезен при тестировании новых функций зависимости.
Используя этот метод вы можете использовать ветку, в которой вы будете
добавлять фичи, а затем, когда она будет готова, вы можете отправить
pull-request в основной репозиторий. Пока вы будете ожидать одобрения
pull-request, вы можете работать локально с использованием [replace], а
затем, когда pull-request будет принят и опубликован, вы можете удалить секцию
[remove] и использовать недавно опубликованную версию.
Примечание: в файле Cargo.lock будут перечислены две версии переопределённого
контейнера: один для оригинального контейнера, а другой для версии, указанной в
[replace]. С помощью cargo build -v можно проверить, что только одна версия
используется при сборке.
Переопределение локальными зависимостями
Иногда вы только временно работаете над контейнером, и вы не хотите изменять
Cargo.toml с помощью секции [replace], описанной выше. Для этого случая
Cargo предлагает ограниченную версию переопределений, называемую путевыми
переопределениями.
Как и раньше, предположим, вы работаете над проектом, использующем
uuid, который зависит от
rand. На этот раз вы тот, кто нашёл ошибку в
контейнере rand и вы хотите написать патч и проверить его, используя вашу
версию rand в контейнере uuid. Вот, как выглядит Cargo.toml
в контейнере uuid:
[package]
name = "uuid"
version = "0.2.2"
authors = ["The Rust Project Developers"]
[dependencies]
rand = { version = "0.3", optional = true }
Вы тестируете локальную копию rand, скажем, в каталоге ~/src:
$ cd ~/src
$ git clone https://github.com/rust-lang-nursery/rand
Переопределение пути передаётся в Cargo через механизм конфигурации
.cargo/config. Если Cargo обнаружит эту конфигурацию при сборке вашего пакета,
он будет использовать переопределённый на вашей локальной машине путь вместо
источника, указанного в Cargo.toml.
Cargo ищет каталог с именем .cargo вверх по иерархии каталогов вашего проекта.
Если ваш проект находится в /path/to/project/uuid, он будет искать .cargo в:
/path/to/project/uuid/path/to/project/path/to/path/
Это позволяет вам указать свои переопределения в родительском каталоге, который включает в себя пакеты, которые вы обычно используете на локальном компьютере, и использовать их во всех проектах.
Чтобы указать переопределения, создайте файл .cargo/config у некоторого предка
каталога вашего проекта (обычно его размещают в корневой директории вашего кода
или в вашем домашнем каталоге).
Поместите это внутрь файла:
paths = ["/path/to/project/rand"]
Этот массив должен заполняться каталогами, содержащими Cargo.toml.
В этом случае мы добавляем переопределение rand, поэтому
этот контейнер будет единственным, который будет переопределен.
Указанный путь должен быть абсолютным.
Однако переопределения пути более ограничены, чем секция [replace], тем более,
что они не могут изменить структуру графика зависимостей.
В заменяемом контейнере набор зависимостей должен быть точно таким же, как и в
оригинальном. Например, это означает, что переопределения пути не могут
использоваться для проверки добавления зависимостей к контейнеру,
в этой ситуации следует использовать секцию [replace].
Примечание: использование локальной конфигурации для переопределения путей работает только для контейнеров, которые были опубликованы на crates.io. Вы не можете использовать эту функцию для локальных неопубликованных контейнеров.
Более подробную информацию о локальной конфигурации можно найти в документации по конфигурации.
Платформо-специфичные зависимости
Специфичные для платформы зависимости указываются в том же формате, как и
обычные, но указаны под секцией target. Для определения этих секций
используется обычный Rust-like синтаксис #[cfg]:
[target.'cfg(windows)'.dependencies]
winhttp = "0.4.0"
[target.'cfg(unix)'.dependencies]
openssl = "1.0.1"
[target.'cfg(target_arch = "x86")'.dependencies]
native = { path = "native/i686" }
[target.'cfg(target_arch = "x86_64")'.dependencies]
native = { path = "native/x86_64" }
Как и в случае с Rust, здесь поддерживаются операторы not,any и all для
объединения различных пар имя/значение. Обратите внимание, что синтаксис cfg
доступен только с Cargo 0.9.0 (Rust 1.8.0).
В дополнение к синтаксису #[cfg] Cargo также поддерживает перечисление полной
цели, к которой будут относиться зависимости:
[target.x86_64-pc-windows-gnu.dependencies]
winhttp = "0.4.0"
[target.i686-unknown-linux-gnu.dependencies]
openssl = "1.0.1"
Если вы используете кастомную спецификацию целевой платформы, укажите полный путь и имя файла:
[target."x86_64/windows.json".dependencies]
winhttp = "0.4.0"
[target."i686/linux.json".dependencies]
openssl = "1.0.1"
native = { path = "native/i686" }
[target."x86_64/linux.json".dependencies]
openssl = "1.0.1"
native = { path = "native/x86_64" }
Зависимости для режима разработки
Вы можете добавить секцию [dev-dependencies] в свой Cargo.toml, формат
которого эквивалентен [dependencies]:
[dev-dependencies]
tempdir = "0.3"
Dev-зависимости используются не при компиляции сборки, а при компиляции тестов, примеров и бенчмарков.
Эти зависимости не распространяются на другие пакеты, которые зависят от этого пакета.
Вы также можете указать платформо-специфичные зависимости, используя
dev-dependencies вместо dependencies в секции target. Например:
[target.'cfg(unix)'.dev-dependencies]
mio = "0.0.1"
Зависимости для сборки
Вы можете объявить зависимость от других контейнеров на основе Cargo для их
использования в сценариях сборки. Зависимости объявляются через раздел
build-dependencies манифеста:
[build-dependencies]
gcc = "0.3"
Сценарий сборки не имеет доступа к зависимостям, указанным в секции
dependencies или dev-dependencies. Зависимости сборки также не будут
доступны для самого пакета, если они не указаны в разделе dependencies.
Сам пакет и сценарий его сборки собираются отдельно, поэтому их зависимости
могут не совпадать. Cargo.toml проще и чище, используя независимые зависимости
для независимых целей.
Выбор возможностей (features)
Если пакет зависит от некоторых возможностей, вы можете выбрать, какие из них использовать:
[dependencies.awesome]
version = "1.3.5"
default-features = false # не включать стандартные возможности, и, опционально,
# включить указанные возможности
features = ["secure-password", "civet"]
Больше информации о возможностях можно найти в документации по manifest.