Skip to content

Files

Latest commit

ae19a9e · Jan 13, 2024

History

History
This branch is up to date with cargo2nix/cargo2nix:release-0.11.0.

2-bigger-project

A bigger project

This is a larger bin project which requires some dependencies from Crates.io.

Introduction

This example assumes that you have already read through the Hello World project and have your machine set up with Nix and cargo2nix. If this is not the case, it is strongly recommended that you follow that guide first.

Let's begin by creating another Cargo crate called bigger-project which depends on some crates from Crates.io, which in turn require some non-Rust system dependencies.

Generating the Cargo project

As described in the previous example, fire up a Rust bin crate project with cargo new bigger-project

Edit your src/main.rs with the following Rust code:

//! A simple HTTP client using `reqwest`.

#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let res = reqwest::get("https://hyper.rs").await?;

    println!("Status: {}", res.status());

    let body = res.text().await?;

    println!("Body:\n\n{}", body);

    Ok(())
}

Then, add the following lines to the [dependencies] table of the Cargo.toml:

reqwest = "0.10"
tokio = { version = "0.2", features = ["macros"] }

Generating a Cargo.nix

Like the previous example, we generate a Cargo.lock and Cargo.nix for our crate by running the two commands below:

cargo generate-lockfile
cargo2nix

Writing the Flake.nix

Let's create a new file called flake.nix and declare a function with the following arguments:

{
  inputs = {
    cargo2nix.url = "path:../../";
    # Use a github flake URL for real packages
    # cargo2nix.url = "github:cargo2nix/cargo2nix/release-0.11.0";
    flake-utils.follows = "cargo2nix/flake-utils";
    nixpkgs.follows = "cargo2nix/nixpkgs";
  };

  outputs = inputs: with inputs; # pass through all inputs and bring them into scope

    # Build the output set for each default system and map system sets into
    # attributes, resulting in paths such as:
    # nix build .#packages.x86_64-linux.<name>
    flake-utils.lib.eachDefaultSystem (system:

      # let-in expressions, very similar to Rust's let bindings.  These names
      # are used to express the output but not themselves paths in the output.
      let

        # create nixpkgs that contains rustBuilder from cargo2nix overlay
        pkgs = import nixpkgs {
          inherit system;
          overlays = [ cargo2nix.overlays.default ];
        };

        # create the workspace & dependencies package set
        rustPkgs = pkgs.rustBuilder.makePackageSet {
          rustVersion = "1.75.0";
          packageFun = import ./Cargo.nix;
          # packageOverrides = pkgs: pkgs.rustBuilder.overrides.all; # Implied, if not specified
        };

      in rec {
        # this is the output (recursive) set (expressed for each system)

        # the packages in `nix build .#packages.<system>.<name>`
        packages = {
          # nix build .#bigger-project
          # nix build .#packages.x86_64-linux.bigger-project
          bigger-project = (rustPkgs.workspace.bigger-project {});
          # nix build
          default = packages.bigger-project;
        };
      }
    );
}

⚠️ Remember to add the flake to version control (and Cargo.nix and flake.lock while you're at it)

git init
git add flake.nix
flake check
# generate the flake.lock
git add flake.lock
git add Cargo.nix

About Overrides

Our bigger-project crate depends on both reqwest and tokio, providing a simple asynchronous HTTP client. By default, reqwest relies on the native-tls crate for SSL support, which requires certain system dependencies depending on the host platform. Specifically:

  • On Linux and NixOS, we depend on OpenSSL (via the openssl crate).
  • On macOS, we depend on Secure Transport (via the security-framework crate).

The required system dependencies will be magically injected into the build by cargo2nix using the packageOverrides argument for makePackageSet without needing any extra work, but we will get into what that means later on.

You might have noticed that we did not specify any external dependencies to be used in our build, such as openssl or darwin.apple_sdk.frameworks.Security. This is because the cargo2nix overlay provides a collection of crate overrides for many popular Crates.io dependencies by default, tucked away in rustBuilder.overrides.all. If you're curious, you can view our existing library of provided crate overrides in overlay/overrides.nix.

rustBuilder.overrides.all is a list, so you can always add your own custom overrides by appending ++ [ myOverride1 myOverride2 ] to the end. We won't delve into how custom overrides work in this example, but you should at least be aware that the option exists.

Side note: if you'd like to submit more crate overrides for default inclusion in the next version of cargo2nix, feel free to open a pull request!

Save the flake.nix file and quit. Your cargo2nix project is ready for building!

Building

To compile the bigger-project binary with Nix, simply run:

nix build

This will create a result symlink in the current directory with the following structure:

ls -al result-bin
result-bin -> /nix/store/kkx76zvrm02hsh09kw694r5ma5f4wrjf-crate-bigger-project-0.1.0-bin

tree result-bin
result-bin
└── bin
    └── bigger-project

Running the bigger-project binary will print the following output to the screen:

$ ./result/bin/hello-world
Status: 200 OK
Body:

<!doctype html>
<html>
  <head>
# lots more HTML here...

Now that we're getting the hang of building crates with cargo2nix, let's create a project which relies on external resources located outside of the src directory.