# Dependency Management with Cargo --- ## Cargo.toml - A manifest file ```toml [package] name = "tcp-mailbox" version = "0.1.0" [dependencies] async-std = "1" # would also choose 1.5 clap = "2.2" # would also choose 2.3 ``` --- ## Cargo.lock - A lock file * contains a list of all project dependencies, de-facto versions and hashes of downloaded dependencies * when a version is *yanked* from `Crates.io` but you have the correct hash for it in a lock file Cargo will still let you download it and use it * still gives you warning about that version being problematic * should be committed to your repository for applications --- ## Dependency resolution * uses "Zero-aware" SemVer for versioning * `1.3.5` is compatible with versions `>= 1.3.5` and `< 2.0.0` * `0.3.5` is compatible with versions `>= 0.3.5` and `< 0.4.0` * `0.0.3` only allows `0.0.3` * allows version-incompatible transitive dependencies * except C/C++ dependencies * combines dependencies with compatible requirements as much as possible * allows path, git, and custom registry dependencies --- ## How a dependency version is selected * for every requirement Cargo selects acceptable version intervals * `[1.1.0; 1.6.0)`, `[1.3.5, 2.0.0)`, `[2.0.0; 3.0.0)` * Cargo checks for interval intersections to reduce the number of unique intervals * `[1.3.5; 1.6.0)`, `[2.0.0; 3.0.0)` * for every unique interval it selects the most recent available version * `=1.5.18`, `=2.7.11` * selected versions and corresponding package hashes are written into `Cargo.lock` --- ## Dependency resolution: Example ```text └── my-app May install: ├── A = "1" │ ├── X = "1" A = "1.0.17" │ └── Y = "1.3" => B = "1.5.0" └── B = "1" X = "2.0.3" ├── X = "2" X = "1.2.14" └── Y = "1.5" Y = "1.8.5" ``` --- ## Where do dependencies come from? * Crates.io * Private registries (open-source, self-hosted, or hosted) * Git and Path dependencies * dependencies can be *vendored* Notes: * private registries * hosted: **Shipyard**, JFrog, CloudSmith * self-hosted: **Kellnr** * open-source: [Ktra](https://github.com/moriturus/ktra) - pronounced `['KO-to-ra]`, [Meuse](https://github.com/mcorbin/meuse) - `[Møs]` *Shipyard and Kellnr will also generate API docs for you* --- ## Crates.io * default package registry * 100k crates and counting * **every Rust Beta release is tested against all of them every week** * packages aren't deleted, but *yanked* * if you have a correct hash for a yanked version in your `Cargo.lock` your build won't break (you still get a warning) --- ## Docs.rs * **complete API documentation for the whole Rust ecosystem** * automatically publishes API documentation for every version of every crate on Crates.io * documentation for old versions stays up, too. Easy to switch between versions. * links across crates just work --- ## Other kinds of dependencies * git dependencies * both `git+https` and `git+ssh` are allowed * can specify branch, tag, commit hash * when downloaded by Cargo exact commit hash used is written into `Cargo.lock` * path dependencies * both relative and absolute paths are allowed * common in workspaces --- ## C Libraries as dependencies * Rust can call functions from C libraries using `unsafe` code * integrate with operating system APIs, frameworks, SDKs, etc. * talk to custom hardware * reuse existing code (SQLite, OpenSSL, libgit2, etc.) * building a crate that relies on C libraries often requires customization * done using `build.rs` file --- ## `build.rs` file * compiled and executed before the rest of the package * can manipulate files, execute external programs, etc. * download / install custom SDKs * call `cc`, `cmake`, etc. to build C++ dependencies * execute `bindgen` to generate Rust bindings to C libraries * output can be used to set Cargo options dynamically ```rust ignore println!("cargo:rustc-link-lib=gizmo"); println!("cargo:rustc-link-search=native={}/gizmo/", library_path); ``` --- ## `-sys` crates * often Rust libraries that integrate with C are split into a pair of crates: * `library-name-sys` * thin wrapper around C functions * often all code is autogenerated by `bindgen` * `library-name` * depends on `library-name-sys` * exposes convenient and idiomatic Rust API to users * examples: * `openssl` and `openssl-sys` * `zstd` and `zstd-sys` * `rusqlite` and `libsqlite3-sys`