Repository: nix-community/crate2nix Branch: master Commit: b873ca53dd64 Files: 260 Total size: 778.9 KB Directory structure: gitextract_j4pbtguo/ ├── .envrc ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ └── bug_report.md │ ├── dependabot.yml │ └── workflows/ │ ├── static-docs.yml │ ├── tests-nix-linux.yml │ ├── tests-nix-macos.yml │ └── update-flake-lock.yml ├── .gitignore ├── CHANGELOG.md ├── CLAUDE.md ├── CONTRIBUTING.md ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── RELEASE.md ├── cargo.sh ├── crate2nix/ │ ├── Cargo.json │ ├── Cargo.nix │ ├── Cargo.toml │ ├── crate-hashes.json │ ├── default-json.nix │ ├── default.nix │ ├── deny.toml │ ├── flake-module.nix │ ├── mk-crate2nix.nix │ ├── rustfmt.toml │ ├── src/ │ │ ├── command.rs │ │ ├── config.rs │ │ ├── json_output.rs │ │ ├── lib.rs │ │ ├── lock.rs │ │ ├── main.rs │ │ ├── metadata.rs │ │ ├── nix_build.rs │ │ ├── prefetch.rs │ │ ├── render.rs │ │ ├── resolve.rs │ │ ├── sources.rs │ │ ├── test.rs │ │ └── util.rs │ ├── templates/ │ │ ├── Cargo-workspace.toml.tera │ │ ├── Cargo.nix.tera │ │ ├── crate2nix-sources.nix.tera │ │ └── nix/ │ │ └── crate2nix/ │ │ ├── default.nix │ │ └── tests/ │ │ ├── default.nix │ │ ├── dependencyDerivations.nix │ │ ├── dependencyFeatures.nix │ │ ├── enableFeatures.nix │ │ ├── expandFeatures.nix │ │ ├── packageFeatures.nix │ │ ├── run.nix │ │ └── tests.nix │ └── tests/ │ └── self_build_up_to_date.rs ├── crate2nix.sh ├── default.nix ├── docs/ │ ├── .gitignore │ ├── .vscode/ │ │ ├── extensions.json │ │ └── launch.json │ ├── README.md │ ├── astro.config.mjs │ ├── flake-module.nix │ ├── package.json │ ├── src/ │ │ ├── content/ │ │ │ ├── config.ts │ │ │ └── docs/ │ │ │ ├── 10_getting_started/ │ │ │ │ ├── 10_flake_template_new.md │ │ │ │ ├── 20_installing_crate2nix.md │ │ │ │ └── 30_running_crate2nix_without_install.md │ │ │ ├── 20_generating/ │ │ │ │ ├── 10_generating.md │ │ │ │ └── 20_auto_generating.mdx │ │ │ ├── 30_building/ │ │ │ │ ├── 10_building_binaries.md │ │ │ │ ├── 20_choosing_features.md │ │ │ │ ├── 30_crateOverrides.md │ │ │ │ └── 40_tests.md │ │ │ ├── 35_toolchains/ │ │ │ │ ├── 10_custom_toolchains.md │ │ │ │ └── 20_using_a_rust_overlay.md │ │ │ ├── 40_external_sources/ │ │ │ │ ├── 10_building_fetched_sources.md │ │ │ │ └── 20_generating_for_fetched_sources.md │ │ │ ├── 50_contributing/ │ │ │ │ ├── 00_intro.md │ │ │ │ ├── 05_code.md │ │ │ │ ├── 10_docs.md │ │ │ │ └── 90_release.md │ │ │ ├── 70_design/ │ │ │ │ ├── 10_structure_and_phases.md │ │ │ │ ├── 50_tools_nix.mdx │ │ │ │ └── 90_inspiration.md │ │ │ ├── 90_reference/ │ │ │ │ ├── 10_runtime_dependencies.md │ │ │ │ ├── 20_known_restrictions.md │ │ │ │ └── 90_CHANGELOG.md │ │ │ └── index.mdx │ │ └── env.d.ts │ └── tsconfig.json ├── flake.nix ├── lib/ │ └── build-from-json.nix ├── nix/ │ ├── devshell/ │ │ └── flake-module.nix │ ├── flakeInput.nix │ ├── nix-test-runner/ │ │ ├── README.md │ │ ├── default.nix │ │ ├── package.nix │ │ └── runTest.nix │ ├── nixpkgs.nix │ ├── perSystem-tools/ │ │ └── flake-module.nix │ └── pre-commit/ │ └── flake-module.nix ├── nix-test.sh ├── nixpkgs-fmt.sh ├── out-of-tree-sources.md ├── regenerate_cargo_nix.sh ├── run_tests.sh ├── sample_projects/ │ ├── aliased-dependencies/ │ │ ├── Cargo.toml │ │ ├── crate-hashes.json │ │ ├── hello/ │ │ │ ├── Cargo.toml │ │ │ └── src/ │ │ │ └── lib.rs │ │ └── src/ │ │ └── main.rs │ ├── bin/ │ │ ├── Cargo.toml │ │ ├── crate-hashes.json │ │ └── src/ │ │ └── main.rs │ ├── bin_required_features/ │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── bin/ │ │ │ └── bin_not_to_be_compiled.rs │ │ └── main.rs │ ├── bin_with_default_features/ │ │ ├── Cargo.toml │ │ ├── crate-hashes.json │ │ ├── override-root-features.nix │ │ └── src/ │ │ └── main.rs │ ├── bin_with_dep_features/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ ├── bin_with_git_branch_dep/ │ │ ├── Cargo.toml │ │ ├── crate-hashes.json │ │ └── src/ │ │ └── main.rs │ ├── bin_with_git_submodule_dep/ │ │ ├── Cargo.nix │ │ ├── Cargo.toml │ │ ├── crate-hashes.json │ │ ├── default-with-customBuildRustCrate.nix │ │ ├── default-with-customBuildRustCrateForPkgs.nix │ │ ├── default.nix │ │ └── src/ │ │ └── main.rs │ ├── bin_with_lib_dep/ │ │ ├── Cargo.toml │ │ ├── crate-hashes.json │ │ └── src/ │ │ └── main.rs │ ├── bin_with_lib_git_dep/ │ │ ├── Cargo.toml │ │ ├── crate-hashes.json │ │ └── src/ │ │ └── main.rs │ ├── bin_with_rerenamed_lib_dep/ │ │ ├── Cargo.toml │ │ ├── crate-hashes.json │ │ └── src/ │ │ └── main.rs │ ├── cdylib/ │ │ ├── Cargo.toml │ │ ├── src/ │ │ │ └── lib.rs │ │ └── test.nix │ ├── cfg-test/ │ │ ├── .gitignore │ │ ├── Cargo.toml │ │ ├── crate-hashes.json │ │ ├── snowflake.txt │ │ ├── src/ │ │ │ ├── lib.rs │ │ │ └── main.rs │ │ ├── test.nix │ │ └── tests/ │ │ └── echo_foo_test.rs │ ├── codegen/ │ │ ├── Cargo.nix │ │ ├── Cargo.toml │ │ ├── build.rs │ │ ├── crate-hashes.json │ │ └── src/ │ │ └── main.rs │ ├── conditional_features/ │ │ ├── Cargo.toml │ │ ├── optional/ │ │ │ ├── Cargo.toml │ │ │ └── src/ │ │ │ └── lib.rs │ │ └── src/ │ │ └── main.rs │ ├── cross_compile_build_dependencies/ │ │ ├── Cargo.toml │ │ ├── alice/ │ │ │ ├── Cargo.toml │ │ │ ├── bob/ │ │ │ │ ├── Cargo.toml │ │ │ │ └── src/ │ │ │ │ └── lib.rs │ │ │ └── src/ │ │ │ └── lib.rs │ │ ├── default.nix │ │ └── src/ │ │ └── main.rs │ ├── dependency_issue_65/ │ │ ├── Cargo.toml │ │ ├── default.nix │ │ └── src/ │ │ └── main.rs │ ├── empty_cross/ │ │ ├── Cargo.toml │ │ ├── build.rs │ │ ├── default.nix │ │ └── src/ │ │ └── lib.rs │ ├── future_util_multi_version/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ ├── futures_compat/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ ├── integration_test/ │ │ ├── Cargo.json │ │ ├── Cargo.toml │ │ ├── src/ │ │ │ └── main.rs │ │ ├── test-json.nix │ │ ├── test.nix │ │ └── tests/ │ │ ├── hello │ │ └── integration_test.rs │ ├── lib/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── lib.rs │ ├── lib_and_bin/ │ │ ├── Cargo.toml │ │ ├── crate-hashes.json │ │ └── src/ │ │ ├── lib.rs │ │ └── main.rs │ ├── multiple_bin/ │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── bin1.rs │ │ ├── bin2.rs │ │ └── main.rs │ ├── nix_workspaces/ │ │ ├── .gitignore │ │ ├── Cargo.toml │ │ └── nix/ │ │ └── sources.nix │ ├── numtest/ │ │ ├── Cargo.toml │ │ ├── crate-hashes.json │ │ └── src/ │ │ └── main.rs │ ├── numtest_new_cargo_lock/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ ├── renamed_build_deps/ │ │ ├── Cargo.toml │ │ ├── build.rs │ │ └── src/ │ │ └── main.rs │ ├── renamed_dev_deps/ │ │ ├── Cargo.toml │ │ ├── src/ │ │ │ └── lib.rs │ │ ├── test.nix │ │ └── tests.rs │ ├── renaming/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ ├── simple_dep/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ ├── sub_dir_crates/ │ │ ├── Cargo.nix │ │ ├── Cargo.toml │ │ ├── crate-hashes.json │ │ └── src/ │ │ └── main.rs │ ├── test_flag_passing/ │ │ ├── Cargo.toml │ │ ├── src/ │ │ │ ├── lib.rs │ │ │ └── main.rs │ │ └── test.nix │ ├── with_problematic_crates/ │ │ ├── Cargo.toml │ │ ├── crate-hashes.json │ │ ├── default.nix │ │ └── src/ │ │ └── main.rs │ └── workspace_with_nondefault_lib/ │ ├── Cargo.toml │ └── crates/ │ ├── main/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ └── somelib/ │ ├── Cargo.toml │ └── src/ │ └── somelib.rs ├── sample_workspace/ │ ├── Cargo.toml │ ├── bin/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ ├── bin_with_cond_lib_dep/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ ├── bin_with_default_features/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ ├── bin_with_lib_dep/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ ├── crate-hashes.json │ ├── lib/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── lib.rs │ ├── lib_and_bin/ │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── lib.rs │ │ └── main.rs │ └── with_tera/ │ ├── Cargo.toml │ └── src/ │ └── main.rs ├── shell.nix ├── templates/ │ └── flake-binary/ │ ├── .envrc │ ├── .gitignore │ ├── Cargo.toml │ ├── flake.nix │ ├── nix/ │ │ ├── devshell/ │ │ │ └── flake-module.nix │ │ └── rust-overlay/ │ │ └── flake-module.nix │ ├── rust-toolchain.toml │ └── src/ │ └── main.rs ├── tests.nix └── tools.nix ================================================ FILE CONTENTS ================================================ ================================================ FILE: .envrc ================================================ #!/usr/bin/env bash # ^ make editor happy # # Use https://direnv.net/ to automatically load the dev shell. # if ! has nix_direnv_version || ! nix_direnv_version 3.0.4; then source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.4/direnvrc" "sha256-DzlYZ33mWF/Gs8DDeyjr8mnVmQGx7ASYqA5WlxwvBG4=" fi watch_file nix/** watch_file -- **/*.nix # Adding files to git includes them in a flake # But it is also a bit much reloading. # watch_file .git/index .git/HEAD use flake . --show-trace ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug Report about: Report a bug with crate2nix title: '' labels: bug assignees: '' --- ## Description A clear description of the bug. ## Steps to Reproduce 1. 2. 3. ## Expected Behavior What you expected to happen. ## Actual Behavior What actually happened. Include error messages if applicable. ## Environment - crate2nix version: - Nix version: - OS: ## Reproducible Test Case **To help us fix this issue quickly, please provide a minimal reproducible example.** The best way to do this is to submit a PR that adds a sample project to `sample_projects/`: 1. Create a minimal Cargo project that reproduces the issue 2. Add it to `sample_projects/your_project_name/` 3. Run `./regenerate_cargo_nix.sh` to generate the `Cargo.nix` 4. The test suite (`./run_tests.sh` or `nix flake check`) should demonstrate the failure See existing projects in `sample_projects/` for examples of the expected structure. If you can't submit a PR, please share: - Your `Cargo.toml` and `Cargo.lock` - Any relevant `crate2nix.json` configuration - The generated `Cargo.nix` (or the error if generation fails) ================================================ FILE: .github/dependabot.yml ================================================ # Set update schedule for GitHub Actions version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: # Check for updates to GitHub Actions every weekday interval: "daily" - package-ecosystem: "cargo" directory: "/" schedule: interval: "monthly" exclude-paths: - "sample_projects/**" - "sample_workspace/**" ================================================ FILE: .github/workflows/static-docs.yml ================================================ # Simple workflow for deploying static content to GitHub Pages name: Deploy static content to Pages on: # Runs on pushes targeting the default branch push: branches: [ "master" ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages permissions: contents: read pages: write id-token: write # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. concurrency: group: "pages" cancel-in-progress: false jobs: # Single deploy job since we're just deploying deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: cachix/install-nix-action@v31 with: nix_path: nixpkgs=channel:nixos-unstable - uses: cachix/cachix-action@v17 with: name: eigenvalue signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' - run: 'nix build .#docs' - name: Setup Pages uses: actions/configure-pages@v5 - name: Upload artifact uses: actions/upload-pages-artifact@v4 with: path: 'result' - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 ================================================ FILE: .github/workflows/tests-nix-linux.yml ================================================ name: "tests-nix-linux" on: pull_request: push: jobs: tests: strategy: matrix: platform: - ubuntu-latest runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@v6 - uses: wimpysworld/nothing-but-nix@main - uses: cachix/install-nix-action@v31 with: install_url: "https://releases.nixos.org/nix/nix-2.33.0/install" nix_path: nixpkgs=channel:nixos-unstable - uses: cachix/cachix-action@v17 with: name: eigenvalue signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' - run: "./run_tests.sh --no-cargo-build" - run: "nix --version" - run: "nix flake check -L" - run: "nix run . -- --help" ================================================ FILE: .github/workflows/tests-nix-macos.yml ================================================ name: "tests-nix-macos" on: pull_request: push: jobs: tests: strategy: matrix: platform: - macos-latest runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@v6 - uses: cachix/install-nix-action@v31 with: install_url: "https://releases.nixos.org/nix/nix-2.33.0/install" nix_path: nixpkgs=channel:nixos-unstable - uses: cachix/cachix-action@v17 with: name: eigenvalue signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' - run: "./run_tests.sh --no-cargo-build" - run: "nix --version" - run: "nix flake check -L" - run: "nix run . -- --help" ================================================ FILE: .github/workflows/update-flake-lock.yml ================================================ name: update-flake-lock on: workflow_dispatch: # allows manual triggering schedule: - cron: '35 12 * * 3' jobs: lockfile: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v6 - name: Install Nix uses: DeterminateSystems/nix-installer-action@main - name: Update flake.lock uses: DeterminateSystems/update-flake-lock@main with: pr-title: "Update flake.lock" pr-labels: | dependencies automated ================================================ FILE: .gitignore ================================================ target **/*.rs.bk .idea *.iml /result* /sample_projects/*/result crate2nix/result* /*.json *.log *~ # cachix tmp file store-path-pre-build # Devenv .devenv* devenv.local.nix # direnv .direnv # pre-commit .pre-commit-config.yaml template/flake.lock # Editor configuration .zed/settings.json .vscode/settings.json ================================================ FILE: CHANGELOG.md ================================================ # CHANGELOG See [CHANGELOG](https://nix-community.github.io/crate2nix/90_reference/90_changelog/). ================================================ FILE: CLAUDE.md ================================================ # CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview crate2nix generates Nix build files (`Cargo.nix`) for Rust/Cargo projects, enabling crate-by-crate hermetic builds with Nix. It reads `Cargo.toml`/`Cargo.lock`, resolves dependencies, prefetches hashes, and renders Nix derivations via Tera templates. ## Development Environment The project uses a Nix flake with direnv integration. Enter the dev shell via `direnv allow` or `nix develop`. All shell scripts (e.g., `cargo.sh`, `run_tests.sh`) auto-enter the pure nix-shell if not already inside. ## Common Commands ### Build ```bash nix build # Build crate2nix via Nix ./cargo.sh build # Build with cargo (inside nix-shell) ``` ### Test ```bash ./run_tests.sh # Full test suite ./run_tests.sh --no-cargo-build # Skip cargo build/test steps (Nix-only) ./cargo.sh test # Rust unit tests only ./cargo.sh test test_name # Single Rust test ./nix-test.sh ./crate2nix/templates/nix/crate2nix/tests/default.nix nix flake check # Nix integration tests ``` ### Lint & Format ```bash ./cargo.sh clippy # Rust linting ./cargo.sh fmt # Rust formatting ./nixpkgs-fmt.sh \ ./{,nix/}{,*/}*.nix \ ./crate2nix/templates/nix/crate2nix/{*.nix,tests/*.nix} \ ./sample_projects/*/[[:lower:]]*.nix # Nix formatting ``` ### Regenerate Generated Files ```bash ./regenerate_cargo_nix.sh # Regenerate all Cargo.nix files ``` ### Docs (Astro/Starlight site in `docs/`) ```bash nix build .#docs # Build static docs site cd docs && npm run dev # Local dev server cd docs && npm run build # Build via npm directly ``` ## Architecture ### Rust Source (`crate2nix/src/`) The CLI entry point is `main.rs` using `structopt`. The main command is `generate`. Core pipeline in `lib.rs` via `BuildInfo::for_config()`: 1. **metadata.rs** - Calls `cargo metadata`, merges results from multiple Cargo.toml files into `IndexedMetadata` 2. **resolve.rs** - Resolves each package into `CrateDerivation` with source type (`CratesIo`, `Git`, `Registry`, etc.), dependencies (normal/build/dev), platform conditions, and features 3. **lock.rs** - Parses `Cargo.lock` to extract checksums, avoiding unnecessary prefetches 4. **prefetch.rs** - Prefetches SHA256 hashes via `nix-prefetch-url`/`nix-prefetch-git`, caches in `crate-hashes.json` and `registry-hashes.json` 5. **render.rs** - Renders output using Tera templates 6. **config.rs** - Reads optional `crate2nix.json` for out-of-tree sources and configuration 7. **sources.rs** - Manages source fetching for crates.io, git, and alternative registries ### Nix Templates (`crate2nix/templates/`) - `Cargo.nix.tera` - Main output template; generates a Nix file that provides `rootCrate`, `workspaceMembers`, and per-crate derivations - `nix/` - Template includes for the build infrastructure (crate building, feature resolution, etc.) ### Key Nix Files (repo root) - `tools.nix` - Public Nix API; provides `generatedCargoNix` helper for generating Cargo.nix in Nix builds - `tests.nix` - Integration test harness; defines `buildTest` function and runs all `sample_projects/` - `default.nix` - Package derivation for crate2nix itself ### Sample Projects (`sample_projects/`) 30+ test projects covering various scenarios: binary/library crates, features, git dependencies, workspaces, cross-compilation, cdylib, codegen, etc. Each has a pregenerated `Cargo.nix` that gets regenerated by `regenerate_cargo_nix.sh`. ## Key Conventions - Rust edition 2021, `#![forbid(unsafe_code)]`, `#![deny(missing_docs)]` in lib.rs - Rust formatting: edition 2018 style with reordered imports (`rustfmt.toml`) - Nix formatting: `nixpkgs-fmt` - License: Apache-2.0 (Rust crate), dual Apache-2.0/MIT (repo) - Version tags use bare numbers (e.g., `0.14.2`, not `v0.14.2`) - The `Cargo.nix` files are checked into git; `run_tests.sh` verifies no uncommitted changes after regeneration ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing See [Contributing](https://nix-community.github.io/crate2nix/50_contributing/00_intro/). ================================================ FILE: LICENSE-APACHE ================================================ Apache License Version 2.0, January 2004 https://www.apache.org/licenses/LICENSE-2.0 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: LICENSE-MIT ================================================ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # crate2nix [![tests-nix-linux](https://github.com/nix-community/crate2nix/actions/workflows/tests-nix-linux.yml/badge.svg)](https://github.com/nix-community/crate2nix/actions/workflows/tests-nix-linux.yml) [![tests-nix-macos](https://github.com/nix-community/crate2nix/actions/workflows/tests-nix-macos.yml/badge.svg)](https://github.com/nix-community/crate2nix/actions/workflows/tests-nix-macos.yml) [![Crate](https://img.shields.io/crates/v/crate2nix.svg)](https://crates.io/crates/crate2nix) `crate2nix` generates [Nix](https://nixos.org/nix/) build files for [Rust](https://www.rust-lang.org/)/[Cargo](https://crates.io/) projects, building each crate individually for precise, incremental rebuilds. - **Incremental CI builds** -- only rebuild the crates that actually changed. - **Full Nix integration** -- remote builds, binary caches, Docker images, NixOS modules. - **Local dev unchanged** -- keep using `cargo` and `rust-analyzer` as usual. ## Quick start ### Without installing ```bash nix run nixpkgs#crate2nix -- generate nix build -f Cargo.nix rootCrate.build ``` ### With a flake template ```bash nix flake init --template github:nix-community/crate2nix ``` ### Installing ```bash # From nixpkgs nix profile install nixpkgs#crate2nix # Latest development version nix profile install github:nix-community/crate2nix ``` Then, inside your project: ```bash crate2nix generate # creates Cargo.nix nix build -f Cargo.nix rootCrate.build ``` ## How it works `crate2nix` reads `Cargo.toml` and `Cargo.lock`, resolves the full dependency tree via `cargo metadata`, prefetches source hashes, and renders a `Cargo.nix` file through Tera templates. The generated file contains one Nix derivation per crate, so Nix rebuilds only what changed. Two generation strategies are supported: | Strategy | Pros | Cons | | --- | --- | --- | | **Manual** (`crate2nix generate`) | No IFD, full build parallelism | Must regenerate when deps change | | **Auto** (Import From Derivation) | Always in sync with `Cargo.lock` | May reduce parallelism | ## Nix API `tools.nix` exposes helpers for use in your own Nix expressions: ```nix let crate2nix = builtins.fetchTarball "https://github.com/nix-community/crate2nix/tarball/master"; tools = import "${crate2nix}/tools.nix" { inherit pkgs; }; generated = tools.generatedCargoNix { name = "my-project"; src = ./.; }; project = pkgs.callPackage "${generated}/default.nix" {}; in project.rootCrate.build ``` Or the shorthand `appliedCargoNix` which combines generation and import. ## JSON output (experimental) `crate2nix generate --format json` emits a pre-resolved JSON file instead of `Cargo.nix`. All dependency resolution — feature expansion, `cfg()` platform filtering, optional dep activation — happens in Rust, so the Nix side is a trivial data consumer with no O(n×m) eval-time logic. ### Generating ```bash crate2nix generate --format json ``` This writes `./Cargo.json` by default (use `-o` to override). The output is compact: empty fields and already-resolved feature maps are omitted, so the JSON is typically smaller than the equivalent `Cargo.nix`. ### Consuming in Nix Use `lib/build-from-json.nix` (shipped in this repo) to turn the JSON into `buildRustCrate` derivations: ```nix let cargoNix = import ./lib/build-from-json.nix { inherit pkgs; src = ./.; resolvedJson = ./Cargo.json; }; in { # Single crate my-binary = cargoNix.workspaceMembers.my-crate.build; # Root crate (if the workspace has one) default = cargoNix.rootCrate.build; # All workspace members linked together all = cargoNix.allWorkspaceMembers; } ``` The consumer accepts two optional arguments for customisation: - `buildRustCrateForPkgs` — override the `buildRustCrate` used (e.g. for a custom toolchain) - `defaultCrateOverrides` — per-crate build fixups, same as the existing `Cargo.nix` workflow ## Documentation Full documentation is at ****, covering: - [Installation options](https://nix-community.github.io/crate2nix/10_getting_started/20_installing_crate2nix/) - [Generation strategies](https://nix-community.github.io/crate2nix/20_generating/10_generating/) - [Building binaries](https://nix-community.github.io/crate2nix/30_building/10_building_binaries/) - [Feature selection](https://nix-community.github.io/crate2nix/30_building/20_choosing_features/) - [Crate overrides](https://nix-community.github.io/crate2nix/30_building/30_crateoverrides/) - [Known restrictions](https://nix-community.github.io/crate2nix/90_reference/20_known_restrictions/) - [Changelog](https://nix-community.github.io/crate2nix/90_reference/90_changelog/) ## Contributing Contributions are welcome! See the [contributing guide](https://nix-community.github.io/crate2nix/50_contributing/) for details. ## License Apache-2.0 ================================================ FILE: RELEASE.md ================================================ # Creating a release Release checklist for crate2nix maintainers. - [ ] Update flake dependencies: `nix flake update` - [ ] `./run_tests.sh` - [ ] Verify build on Mac OS X - [ ] Verify that generated output looks nice - [ ] Verify that CHANGELOG is up-to-date - [ ] Verify that new features are documented - [ ] Bump version in `crate2nix/Cargo.toml` - [ ] `./run_tests.sh` to regenerate sources after version bump - [ ] Tag version e.g. `0.14.2` (without leading `v`) - [ ] Push - [ ] `cargo publish` - [ ] In `flake.nix`: bump input `crate2nix_stable` to the new tag - [ ] Create release from tag ================================================ FILE: cargo.sh ================================================ #!/usr/bin/env bash # Executes cargo from the pinned nixpkgs # # Example: ./cargo.sh test set -Eeuo pipefail top="$(dirname "$0")" top="$(cd "$top"; pwd)" if [ -z "${IN_CRATE2NIX_SHELL:-}" ]; then echo "=== Entering $top/shell.nix" exec nix-shell --pure "$top/shell.nix" --run "$(printf "%q " $0 "$@")" fi export TEMPLATES_DIR="$top/crate2nix/templates" cargo "$@" ================================================ FILE: crate2nix/Cargo.json ================================================ { "generator": "@generated by crate2nix", "root": "crate2nix", "workspaceMembers": { "crate2nix": "crate2nix" }, "crates": { "aho-corasick": { "crateName": "aho-corasick", "version": "1.1.3", "edition": "2021", "sha256": "05mrpkvdgp5d20y2p989f187ry9diliijgwrs254fs9s1m1x6q4f", "source": { "type": "crates-io" }, "dependencies": [ { "name": "memchr", "packageId": "memchr" } ], "resolvedDefaultFeatures": [ "default", "perf-literal", "std" ], "libCrateTypes": [ "lib" ], "authors": [ "Andrew Gallant " ] }, "ansi_term": { "crateName": "ansi_term", "version": "0.12.1", "edition": "2015", "sha256": "1ljmkbilxgmhavxvxqa7qvm6f3fjggi7q2l3a72q9x0cxjvrnanm", "source": { "type": "crates-io" }, "dependencies": [ { "name": "winapi", "packageId": "winapi", "target": "cfg(target_os = \"windows\")" } ], "libCrateTypes": [ "lib" ], "authors": [ "ogham@bsago.me", "Ryan Scheel (Havvy) ", "Josh Triplett " ] }, "anyhow": { "crateName": "anyhow", "version": "1.0.86", "edition": "2018", "sha256": "1nk301x8qhpdaks6a9zvcp7yakjqnczjmqndbg7vk4494d3d1ldk", "source": { "type": "crates-io" }, "resolvedDefaultFeatures": [ "default", "std" ], "libCrateTypes": [ "lib" ], "authors": [ "David Tolnay " ] }, "atty": { "crateName": "atty", "version": "0.2.14", "edition": "2015", "sha256": "1s7yslcs6a28c5vz7jwj63lkfgyx8mx99fdirlhi9lbhhzhrpcyr", "source": { "type": "crates-io" }, "dependencies": [ { "name": "hermit-abi", "packageId": "hermit-abi", "target": "cfg(target_os = \"hermit\")" }, { "name": "libc", "packageId": "libc", "target": "cfg(unix)" }, { "name": "winapi", "packageId": "winapi", "target": "cfg(windows)" } ], "libCrateTypes": [ "lib" ], "authors": [ "softprops " ] }, "bitflags 1.3.2": { "crateName": "bitflags", "version": "1.3.2", "edition": "2018", "sha256": "12ki6w8gn1ldq7yz9y680llwk5gmrhrzszaa17g1sbrw2r2qvwxy", "source": { "type": "crates-io" }, "resolvedDefaultFeatures": [ "default" ], "libCrateTypes": [ "lib" ], "authors": [ "The Rust Project Developers" ] }, "bitflags 2.6.0": { "crateName": "bitflags", "version": "2.6.0", "edition": "2021", "sha256": "1pkidwzn3hnxlsl8zizh0bncgbjnw7c41cx7bby26ncbzmiznj5h", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "The Rust Project Developers" ] }, "block-buffer": { "crateName": "block-buffer", "version": "0.10.4", "edition": "2018", "sha256": "0w9sa2ypmrsqqvc20nhwr75wbb5cjr4kkyhpjm1z1lv2kdicfy1h", "source": { "type": "crates-io" }, "dependencies": [ { "name": "generic-array", "packageId": "generic-array" } ], "libCrateTypes": [ "lib" ], "authors": [ "RustCrypto Developers" ] }, "bstr": { "crateName": "bstr", "version": "1.9.1", "edition": "2021", "sha256": "01ipr5rncw3kf4dyc1p2g00njn1df2b0xpviwhb8830iv77wbvq5", "source": { "type": "crates-io" }, "dependencies": [ { "name": "memchr", "packageId": "memchr" } ], "resolvedDefaultFeatures": [ "alloc", "std" ], "libCrateTypes": [ "lib" ], "authors": [ "Andrew Gallant " ] }, "camino": { "crateName": "camino", "version": "1.1.7", "edition": "2018", "sha256": "0ff28kc3qjcrmi8k88b2j2p7mzrvbag20yqcrj9sl30n3fanpv70", "source": { "type": "crates-io" }, "dependencies": [ { "name": "serde", "packageId": "serde" } ], "resolvedDefaultFeatures": [ "serde", "serde1" ], "libCrateTypes": [ "lib" ], "authors": [ "Without Boats ", "Ashley Williams ", "Steve Klabnik ", "Rain " ] }, "cargo-platform": { "crateName": "cargo-platform", "version": "0.1.8", "edition": "2021", "sha256": "1z5b7ivbj508wkqdg2vb0hw4vi1k1pyhcn6h1h1b8svcb8vg1c94", "source": { "type": "crates-io" }, "dependencies": [ { "name": "serde", "packageId": "serde" } ], "libCrateTypes": [ "lib" ] }, "cargo_metadata": { "crateName": "cargo_metadata", "version": "0.18.1", "edition": "2018", "sha256": "0drh0zndl4qgndy6kg6783cydbvhxgv0hcg7d9hhqx0zwi3nb21d", "source": { "type": "crates-io" }, "dependencies": [ { "name": "camino", "packageId": "camino" }, { "name": "cargo-platform", "packageId": "cargo-platform" }, { "name": "semver", "packageId": "semver" }, { "name": "serde", "packageId": "serde" }, { "name": "serde_json", "packageId": "serde_json" }, { "name": "thiserror", "packageId": "thiserror" } ], "resolvedDefaultFeatures": [ "default" ], "libCrateTypes": [ "lib" ], "authors": [ "Oliver Schneider " ] }, "cfg-if": { "crateName": "cfg-if", "version": "1.0.0", "edition": "2018", "sha256": "1za0vb97n4brpzpv8lsbnzmq5r8f2b0cpqqr0sy8h5bn751xxwds", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "Alex Crichton " ] }, "clap": { "crateName": "clap", "version": "2.34.0", "edition": "2018", "sha256": "071q5d8jfwbazi6zhik9xwpacx5i6kb2vkzy060vhf0c3120aqd0", "source": { "type": "crates-io" }, "dependencies": [ { "name": "ansi_term", "packageId": "ansi_term", "target": "cfg(not(windows))" }, { "name": "atty", "packageId": "atty" }, { "name": "bitflags", "packageId": "bitflags 1.3.2" }, { "name": "strsim", "packageId": "strsim" }, { "name": "textwrap", "packageId": "textwrap" }, { "name": "unicode-width", "packageId": "unicode-width" }, { "name": "vec_map", "packageId": "vec_map" } ], "resolvedDefaultFeatures": [ "ansi_term", "atty", "color", "default", "strsim", "suggestions", "vec_map" ], "libCrateTypes": [ "lib" ], "authors": [ "Kevin K. " ] }, "colored-diff": { "crateName": "colored-diff", "version": "0.2.3", "edition": "2015", "sha256": "1dfwjxd13f8l8bdzm76kkp6cp4sr1pyc8lavp52avwy313mhh0j1", "source": { "type": "crates-io" }, "dependencies": [ { "name": "ansi_term", "packageId": "ansi_term" }, { "name": "dissimilar", "packageId": "dissimilar" }, { "name": "itertools", "packageId": "itertools 0.10.5" } ], "libCrateTypes": [ "lib" ] }, "cpufeatures": { "crateName": "cpufeatures", "version": "0.2.12", "edition": "2018", "sha256": "012m7rrak4girqlii3jnqwrr73gv1i980q4wra5yyyhvzwk5xzjk", "source": { "type": "crates-io" }, "dependencies": [ { "name": "libc", "packageId": "libc", "target": "aarch64-linux-android" }, { "name": "libc", "packageId": "libc", "target": "cfg(all(target_arch = \"aarch64\", target_os = \"linux\"))" }, { "name": "libc", "packageId": "libc", "target": "cfg(all(target_arch = \"aarch64\", target_vendor = \"apple\"))" }, { "name": "libc", "packageId": "libc", "target": "cfg(all(target_arch = \"loongarch64\", target_os = \"linux\"))" } ], "libCrateTypes": [ "lib" ], "authors": [ "RustCrypto Developers" ] }, "crate2nix": { "crateName": "crate2nix", "version": "0.15.0", "edition": "2021", "source": { "type": "local", "path": "." }, "dependencies": [ { "name": "anyhow", "packageId": "anyhow" }, { "name": "cargo-platform", "packageId": "cargo-platform" }, { "name": "cargo_metadata", "packageId": "cargo_metadata" }, { "name": "hex", "packageId": "hex" }, { "name": "itertools", "packageId": "itertools 0.12.1" }, { "name": "lazy_static", "packageId": "lazy_static" }, { "name": "nix-base32", "packageId": "nix-base32" }, { "name": "pathdiff", "packageId": "pathdiff" }, { "name": "semver", "packageId": "semver" }, { "name": "serde", "packageId": "serde" }, { "name": "serde_json", "packageId": "serde_json" }, { "name": "structopt", "packageId": "structopt" }, { "name": "tera", "packageId": "tera" }, { "name": "toml", "packageId": "toml" }, { "name": "url", "packageId": "url" } ], "devDependencies": [ { "name": "colored-diff", "packageId": "colored-diff" }, { "name": "fs_extra", "packageId": "fs_extra" }, { "name": "tempdir", "packageId": "tempdir" } ], "crateBin": [ { "name": "crate2nix", "path": "src/main.rs" } ], "libCrateTypes": [ "lib" ], "authors": [ "Peter Kolloch " ] }, "crossbeam-deque": { "crateName": "crossbeam-deque", "version": "0.8.5", "edition": "2021", "sha256": "03bp38ljx4wj6vvy4fbhx41q8f585zyqix6pncz1mkz93z08qgv1", "source": { "type": "crates-io" }, "dependencies": [ { "name": "crossbeam-epoch", "packageId": "crossbeam-epoch" }, { "name": "crossbeam-utils", "packageId": "crossbeam-utils" } ], "resolvedDefaultFeatures": [ "default", "std" ], "libCrateTypes": [ "lib" ] }, "crossbeam-epoch": { "crateName": "crossbeam-epoch", "version": "0.9.18", "edition": "2021", "sha256": "03j2np8llwf376m3fxqx859mgp9f83hj1w34153c7a9c7i5ar0jv", "source": { "type": "crates-io" }, "dependencies": [ { "name": "crossbeam-utils", "packageId": "crossbeam-utils" } ], "resolvedDefaultFeatures": [ "alloc", "std" ], "libCrateTypes": [ "lib" ] }, "crossbeam-utils": { "crateName": "crossbeam-utils", "version": "0.8.20", "edition": "2021", "sha256": "100fksq5mm1n7zj242cclkw6yf7a4a8ix3lvpfkhxvdhbda9kv12", "source": { "type": "crates-io" }, "resolvedDefaultFeatures": [ "std" ], "libCrateTypes": [ "lib" ] }, "crypto-common": { "crateName": "crypto-common", "version": "0.1.6", "edition": "2018", "sha256": "1cvby95a6xg7kxdz5ln3rl9xh66nz66w46mm3g56ri1z5x815yqv", "source": { "type": "crates-io" }, "dependencies": [ { "name": "generic-array", "packageId": "generic-array" }, { "name": "typenum", "packageId": "typenum" } ], "libCrateTypes": [ "lib" ], "authors": [ "RustCrypto Developers" ] }, "digest": { "crateName": "digest", "version": "0.10.7", "edition": "2018", "sha256": "14p2n6ih29x81akj097lvz7wi9b6b9hvls0lwrv7b6xwyy0s5ncy", "source": { "type": "crates-io" }, "dependencies": [ { "name": "block-buffer", "packageId": "block-buffer" }, { "name": "crypto-common", "packageId": "crypto-common" } ], "resolvedDefaultFeatures": [ "block-buffer", "core-api", "default" ], "libCrateTypes": [ "lib" ], "authors": [ "RustCrypto Developers" ] }, "dissimilar": { "crateName": "dissimilar", "version": "1.0.9", "edition": "2018", "sha256": "0bcn4s99ghigd3yadpd7i3gljv5z2hkr07ijvvxvsxmz3yfygy2r", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "David Tolnay " ] }, "either": { "crateName": "either", "version": "1.13.0", "edition": "2018", "sha256": "1w2c1mybrd7vljyxk77y9f4w9dyjrmp3yp82mk7bcm8848fazcb0", "source": { "type": "crates-io" }, "resolvedDefaultFeatures": [ "use_std" ], "libCrateTypes": [ "lib" ], "authors": [ "bluss" ] }, "equivalent": { "crateName": "equivalent", "version": "1.0.1", "edition": "2015", "sha256": "1malmx5f4lkfvqasz319lq6gb3ddg19yzf9s8cykfsgzdmyq0hsl", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ] }, "form_urlencoded": { "crateName": "form_urlencoded", "version": "1.2.1", "edition": "2018", "sha256": "0milh8x7nl4f450s3ddhg57a3flcv6yq8hlkyk6fyr3mcb128dp1", "source": { "type": "crates-io" }, "dependencies": [ { "name": "percent-encoding", "packageId": "percent-encoding" } ], "resolvedDefaultFeatures": [ "alloc", "default", "std" ], "libCrateTypes": [ "lib" ], "authors": [ "The rust-url developers" ] }, "fs_extra": { "crateName": "fs_extra", "version": "1.3.0", "edition": "2018", "sha256": "075i25z70j2mz9r7i9p9r521y8xdj81q7skslyb7zhqnnw33fw22", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "Denis Kurilenko " ] }, "fuchsia-cprng": { "crateName": "fuchsia-cprng", "version": "0.1.1", "edition": "2018", "sha256": "1fnkqrbz7ixxzsb04bsz9p0zzazanma8znfdqjvh39n14vapfvx0", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "Erick Tryzelaar " ] }, "generic-array": { "crateName": "generic-array", "version": "0.14.7", "edition": "2015", "sha256": "16lyyrzrljfq424c3n8kfwkqihlimmsg5nhshbbp48np3yjrqr45", "source": { "type": "crates-io" }, "dependencies": [ { "name": "typenum", "packageId": "typenum" } ], "buildDependencies": [ { "name": "version_check", "packageId": "version_check" } ], "resolvedDefaultFeatures": [ "more_lengths" ], "libCrateTypes": [ "lib" ], "authors": [ "Bartłomiej Kamiński ", "Aaron Trent " ] }, "globset": { "crateName": "globset", "version": "0.4.14", "edition": "2021", "sha256": "1qab0c1drpybgm4nc92lf8b46x0ap44c9y4k23rndgc5bfdkpnjp", "source": { "type": "crates-io" }, "dependencies": [ { "name": "aho-corasick", "packageId": "aho-corasick" }, { "name": "bstr", "packageId": "bstr" }, { "name": "log", "packageId": "log" }, { "name": "regex-automata", "packageId": "regex-automata" }, { "name": "regex-syntax", "packageId": "regex-syntax" } ], "resolvedDefaultFeatures": [ "default", "log" ], "libCrateTypes": [ "lib" ], "authors": [ "Andrew Gallant " ] }, "globwalk": { "crateName": "globwalk", "version": "0.9.1", "edition": "2021", "sha256": "0mz7bsa66p2rrgnz3l94ac4kbklh7mq8j30iizyxjy4qyvmn1xqb", "source": { "type": "crates-io" }, "dependencies": [ { "name": "bitflags", "packageId": "bitflags 2.6.0" }, { "name": "ignore", "packageId": "ignore" }, { "name": "walkdir", "packageId": "walkdir" } ], "libCrateTypes": [ "lib" ], "authors": [ "Gilad Naaman " ] }, "hashbrown": { "crateName": "hashbrown", "version": "0.14.5", "edition": "2021", "sha256": "1wa1vy1xs3mp11bn3z9dv0jricgr6a2j0zkf1g19yz3vw4il89z5", "source": { "type": "crates-io" }, "resolvedDefaultFeatures": [ "raw" ], "libCrateTypes": [ "lib" ], "authors": [ "Amanieu d'Antras " ] }, "heck": { "crateName": "heck", "version": "0.3.3", "edition": "2018", "sha256": "0b0kkr790p66lvzn9nsmfjvydrbmh9z5gb664jchwgw64vxiwqkd", "source": { "type": "crates-io" }, "dependencies": [ { "name": "unicode-segmentation", "packageId": "unicode-segmentation" } ], "libCrateTypes": [ "lib" ], "authors": [ "Without Boats " ] }, "hermit-abi": { "crateName": "hermit-abi", "version": "0.1.19", "edition": "2018", "sha256": "0cxcm8093nf5fyn114w8vxbrbcyvv91d4015rdnlgfll7cs6gd32", "source": { "type": "crates-io" }, "dependencies": [ { "name": "libc", "packageId": "libc" } ], "resolvedDefaultFeatures": [ "default" ], "libCrateTypes": [ "lib" ], "authors": [ "Stefan Lankes" ] }, "hex": { "crateName": "hex", "version": "0.4.3", "edition": "2018", "sha256": "0w1a4davm1lgzpamwnba907aysmlrnygbqmfis2mqjx5m552a93z", "source": { "type": "crates-io" }, "resolvedDefaultFeatures": [ "alloc", "default", "std" ], "libCrateTypes": [ "lib" ], "authors": [ "KokaKiwi " ] }, "idna": { "crateName": "idna", "version": "0.5.0", "edition": "2018", "sha256": "1xhjrcjqq0l5bpzvdgylvpkgk94panxgsirzhjnnqfdgc4a9nkb3", "source": { "type": "crates-io" }, "dependencies": [ { "name": "unicode-bidi", "packageId": "unicode-bidi" }, { "name": "unicode-normalization", "packageId": "unicode-normalization" } ], "resolvedDefaultFeatures": [ "alloc", "default", "std" ], "libCrateTypes": [ "lib" ], "authors": [ "The rust-url developers" ] }, "ignore": { "crateName": "ignore", "version": "0.4.22", "edition": "2021", "sha256": "1wcaqpi6djqgi1brghrdyw4d5qgnwzhqrqyn4mar4vp677gi0s5l", "source": { "type": "crates-io" }, "dependencies": [ { "name": "crossbeam-deque", "packageId": "crossbeam-deque" }, { "name": "globset", "packageId": "globset" }, { "name": "log", "packageId": "log" }, { "name": "memchr", "packageId": "memchr" }, { "name": "regex-automata", "packageId": "regex-automata" }, { "name": "same-file", "packageId": "same-file" }, { "name": "walkdir", "packageId": "walkdir" }, { "name": "winapi-util", "packageId": "winapi-util", "target": "cfg(windows)" } ], "libCrateTypes": [ "lib" ], "authors": [ "Andrew Gallant " ] }, "indexmap": { "crateName": "indexmap", "version": "2.2.6", "edition": "2021", "sha256": "09hgwi2ig0wyj5rjziia76zmhgfj95k0jb4ic3iiawm4vlavg3qn", "source": { "type": "crates-io" }, "dependencies": [ { "name": "equivalent", "packageId": "equivalent" }, { "name": "hashbrown", "packageId": "hashbrown" } ], "resolvedDefaultFeatures": [ "default", "std" ], "libCrateTypes": [ "lib" ] }, "itertools 0.10.5": { "crateName": "itertools", "version": "0.10.5", "edition": "2018", "sha256": "0ww45h7nxx5kj6z2y6chlskxd1igvs4j507anr6dzg99x1h25zdh", "source": { "type": "crates-io" }, "dependencies": [ { "name": "either", "packageId": "either" } ], "libCrateTypes": [ "lib" ], "authors": [ "bluss" ] }, "itertools 0.12.1": { "crateName": "itertools", "version": "0.12.1", "edition": "2018", "sha256": "0s95jbb3ndj1lvfxyq5wanc0fm0r6hg6q4ngb92qlfdxvci10ads", "source": { "type": "crates-io" }, "dependencies": [ { "name": "either", "packageId": "either" } ], "resolvedDefaultFeatures": [ "default", "use_alloc", "use_std" ], "libCrateTypes": [ "lib" ], "authors": [ "bluss" ] }, "itoa": { "crateName": "itoa", "version": "1.0.11", "edition": "2018", "sha256": "0nv9cqjwzr3q58qz84dcz63ggc54yhf1yqar1m858m1kfd4g3wa9", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "David Tolnay " ] }, "lazy_static": { "crateName": "lazy_static", "version": "1.5.0", "edition": "2015", "sha256": "1zk6dqqni0193xg6iijh7i3i44sryglwgvx20spdvwk3r6sbrlmv", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "Marvin Löbel " ] }, "libc": { "crateName": "libc", "version": "0.2.155", "edition": "2015", "sha256": "0z44c53z54znna8n322k5iwg80arxxpdzjj5260pxxzc9a58icwp", "source": { "type": "crates-io" }, "resolvedDefaultFeatures": [ "default", "std" ], "libCrateTypes": [ "lib" ], "authors": [ "The Rust Project Developers" ] }, "log": { "crateName": "log", "version": "0.4.22", "edition": "2021", "sha256": "093vs0wkm1rgyykk7fjbqp2lwizbixac1w52gv109p5r4jh0p9x7", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "The Rust Project Developers" ] }, "memchr": { "crateName": "memchr", "version": "2.7.4", "edition": "2021", "sha256": "18z32bhxrax0fnjikv475z7ii718hq457qwmaryixfxsl2qrmjkq", "source": { "type": "crates-io" }, "resolvedDefaultFeatures": [ "alloc", "default", "std" ], "libCrateTypes": [ "lib" ], "authors": [ "Andrew Gallant ", "bluss" ] }, "nix-base32": { "crateName": "nix-base32", "version": "0.1.1", "edition": "2018", "sha256": "04jnq6arig0amz0scadavbzn9bg9k4zphmrm1562n6ygfj1dnj45", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "Peter Kolloch " ] }, "once_cell": { "crateName": "once_cell", "version": "1.19.0", "edition": "2021", "sha256": "14kvw7px5z96dk4dwdm1r9cqhhy2cyj1l5n5b29mynbb8yr15nrz", "source": { "type": "crates-io" }, "resolvedDefaultFeatures": [ "alloc", "default", "race", "std" ], "libCrateTypes": [ "lib" ], "authors": [ "Aleksey Kladov " ] }, "pathdiff": { "crateName": "pathdiff", "version": "0.2.1", "edition": "2018", "sha256": "1pa4dcmb7lwir4himg1mnl97a05b2z0svczg62l8940pbim12dc8", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "Manish Goregaokar " ] }, "percent-encoding": { "crateName": "percent-encoding", "version": "2.3.1", "edition": "2018", "sha256": "0gi8wgx0dcy8rnv1kywdv98lwcx67hz0a0zwpib5v2i08r88y573", "source": { "type": "crates-io" }, "resolvedDefaultFeatures": [ "alloc", "default", "std" ], "libCrateTypes": [ "lib" ], "authors": [ "The rust-url developers" ] }, "pest": { "crateName": "pest", "version": "2.7.10", "edition": "2021", "sha256": "1s4fvis7h6l872g6nk17r130kcllj4c0hjvwkzd3hi196g3320an", "source": { "type": "crates-io" }, "dependencies": [ { "name": "memchr", "packageId": "memchr" }, { "name": "thiserror", "packageId": "thiserror" }, { "name": "ucd-trie", "packageId": "ucd-trie" } ], "resolvedDefaultFeatures": [ "default", "memchr", "std" ], "libCrateTypes": [ "lib" ], "authors": [ "Dragoș Tiselice " ] }, "pest_derive": { "crateName": "pest_derive", "version": "2.7.10", "edition": "2021", "sha256": "0n8lsk9s21dp7958p9yarbk2gsc8wg0rvdzr7cd7pjpvjf8kqa96", "source": { "type": "crates-io" }, "dependencies": [ { "name": "pest", "packageId": "pest" }, { "name": "pest_generator", "packageId": "pest_generator" } ], "resolvedDefaultFeatures": [ "default", "std" ], "procMacro": true, "authors": [ "Dragoș Tiselice " ] }, "pest_generator": { "crateName": "pest_generator", "version": "2.7.10", "edition": "2021", "sha256": "11s6q0vf25lckbzak0qndzpv87ksaxy6pa9cvn2hlizvsgvjmhiy", "source": { "type": "crates-io" }, "dependencies": [ { "name": "pest", "packageId": "pest" }, { "name": "pest_meta", "packageId": "pest_meta" }, { "name": "proc-macro2", "packageId": "proc-macro2" }, { "name": "quote", "packageId": "quote" }, { "name": "syn", "packageId": "syn 2.0.68" } ], "resolvedDefaultFeatures": [ "std" ], "libCrateTypes": [ "lib" ], "authors": [ "Dragoș Tiselice " ] }, "pest_meta": { "crateName": "pest_meta", "version": "2.7.10", "edition": "2021", "sha256": "1kdxl164yyjsmn01lvllsll4sz3xbgy4dmkq33n63hrp5w1418np", "source": { "type": "crates-io" }, "dependencies": [ { "name": "once_cell", "packageId": "once_cell" }, { "name": "pest", "packageId": "pest" } ], "buildDependencies": [ { "name": "sha2", "packageId": "sha2" } ], "resolvedDefaultFeatures": [ "default" ], "libCrateTypes": [ "lib" ], "authors": [ "Dragoș Tiselice " ] }, "proc-macro-error": { "crateName": "proc-macro-error", "version": "1.0.4", "edition": "2018", "sha256": "1373bhxaf0pagd8zkyd03kkx6bchzf6g0dkwrwzsnal9z47lj9fs", "source": { "type": "crates-io" }, "dependencies": [ { "name": "proc-macro-error-attr", "packageId": "proc-macro-error-attr" }, { "name": "proc-macro2", "packageId": "proc-macro2" }, { "name": "quote", "packageId": "quote" }, { "name": "syn", "packageId": "syn 1.0.109" } ], "buildDependencies": [ { "name": "version_check", "packageId": "version_check" } ], "resolvedDefaultFeatures": [ "default", "syn", "syn-error" ], "libCrateTypes": [ "lib" ], "authors": [ "CreepySkeleton " ] }, "proc-macro-error-attr": { "crateName": "proc-macro-error-attr", "version": "1.0.4", "edition": "2018", "sha256": "0sgq6m5jfmasmwwy8x4mjygx5l7kp8s4j60bv25ckv2j1qc41gm1", "source": { "type": "crates-io" }, "dependencies": [ { "name": "proc-macro2", "packageId": "proc-macro2" }, { "name": "quote", "packageId": "quote" } ], "buildDependencies": [ { "name": "version_check", "packageId": "version_check" } ], "procMacro": true, "authors": [ "CreepySkeleton " ] }, "proc-macro2": { "crateName": "proc-macro2", "version": "1.0.86", "edition": "2021", "sha256": "0xrv22p8lqlfdf1w0pj4si8n2ws4aw0kilmziwf0vpv5ys6rwway", "source": { "type": "crates-io" }, "dependencies": [ { "name": "unicode-ident", "packageId": "unicode-ident" } ], "resolvedDefaultFeatures": [ "default", "proc-macro" ], "libCrateTypes": [ "lib" ], "authors": [ "David Tolnay ", "Alex Crichton " ] }, "quote": { "crateName": "quote", "version": "1.0.36", "edition": "2018", "sha256": "19xcmh445bg6simirnnd4fvkmp6v2qiwxh5f6rw4a70h76pnm9qg", "source": { "type": "crates-io" }, "dependencies": [ { "name": "proc-macro2", "packageId": "proc-macro2" } ], "resolvedDefaultFeatures": [ "default", "proc-macro" ], "libCrateTypes": [ "lib" ], "authors": [ "David Tolnay " ] }, "rand": { "crateName": "rand", "version": "0.4.6", "edition": "2015", "sha256": "14qjfv3gggzhnma20k0sc1jf8y6pplsaq7n1j9ls5c8kf2wl0a2m", "source": { "type": "crates-io" }, "dependencies": [ { "name": "fuchsia-cprng", "packageId": "fuchsia-cprng", "target": "cfg(target_os = \"fuchsia\")" }, { "name": "libc", "packageId": "libc", "target": "cfg(unix)" }, { "name": "rand_core", "packageId": "rand_core 0.3.1", "target": "cfg(target_env = \"sgx\")" }, { "name": "rdrand", "packageId": "rdrand", "target": "cfg(target_env = \"sgx\")" }, { "name": "winapi", "packageId": "winapi", "target": "cfg(windows)" } ], "resolvedDefaultFeatures": [ "default", "libc", "std" ], "libCrateTypes": [ "lib" ], "authors": [ "The Rust Project Developers" ] }, "rand_core 0.3.1": { "crateName": "rand_core", "version": "0.3.1", "edition": "2015", "sha256": "0jzdgszfa4bliigiy4hi66k7fs3gfwi2qxn8vik84ph77fwdwvvs", "source": { "type": "crates-io" }, "dependencies": [ { "name": "rand_core", "packageId": "rand_core 0.4.2" } ], "libCrateTypes": [ "lib" ], "authors": [ "The Rand Project Developers", "The Rust Project Developers" ] }, "rand_core 0.4.2": { "crateName": "rand_core", "version": "0.4.2", "edition": "2015", "sha256": "1p09ynysrq1vcdlmcqnapq4qakl2yd1ng3kxh3qscpx09k2a6cww", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "The Rand Project Developers", "The Rust Project Developers" ] }, "rdrand": { "crateName": "rdrand", "version": "0.4.0", "edition": "2015", "sha256": "1cjq0kwx1bk7jx3kzyciiish5gqsj7620dm43dc52sr8fzmm9037", "source": { "type": "crates-io" }, "dependencies": [ { "name": "rand_core", "packageId": "rand_core 0.3.1" } ], "resolvedDefaultFeatures": [ "default", "std" ], "libCrateTypes": [ "lib" ], "authors": [ "Simonas Kazlauskas " ] }, "regex": { "crateName": "regex", "version": "1.10.5", "edition": "2021", "sha256": "0zsiqk2sxc1kd46qw0yp87s2a14ialwyxinpl0k266ddkm1i64mr", "source": { "type": "crates-io" }, "dependencies": [ { "name": "aho-corasick", "packageId": "aho-corasick" }, { "name": "memchr", "packageId": "memchr" }, { "name": "regex-automata", "packageId": "regex-automata" }, { "name": "regex-syntax", "packageId": "regex-syntax" } ], "resolvedDefaultFeatures": [ "default", "perf", "perf-backtrack", "perf-cache", "perf-dfa", "perf-inline", "perf-literal", "perf-onepass", "std", "unicode", "unicode-age", "unicode-bool", "unicode-case", "unicode-gencat", "unicode-perl", "unicode-script", "unicode-segment" ], "libCrateTypes": [ "lib" ], "authors": [ "The Rust Project Developers", "Andrew Gallant " ] }, "regex-automata": { "crateName": "regex-automata", "version": "0.4.7", "edition": "2021", "sha256": "1pwjdi4jckpbaivpl6x4v5g4crb37zr2wac93wlfsbzgqn6gbjiq", "source": { "type": "crates-io" }, "dependencies": [ { "name": "aho-corasick", "packageId": "aho-corasick" }, { "name": "memchr", "packageId": "memchr" }, { "name": "regex-syntax", "packageId": "regex-syntax" } ], "resolvedDefaultFeatures": [ "alloc", "dfa-onepass", "hybrid", "meta", "nfa", "nfa-backtrack", "nfa-pikevm", "nfa-thompson", "perf", "perf-inline", "perf-literal", "perf-literal-multisubstring", "perf-literal-substring", "std", "syntax", "unicode", "unicode-age", "unicode-bool", "unicode-case", "unicode-gencat", "unicode-perl", "unicode-script", "unicode-segment", "unicode-word-boundary" ], "libCrateTypes": [ "lib" ], "authors": [ "The Rust Project Developers", "Andrew Gallant " ] }, "regex-syntax": { "crateName": "regex-syntax", "version": "0.8.4", "edition": "2021", "sha256": "16r0kjy20vx33dr4mhasj5l1f87czas714x2fz6zl0f8wwxa0rks", "source": { "type": "crates-io" }, "resolvedDefaultFeatures": [ "default", "std", "unicode", "unicode-age", "unicode-bool", "unicode-case", "unicode-gencat", "unicode-perl", "unicode-script", "unicode-segment" ], "libCrateTypes": [ "lib" ], "authors": [ "The Rust Project Developers", "Andrew Gallant " ] }, "remove_dir_all": { "crateName": "remove_dir_all", "version": "0.5.3", "edition": "2015", "sha256": "1rzqbsgkmr053bxxl04vmvsd1njyz0nxvly97aip6aa2cmb15k9s", "source": { "type": "crates-io" }, "dependencies": [ { "name": "winapi", "packageId": "winapi", "target": "cfg(windows)" } ], "libCrateTypes": [ "lib" ], "authors": [ "Aaronepower " ] }, "ryu": { "crateName": "ryu", "version": "1.0.18", "edition": "2018", "sha256": "17xx2s8j1lln7iackzd9p0sv546vjq71i779gphjq923vjh5pjzk", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "David Tolnay " ] }, "same-file": { "crateName": "same-file", "version": "1.0.6", "edition": "2018", "sha256": "00h5j1w87dmhnvbv9l8bic3y7xxsnjmssvifw2ayvgx9mb1ivz4k", "source": { "type": "crates-io" }, "dependencies": [ { "name": "winapi-util", "packageId": "winapi-util", "target": "cfg(windows)" } ], "libCrateTypes": [ "lib" ], "authors": [ "Andrew Gallant " ] }, "semver": { "crateName": "semver", "version": "1.0.23", "edition": "2018", "sha256": "12wqpxfflclbq4dv8sa6gchdh92ahhwn4ci1ls22wlby3h57wsb1", "source": { "type": "crates-io" }, "dependencies": [ { "name": "serde", "packageId": "serde" } ], "resolvedDefaultFeatures": [ "default", "serde", "std" ], "libCrateTypes": [ "lib" ], "authors": [ "David Tolnay " ] }, "serde": { "crateName": "serde", "version": "1.0.203", "edition": "2018", "sha256": "1500ghq198n6py5anvz5qbqagd9h1hq04f4qpsvjzrvix56snlvj", "source": { "type": "crates-io" }, "dependencies": [ { "name": "serde_derive", "packageId": "serde_derive" }, { "name": "serde_derive", "packageId": "serde_derive", "target": "cfg(any())" } ], "resolvedDefaultFeatures": [ "alloc", "default", "derive", "serde_derive", "std" ], "libCrateTypes": [ "lib" ], "authors": [ "Erick Tryzelaar ", "David Tolnay " ] }, "serde_derive": { "crateName": "serde_derive", "version": "1.0.203", "edition": "2015", "sha256": "1fmmqmfza3mwxb1v80737dj01gznrh8mhgqgylkndx5npq7bq32h", "source": { "type": "crates-io" }, "dependencies": [ { "name": "proc-macro2", "packageId": "proc-macro2" }, { "name": "quote", "packageId": "quote" }, { "name": "syn", "packageId": "syn 2.0.68" } ], "resolvedDefaultFeatures": [ "default" ], "procMacro": true, "authors": [ "Erick Tryzelaar ", "David Tolnay " ] }, "serde_json": { "crateName": "serde_json", "version": "1.0.118", "edition": "2021", "sha256": "1r7jpqdfnrv8skn5va1r202g6lhdhka0vyn42vm5g21x2srzciyr", "source": { "type": "crates-io" }, "dependencies": [ { "name": "itoa", "packageId": "itoa" }, { "name": "ryu", "packageId": "ryu" }, { "name": "serde", "packageId": "serde" } ], "resolvedDefaultFeatures": [ "default", "std", "unbounded_depth" ], "libCrateTypes": [ "lib" ], "authors": [ "Erick Tryzelaar ", "David Tolnay " ] }, "serde_spanned": { "crateName": "serde_spanned", "version": "0.6.6", "edition": "2021", "sha256": "1839b6m5p9ijjmcwamiya2r612ks2vg6w2pp95yg76lr3zh79rkr", "source": { "type": "crates-io" }, "dependencies": [ { "name": "serde", "packageId": "serde" } ], "resolvedDefaultFeatures": [ "serde" ], "libCrateTypes": [ "lib" ] }, "sha2": { "crateName": "sha2", "version": "0.10.8", "edition": "2018", "sha256": "1j1x78zk9il95w9iv46dh9wm73r6xrgj32y6lzzw7bxws9dbfgbr", "source": { "type": "crates-io" }, "dependencies": [ { "name": "cfg-if", "packageId": "cfg-if" }, { "name": "cpufeatures", "packageId": "cpufeatures", "target": "cfg(any(target_arch = \"aarch64\", target_arch = \"x86_64\", target_arch = \"x86\"))" }, { "name": "digest", "packageId": "digest" } ], "libCrateTypes": [ "lib" ], "authors": [ "RustCrypto Developers" ] }, "strsim": { "crateName": "strsim", "version": "0.8.0", "edition": "2015", "sha256": "0sjsm7hrvjdifz661pjxq5w4hf190hx53fra8dfvamacvff139cf", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "Danny Guo " ] }, "structopt": { "crateName": "structopt", "version": "0.3.26", "edition": "2018", "sha256": "043sg3qxllann6q9i71d05qp3q13scmcvhxhd950ka2v8ij5qsqc", "source": { "type": "crates-io" }, "dependencies": [ { "name": "clap", "packageId": "clap" }, { "name": "lazy_static", "packageId": "lazy_static" }, { "name": "structopt-derive", "packageId": "structopt-derive" } ], "resolvedDefaultFeatures": [ "default" ], "libCrateTypes": [ "lib" ], "authors": [ "Guillaume Pinot ", "others" ] }, "structopt-derive": { "crateName": "structopt-derive", "version": "0.4.18", "edition": "2018", "sha256": "1q5gcigmvw0cinjxzpyrkflliq5r1ivljmrvfrl3phcwgwraxdfw", "source": { "type": "crates-io" }, "dependencies": [ { "name": "heck", "packageId": "heck" }, { "name": "proc-macro-error", "packageId": "proc-macro-error" }, { "name": "proc-macro2", "packageId": "proc-macro2" }, { "name": "quote", "packageId": "quote" }, { "name": "syn", "packageId": "syn 1.0.109" } ], "procMacro": true, "authors": [ "Guillaume Pinot " ] }, "syn 1.0.109": { "crateName": "syn", "version": "1.0.109", "edition": "2018", "sha256": "0ds2if4600bd59wsv7jjgfkayfzy3hnazs394kz6zdkmna8l3dkj", "source": { "type": "crates-io" }, "dependencies": [ { "name": "proc-macro2", "packageId": "proc-macro2" }, { "name": "quote", "packageId": "quote" }, { "name": "unicode-ident", "packageId": "unicode-ident" } ], "resolvedDefaultFeatures": [ "clone-impls", "default", "derive", "full", "parsing", "printing", "proc-macro", "quote" ], "libCrateTypes": [ "lib" ], "authors": [ "David Tolnay " ] }, "syn 2.0.68": { "crateName": "syn", "version": "2.0.68", "edition": "2021", "sha256": "1sf1y2hajhjav38ipg63c934xrgkz4v42fz24a0ckmmri06sf7wh", "source": { "type": "crates-io" }, "dependencies": [ { "name": "proc-macro2", "packageId": "proc-macro2" }, { "name": "quote", "packageId": "quote" }, { "name": "unicode-ident", "packageId": "unicode-ident" } ], "resolvedDefaultFeatures": [ "clone-impls", "default", "derive", "parsing", "printing", "proc-macro" ], "libCrateTypes": [ "lib" ], "authors": [ "David Tolnay " ] }, "tempdir": { "crateName": "tempdir", "version": "0.3.7", "edition": "2015", "sha256": "1n5n86zxpgd85y0mswrp5cfdisizq2rv3la906g6ipyc03xvbwhm", "source": { "type": "crates-io" }, "dependencies": [ { "name": "rand", "packageId": "rand" }, { "name": "remove_dir_all", "packageId": "remove_dir_all" } ], "libCrateTypes": [ "lib" ], "authors": [ "The Rust Project Developers" ] }, "tera": { "crateName": "tera", "version": "1.20.0", "edition": "2018", "sha256": "1vnj9imw2h9szkd1izsrhwrc9jvazvdsp84x65wg2rg88ldqb7db", "source": { "type": "crates-io" }, "dependencies": [ { "name": "globwalk", "packageId": "globwalk" }, { "name": "lazy_static", "packageId": "lazy_static" }, { "name": "pest", "packageId": "pest" }, { "name": "pest_derive", "packageId": "pest_derive" }, { "name": "regex", "packageId": "regex" }, { "name": "serde", "packageId": "serde" }, { "name": "serde_json", "packageId": "serde_json" }, { "name": "unic-segment", "packageId": "unic-segment" } ], "libCrateTypes": [ "lib" ], "authors": [ "Vincent Prouillet " ] }, "textwrap": { "crateName": "textwrap", "version": "0.11.0", "edition": "2015", "sha256": "0q5hky03ik3y50s9sz25r438bc4nwhqc6dqwynv4wylc807n29nk", "source": { "type": "crates-io" }, "dependencies": [ { "name": "unicode-width", "packageId": "unicode-width" } ], "libCrateTypes": [ "lib" ], "authors": [ "Martin Geisler " ] }, "thiserror": { "crateName": "thiserror", "version": "1.0.61", "edition": "2021", "sha256": "028prh962l16cmjivwb1g9xalbpqip0305zhq006mg74dc6whin5", "source": { "type": "crates-io" }, "dependencies": [ { "name": "thiserror-impl", "packageId": "thiserror-impl" } ], "libCrateTypes": [ "lib" ], "authors": [ "David Tolnay " ] }, "thiserror-impl": { "crateName": "thiserror-impl", "version": "1.0.61", "edition": "2021", "sha256": "0cvm37hp0kbcyk1xac1z0chpbd9pbn2g456iyid6sah0a113ihs6", "source": { "type": "crates-io" }, "dependencies": [ { "name": "proc-macro2", "packageId": "proc-macro2" }, { "name": "quote", "packageId": "quote" }, { "name": "syn", "packageId": "syn 2.0.68" } ], "procMacro": true, "authors": [ "David Tolnay " ] }, "tinyvec": { "crateName": "tinyvec", "version": "1.6.1", "edition": "2018", "sha256": "10idfhsvp7zhbr8pn37wfra2bn02vr5xg6mhdvrbxlp2zg31alf5", "source": { "type": "crates-io" }, "dependencies": [ { "name": "tinyvec_macros", "packageId": "tinyvec_macros" } ], "resolvedDefaultFeatures": [ "alloc", "default", "tinyvec_macros" ], "libCrateTypes": [ "lib" ], "authors": [ "Lokathor " ] }, "tinyvec_macros": { "crateName": "tinyvec_macros", "version": "0.1.1", "edition": "2018", "sha256": "081gag86208sc3y6sdkshgw3vysm5d34p431dzw0bshz66ncng0z", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "Soveu " ] }, "toml": { "crateName": "toml", "version": "0.8.14", "edition": "2021", "sha256": "0dgk8bacrza09npifba1xsx7wyjjvhz3igxpdnyjcbqxn8mfnjbg", "source": { "type": "crates-io" }, "dependencies": [ { "name": "serde", "packageId": "serde" }, { "name": "serde_spanned", "packageId": "serde_spanned" }, { "name": "toml_datetime", "packageId": "toml_datetime" }, { "name": "toml_edit", "packageId": "toml_edit" } ], "resolvedDefaultFeatures": [ "default", "display", "parse" ], "libCrateTypes": [ "lib" ], "authors": [ "Alex Crichton " ] }, "toml_datetime": { "crateName": "toml_datetime", "version": "0.6.6", "edition": "2021", "sha256": "1grcrr3gh7id3cy3j700kczwwfbn04p5ncrrj369prjaj9bgvbab", "source": { "type": "crates-io" }, "dependencies": [ { "name": "serde", "packageId": "serde" } ], "resolvedDefaultFeatures": [ "serde" ], "libCrateTypes": [ "lib" ], "authors": [ "Alex Crichton " ] }, "toml_edit": { "crateName": "toml_edit", "version": "0.22.14", "edition": "2021", "sha256": "0f2fw0viqvisjhqwjavgypz5mgbldh53przrsjlrrggijyppl77j", "source": { "type": "crates-io" }, "dependencies": [ { "name": "indexmap", "packageId": "indexmap" }, { "name": "serde", "packageId": "serde" }, { "name": "serde_spanned", "packageId": "serde_spanned" }, { "name": "toml_datetime", "packageId": "toml_datetime" }, { "name": "winnow", "packageId": "winnow" } ], "resolvedDefaultFeatures": [ "display", "parse", "serde" ], "libCrateTypes": [ "lib" ], "authors": [ "Andronik Ordian ", "Ed Page " ] }, "typenum": { "crateName": "typenum", "version": "1.17.0", "edition": "2018", "sha256": "09dqxv69m9lj9zvv6xw5vxaqx15ps0vxyy5myg33i0kbqvq0pzs2", "source": { "type": "crates-io" }, "build": "build/main.rs", "libCrateTypes": [ "lib" ], "authors": [ "Paho Lurie-Gregg ", "Andre Bogus " ] }, "ucd-trie": { "crateName": "ucd-trie", "version": "0.1.6", "edition": "2021", "sha256": "1ff4yfksirqs37ybin9aw71aa5gva00hw7jdxbw8w668zy964r7d", "source": { "type": "crates-io" }, "resolvedDefaultFeatures": [ "std" ], "libCrateTypes": [ "lib" ], "authors": [ "Andrew Gallant " ] }, "unic-char-property": { "crateName": "unic-char-property", "version": "0.9.0", "edition": "2018", "sha256": "08g21dn3wwix3ycfl0vrbahn0835nv2q3swm8wms0vwvgm07mid8", "source": { "type": "crates-io" }, "dependencies": [ { "name": "unic-char-range", "packageId": "unic-char-range" } ], "libCrateTypes": [ "lib" ], "authors": [ "The UNIC Project Developers" ] }, "unic-char-range": { "crateName": "unic-char-range", "version": "0.9.0", "edition": "2018", "sha256": "1g0z7iwvjhqspi6194zsff8vy6i3921hpqcrp3v1813hbwnh5603", "source": { "type": "crates-io" }, "resolvedDefaultFeatures": [ "default" ], "libCrateTypes": [ "lib" ], "authors": [ "The UNIC Project Developers" ] }, "unic-common": { "crateName": "unic-common", "version": "0.9.0", "edition": "2018", "sha256": "1g1mm954m0zr497dl4kx3vr09yaly290zs33bbl4wrbaba1gzmw0", "source": { "type": "crates-io" }, "resolvedDefaultFeatures": [ "default" ], "libCrateTypes": [ "lib" ], "authors": [ "The UNIC Project Developers" ] }, "unic-segment": { "crateName": "unic-segment", "version": "0.9.0", "edition": "2018", "sha256": "08wgz2q6vrdvmbd23kf9pbg8cyzm5q8hq9spc4blzy2ppqk5vvg4", "source": { "type": "crates-io" }, "dependencies": [ { "name": "unic-ucd-segment", "packageId": "unic-ucd-segment" } ], "libCrateTypes": [ "lib" ], "authors": [ "The UNIC Project Developers" ] }, "unic-ucd-segment": { "crateName": "unic-ucd-segment", "version": "0.9.0", "edition": "2018", "sha256": "0027lczcg0r401g6fnzm2bq9fxhgxvri1nlryhhv8192lqic2y90", "source": { "type": "crates-io" }, "dependencies": [ { "name": "unic-char-property", "packageId": "unic-char-property" }, { "name": "unic-char-range", "packageId": "unic-char-range" }, { "name": "unic-ucd-version", "packageId": "unic-ucd-version" } ], "libCrateTypes": [ "lib" ], "authors": [ "The UNIC Project Developers" ] }, "unic-ucd-version": { "crateName": "unic-ucd-version", "version": "0.9.0", "edition": "2018", "sha256": "1i5hnzpfnxkp4ijfk8kvhpvj84bij575ybqx1b6hyigy6wi2zgcn", "source": { "type": "crates-io" }, "dependencies": [ { "name": "unic-common", "packageId": "unic-common" } ], "libCrateTypes": [ "lib" ], "authors": [ "The UNIC Project Developers" ] }, "unicode-bidi": { "crateName": "unicode-bidi", "version": "0.3.15", "edition": "2018", "sha256": "0xcdxm7h0ydyprwpcbh436rbs6s6lph7f3gr527lzgv6lw053y88", "source": { "type": "crates-io" }, "resolvedDefaultFeatures": [ "hardcoded-data", "std" ], "libCrateTypes": [ "lib" ], "authors": [ "The Servo Project Developers" ] }, "unicode-ident": { "crateName": "unicode-ident", "version": "1.0.12", "edition": "2018", "sha256": "0jzf1znfpb2gx8nr8mvmyqs1crnv79l57nxnbiszc7xf7ynbjm1k", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "David Tolnay " ] }, "unicode-normalization": { "crateName": "unicode-normalization", "version": "0.1.23", "edition": "2018", "sha256": "1x81a50h2zxigj74b9bqjsirxxbyhmis54kg600xj213vf31cvd5", "source": { "type": "crates-io" }, "dependencies": [ { "name": "tinyvec", "packageId": "tinyvec" } ], "resolvedDefaultFeatures": [ "std" ], "libCrateTypes": [ "lib" ], "authors": [ "kwantam ", "Manish Goregaokar " ] }, "unicode-segmentation": { "crateName": "unicode-segmentation", "version": "1.11.0", "edition": "2018", "sha256": "00kjpwp1g8fqm45drmwivlacn3y9jx73bvs09n6s3x73nqi7vj6l", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "kwantam ", "Manish Goregaokar " ] }, "unicode-width": { "crateName": "unicode-width", "version": "0.1.13", "edition": "2021", "sha256": "0p92vl8n7qc8mxz45xn6qbgi0259z96n32a158l6vj5bywwdadh3", "source": { "type": "crates-io" }, "resolvedDefaultFeatures": [ "default" ], "libCrateTypes": [ "lib" ], "authors": [ "kwantam ", "Manish Goregaokar " ] }, "url": { "crateName": "url", "version": "2.5.2", "edition": "2018", "sha256": "0v2dx50mx7xzl9454cl5qmpjnhkbahmn59gd3apyipbgyyylsy12", "source": { "type": "crates-io" }, "dependencies": [ { "name": "form_urlencoded", "packageId": "form_urlencoded" }, { "name": "idna", "packageId": "idna" }, { "name": "percent-encoding", "packageId": "percent-encoding" }, { "name": "serde", "packageId": "serde" } ], "resolvedDefaultFeatures": [ "default", "serde" ], "libCrateTypes": [ "lib" ], "authors": [ "The rust-url developers" ] }, "vec_map": { "crateName": "vec_map", "version": "0.8.2", "edition": "2015", "sha256": "1481w9g1dw9rxp3l6snkdqihzyrd2f8vispzqmwjwsdyhw8xzggi", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "Alex Crichton ", "Jorge Aparicio ", "Alexis Beingessner ", "Brian Anderson <>", "tbu- <>", "Manish Goregaokar <>", "Aaron Turon ", "Adolfo Ochagavía <>", "Niko Matsakis <>", "Steven Fackler <>", "Chase Southwood ", "Eduard Burtescu <>", "Florian Wilkens <>", "Félix Raimundo <>", "Tibor Benke <>", "Markus Siemens ", "Josh Branchaud ", "Huon Wilson ", "Corey Farwell ", "Aaron Liblong <>", "Nick Cameron ", "Patrick Walton ", "Felix S Klock II <>", "Andrew Paseltiner ", "Sean McArthur ", "Vadim Petrochenkov <>" ] }, "version_check": { "crateName": "version_check", "version": "0.9.4", "edition": "2015", "sha256": "0gs8grwdlgh0xq660d7wr80x14vxbizmd8dbp29p2pdncx8lp1s9", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "Sergio Benitez " ] }, "walkdir": { "crateName": "walkdir", "version": "2.5.0", "edition": "2018", "sha256": "0jsy7a710qv8gld5957ybrnc07gavppp963gs32xk4ag8130jy99", "source": { "type": "crates-io" }, "dependencies": [ { "name": "same-file", "packageId": "same-file" }, { "name": "winapi-util", "packageId": "winapi-util", "target": "cfg(windows)" } ], "libCrateTypes": [ "lib" ], "authors": [ "Andrew Gallant " ] }, "winapi": { "crateName": "winapi", "version": "0.3.9", "edition": "2015", "sha256": "06gl025x418lchw1wxj64ycr7gha83m44cjr5sarhynd9xkrm0sw", "source": { "type": "crates-io" }, "dependencies": [ { "name": "winapi-i686-pc-windows-gnu", "packageId": "winapi-i686-pc-windows-gnu", "target": "i686-pc-windows-gnu" }, { "name": "winapi-x86_64-pc-windows-gnu", "packageId": "winapi-x86_64-pc-windows-gnu", "target": "x86_64-pc-windows-gnu" } ], "resolvedDefaultFeatures": [ "consoleapi", "errhandlingapi", "fileapi", "handleapi", "minwinbase", "minwindef", "ntsecapi", "processenv", "profileapi", "std", "winbase", "winerror", "winnt" ], "libCrateTypes": [ "lib" ], "authors": [ "Peter Atashian " ] }, "winapi-i686-pc-windows-gnu": { "crateName": "winapi-i686-pc-windows-gnu", "version": "0.4.0", "edition": "2015", "sha256": "1dmpa6mvcvzz16zg6d5vrfy4bxgg541wxrcip7cnshi06v38ffxc", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "Peter Atashian " ] }, "winapi-util": { "crateName": "winapi-util", "version": "0.1.8", "edition": "2021", "sha256": "0svcgddd2rw06mj4r76gj655qsa1ikgz3d3gzax96fz7w62c6k2d", "source": { "type": "crates-io" }, "dependencies": [ { "name": "windows-sys", "packageId": "windows-sys", "target": "cfg(windows)" } ], "libCrateTypes": [ "lib" ], "authors": [ "Andrew Gallant " ] }, "winapi-x86_64-pc-windows-gnu": { "crateName": "winapi-x86_64-pc-windows-gnu", "version": "0.4.0", "edition": "2015", "sha256": "0gqq64czqb64kskjryj8isp62m2sgvx25yyj3kpc2myh85w24bki", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "Peter Atashian " ] }, "windows-sys": { "crateName": "windows-sys", "version": "0.52.0", "edition": "2021", "sha256": "0gd3v4ji88490zgb6b5mq5zgbvwv7zx1ibn8v3x83rwcdbryaar8", "source": { "type": "crates-io" }, "dependencies": [ { "name": "windows-targets", "packageId": "windows-targets" } ], "resolvedDefaultFeatures": [ "Win32", "Win32_Foundation", "Win32_Storage", "Win32_Storage_FileSystem", "Win32_System", "Win32_System_Console", "Win32_System_SystemInformation", "default" ], "libCrateTypes": [ "lib" ], "authors": [ "Microsoft" ] }, "windows-targets": { "crateName": "windows-targets", "version": "0.52.5", "edition": "2021", "sha256": "1sz7jrnkygmmlj1ia8fk85wbyil450kq5qkh5qh9sh2rcnj161vg", "source": { "type": "crates-io" }, "dependencies": [ { "name": "windows_aarch64_gnullvm", "packageId": "windows_aarch64_gnullvm", "target": "aarch64-pc-windows-gnullvm" }, { "name": "windows_aarch64_msvc", "packageId": "windows_aarch64_msvc", "target": "cfg(all(target_arch = \"aarch64\", target_env = \"msvc\", not(windows_raw_dylib)))" }, { "name": "windows_i686_gnu", "packageId": "windows_i686_gnu", "target": "cfg(all(target_arch = \"x86\", target_env = \"gnu\", not(target_abi = \"llvm\"), not(windows_raw_dylib)))" }, { "name": "windows_i686_gnullvm", "packageId": "windows_i686_gnullvm", "target": "i686-pc-windows-gnullvm" }, { "name": "windows_i686_msvc", "packageId": "windows_i686_msvc", "target": "cfg(all(target_arch = \"x86\", target_env = \"msvc\", not(windows_raw_dylib)))" }, { "name": "windows_x86_64_gnu", "packageId": "windows_x86_64_gnu", "target": "cfg(all(target_arch = \"x86_64\", target_env = \"gnu\", not(target_abi = \"llvm\"), not(windows_raw_dylib)))" }, { "name": "windows_x86_64_gnullvm", "packageId": "windows_x86_64_gnullvm", "target": "x86_64-pc-windows-gnullvm" }, { "name": "windows_x86_64_msvc", "packageId": "windows_x86_64_msvc", "target": "cfg(all(any(target_arch = \"x86_64\", target_arch = \"arm64ec\"), target_env = \"msvc\", not(windows_raw_dylib)))" } ], "libCrateTypes": [ "lib" ], "authors": [ "Microsoft" ] }, "windows_aarch64_gnullvm": { "crateName": "windows_aarch64_gnullvm", "version": "0.52.5", "edition": "2021", "sha256": "0qrjimbj67nnyn7zqy15mzzmqg0mn5gsr2yciqjxm3cb3vbyx23h", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "Microsoft" ] }, "windows_aarch64_msvc": { "crateName": "windows_aarch64_msvc", "version": "0.52.5", "edition": "2021", "sha256": "1dmga8kqlmln2ibckk6mxc9n59vdg8ziqa2zr8awcl720hazv1cr", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "Microsoft" ] }, "windows_i686_gnu": { "crateName": "windows_i686_gnu", "version": "0.52.5", "edition": "2021", "sha256": "0w4np3l6qwlra9s2xpflqrs60qk1pz6ahhn91rr74lvdy4y0gfl8", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "Microsoft" ] }, "windows_i686_gnullvm": { "crateName": "windows_i686_gnullvm", "version": "0.52.5", "edition": "2021", "sha256": "1s9f4gff0cixd86mw3n63rpmsm4pmr4ffndl6s7qa2h35492dx47", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "Microsoft" ] }, "windows_i686_msvc": { "crateName": "windows_i686_msvc", "version": "0.52.5", "edition": "2021", "sha256": "1gw7fklxywgpnwbwg43alb4hm0qjmx72hqrlwy5nanrxs7rjng6v", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "Microsoft" ] }, "windows_x86_64_gnu": { "crateName": "windows_x86_64_gnu", "version": "0.52.5", "edition": "2021", "sha256": "1n8p2mcf3lw6300k77a0knksssmgwb9hynl793mhkzyydgvlchjf", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "Microsoft" ] }, "windows_x86_64_gnullvm": { "crateName": "windows_x86_64_gnullvm", "version": "0.52.5", "edition": "2021", "sha256": "15n56jrh4s5bz66zimavr1rmcaw6wa306myrvmbc6rydhbj9h8l5", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "Microsoft" ] }, "windows_x86_64_msvc": { "crateName": "windows_x86_64_msvc", "version": "0.52.5", "edition": "2021", "sha256": "1w1bn24ap8dp9i85s8mlg8cim2bl2368bd6qyvm0xzqvzmdpxi5y", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "Microsoft" ] }, "winnow": { "crateName": "winnow", "version": "0.6.13", "edition": "2021", "sha256": "189b0mrr9lkckdyr0177hwj1c59igxc2lsl71f4wg8wrqbvfbdar", "source": { "type": "crates-io" }, "resolvedDefaultFeatures": [ "alloc", "default", "std" ], "libCrateTypes": [ "lib" ] } } } ================================================ FILE: crate2nix/Cargo.nix ================================================ # This file was @generated by crate2nix 0.15.0 with the command: # "generate" "-n" "../nix/nixpkgs.nix" "-f" "./crate2nix/Cargo.toml" "-o" "./crate2nix/Cargo.nix" # See https://github.com/kolloch/crate2nix for more info. { nixpkgs ? ../nix/nixpkgs.nix , pkgs ? import nixpkgs { config = {}; } , fetchurl ? pkgs.fetchurl , lib ? pkgs.lib , stdenv ? pkgs.stdenv , buildRustCrateForPkgs ? pkgs: pkgs.buildRustCrate # This is used as the `crateOverrides` argument for `buildRustCrate`. , defaultCrateOverrides ? pkgs.defaultCrateOverrides # The features to enable for the root_crate or the workspace_members. , rootFeatures ? [ "default" ] # If true, throw errors instead of issueing deprecation warnings. , strictDeprecation ? false # Elements to add to the `-C target-feature=` argument passed to `rustc` # (separated by `,`, prefixed with `+`). # Used for conditional compilation based on CPU feature detection. , targetFeatures ? [] # Additional target attributes for conditional dependencies. # Use this for custom cfg flags that are passed via rustcflags but need to # be known at Nix evaluation time for dependency resolution. # Example: { tracing_unstable = true; } for crates using cfg(tracing_unstable). , extraTargetFlags ? {} # Whether to perform release builds: longer compile times, faster binaries. , release ? true # Additional crate2nix configuration if it exists. , crateConfig ? if builtins.pathExists ./crate-config.nix then pkgs.callPackage ./crate-config.nix {} else {} }: rec { # # "public" attributes that we attempt to keep stable with new versions of crate2nix. # rootCrate = rec { packageId = "crate2nix"; # Use this attribute to refer to the derivation building your root crate package. # You can override the features with rootCrate.build.override { features = [ "default" "feature1" ... ]; }. build = internal.buildRustCrateWithFeatures { inherit packageId; }; # Debug support which might change between releases. # File a bug if you depend on any for non-debug work! debug = internal.debugCrate { inherit packageId; }; }; # Refer your crate build derivation by name here. # You can override the features with # workspaceMembers."${crateName}".build.override { features = [ "default" "feature1" ... ]; }. workspaceMembers = { "crate2nix" = rec { packageId = "crate2nix"; build = internal.buildRustCrateWithFeatures { packageId = "crate2nix"; }; # Debug support which might change between releases. # File a bug if you depend on any for non-debug work! debug = internal.debugCrate { inherit packageId; }; }; }; # A derivation that joins the outputs of all workspace members together. allWorkspaceMembers = pkgs.symlinkJoin { name = "all-workspace-members"; paths = let members = builtins.attrValues workspaceMembers; in builtins.map (m: m.build) members; }; # # "internal" ("private") attributes that may change in every new version of crate2nix. # internal = rec { # Build and dependency information for crates. # Many of the fields are passed one-to-one to buildRustCrate. # # Noteworthy: # * `dependencies`/`buildDependencies`: similar to the corresponding fields for buildRustCrate. # but with additional information which is used during dependency/feature resolution. # * `resolvedDependencies`: the selected default features reported by cargo - only included for debugging. # * `devDependencies` as of now not used by `buildRustCrate` but used to # inject test dependencies into the build crates = { "aho-corasick" = rec { crateName = "aho-corasick"; version = "1.1.3"; edition = "2021"; sha256 = "05mrpkvdgp5d20y2p989f187ry9diliijgwrs254fs9s1m1x6q4f"; libName = "aho_corasick"; authors = [ "Andrew Gallant " ]; dependencies = [ { name = "memchr"; packageId = "memchr"; optional = true; usesDefaultFeatures = false; } ]; features = { "default" = [ "std" "perf-literal" ]; "logging" = [ "dep:log" ]; "perf-literal" = [ "dep:memchr" ]; "std" = [ "memchr?/std" ]; }; resolvedDefaultFeatures = [ "default" "perf-literal" "std" ]; }; "ansi_term" = rec { crateName = "ansi_term"; version = "0.12.1"; edition = "2015"; sha256 = "1ljmkbilxgmhavxvxqa7qvm6f3fjggi7q2l3a72q9x0cxjvrnanm"; authors = [ "ogham@bsago.me" "Ryan Scheel (Havvy) " "Josh Triplett " ]; dependencies = [ { name = "winapi"; packageId = "winapi"; target = { target, features }: ("windows" == target."os" or null); features = [ "consoleapi" "errhandlingapi" "fileapi" "handleapi" "processenv" ]; } ]; features = { "derive_serde_style" = [ "serde" ]; "serde" = [ "dep:serde" ]; }; }; "anyhow" = rec { crateName = "anyhow"; version = "1.0.86"; edition = "2018"; sha256 = "1nk301x8qhpdaks6a9zvcp7yakjqnczjmqndbg7vk4494d3d1ldk"; authors = [ "David Tolnay " ]; features = { "backtrace" = [ "dep:backtrace" ]; "default" = [ "std" ]; }; resolvedDefaultFeatures = [ "default" "std" ]; }; "atty" = rec { crateName = "atty"; version = "0.2.14"; edition = "2015"; sha256 = "1s7yslcs6a28c5vz7jwj63lkfgyx8mx99fdirlhi9lbhhzhrpcyr"; authors = [ "softprops " ]; dependencies = [ { name = "hermit-abi"; packageId = "hermit-abi"; target = { target, features }: ("hermit" == target."os" or null); } { name = "libc"; packageId = "libc"; usesDefaultFeatures = false; target = { target, features }: (target."unix" or false); } { name = "winapi"; packageId = "winapi"; target = { target, features }: (target."windows" or false); features = [ "consoleapi" "processenv" "minwinbase" "minwindef" "winbase" ]; } ]; }; "bitflags 1.3.2" = rec { crateName = "bitflags"; version = "1.3.2"; edition = "2018"; sha256 = "12ki6w8gn1ldq7yz9y680llwk5gmrhrzszaa17g1sbrw2r2qvwxy"; authors = [ "The Rust Project Developers" ]; features = { "compiler_builtins" = [ "dep:compiler_builtins" ]; "core" = [ "dep:core" ]; "rustc-dep-of-std" = [ "core" "compiler_builtins" ]; }; resolvedDefaultFeatures = [ "default" ]; }; "bitflags 2.6.0" = rec { crateName = "bitflags"; version = "2.6.0"; edition = "2021"; sha256 = "1pkidwzn3hnxlsl8zizh0bncgbjnw7c41cx7bby26ncbzmiznj5h"; authors = [ "The Rust Project Developers" ]; features = { "arbitrary" = [ "dep:arbitrary" ]; "bytemuck" = [ "dep:bytemuck" ]; "compiler_builtins" = [ "dep:compiler_builtins" ]; "core" = [ "dep:core" ]; "rustc-dep-of-std" = [ "core" "compiler_builtins" ]; "serde" = [ "dep:serde" ]; }; }; "block-buffer" = rec { crateName = "block-buffer"; version = "0.10.4"; edition = "2018"; sha256 = "0w9sa2ypmrsqqvc20nhwr75wbb5cjr4kkyhpjm1z1lv2kdicfy1h"; libName = "block_buffer"; authors = [ "RustCrypto Developers" ]; dependencies = [ { name = "generic-array"; packageId = "generic-array"; } ]; }; "bstr" = rec { crateName = "bstr"; version = "1.9.1"; edition = "2021"; sha256 = "01ipr5rncw3kf4dyc1p2g00njn1df2b0xpviwhb8830iv77wbvq5"; authors = [ "Andrew Gallant " ]; dependencies = [ { name = "memchr"; packageId = "memchr"; usesDefaultFeatures = false; } { name = "serde"; packageId = "serde"; optional = true; usesDefaultFeatures = false; } ]; features = { "alloc" = [ "memchr/alloc" "serde?/alloc" ]; "default" = [ "std" "unicode" ]; "serde" = [ "dep:serde" ]; "std" = [ "alloc" "memchr/std" "serde?/std" ]; "unicode" = [ "dep:regex-automata" ]; }; resolvedDefaultFeatures = [ "alloc" "std" ]; }; "camino" = rec { crateName = "camino"; version = "1.1.7"; edition = "2018"; sha256 = "0ff28kc3qjcrmi8k88b2j2p7mzrvbag20yqcrj9sl30n3fanpv70"; authors = [ "Without Boats " "Ashley Williams " "Steve Klabnik " "Rain " ]; dependencies = [ { name = "serde"; packageId = "serde"; optional = true; features = [ "derive" ]; } ]; features = { "proptest" = [ "dep:proptest" ]; "proptest1" = [ "proptest" ]; "serde" = [ "dep:serde" ]; "serde1" = [ "serde" ]; }; resolvedDefaultFeatures = [ "serde" "serde1" ]; }; "cargo-platform" = rec { crateName = "cargo-platform"; version = "0.1.8"; edition = "2021"; sha256 = "1z5b7ivbj508wkqdg2vb0hw4vi1k1pyhcn6h1h1b8svcb8vg1c94"; libName = "cargo_platform"; dependencies = [ { name = "serde"; packageId = "serde"; } ]; }; "cargo_metadata" = rec { crateName = "cargo_metadata"; version = "0.18.1"; edition = "2018"; sha256 = "0drh0zndl4qgndy6kg6783cydbvhxgv0hcg7d9hhqx0zwi3nb21d"; authors = [ "Oliver Schneider " ]; dependencies = [ { name = "camino"; packageId = "camino"; features = [ "serde1" ]; } { name = "cargo-platform"; packageId = "cargo-platform"; } { name = "semver"; packageId = "semver"; features = [ "serde" ]; } { name = "serde"; packageId = "serde"; features = [ "derive" ]; } { name = "serde_json"; packageId = "serde_json"; features = [ "unbounded_depth" ]; } { name = "thiserror"; packageId = "thiserror"; } ]; features = { "builder" = [ "derive_builder" ]; "derive_builder" = [ "dep:derive_builder" ]; }; resolvedDefaultFeatures = [ "default" ]; }; "cfg-if" = rec { crateName = "cfg-if"; version = "1.0.0"; edition = "2018"; sha256 = "1za0vb97n4brpzpv8lsbnzmq5r8f2b0cpqqr0sy8h5bn751xxwds"; libName = "cfg_if"; authors = [ "Alex Crichton " ]; features = { "compiler_builtins" = [ "dep:compiler_builtins" ]; "core" = [ "dep:core" ]; "rustc-dep-of-std" = [ "core" "compiler_builtins" ]; }; }; "clap" = rec { crateName = "clap"; version = "2.34.0"; edition = "2018"; sha256 = "071q5d8jfwbazi6zhik9xwpacx5i6kb2vkzy060vhf0c3120aqd0"; authors = [ "Kevin K. " ]; dependencies = [ { name = "ansi_term"; packageId = "ansi_term"; optional = true; target = { target, features }: (!(target."windows" or false)); } { name = "atty"; packageId = "atty"; optional = true; } { name = "bitflags"; packageId = "bitflags 1.3.2"; } { name = "strsim"; packageId = "strsim"; optional = true; } { name = "textwrap"; packageId = "textwrap"; } { name = "unicode-width"; packageId = "unicode-width"; } { name = "vec_map"; packageId = "vec_map"; optional = true; } ]; features = { "ansi_term" = [ "dep:ansi_term" ]; "atty" = [ "dep:atty" ]; "clippy" = [ "dep:clippy" ]; "color" = [ "ansi_term" "atty" ]; "default" = [ "suggestions" "color" "vec_map" ]; "doc" = [ "yaml" ]; "strsim" = [ "dep:strsim" ]; "suggestions" = [ "strsim" ]; "term_size" = [ "dep:term_size" ]; "vec_map" = [ "dep:vec_map" ]; "wrap_help" = [ "term_size" "textwrap/term_size" ]; "yaml" = [ "yaml-rust" ]; "yaml-rust" = [ "dep:yaml-rust" ]; }; resolvedDefaultFeatures = [ "ansi_term" "atty" "color" "default" "strsim" "suggestions" "vec_map" ]; }; "colored-diff" = rec { crateName = "colored-diff"; version = "0.2.3"; edition = "2015"; sha256 = "1dfwjxd13f8l8bdzm76kkp6cp4sr1pyc8lavp52avwy313mhh0j1"; libName = "colored_diff"; dependencies = [ { name = "ansi_term"; packageId = "ansi_term"; } { name = "dissimilar"; packageId = "dissimilar"; } { name = "itertools"; packageId = "itertools 0.10.5"; usesDefaultFeatures = false; } ]; }; "cpufeatures" = rec { crateName = "cpufeatures"; version = "0.2.12"; edition = "2018"; sha256 = "012m7rrak4girqlii3jnqwrr73gv1i980q4wra5yyyhvzwk5xzjk"; authors = [ "RustCrypto Developers" ]; dependencies = [ { name = "libc"; packageId = "libc"; target = { target, features }: (target.name == "aarch64-linux-android"); } { name = "libc"; packageId = "libc"; target = { target, features }: (("aarch64" == target."arch" or null) && ("linux" == target."os" or null)); } { name = "libc"; packageId = "libc"; target = { target, features }: (("aarch64" == target."arch" or null) && ("apple" == target."vendor" or null)); } { name = "libc"; packageId = "libc"; target = { target, features }: (("loongarch64" == target."arch" or null) && ("linux" == target."os" or null)); } ]; }; "crate2nix" = rec { crateName = "crate2nix"; version = "0.15.0"; edition = "2021"; crateBin = [ { name = "crate2nix"; path = "src/main.rs"; requiredFeatures = [ ]; } ]; src = lib.cleanSourceWith { filter = sourceFilter; src = ./.; }; authors = [ "Peter Kolloch " ]; dependencies = [ { name = "anyhow"; packageId = "anyhow"; } { name = "cargo-platform"; packageId = "cargo-platform"; } { name = "cargo_metadata"; packageId = "cargo_metadata"; } { name = "hex"; packageId = "hex"; } { name = "itertools"; packageId = "itertools 0.12.1"; } { name = "lazy_static"; packageId = "lazy_static"; } { name = "nix-base32"; packageId = "nix-base32"; } { name = "pathdiff"; packageId = "pathdiff"; } { name = "semver"; packageId = "semver"; features = [ "serde" ]; } { name = "serde"; packageId = "serde"; features = [ "derive" ]; } { name = "serde_json"; packageId = "serde_json"; features = [ "unbounded_depth" ]; } { name = "structopt"; packageId = "structopt"; } { name = "tera"; packageId = "tera"; usesDefaultFeatures = false; } { name = "toml"; packageId = "toml"; } { name = "url"; packageId = "url"; features = [ "serde" ]; } ]; devDependencies = [ { name = "colored-diff"; packageId = "colored-diff"; } { name = "fs_extra"; packageId = "fs_extra"; } { name = "tempdir"; packageId = "tempdir"; } ]; }; "crossbeam-deque" = rec { crateName = "crossbeam-deque"; version = "0.8.5"; edition = "2021"; sha256 = "03bp38ljx4wj6vvy4fbhx41q8f585zyqix6pncz1mkz93z08qgv1"; libName = "crossbeam_deque"; dependencies = [ { name = "crossbeam-epoch"; packageId = "crossbeam-epoch"; usesDefaultFeatures = false; } { name = "crossbeam-utils"; packageId = "crossbeam-utils"; usesDefaultFeatures = false; } ]; features = { "default" = [ "std" ]; "std" = [ "crossbeam-epoch/std" "crossbeam-utils/std" ]; }; resolvedDefaultFeatures = [ "default" "std" ]; }; "crossbeam-epoch" = rec { crateName = "crossbeam-epoch"; version = "0.9.18"; edition = "2021"; sha256 = "03j2np8llwf376m3fxqx859mgp9f83hj1w34153c7a9c7i5ar0jv"; libName = "crossbeam_epoch"; dependencies = [ { name = "crossbeam-utils"; packageId = "crossbeam-utils"; usesDefaultFeatures = false; } ]; features = { "default" = [ "std" ]; "loom" = [ "loom-crate" "crossbeam-utils/loom" ]; "loom-crate" = [ "dep:loom-crate" ]; "nightly" = [ "crossbeam-utils/nightly" ]; "std" = [ "alloc" "crossbeam-utils/std" ]; }; resolvedDefaultFeatures = [ "alloc" "std" ]; }; "crossbeam-utils" = rec { crateName = "crossbeam-utils"; version = "0.8.20"; edition = "2021"; sha256 = "100fksq5mm1n7zj242cclkw6yf7a4a8ix3lvpfkhxvdhbda9kv12"; libName = "crossbeam_utils"; features = { "default" = [ "std" ]; "loom" = [ "dep:loom" ]; }; resolvedDefaultFeatures = [ "std" ]; }; "crypto-common" = rec { crateName = "crypto-common"; version = "0.1.6"; edition = "2018"; sha256 = "1cvby95a6xg7kxdz5ln3rl9xh66nz66w46mm3g56ri1z5x815yqv"; libName = "crypto_common"; authors = [ "RustCrypto Developers" ]; dependencies = [ { name = "generic-array"; packageId = "generic-array"; features = [ "more_lengths" ]; } { name = "typenum"; packageId = "typenum"; } ]; features = { "getrandom" = [ "rand_core/getrandom" ]; "rand_core" = [ "dep:rand_core" ]; }; }; "digest" = rec { crateName = "digest"; version = "0.10.7"; edition = "2018"; sha256 = "14p2n6ih29x81akj097lvz7wi9b6b9hvls0lwrv7b6xwyy0s5ncy"; authors = [ "RustCrypto Developers" ]; dependencies = [ { name = "block-buffer"; packageId = "block-buffer"; optional = true; } { name = "crypto-common"; packageId = "crypto-common"; } ]; features = { "blobby" = [ "dep:blobby" ]; "block-buffer" = [ "dep:block-buffer" ]; "const-oid" = [ "dep:const-oid" ]; "core-api" = [ "block-buffer" ]; "default" = [ "core-api" ]; "dev" = [ "blobby" ]; "mac" = [ "subtle" ]; "oid" = [ "const-oid" ]; "rand_core" = [ "crypto-common/rand_core" ]; "std" = [ "alloc" "crypto-common/std" ]; "subtle" = [ "dep:subtle" ]; }; resolvedDefaultFeatures = [ "block-buffer" "core-api" "default" ]; }; "dissimilar" = rec { crateName = "dissimilar"; version = "1.0.9"; edition = "2018"; sha256 = "0bcn4s99ghigd3yadpd7i3gljv5z2hkr07ijvvxvsxmz3yfygy2r"; authors = [ "David Tolnay " ]; }; "either" = rec { crateName = "either"; version = "1.13.0"; edition = "2018"; sha256 = "1w2c1mybrd7vljyxk77y9f4w9dyjrmp3yp82mk7bcm8848fazcb0"; authors = [ "bluss" ]; features = { "default" = [ "use_std" ]; "serde" = [ "dep:serde" ]; }; resolvedDefaultFeatures = [ "use_std" ]; }; "equivalent" = rec { crateName = "equivalent"; version = "1.0.1"; edition = "2015"; sha256 = "1malmx5f4lkfvqasz319lq6gb3ddg19yzf9s8cykfsgzdmyq0hsl"; }; "form_urlencoded" = rec { crateName = "form_urlencoded"; version = "1.2.1"; edition = "2018"; sha256 = "0milh8x7nl4f450s3ddhg57a3flcv6yq8hlkyk6fyr3mcb128dp1"; authors = [ "The rust-url developers" ]; dependencies = [ { name = "percent-encoding"; packageId = "percent-encoding"; usesDefaultFeatures = false; } ]; features = { "alloc" = [ "percent-encoding/alloc" ]; "default" = [ "std" ]; "std" = [ "alloc" "percent-encoding/std" ]; }; resolvedDefaultFeatures = [ "alloc" "default" "std" ]; }; "fs_extra" = rec { crateName = "fs_extra"; version = "1.3.0"; edition = "2018"; sha256 = "075i25z70j2mz9r7i9p9r521y8xdj81q7skslyb7zhqnnw33fw22"; authors = [ "Denis Kurilenko " ]; }; "fuchsia-cprng" = rec { crateName = "fuchsia-cprng"; version = "0.1.1"; edition = "2018"; sha256 = "1fnkqrbz7ixxzsb04bsz9p0zzazanma8znfdqjvh39n14vapfvx0"; libName = "fuchsia_cprng"; authors = [ "Erick Tryzelaar " ]; }; "generic-array" = rec { crateName = "generic-array"; version = "0.14.7"; edition = "2015"; sha256 = "16lyyrzrljfq424c3n8kfwkqihlimmsg5nhshbbp48np3yjrqr45"; libName = "generic_array"; authors = [ "Bartłomiej Kamiński " "Aaron Trent " ]; dependencies = [ { name = "typenum"; packageId = "typenum"; } ]; buildDependencies = [ { name = "version_check"; packageId = "version_check"; } ]; features = { "serde" = [ "dep:serde" ]; "zeroize" = [ "dep:zeroize" ]; }; resolvedDefaultFeatures = [ "more_lengths" ]; }; "globset" = rec { crateName = "globset"; version = "0.4.14"; edition = "2021"; sha256 = "1qab0c1drpybgm4nc92lf8b46x0ap44c9y4k23rndgc5bfdkpnjp"; authors = [ "Andrew Gallant " ]; dependencies = [ { name = "aho-corasick"; packageId = "aho-corasick"; } { name = "bstr"; packageId = "bstr"; usesDefaultFeatures = false; features = [ "std" ]; } { name = "log"; packageId = "log"; optional = true; } { name = "regex-automata"; packageId = "regex-automata"; usesDefaultFeatures = false; features = [ "std" "perf" "syntax" "meta" "nfa" "hybrid" ]; } { name = "regex-syntax"; packageId = "regex-syntax"; usesDefaultFeatures = false; features = [ "std" ]; } ]; features = { "default" = [ "log" ]; "log" = [ "dep:log" ]; "serde" = [ "dep:serde" ]; "serde1" = [ "serde" ]; }; resolvedDefaultFeatures = [ "default" "log" ]; }; "globwalk" = rec { crateName = "globwalk"; version = "0.9.1"; edition = "2021"; sha256 = "0mz7bsa66p2rrgnz3l94ac4kbklh7mq8j30iizyxjy4qyvmn1xqb"; authors = [ "Gilad Naaman " ]; dependencies = [ { name = "bitflags"; packageId = "bitflags 2.6.0"; } { name = "ignore"; packageId = "ignore"; } { name = "walkdir"; packageId = "walkdir"; } ]; }; "hashbrown" = rec { crateName = "hashbrown"; version = "0.14.5"; edition = "2021"; sha256 = "1wa1vy1xs3mp11bn3z9dv0jricgr6a2j0zkf1g19yz3vw4il89z5"; authors = [ "Amanieu d'Antras " ]; features = { "ahash" = [ "dep:ahash" ]; "alloc" = [ "dep:alloc" ]; "allocator-api2" = [ "dep:allocator-api2" ]; "compiler_builtins" = [ "dep:compiler_builtins" ]; "core" = [ "dep:core" ]; "default" = [ "ahash" "inline-more" "allocator-api2" ]; "equivalent" = [ "dep:equivalent" ]; "nightly" = [ "allocator-api2?/nightly" "bumpalo/allocator_api" ]; "rayon" = [ "dep:rayon" ]; "rkyv" = [ "dep:rkyv" ]; "rustc-dep-of-std" = [ "nightly" "core" "compiler_builtins" "alloc" "rustc-internal-api" ]; "serde" = [ "dep:serde" ]; }; resolvedDefaultFeatures = [ "raw" ]; }; "heck" = rec { crateName = "heck"; version = "0.3.3"; edition = "2018"; sha256 = "0b0kkr790p66lvzn9nsmfjvydrbmh9z5gb664jchwgw64vxiwqkd"; authors = [ "Without Boats " ]; dependencies = [ { name = "unicode-segmentation"; packageId = "unicode-segmentation"; } ]; }; "hermit-abi" = rec { crateName = "hermit-abi"; version = "0.1.19"; edition = "2018"; sha256 = "0cxcm8093nf5fyn114w8vxbrbcyvv91d4015rdnlgfll7cs6gd32"; libName = "hermit_abi"; authors = [ "Stefan Lankes" ]; dependencies = [ { name = "libc"; packageId = "libc"; usesDefaultFeatures = false; } ]; features = { "compiler_builtins" = [ "dep:compiler_builtins" ]; "core" = [ "dep:core" ]; "rustc-dep-of-std" = [ "core" "compiler_builtins/rustc-dep-of-std" "libc/rustc-dep-of-std" ]; }; resolvedDefaultFeatures = [ "default" ]; }; "hex" = rec { crateName = "hex"; version = "0.4.3"; edition = "2018"; sha256 = "0w1a4davm1lgzpamwnba907aysmlrnygbqmfis2mqjx5m552a93z"; authors = [ "KokaKiwi " ]; features = { "default" = [ "std" ]; "serde" = [ "dep:serde" ]; "std" = [ "alloc" ]; }; resolvedDefaultFeatures = [ "alloc" "default" "std" ]; }; "idna" = rec { crateName = "idna"; version = "0.5.0"; edition = "2018"; sha256 = "1xhjrcjqq0l5bpzvdgylvpkgk94panxgsirzhjnnqfdgc4a9nkb3"; authors = [ "The rust-url developers" ]; dependencies = [ { name = "unicode-bidi"; packageId = "unicode-bidi"; usesDefaultFeatures = false; features = [ "hardcoded-data" ]; } { name = "unicode-normalization"; packageId = "unicode-normalization"; usesDefaultFeatures = false; } ]; features = { "default" = [ "std" ]; "std" = [ "alloc" "unicode-bidi/std" "unicode-normalization/std" ]; }; resolvedDefaultFeatures = [ "alloc" "default" "std" ]; }; "ignore" = rec { crateName = "ignore"; version = "0.4.22"; edition = "2021"; sha256 = "1wcaqpi6djqgi1brghrdyw4d5qgnwzhqrqyn4mar4vp677gi0s5l"; authors = [ "Andrew Gallant " ]; dependencies = [ { name = "crossbeam-deque"; packageId = "crossbeam-deque"; } { name = "globset"; packageId = "globset"; } { name = "log"; packageId = "log"; } { name = "memchr"; packageId = "memchr"; } { name = "regex-automata"; packageId = "regex-automata"; usesDefaultFeatures = false; features = [ "std" "perf" "syntax" "meta" "nfa" "hybrid" "dfa-onepass" ]; } { name = "same-file"; packageId = "same-file"; } { name = "walkdir"; packageId = "walkdir"; } { name = "winapi-util"; packageId = "winapi-util"; target = { target, features }: (target."windows" or false); } ]; features = { }; }; "indexmap" = rec { crateName = "indexmap"; version = "2.2.6"; edition = "2021"; sha256 = "09hgwi2ig0wyj5rjziia76zmhgfj95k0jb4ic3iiawm4vlavg3qn"; dependencies = [ { name = "equivalent"; packageId = "equivalent"; usesDefaultFeatures = false; } { name = "hashbrown"; packageId = "hashbrown"; usesDefaultFeatures = false; features = [ "raw" ]; } ]; features = { "arbitrary" = [ "dep:arbitrary" ]; "borsh" = [ "dep:borsh" ]; "default" = [ "std" ]; "quickcheck" = [ "dep:quickcheck" ]; "rayon" = [ "dep:rayon" ]; "rustc-rayon" = [ "dep:rustc-rayon" ]; "serde" = [ "dep:serde" ]; }; resolvedDefaultFeatures = [ "default" "std" ]; }; "itertools 0.10.5" = rec { crateName = "itertools"; version = "0.10.5"; edition = "2018"; sha256 = "0ww45h7nxx5kj6z2y6chlskxd1igvs4j507anr6dzg99x1h25zdh"; authors = [ "bluss" ]; dependencies = [ { name = "either"; packageId = "either"; usesDefaultFeatures = false; } ]; features = { "default" = [ "use_std" ]; "use_std" = [ "use_alloc" "either/use_std" ]; }; }; "itertools 0.12.1" = rec { crateName = "itertools"; version = "0.12.1"; edition = "2018"; sha256 = "0s95jbb3ndj1lvfxyq5wanc0fm0r6hg6q4ngb92qlfdxvci10ads"; authors = [ "bluss" ]; dependencies = [ { name = "either"; packageId = "either"; usesDefaultFeatures = false; } ]; features = { "default" = [ "use_std" ]; "use_std" = [ "use_alloc" "either/use_std" ]; }; resolvedDefaultFeatures = [ "default" "use_alloc" "use_std" ]; }; "itoa" = rec { crateName = "itoa"; version = "1.0.11"; edition = "2018"; sha256 = "0nv9cqjwzr3q58qz84dcz63ggc54yhf1yqar1m858m1kfd4g3wa9"; authors = [ "David Tolnay " ]; features = { "no-panic" = [ "dep:no-panic" ]; }; }; "lazy_static" = rec { crateName = "lazy_static"; version = "1.5.0"; edition = "2015"; sha256 = "1zk6dqqni0193xg6iijh7i3i44sryglwgvx20spdvwk3r6sbrlmv"; authors = [ "Marvin Löbel " ]; features = { "spin" = [ "dep:spin" ]; "spin_no_std" = [ "spin" ]; }; }; "libc" = rec { crateName = "libc"; version = "0.2.155"; edition = "2015"; sha256 = "0z44c53z54znna8n322k5iwg80arxxpdzjj5260pxxzc9a58icwp"; authors = [ "The Rust Project Developers" ]; features = { "default" = [ "std" ]; "rustc-dep-of-std" = [ "align" "rustc-std-workspace-core" ]; "rustc-std-workspace-core" = [ "dep:rustc-std-workspace-core" ]; "use_std" = [ "std" ]; }; resolvedDefaultFeatures = [ "default" "std" ]; }; "log" = rec { crateName = "log"; version = "0.4.22"; edition = "2021"; sha256 = "093vs0wkm1rgyykk7fjbqp2lwizbixac1w52gv109p5r4jh0p9x7"; authors = [ "The Rust Project Developers" ]; features = { "kv_serde" = [ "kv_std" "value-bag/serde" "serde" ]; "kv_std" = [ "std" "kv" "value-bag/error" ]; "kv_sval" = [ "kv" "value-bag/sval" "sval" "sval_ref" ]; "kv_unstable" = [ "kv" "value-bag" ]; "kv_unstable_serde" = [ "kv_serde" "kv_unstable_std" ]; "kv_unstable_std" = [ "kv_std" "kv_unstable" ]; "kv_unstable_sval" = [ "kv_sval" "kv_unstable" ]; "serde" = [ "dep:serde" ]; "sval" = [ "dep:sval" ]; "sval_ref" = [ "dep:sval_ref" ]; "value-bag" = [ "dep:value-bag" ]; }; }; "memchr" = rec { crateName = "memchr"; version = "2.7.4"; edition = "2021"; sha256 = "18z32bhxrax0fnjikv475z7ii718hq457qwmaryixfxsl2qrmjkq"; authors = [ "Andrew Gallant " "bluss" ]; features = { "compiler_builtins" = [ "dep:compiler_builtins" ]; "core" = [ "dep:core" ]; "default" = [ "std" ]; "logging" = [ "dep:log" ]; "rustc-dep-of-std" = [ "core" "compiler_builtins" ]; "std" = [ "alloc" ]; "use_std" = [ "std" ]; }; resolvedDefaultFeatures = [ "alloc" "default" "std" ]; }; "nix-base32" = rec { crateName = "nix-base32"; version = "0.1.1"; edition = "2018"; sha256 = "04jnq6arig0amz0scadavbzn9bg9k4zphmrm1562n6ygfj1dnj45"; libName = "nix_base32"; authors = [ "Peter Kolloch " ]; }; "once_cell" = rec { crateName = "once_cell"; version = "1.19.0"; edition = "2021"; sha256 = "14kvw7px5z96dk4dwdm1r9cqhhy2cyj1l5n5b29mynbb8yr15nrz"; authors = [ "Aleksey Kladov " ]; features = { "alloc" = [ "race" ]; "atomic-polyfill" = [ "critical-section" ]; "critical-section" = [ "dep:critical-section" "portable-atomic" ]; "default" = [ "std" ]; "parking_lot" = [ "dep:parking_lot_core" ]; "portable-atomic" = [ "dep:portable-atomic" ]; "std" = [ "alloc" ]; }; resolvedDefaultFeatures = [ "alloc" "default" "race" "std" ]; }; "pathdiff" = rec { crateName = "pathdiff"; version = "0.2.1"; edition = "2018"; sha256 = "1pa4dcmb7lwir4himg1mnl97a05b2z0svczg62l8940pbim12dc8"; authors = [ "Manish Goregaokar " ]; features = { "camino" = [ "dep:camino" ]; }; }; "percent-encoding" = rec { crateName = "percent-encoding"; version = "2.3.1"; edition = "2018"; sha256 = "0gi8wgx0dcy8rnv1kywdv98lwcx67hz0a0zwpib5v2i08r88y573"; libName = "percent_encoding"; authors = [ "The rust-url developers" ]; features = { "default" = [ "std" ]; "std" = [ "alloc" ]; }; resolvedDefaultFeatures = [ "alloc" "default" "std" ]; }; "pest" = rec { crateName = "pest"; version = "2.7.10"; edition = "2021"; sha256 = "1s4fvis7h6l872g6nk17r130kcllj4c0hjvwkzd3hi196g3320an"; authors = [ "Dragoș Tiselice " ]; dependencies = [ { name = "memchr"; packageId = "memchr"; optional = true; } { name = "thiserror"; packageId = "thiserror"; optional = true; } { name = "ucd-trie"; packageId = "ucd-trie"; usesDefaultFeatures = false; } ]; features = { "default" = [ "std" "memchr" ]; "memchr" = [ "dep:memchr" ]; "pretty-print" = [ "dep:serde" "dep:serde_json" ]; "std" = [ "ucd-trie/std" "dep:thiserror" ]; }; resolvedDefaultFeatures = [ "default" "memchr" "std" ]; }; "pest_derive" = rec { crateName = "pest_derive"; version = "2.7.10"; edition = "2021"; sha256 = "0n8lsk9s21dp7958p9yarbk2gsc8wg0rvdzr7cd7pjpvjf8kqa96"; procMacro = true; authors = [ "Dragoș Tiselice " ]; dependencies = [ { name = "pest"; packageId = "pest"; usesDefaultFeatures = false; } { name = "pest_generator"; packageId = "pest_generator"; usesDefaultFeatures = false; } ]; features = { "default" = [ "std" ]; "grammar-extras" = [ "pest_generator/grammar-extras" ]; "not-bootstrap-in-src" = [ "pest_generator/not-bootstrap-in-src" ]; "std" = [ "pest/std" "pest_generator/std" ]; }; resolvedDefaultFeatures = [ "default" "std" ]; }; "pest_generator" = rec { crateName = "pest_generator"; version = "2.7.10"; edition = "2021"; sha256 = "11s6q0vf25lckbzak0qndzpv87ksaxy6pa9cvn2hlizvsgvjmhiy"; authors = [ "Dragoș Tiselice " ]; dependencies = [ { name = "pest"; packageId = "pest"; usesDefaultFeatures = false; } { name = "pest_meta"; packageId = "pest_meta"; } { name = "proc-macro2"; packageId = "proc-macro2"; } { name = "quote"; packageId = "quote"; } { name = "syn"; packageId = "syn 2.0.68"; } ]; features = { "default" = [ "std" ]; "grammar-extras" = [ "pest_meta/grammar-extras" ]; "not-bootstrap-in-src" = [ "pest_meta/not-bootstrap-in-src" ]; "std" = [ "pest/std" ]; }; resolvedDefaultFeatures = [ "std" ]; }; "pest_meta" = rec { crateName = "pest_meta"; version = "2.7.10"; edition = "2021"; sha256 = "1kdxl164yyjsmn01lvllsll4sz3xbgy4dmkq33n63hrp5w1418np"; authors = [ "Dragoș Tiselice " ]; dependencies = [ { name = "once_cell"; packageId = "once_cell"; } { name = "pest"; packageId = "pest"; } ]; buildDependencies = [ { name = "sha2"; packageId = "sha2"; usesDefaultFeatures = false; } ]; features = { "not-bootstrap-in-src" = [ "dep:cargo" ]; }; resolvedDefaultFeatures = [ "default" ]; }; "proc-macro-error" = rec { crateName = "proc-macro-error"; version = "1.0.4"; edition = "2018"; sha256 = "1373bhxaf0pagd8zkyd03kkx6bchzf6g0dkwrwzsnal9z47lj9fs"; libName = "proc_macro_error"; authors = [ "CreepySkeleton " ]; dependencies = [ { name = "proc-macro-error-attr"; packageId = "proc-macro-error-attr"; } { name = "proc-macro2"; packageId = "proc-macro2"; } { name = "quote"; packageId = "quote"; } { name = "syn"; packageId = "syn 1.0.109"; optional = true; usesDefaultFeatures = false; } ]; buildDependencies = [ { name = "version_check"; packageId = "version_check"; } ]; features = { "default" = [ "syn-error" ]; "syn" = [ "dep:syn" ]; "syn-error" = [ "syn" ]; }; resolvedDefaultFeatures = [ "default" "syn" "syn-error" ]; }; "proc-macro-error-attr" = rec { crateName = "proc-macro-error-attr"; version = "1.0.4"; edition = "2018"; sha256 = "0sgq6m5jfmasmwwy8x4mjygx5l7kp8s4j60bv25ckv2j1qc41gm1"; procMacro = true; libName = "proc_macro_error_attr"; authors = [ "CreepySkeleton " ]; dependencies = [ { name = "proc-macro2"; packageId = "proc-macro2"; } { name = "quote"; packageId = "quote"; } ]; buildDependencies = [ { name = "version_check"; packageId = "version_check"; } ]; }; "proc-macro2" = rec { crateName = "proc-macro2"; version = "1.0.86"; edition = "2021"; sha256 = "0xrv22p8lqlfdf1w0pj4si8n2ws4aw0kilmziwf0vpv5ys6rwway"; libName = "proc_macro2"; authors = [ "David Tolnay " "Alex Crichton " ]; dependencies = [ { name = "unicode-ident"; packageId = "unicode-ident"; } ]; features = { "default" = [ "proc-macro" ]; }; resolvedDefaultFeatures = [ "default" "proc-macro" ]; }; "quote" = rec { crateName = "quote"; version = "1.0.36"; edition = "2018"; sha256 = "19xcmh445bg6simirnnd4fvkmp6v2qiwxh5f6rw4a70h76pnm9qg"; authors = [ "David Tolnay " ]; dependencies = [ { name = "proc-macro2"; packageId = "proc-macro2"; usesDefaultFeatures = false; } ]; features = { "default" = [ "proc-macro" ]; "proc-macro" = [ "proc-macro2/proc-macro" ]; }; resolvedDefaultFeatures = [ "default" "proc-macro" ]; }; "rand" = rec { crateName = "rand"; version = "0.4.6"; edition = "2015"; sha256 = "14qjfv3gggzhnma20k0sc1jf8y6pplsaq7n1j9ls5c8kf2wl0a2m"; authors = [ "The Rust Project Developers" ]; dependencies = [ { name = "fuchsia-cprng"; packageId = "fuchsia-cprng"; target = { target, features }: ("fuchsia" == target."os" or null); } { name = "libc"; packageId = "libc"; optional = true; target = { target, features }: (target."unix" or false); } { name = "rand_core"; packageId = "rand_core 0.3.1"; usesDefaultFeatures = false; target = { target, features }: ("sgx" == target."env" or null); } { name = "rdrand"; packageId = "rdrand"; target = { target, features }: ("sgx" == target."env" or null); } { name = "winapi"; packageId = "winapi"; target = { target, features }: (target."windows" or false); features = [ "minwindef" "ntsecapi" "profileapi" "winnt" ]; } ]; features = { "default" = [ "std" ]; "libc" = [ "dep:libc" ]; "nightly" = [ "i128_support" ]; "std" = [ "libc" ]; }; resolvedDefaultFeatures = [ "default" "libc" "std" ]; }; "rand_core 0.3.1" = rec { crateName = "rand_core"; version = "0.3.1"; edition = "2015"; sha256 = "0jzdgszfa4bliigiy4hi66k7fs3gfwi2qxn8vik84ph77fwdwvvs"; authors = [ "The Rand Project Developers" "The Rust Project Developers" ]; dependencies = [ { name = "rand_core"; packageId = "rand_core 0.4.2"; } ]; features = { "alloc" = [ "rand_core/alloc" ]; "default" = [ "std" ]; "serde1" = [ "rand_core/serde1" ]; "std" = [ "rand_core/std" ]; }; }; "rand_core 0.4.2" = rec { crateName = "rand_core"; version = "0.4.2"; edition = "2015"; sha256 = "1p09ynysrq1vcdlmcqnapq4qakl2yd1ng3kxh3qscpx09k2a6cww"; authors = [ "The Rand Project Developers" "The Rust Project Developers" ]; features = { "serde" = [ "dep:serde" ]; "serde1" = [ "serde" "serde_derive" ]; "serde_derive" = [ "dep:serde_derive" ]; "std" = [ "alloc" ]; }; }; "rdrand" = rec { crateName = "rdrand"; version = "0.4.0"; edition = "2015"; sha256 = "1cjq0kwx1bk7jx3kzyciiish5gqsj7620dm43dc52sr8fzmm9037"; authors = [ "Simonas Kazlauskas " ]; dependencies = [ { name = "rand_core"; packageId = "rand_core 0.3.1"; usesDefaultFeatures = false; } ]; features = { "default" = [ "std" ]; }; resolvedDefaultFeatures = [ "default" "std" ]; }; "regex" = rec { crateName = "regex"; version = "1.10.5"; edition = "2021"; sha256 = "0zsiqk2sxc1kd46qw0yp87s2a14ialwyxinpl0k266ddkm1i64mr"; authors = [ "The Rust Project Developers" "Andrew Gallant " ]; dependencies = [ { name = "aho-corasick"; packageId = "aho-corasick"; optional = true; usesDefaultFeatures = false; } { name = "memchr"; packageId = "memchr"; optional = true; usesDefaultFeatures = false; } { name = "regex-automata"; packageId = "regex-automata"; usesDefaultFeatures = false; features = [ "alloc" "syntax" "meta" "nfa-pikevm" ]; } { name = "regex-syntax"; packageId = "regex-syntax"; usesDefaultFeatures = false; } ]; features = { "default" = [ "std" "perf" "unicode" "regex-syntax/default" ]; "logging" = [ "aho-corasick?/logging" "memchr?/logging" "regex-automata/logging" ]; "perf" = [ "perf-cache" "perf-dfa" "perf-onepass" "perf-backtrack" "perf-inline" "perf-literal" ]; "perf-backtrack" = [ "regex-automata/nfa-backtrack" ]; "perf-dfa" = [ "regex-automata/hybrid" ]; "perf-dfa-full" = [ "regex-automata/dfa-build" "regex-automata/dfa-search" ]; "perf-inline" = [ "regex-automata/perf-inline" ]; "perf-literal" = [ "dep:aho-corasick" "dep:memchr" "regex-automata/perf-literal" ]; "perf-onepass" = [ "regex-automata/dfa-onepass" ]; "std" = [ "aho-corasick?/std" "memchr?/std" "regex-automata/std" "regex-syntax/std" ]; "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "regex-automata/unicode" "regex-syntax/unicode" ]; "unicode-age" = [ "regex-automata/unicode-age" "regex-syntax/unicode-age" ]; "unicode-bool" = [ "regex-automata/unicode-bool" "regex-syntax/unicode-bool" ]; "unicode-case" = [ "regex-automata/unicode-case" "regex-syntax/unicode-case" ]; "unicode-gencat" = [ "regex-automata/unicode-gencat" "regex-syntax/unicode-gencat" ]; "unicode-perl" = [ "regex-automata/unicode-perl" "regex-automata/unicode-word-boundary" "regex-syntax/unicode-perl" ]; "unicode-script" = [ "regex-automata/unicode-script" "regex-syntax/unicode-script" ]; "unicode-segment" = [ "regex-automata/unicode-segment" "regex-syntax/unicode-segment" ]; "unstable" = [ "pattern" ]; "use_std" = [ "std" ]; }; resolvedDefaultFeatures = [ "default" "perf" "perf-backtrack" "perf-cache" "perf-dfa" "perf-inline" "perf-literal" "perf-onepass" "std" "unicode" "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ]; }; "regex-automata" = rec { crateName = "regex-automata"; version = "0.4.7"; edition = "2021"; sha256 = "1pwjdi4jckpbaivpl6x4v5g4crb37zr2wac93wlfsbzgqn6gbjiq"; libName = "regex_automata"; authors = [ "The Rust Project Developers" "Andrew Gallant " ]; dependencies = [ { name = "aho-corasick"; packageId = "aho-corasick"; optional = true; usesDefaultFeatures = false; } { name = "memchr"; packageId = "memchr"; optional = true; usesDefaultFeatures = false; } { name = "regex-syntax"; packageId = "regex-syntax"; optional = true; usesDefaultFeatures = false; } ]; features = { "default" = [ "std" "syntax" "perf" "unicode" "meta" "nfa" "dfa" "hybrid" ]; "dfa" = [ "dfa-build" "dfa-search" "dfa-onepass" ]; "dfa-build" = [ "nfa-thompson" "dfa-search" ]; "dfa-onepass" = [ "nfa-thompson" ]; "hybrid" = [ "alloc" "nfa-thompson" ]; "internal-instrument" = [ "internal-instrument-pikevm" ]; "internal-instrument-pikevm" = [ "logging" "std" ]; "logging" = [ "dep:log" "aho-corasick?/logging" "memchr?/logging" ]; "meta" = [ "syntax" "nfa-pikevm" ]; "nfa" = [ "nfa-thompson" "nfa-pikevm" "nfa-backtrack" ]; "nfa-backtrack" = [ "nfa-thompson" ]; "nfa-pikevm" = [ "nfa-thompson" ]; "nfa-thompson" = [ "alloc" ]; "perf" = [ "perf-inline" "perf-literal" ]; "perf-literal" = [ "perf-literal-substring" "perf-literal-multisubstring" ]; "perf-literal-multisubstring" = [ "std" "dep:aho-corasick" ]; "perf-literal-substring" = [ "aho-corasick?/perf-literal" "dep:memchr" ]; "std" = [ "regex-syntax?/std" "memchr?/std" "aho-corasick?/std" "alloc" ]; "syntax" = [ "dep:regex-syntax" "alloc" ]; "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "unicode-word-boundary" "regex-syntax?/unicode" ]; "unicode-age" = [ "regex-syntax?/unicode-age" ]; "unicode-bool" = [ "regex-syntax?/unicode-bool" ]; "unicode-case" = [ "regex-syntax?/unicode-case" ]; "unicode-gencat" = [ "regex-syntax?/unicode-gencat" ]; "unicode-perl" = [ "regex-syntax?/unicode-perl" ]; "unicode-script" = [ "regex-syntax?/unicode-script" ]; "unicode-segment" = [ "regex-syntax?/unicode-segment" ]; }; resolvedDefaultFeatures = [ "alloc" "dfa-onepass" "hybrid" "meta" "nfa" "nfa-backtrack" "nfa-pikevm" "nfa-thompson" "perf" "perf-inline" "perf-literal" "perf-literal-multisubstring" "perf-literal-substring" "std" "syntax" "unicode" "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "unicode-word-boundary" ]; }; "regex-syntax" = rec { crateName = "regex-syntax"; version = "0.8.4"; edition = "2021"; sha256 = "16r0kjy20vx33dr4mhasj5l1f87czas714x2fz6zl0f8wwxa0rks"; libName = "regex_syntax"; authors = [ "The Rust Project Developers" "Andrew Gallant " ]; features = { "arbitrary" = [ "dep:arbitrary" ]; "default" = [ "std" "unicode" ]; "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ]; }; resolvedDefaultFeatures = [ "default" "std" "unicode" "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ]; }; "remove_dir_all" = rec { crateName = "remove_dir_all"; version = "0.5.3"; edition = "2015"; sha256 = "1rzqbsgkmr053bxxl04vmvsd1njyz0nxvly97aip6aa2cmb15k9s"; authors = [ "Aaronepower " ]; dependencies = [ { name = "winapi"; packageId = "winapi"; target = { target, features }: (target."windows" or false); features = [ "std" "errhandlingapi" "winerror" "fileapi" "winbase" ]; } ]; }; "ryu" = rec { crateName = "ryu"; version = "1.0.18"; edition = "2018"; sha256 = "17xx2s8j1lln7iackzd9p0sv546vjq71i779gphjq923vjh5pjzk"; authors = [ "David Tolnay " ]; features = { "no-panic" = [ "dep:no-panic" ]; }; }; "same-file" = rec { crateName = "same-file"; version = "1.0.6"; edition = "2018"; sha256 = "00h5j1w87dmhnvbv9l8bic3y7xxsnjmssvifw2ayvgx9mb1ivz4k"; libName = "same_file"; authors = [ "Andrew Gallant " ]; dependencies = [ { name = "winapi-util"; packageId = "winapi-util"; target = { target, features }: (target."windows" or false); } ]; }; "semver" = rec { crateName = "semver"; version = "1.0.23"; edition = "2018"; sha256 = "12wqpxfflclbq4dv8sa6gchdh92ahhwn4ci1ls22wlby3h57wsb1"; authors = [ "David Tolnay " ]; dependencies = [ { name = "serde"; packageId = "serde"; optional = true; usesDefaultFeatures = false; } ]; features = { "default" = [ "std" ]; "serde" = [ "dep:serde" ]; }; resolvedDefaultFeatures = [ "default" "serde" "std" ]; }; "serde" = rec { crateName = "serde"; version = "1.0.203"; edition = "2018"; sha256 = "1500ghq198n6py5anvz5qbqagd9h1hq04f4qpsvjzrvix56snlvj"; authors = [ "Erick Tryzelaar " "David Tolnay " ]; dependencies = [ { name = "serde_derive"; packageId = "serde_derive"; optional = true; } { name = "serde_derive"; packageId = "serde_derive"; target = { target, features }: false; } ]; devDependencies = [ { name = "serde_derive"; packageId = "serde_derive"; } ]; features = { "default" = [ "std" ]; "derive" = [ "serde_derive" ]; "serde_derive" = [ "dep:serde_derive" ]; }; resolvedDefaultFeatures = [ "alloc" "default" "derive" "serde_derive" "std" ]; }; "serde_derive" = rec { crateName = "serde_derive"; version = "1.0.203"; edition = "2015"; sha256 = "1fmmqmfza3mwxb1v80737dj01gznrh8mhgqgylkndx5npq7bq32h"; procMacro = true; authors = [ "Erick Tryzelaar " "David Tolnay " ]; dependencies = [ { name = "proc-macro2"; packageId = "proc-macro2"; usesDefaultFeatures = false; features = [ "proc-macro" ]; } { name = "quote"; packageId = "quote"; usesDefaultFeatures = false; features = [ "proc-macro" ]; } { name = "syn"; packageId = "syn 2.0.68"; usesDefaultFeatures = false; features = [ "clone-impls" "derive" "parsing" "printing" "proc-macro" ]; } ]; features = { }; resolvedDefaultFeatures = [ "default" ]; }; "serde_json" = rec { crateName = "serde_json"; version = "1.0.118"; edition = "2021"; sha256 = "1r7jpqdfnrv8skn5va1r202g6lhdhka0vyn42vm5g21x2srzciyr"; authors = [ "Erick Tryzelaar " "David Tolnay " ]; dependencies = [ { name = "itoa"; packageId = "itoa"; } { name = "ryu"; packageId = "ryu"; } { name = "serde"; packageId = "serde"; usesDefaultFeatures = false; } ]; devDependencies = [ { name = "serde"; packageId = "serde"; features = [ "derive" ]; } ]; features = { "alloc" = [ "serde/alloc" ]; "default" = [ "std" ]; "indexmap" = [ "dep:indexmap" ]; "preserve_order" = [ "indexmap" "std" ]; "std" = [ "serde/std" ]; }; resolvedDefaultFeatures = [ "default" "std" "unbounded_depth" ]; }; "serde_spanned" = rec { crateName = "serde_spanned"; version = "0.6.6"; edition = "2021"; sha256 = "1839b6m5p9ijjmcwamiya2r612ks2vg6w2pp95yg76lr3zh79rkr"; dependencies = [ { name = "serde"; packageId = "serde"; optional = true; } ]; devDependencies = [ { name = "serde"; packageId = "serde"; } ]; features = { "serde" = [ "dep:serde" ]; }; resolvedDefaultFeatures = [ "serde" ]; }; "sha2" = rec { crateName = "sha2"; version = "0.10.8"; edition = "2018"; sha256 = "1j1x78zk9il95w9iv46dh9wm73r6xrgj32y6lzzw7bxws9dbfgbr"; authors = [ "RustCrypto Developers" ]; dependencies = [ { name = "cfg-if"; packageId = "cfg-if"; } { name = "cpufeatures"; packageId = "cpufeatures"; target = { target, features }: (("aarch64" == target."arch" or null) || ("x86_64" == target."arch" or null) || ("x86" == target."arch" or null)); } { name = "digest"; packageId = "digest"; } ]; devDependencies = [ { name = "digest"; packageId = "digest"; features = [ "dev" ]; } ]; features = { "asm" = [ "sha2-asm" ]; "asm-aarch64" = [ "asm" ]; "default" = [ "std" ]; "oid" = [ "digest/oid" ]; "sha2-asm" = [ "dep:sha2-asm" ]; "std" = [ "digest/std" ]; }; }; "strsim" = rec { crateName = "strsim"; version = "0.8.0"; edition = "2015"; sha256 = "0sjsm7hrvjdifz661pjxq5w4hf190hx53fra8dfvamacvff139cf"; authors = [ "Danny Guo " ]; }; "structopt" = rec { crateName = "structopt"; version = "0.3.26"; edition = "2018"; sha256 = "043sg3qxllann6q9i71d05qp3q13scmcvhxhd950ka2v8ij5qsqc"; authors = [ "Guillaume Pinot " "others" ]; dependencies = [ { name = "clap"; packageId = "clap"; usesDefaultFeatures = false; } { name = "lazy_static"; packageId = "lazy_static"; } { name = "structopt-derive"; packageId = "structopt-derive"; } ]; features = { "color" = [ "clap/color" ]; "debug" = [ "clap/debug" ]; "default" = [ "clap/default" ]; "doc" = [ "clap/doc" ]; "lints" = [ "clap/lints" ]; "no_cargo" = [ "clap/no_cargo" ]; "paw" = [ "structopt-derive/paw" "paw_dep" ]; "paw_dep" = [ "dep:paw_dep" ]; "suggestions" = [ "clap/suggestions" ]; "wrap_help" = [ "clap/wrap_help" ]; "yaml" = [ "clap/yaml" ]; }; resolvedDefaultFeatures = [ "default" ]; }; "structopt-derive" = rec { crateName = "structopt-derive"; version = "0.4.18"; edition = "2018"; sha256 = "1q5gcigmvw0cinjxzpyrkflliq5r1ivljmrvfrl3phcwgwraxdfw"; procMacro = true; libName = "structopt_derive"; authors = [ "Guillaume Pinot " ]; dependencies = [ { name = "heck"; packageId = "heck"; } { name = "proc-macro-error"; packageId = "proc-macro-error"; } { name = "proc-macro2"; packageId = "proc-macro2"; } { name = "quote"; packageId = "quote"; } { name = "syn"; packageId = "syn 1.0.109"; features = [ "full" ]; } ]; features = { }; }; "syn 1.0.109" = rec { crateName = "syn"; version = "1.0.109"; edition = "2018"; sha256 = "0ds2if4600bd59wsv7jjgfkayfzy3hnazs394kz6zdkmna8l3dkj"; authors = [ "David Tolnay " ]; dependencies = [ { name = "proc-macro2"; packageId = "proc-macro2"; usesDefaultFeatures = false; } { name = "quote"; packageId = "quote"; optional = true; usesDefaultFeatures = false; } { name = "unicode-ident"; packageId = "unicode-ident"; } ]; features = { "default" = [ "derive" "parsing" "printing" "clone-impls" "proc-macro" ]; "printing" = [ "quote" ]; "proc-macro" = [ "proc-macro2/proc-macro" "quote/proc-macro" ]; "quote" = [ "dep:quote" ]; "test" = [ "syn-test-suite/all-features" ]; }; resolvedDefaultFeatures = [ "clone-impls" "default" "derive" "full" "parsing" "printing" "proc-macro" "quote" ]; }; "syn 2.0.68" = rec { crateName = "syn"; version = "2.0.68"; edition = "2021"; sha256 = "1sf1y2hajhjav38ipg63c934xrgkz4v42fz24a0ckmmri06sf7wh"; authors = [ "David Tolnay " ]; dependencies = [ { name = "proc-macro2"; packageId = "proc-macro2"; usesDefaultFeatures = false; } { name = "quote"; packageId = "quote"; optional = true; usesDefaultFeatures = false; } { name = "unicode-ident"; packageId = "unicode-ident"; } ]; features = { "default" = [ "derive" "parsing" "printing" "clone-impls" "proc-macro" ]; "printing" = [ "dep:quote" ]; "proc-macro" = [ "proc-macro2/proc-macro" "quote?/proc-macro" ]; "test" = [ "syn-test-suite/all-features" ]; }; resolvedDefaultFeatures = [ "clone-impls" "default" "derive" "parsing" "printing" "proc-macro" ]; }; "tempdir" = rec { crateName = "tempdir"; version = "0.3.7"; edition = "2015"; sha256 = "1n5n86zxpgd85y0mswrp5cfdisizq2rv3la906g6ipyc03xvbwhm"; authors = [ "The Rust Project Developers" ]; dependencies = [ { name = "rand"; packageId = "rand"; } { name = "remove_dir_all"; packageId = "remove_dir_all"; } ]; }; "tera" = rec { crateName = "tera"; version = "1.20.0"; edition = "2018"; sha256 = "1vnj9imw2h9szkd1izsrhwrc9jvazvdsp84x65wg2rg88ldqb7db"; authors = [ "Vincent Prouillet " ]; dependencies = [ { name = "globwalk"; packageId = "globwalk"; } { name = "lazy_static"; packageId = "lazy_static"; } { name = "pest"; packageId = "pest"; } { name = "pest_derive"; packageId = "pest_derive"; } { name = "regex"; packageId = "regex"; } { name = "serde"; packageId = "serde"; } { name = "serde_json"; packageId = "serde_json"; } { name = "unic-segment"; packageId = "unic-segment"; } ]; features = { "builtins" = [ "urlencode" "slug" "humansize" "chrono" "chrono-tz" "rand" ]; "chrono" = [ "dep:chrono" ]; "chrono-tz" = [ "dep:chrono-tz" ]; "date-locale" = [ "builtins" "chrono/unstable-locales" ]; "default" = [ "builtins" ]; "humansize" = [ "dep:humansize" ]; "percent-encoding" = [ "dep:percent-encoding" ]; "preserve_order" = [ "serde_json/preserve_order" ]; "rand" = [ "dep:rand" ]; "slug" = [ "dep:slug" ]; "urlencode" = [ "percent-encoding" ]; }; }; "textwrap" = rec { crateName = "textwrap"; version = "0.11.0"; edition = "2015"; sha256 = "0q5hky03ik3y50s9sz25r438bc4nwhqc6dqwynv4wylc807n29nk"; authors = [ "Martin Geisler " ]; dependencies = [ { name = "unicode-width"; packageId = "unicode-width"; } ]; features = { "hyphenation" = [ "dep:hyphenation" ]; "term_size" = [ "dep:term_size" ]; }; }; "thiserror" = rec { crateName = "thiserror"; version = "1.0.61"; edition = "2021"; sha256 = "028prh962l16cmjivwb1g9xalbpqip0305zhq006mg74dc6whin5"; authors = [ "David Tolnay " ]; dependencies = [ { name = "thiserror-impl"; packageId = "thiserror-impl"; } ]; }; "thiserror-impl" = rec { crateName = "thiserror-impl"; version = "1.0.61"; edition = "2021"; sha256 = "0cvm37hp0kbcyk1xac1z0chpbd9pbn2g456iyid6sah0a113ihs6"; procMacro = true; libName = "thiserror_impl"; authors = [ "David Tolnay " ]; dependencies = [ { name = "proc-macro2"; packageId = "proc-macro2"; } { name = "quote"; packageId = "quote"; } { name = "syn"; packageId = "syn 2.0.68"; } ]; }; "tinyvec" = rec { crateName = "tinyvec"; version = "1.6.1"; edition = "2018"; sha256 = "10idfhsvp7zhbr8pn37wfra2bn02vr5xg6mhdvrbxlp2zg31alf5"; authors = [ "Lokathor " ]; dependencies = [ { name = "tinyvec_macros"; packageId = "tinyvec_macros"; optional = true; } ]; features = { "alloc" = [ "tinyvec_macros" ]; "arbitrary" = [ "dep:arbitrary" ]; "real_blackbox" = [ "criterion/real_blackbox" ]; "rustc_1_57" = [ "rustc_1_55" ]; "serde" = [ "dep:serde" ]; "std" = [ "alloc" ]; "tinyvec_macros" = [ "dep:tinyvec_macros" ]; }; resolvedDefaultFeatures = [ "alloc" "default" "tinyvec_macros" ]; }; "tinyvec_macros" = rec { crateName = "tinyvec_macros"; version = "0.1.1"; edition = "2018"; sha256 = "081gag86208sc3y6sdkshgw3vysm5d34p431dzw0bshz66ncng0z"; authors = [ "Soveu " ]; }; "toml" = rec { crateName = "toml"; version = "0.8.14"; edition = "2021"; sha256 = "0dgk8bacrza09npifba1xsx7wyjjvhz3igxpdnyjcbqxn8mfnjbg"; authors = [ "Alex Crichton " ]; dependencies = [ { name = "serde"; packageId = "serde"; } { name = "serde_spanned"; packageId = "serde_spanned"; features = [ "serde" ]; } { name = "toml_datetime"; packageId = "toml_datetime"; features = [ "serde" ]; } { name = "toml_edit"; packageId = "toml_edit"; optional = true; usesDefaultFeatures = false; features = [ "serde" ]; } ]; devDependencies = [ { name = "serde"; packageId = "serde"; features = [ "derive" ]; } ]; features = { "default" = [ "parse" "display" ]; "display" = [ "dep:toml_edit" "toml_edit?/display" ]; "indexmap" = [ "dep:indexmap" ]; "parse" = [ "dep:toml_edit" "toml_edit?/parse" ]; "preserve_order" = [ "indexmap" ]; }; resolvedDefaultFeatures = [ "default" "display" "parse" ]; }; "toml_datetime" = rec { crateName = "toml_datetime"; version = "0.6.6"; edition = "2021"; sha256 = "1grcrr3gh7id3cy3j700kczwwfbn04p5ncrrj369prjaj9bgvbab"; authors = [ "Alex Crichton " ]; dependencies = [ { name = "serde"; packageId = "serde"; optional = true; } ]; features = { "serde" = [ "dep:serde" ]; }; resolvedDefaultFeatures = [ "serde" ]; }; "toml_edit" = rec { crateName = "toml_edit"; version = "0.22.14"; edition = "2021"; sha256 = "0f2fw0viqvisjhqwjavgypz5mgbldh53przrsjlrrggijyppl77j"; authors = [ "Andronik Ordian " "Ed Page " ]; dependencies = [ { name = "indexmap"; packageId = "indexmap"; features = [ "std" ]; } { name = "serde"; packageId = "serde"; optional = true; } { name = "serde_spanned"; packageId = "serde_spanned"; optional = true; features = [ "serde" ]; } { name = "toml_datetime"; packageId = "toml_datetime"; } { name = "winnow"; packageId = "winnow"; optional = true; } ]; features = { "default" = [ "parse" "display" ]; "parse" = [ "dep:winnow" ]; "perf" = [ "dep:kstring" ]; "serde" = [ "dep:serde" "toml_datetime/serde" "dep:serde_spanned" ]; }; resolvedDefaultFeatures = [ "display" "parse" "serde" ]; }; "typenum" = rec { crateName = "typenum"; version = "1.17.0"; edition = "2018"; sha256 = "09dqxv69m9lj9zvv6xw5vxaqx15ps0vxyy5myg33i0kbqvq0pzs2"; build = "build/main.rs"; authors = [ "Paho Lurie-Gregg " "Andre Bogus " ]; features = { "scale-info" = [ "dep:scale-info" ]; "scale_info" = [ "scale-info/derive" ]; }; }; "ucd-trie" = rec { crateName = "ucd-trie"; version = "0.1.6"; edition = "2021"; sha256 = "1ff4yfksirqs37ybin9aw71aa5gva00hw7jdxbw8w668zy964r7d"; libName = "ucd_trie"; authors = [ "Andrew Gallant " ]; features = { "default" = [ "std" ]; }; resolvedDefaultFeatures = [ "std" ]; }; "unic-char-property" = rec { crateName = "unic-char-property"; version = "0.9.0"; edition = "2018"; sha256 = "08g21dn3wwix3ycfl0vrbahn0835nv2q3swm8wms0vwvgm07mid8"; libName = "unic_char_property"; authors = [ "The UNIC Project Developers" ]; dependencies = [ { name = "unic-char-range"; packageId = "unic-char-range"; } ]; }; "unic-char-range" = rec { crateName = "unic-char-range"; version = "0.9.0"; edition = "2018"; sha256 = "1g0z7iwvjhqspi6194zsff8vy6i3921hpqcrp3v1813hbwnh5603"; libName = "unic_char_range"; authors = [ "The UNIC Project Developers" ]; features = { "rayon" = [ "dep:rayon" ]; "unstable" = [ "exact-size-is-empty" "fused" "trusted-len" ]; }; resolvedDefaultFeatures = [ "default" ]; }; "unic-common" = rec { crateName = "unic-common"; version = "0.9.0"; edition = "2018"; sha256 = "1g1mm954m0zr497dl4kx3vr09yaly290zs33bbl4wrbaba1gzmw0"; libName = "unic_common"; authors = [ "The UNIC Project Developers" ]; features = { }; resolvedDefaultFeatures = [ "default" ]; }; "unic-segment" = rec { crateName = "unic-segment"; version = "0.9.0"; edition = "2018"; sha256 = "08wgz2q6vrdvmbd23kf9pbg8cyzm5q8hq9spc4blzy2ppqk5vvg4"; libName = "unic_segment"; authors = [ "The UNIC Project Developers" ]; dependencies = [ { name = "unic-ucd-segment"; packageId = "unic-ucd-segment"; } ]; }; "unic-ucd-segment" = rec { crateName = "unic-ucd-segment"; version = "0.9.0"; edition = "2018"; sha256 = "0027lczcg0r401g6fnzm2bq9fxhgxvri1nlryhhv8192lqic2y90"; libName = "unic_ucd_segment"; authors = [ "The UNIC Project Developers" ]; dependencies = [ { name = "unic-char-property"; packageId = "unic-char-property"; } { name = "unic-char-range"; packageId = "unic-char-range"; } { name = "unic-ucd-version"; packageId = "unic-ucd-version"; } ]; }; "unic-ucd-version" = rec { crateName = "unic-ucd-version"; version = "0.9.0"; edition = "2018"; sha256 = "1i5hnzpfnxkp4ijfk8kvhpvj84bij575ybqx1b6hyigy6wi2zgcn"; libName = "unic_ucd_version"; authors = [ "The UNIC Project Developers" ]; dependencies = [ { name = "unic-common"; packageId = "unic-common"; } ]; }; "unicode-bidi" = rec { crateName = "unicode-bidi"; version = "0.3.15"; edition = "2018"; sha256 = "0xcdxm7h0ydyprwpcbh436rbs6s6lph7f3gr527lzgv6lw053y88"; libName = "unicode_bidi"; authors = [ "The Servo Project Developers" ]; features = { "default" = [ "std" "hardcoded-data" ]; "flame" = [ "dep:flame" ]; "flame_it" = [ "flame" "flamer" ]; "flamer" = [ "dep:flamer" ]; "serde" = [ "dep:serde" ]; "with_serde" = [ "serde" ]; }; resolvedDefaultFeatures = [ "hardcoded-data" "std" ]; }; "unicode-ident" = rec { crateName = "unicode-ident"; version = "1.0.12"; edition = "2018"; sha256 = "0jzf1znfpb2gx8nr8mvmyqs1crnv79l57nxnbiszc7xf7ynbjm1k"; libName = "unicode_ident"; authors = [ "David Tolnay " ]; }; "unicode-normalization" = rec { crateName = "unicode-normalization"; version = "0.1.23"; edition = "2018"; sha256 = "1x81a50h2zxigj74b9bqjsirxxbyhmis54kg600xj213vf31cvd5"; libName = "unicode_normalization"; authors = [ "kwantam " "Manish Goregaokar " ]; dependencies = [ { name = "tinyvec"; packageId = "tinyvec"; features = [ "alloc" ]; } ]; features = { "default" = [ "std" ]; }; resolvedDefaultFeatures = [ "std" ]; }; "unicode-segmentation" = rec { crateName = "unicode-segmentation"; version = "1.11.0"; edition = "2018"; sha256 = "00kjpwp1g8fqm45drmwivlacn3y9jx73bvs09n6s3x73nqi7vj6l"; libName = "unicode_segmentation"; authors = [ "kwantam " "Manish Goregaokar " ]; features = { }; }; "unicode-width" = rec { crateName = "unicode-width"; version = "0.1.13"; edition = "2021"; sha256 = "0p92vl8n7qc8mxz45xn6qbgi0259z96n32a158l6vj5bywwdadh3"; libName = "unicode_width"; authors = [ "kwantam " "Manish Goregaokar " ]; features = { "compiler_builtins" = [ "dep:compiler_builtins" ]; "core" = [ "dep:core" ]; "rustc-dep-of-std" = [ "std" "core" "compiler_builtins" ]; "std" = [ "dep:std" ]; }; resolvedDefaultFeatures = [ "default" ]; }; "url" = rec { crateName = "url"; version = "2.5.2"; edition = "2018"; sha256 = "0v2dx50mx7xzl9454cl5qmpjnhkbahmn59gd3apyipbgyyylsy12"; authors = [ "The rust-url developers" ]; dependencies = [ { name = "form_urlencoded"; packageId = "form_urlencoded"; } { name = "idna"; packageId = "idna"; } { name = "percent-encoding"; packageId = "percent-encoding"; } { name = "serde"; packageId = "serde"; optional = true; features = [ "derive" ]; } ]; devDependencies = [ { name = "serde"; packageId = "serde"; features = [ "derive" ]; } ]; features = { "serde" = [ "dep:serde" ]; }; resolvedDefaultFeatures = [ "default" "serde" ]; }; "vec_map" = rec { crateName = "vec_map"; version = "0.8.2"; edition = "2015"; sha256 = "1481w9g1dw9rxp3l6snkdqihzyrd2f8vispzqmwjwsdyhw8xzggi"; authors = [ "Alex Crichton " "Jorge Aparicio " "Alexis Beingessner " "Brian Anderson <>" "tbu- <>" "Manish Goregaokar <>" "Aaron Turon " "Adolfo Ochagavía <>" "Niko Matsakis <>" "Steven Fackler <>" "Chase Southwood " "Eduard Burtescu <>" "Florian Wilkens <>" "Félix Raimundo <>" "Tibor Benke <>" "Markus Siemens " "Josh Branchaud " "Huon Wilson " "Corey Farwell " "Aaron Liblong <>" "Nick Cameron " "Patrick Walton " "Felix S Klock II <>" "Andrew Paseltiner " "Sean McArthur " "Vadim Petrochenkov <>" ]; features = { "eders" = [ "serde" ]; "serde" = [ "dep:serde" ]; }; }; "version_check" = rec { crateName = "version_check"; version = "0.9.4"; edition = "2015"; sha256 = "0gs8grwdlgh0xq660d7wr80x14vxbizmd8dbp29p2pdncx8lp1s9"; authors = [ "Sergio Benitez " ]; }; "walkdir" = rec { crateName = "walkdir"; version = "2.5.0"; edition = "2018"; sha256 = "0jsy7a710qv8gld5957ybrnc07gavppp963gs32xk4ag8130jy99"; authors = [ "Andrew Gallant " ]; dependencies = [ { name = "same-file"; packageId = "same-file"; } { name = "winapi-util"; packageId = "winapi-util"; target = { target, features }: (target."windows" or false); } ]; }; "winapi" = rec { crateName = "winapi"; version = "0.3.9"; edition = "2015"; sha256 = "06gl025x418lchw1wxj64ycr7gha83m44cjr5sarhynd9xkrm0sw"; authors = [ "Peter Atashian " ]; dependencies = [ { name = "winapi-i686-pc-windows-gnu"; packageId = "winapi-i686-pc-windows-gnu"; target = { target, features }: (target.name == "i686-pc-windows-gnu"); } { name = "winapi-x86_64-pc-windows-gnu"; packageId = "winapi-x86_64-pc-windows-gnu"; target = { target, features }: (target.name == "x86_64-pc-windows-gnu"); } ]; features = { "debug" = [ "impl-debug" ]; }; resolvedDefaultFeatures = [ "consoleapi" "errhandlingapi" "fileapi" "handleapi" "minwinbase" "minwindef" "ntsecapi" "processenv" "profileapi" "std" "winbase" "winerror" "winnt" ]; }; "winapi-i686-pc-windows-gnu" = rec { crateName = "winapi-i686-pc-windows-gnu"; version = "0.4.0"; edition = "2015"; sha256 = "1dmpa6mvcvzz16zg6d5vrfy4bxgg541wxrcip7cnshi06v38ffxc"; libName = "winapi_i686_pc_windows_gnu"; authors = [ "Peter Atashian " ]; }; "winapi-util" = rec { crateName = "winapi-util"; version = "0.1.8"; edition = "2021"; sha256 = "0svcgddd2rw06mj4r76gj655qsa1ikgz3d3gzax96fz7w62c6k2d"; libName = "winapi_util"; authors = [ "Andrew Gallant " ]; dependencies = [ { name = "windows-sys"; packageId = "windows-sys"; target = { target, features }: (target."windows" or false); features = [ "Win32_Foundation" "Win32_Storage_FileSystem" "Win32_System_Console" "Win32_System_SystemInformation" ]; } ]; }; "winapi-x86_64-pc-windows-gnu" = rec { crateName = "winapi-x86_64-pc-windows-gnu"; version = "0.4.0"; edition = "2015"; sha256 = "0gqq64czqb64kskjryj8isp62m2sgvx25yyj3kpc2myh85w24bki"; libName = "winapi_x86_64_pc_windows_gnu"; authors = [ "Peter Atashian " ]; }; "windows-sys" = rec { crateName = "windows-sys"; version = "0.52.0"; edition = "2021"; sha256 = "0gd3v4ji88490zgb6b5mq5zgbvwv7zx1ibn8v3x83rwcdbryaar8"; libName = "windows_sys"; authors = [ "Microsoft" ]; dependencies = [ { name = "windows-targets"; packageId = "windows-targets"; } ]; features = { "Wdk_Foundation" = [ "Wdk" ]; "Wdk_Graphics" = [ "Wdk" ]; "Wdk_Graphics_Direct3D" = [ "Wdk_Graphics" ]; "Wdk_Storage" = [ "Wdk" ]; "Wdk_Storage_FileSystem" = [ "Wdk_Storage" ]; "Wdk_Storage_FileSystem_Minifilters" = [ "Wdk_Storage_FileSystem" ]; "Wdk_System" = [ "Wdk" ]; "Wdk_System_IO" = [ "Wdk_System" ]; "Wdk_System_OfflineRegistry" = [ "Wdk_System" ]; "Wdk_System_Registry" = [ "Wdk_System" ]; "Wdk_System_SystemInformation" = [ "Wdk_System" ]; "Wdk_System_SystemServices" = [ "Wdk_System" ]; "Wdk_System_Threading" = [ "Wdk_System" ]; "Win32_Data" = [ "Win32" ]; "Win32_Data_HtmlHelp" = [ "Win32_Data" ]; "Win32_Data_RightsManagement" = [ "Win32_Data" ]; "Win32_Devices" = [ "Win32" ]; "Win32_Devices_AllJoyn" = [ "Win32_Devices" ]; "Win32_Devices_BiometricFramework" = [ "Win32_Devices" ]; "Win32_Devices_Bluetooth" = [ "Win32_Devices" ]; "Win32_Devices_Communication" = [ "Win32_Devices" ]; "Win32_Devices_DeviceAndDriverInstallation" = [ "Win32_Devices" ]; "Win32_Devices_DeviceQuery" = [ "Win32_Devices" ]; "Win32_Devices_Display" = [ "Win32_Devices" ]; "Win32_Devices_Enumeration" = [ "Win32_Devices" ]; "Win32_Devices_Enumeration_Pnp" = [ "Win32_Devices_Enumeration" ]; "Win32_Devices_Fax" = [ "Win32_Devices" ]; "Win32_Devices_HumanInterfaceDevice" = [ "Win32_Devices" ]; "Win32_Devices_PortableDevices" = [ "Win32_Devices" ]; "Win32_Devices_Properties" = [ "Win32_Devices" ]; "Win32_Devices_Pwm" = [ "Win32_Devices" ]; "Win32_Devices_Sensors" = [ "Win32_Devices" ]; "Win32_Devices_SerialCommunication" = [ "Win32_Devices" ]; "Win32_Devices_Tapi" = [ "Win32_Devices" ]; "Win32_Devices_Usb" = [ "Win32_Devices" ]; "Win32_Devices_WebServicesOnDevices" = [ "Win32_Devices" ]; "Win32_Foundation" = [ "Win32" ]; "Win32_Gaming" = [ "Win32" ]; "Win32_Globalization" = [ "Win32" ]; "Win32_Graphics" = [ "Win32" ]; "Win32_Graphics_Dwm" = [ "Win32_Graphics" ]; "Win32_Graphics_Gdi" = [ "Win32_Graphics" ]; "Win32_Graphics_GdiPlus" = [ "Win32_Graphics" ]; "Win32_Graphics_Hlsl" = [ "Win32_Graphics" ]; "Win32_Graphics_OpenGL" = [ "Win32_Graphics" ]; "Win32_Graphics_Printing" = [ "Win32_Graphics" ]; "Win32_Graphics_Printing_PrintTicket" = [ "Win32_Graphics_Printing" ]; "Win32_Management" = [ "Win32" ]; "Win32_Management_MobileDeviceManagementRegistration" = [ "Win32_Management" ]; "Win32_Media" = [ "Win32" ]; "Win32_Media_Audio" = [ "Win32_Media" ]; "Win32_Media_DxMediaObjects" = [ "Win32_Media" ]; "Win32_Media_KernelStreaming" = [ "Win32_Media" ]; "Win32_Media_Multimedia" = [ "Win32_Media" ]; "Win32_Media_Streaming" = [ "Win32_Media" ]; "Win32_Media_WindowsMediaFormat" = [ "Win32_Media" ]; "Win32_NetworkManagement" = [ "Win32" ]; "Win32_NetworkManagement_Dhcp" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_Dns" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_InternetConnectionWizard" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_IpHelper" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_Multicast" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_Ndis" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_NetBios" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_NetManagement" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_NetShell" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_NetworkDiagnosticsFramework" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_P2P" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_QoS" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_Rras" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_Snmp" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_WNet" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_WebDav" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_WiFi" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_WindowsConnectionManager" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_WindowsFilteringPlatform" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_WindowsFirewall" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_WindowsNetworkVirtualization" = [ "Win32_NetworkManagement" ]; "Win32_Networking" = [ "Win32" ]; "Win32_Networking_ActiveDirectory" = [ "Win32_Networking" ]; "Win32_Networking_Clustering" = [ "Win32_Networking" ]; "Win32_Networking_HttpServer" = [ "Win32_Networking" ]; "Win32_Networking_Ldap" = [ "Win32_Networking" ]; "Win32_Networking_WebSocket" = [ "Win32_Networking" ]; "Win32_Networking_WinHttp" = [ "Win32_Networking" ]; "Win32_Networking_WinInet" = [ "Win32_Networking" ]; "Win32_Networking_WinSock" = [ "Win32_Networking" ]; "Win32_Networking_WindowsWebServices" = [ "Win32_Networking" ]; "Win32_Security" = [ "Win32" ]; "Win32_Security_AppLocker" = [ "Win32_Security" ]; "Win32_Security_Authentication" = [ "Win32_Security" ]; "Win32_Security_Authentication_Identity" = [ "Win32_Security_Authentication" ]; "Win32_Security_Authorization" = [ "Win32_Security" ]; "Win32_Security_Credentials" = [ "Win32_Security" ]; "Win32_Security_Cryptography" = [ "Win32_Security" ]; "Win32_Security_Cryptography_Catalog" = [ "Win32_Security_Cryptography" ]; "Win32_Security_Cryptography_Certificates" = [ "Win32_Security_Cryptography" ]; "Win32_Security_Cryptography_Sip" = [ "Win32_Security_Cryptography" ]; "Win32_Security_Cryptography_UI" = [ "Win32_Security_Cryptography" ]; "Win32_Security_DiagnosticDataQuery" = [ "Win32_Security" ]; "Win32_Security_DirectoryServices" = [ "Win32_Security" ]; "Win32_Security_EnterpriseData" = [ "Win32_Security" ]; "Win32_Security_ExtensibleAuthenticationProtocol" = [ "Win32_Security" ]; "Win32_Security_Isolation" = [ "Win32_Security" ]; "Win32_Security_LicenseProtection" = [ "Win32_Security" ]; "Win32_Security_NetworkAccessProtection" = [ "Win32_Security" ]; "Win32_Security_WinTrust" = [ "Win32_Security" ]; "Win32_Security_WinWlx" = [ "Win32_Security" ]; "Win32_Storage" = [ "Win32" ]; "Win32_Storage_Cabinets" = [ "Win32_Storage" ]; "Win32_Storage_CloudFilters" = [ "Win32_Storage" ]; "Win32_Storage_Compression" = [ "Win32_Storage" ]; "Win32_Storage_DistributedFileSystem" = [ "Win32_Storage" ]; "Win32_Storage_FileHistory" = [ "Win32_Storage" ]; "Win32_Storage_FileSystem" = [ "Win32_Storage" ]; "Win32_Storage_Imapi" = [ "Win32_Storage" ]; "Win32_Storage_IndexServer" = [ "Win32_Storage" ]; "Win32_Storage_InstallableFileSystems" = [ "Win32_Storage" ]; "Win32_Storage_IscsiDisc" = [ "Win32_Storage" ]; "Win32_Storage_Jet" = [ "Win32_Storage" ]; "Win32_Storage_Nvme" = [ "Win32_Storage" ]; "Win32_Storage_OfflineFiles" = [ "Win32_Storage" ]; "Win32_Storage_OperationRecorder" = [ "Win32_Storage" ]; "Win32_Storage_Packaging" = [ "Win32_Storage" ]; "Win32_Storage_Packaging_Appx" = [ "Win32_Storage_Packaging" ]; "Win32_Storage_ProjectedFileSystem" = [ "Win32_Storage" ]; "Win32_Storage_StructuredStorage" = [ "Win32_Storage" ]; "Win32_Storage_Vhd" = [ "Win32_Storage" ]; "Win32_Storage_Xps" = [ "Win32_Storage" ]; "Win32_System" = [ "Win32" ]; "Win32_System_AddressBook" = [ "Win32_System" ]; "Win32_System_Antimalware" = [ "Win32_System" ]; "Win32_System_ApplicationInstallationAndServicing" = [ "Win32_System" ]; "Win32_System_ApplicationVerifier" = [ "Win32_System" ]; "Win32_System_ClrHosting" = [ "Win32_System" ]; "Win32_System_Com" = [ "Win32_System" ]; "Win32_System_Com_Marshal" = [ "Win32_System_Com" ]; "Win32_System_Com_StructuredStorage" = [ "Win32_System_Com" ]; "Win32_System_Com_Urlmon" = [ "Win32_System_Com" ]; "Win32_System_ComponentServices" = [ "Win32_System" ]; "Win32_System_Console" = [ "Win32_System" ]; "Win32_System_CorrelationVector" = [ "Win32_System" ]; "Win32_System_DataExchange" = [ "Win32_System" ]; "Win32_System_DeploymentServices" = [ "Win32_System" ]; "Win32_System_DeveloperLicensing" = [ "Win32_System" ]; "Win32_System_Diagnostics" = [ "Win32_System" ]; "Win32_System_Diagnostics_Ceip" = [ "Win32_System_Diagnostics" ]; "Win32_System_Diagnostics_Debug" = [ "Win32_System_Diagnostics" ]; "Win32_System_Diagnostics_Debug_Extensions" = [ "Win32_System_Diagnostics_Debug" ]; "Win32_System_Diagnostics_Etw" = [ "Win32_System_Diagnostics" ]; "Win32_System_Diagnostics_ProcessSnapshotting" = [ "Win32_System_Diagnostics" ]; "Win32_System_Diagnostics_ToolHelp" = [ "Win32_System_Diagnostics" ]; "Win32_System_DistributedTransactionCoordinator" = [ "Win32_System" ]; "Win32_System_Environment" = [ "Win32_System" ]; "Win32_System_ErrorReporting" = [ "Win32_System" ]; "Win32_System_EventCollector" = [ "Win32_System" ]; "Win32_System_EventLog" = [ "Win32_System" ]; "Win32_System_EventNotificationService" = [ "Win32_System" ]; "Win32_System_GroupPolicy" = [ "Win32_System" ]; "Win32_System_HostCompute" = [ "Win32_System" ]; "Win32_System_HostComputeNetwork" = [ "Win32_System" ]; "Win32_System_HostComputeSystem" = [ "Win32_System" ]; "Win32_System_Hypervisor" = [ "Win32_System" ]; "Win32_System_IO" = [ "Win32_System" ]; "Win32_System_Iis" = [ "Win32_System" ]; "Win32_System_Ioctl" = [ "Win32_System" ]; "Win32_System_JobObjects" = [ "Win32_System" ]; "Win32_System_Js" = [ "Win32_System" ]; "Win32_System_Kernel" = [ "Win32_System" ]; "Win32_System_LibraryLoader" = [ "Win32_System" ]; "Win32_System_Mailslots" = [ "Win32_System" ]; "Win32_System_Mapi" = [ "Win32_System" ]; "Win32_System_Memory" = [ "Win32_System" ]; "Win32_System_Memory_NonVolatile" = [ "Win32_System_Memory" ]; "Win32_System_MessageQueuing" = [ "Win32_System" ]; "Win32_System_MixedReality" = [ "Win32_System" ]; "Win32_System_Ole" = [ "Win32_System" ]; "Win32_System_PasswordManagement" = [ "Win32_System" ]; "Win32_System_Performance" = [ "Win32_System" ]; "Win32_System_Performance_HardwareCounterProfiling" = [ "Win32_System_Performance" ]; "Win32_System_Pipes" = [ "Win32_System" ]; "Win32_System_Power" = [ "Win32_System" ]; "Win32_System_ProcessStatus" = [ "Win32_System" ]; "Win32_System_Recovery" = [ "Win32_System" ]; "Win32_System_Registry" = [ "Win32_System" ]; "Win32_System_RemoteDesktop" = [ "Win32_System" ]; "Win32_System_RemoteManagement" = [ "Win32_System" ]; "Win32_System_RestartManager" = [ "Win32_System" ]; "Win32_System_Restore" = [ "Win32_System" ]; "Win32_System_Rpc" = [ "Win32_System" ]; "Win32_System_Search" = [ "Win32_System" ]; "Win32_System_Search_Common" = [ "Win32_System_Search" ]; "Win32_System_SecurityCenter" = [ "Win32_System" ]; "Win32_System_Services" = [ "Win32_System" ]; "Win32_System_SetupAndMigration" = [ "Win32_System" ]; "Win32_System_Shutdown" = [ "Win32_System" ]; "Win32_System_StationsAndDesktops" = [ "Win32_System" ]; "Win32_System_SubsystemForLinux" = [ "Win32_System" ]; "Win32_System_SystemInformation" = [ "Win32_System" ]; "Win32_System_SystemServices" = [ "Win32_System" ]; "Win32_System_Threading" = [ "Win32_System" ]; "Win32_System_Time" = [ "Win32_System" ]; "Win32_System_TpmBaseServices" = [ "Win32_System" ]; "Win32_System_UserAccessLogging" = [ "Win32_System" ]; "Win32_System_Variant" = [ "Win32_System" ]; "Win32_System_VirtualDosMachines" = [ "Win32_System" ]; "Win32_System_WindowsProgramming" = [ "Win32_System" ]; "Win32_System_Wmi" = [ "Win32_System" ]; "Win32_UI" = [ "Win32" ]; "Win32_UI_Accessibility" = [ "Win32_UI" ]; "Win32_UI_ColorSystem" = [ "Win32_UI" ]; "Win32_UI_Controls" = [ "Win32_UI" ]; "Win32_UI_Controls_Dialogs" = [ "Win32_UI_Controls" ]; "Win32_UI_HiDpi" = [ "Win32_UI" ]; "Win32_UI_Input" = [ "Win32_UI" ]; "Win32_UI_Input_Ime" = [ "Win32_UI_Input" ]; "Win32_UI_Input_KeyboardAndMouse" = [ "Win32_UI_Input" ]; "Win32_UI_Input_Pointer" = [ "Win32_UI_Input" ]; "Win32_UI_Input_Touch" = [ "Win32_UI_Input" ]; "Win32_UI_Input_XboxController" = [ "Win32_UI_Input" ]; "Win32_UI_InteractionContext" = [ "Win32_UI" ]; "Win32_UI_Magnification" = [ "Win32_UI" ]; "Win32_UI_Shell" = [ "Win32_UI" ]; "Win32_UI_Shell_PropertiesSystem" = [ "Win32_UI_Shell" ]; "Win32_UI_TabletPC" = [ "Win32_UI" ]; "Win32_UI_TextServices" = [ "Win32_UI" ]; "Win32_UI_WindowsAndMessaging" = [ "Win32_UI" ]; "Win32_Web" = [ "Win32" ]; "Win32_Web_InternetExplorer" = [ "Win32_Web" ]; }; resolvedDefaultFeatures = [ "Win32" "Win32_Foundation" "Win32_Storage" "Win32_Storage_FileSystem" "Win32_System" "Win32_System_Console" "Win32_System_SystemInformation" "default" ]; }; "windows-targets" = rec { crateName = "windows-targets"; version = "0.52.5"; edition = "2021"; sha256 = "1sz7jrnkygmmlj1ia8fk85wbyil450kq5qkh5qh9sh2rcnj161vg"; libName = "windows_targets"; authors = [ "Microsoft" ]; dependencies = [ { name = "windows_aarch64_gnullvm"; packageId = "windows_aarch64_gnullvm"; target = { target, features }: (target.name == "aarch64-pc-windows-gnullvm"); } { name = "windows_aarch64_msvc"; packageId = "windows_aarch64_msvc"; target = { target, features }: (("aarch64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false))); } { name = "windows_i686_gnu"; packageId = "windows_i686_gnu"; target = { target, features }: (("x86" == target."arch" or null) && ("gnu" == target."env" or null) && (!("llvm" == target."abi" or null)) && (!(target."windows_raw_dylib" or false))); } { name = "windows_i686_gnullvm"; packageId = "windows_i686_gnullvm"; target = { target, features }: (target.name == "i686-pc-windows-gnullvm"); } { name = "windows_i686_msvc"; packageId = "windows_i686_msvc"; target = { target, features }: (("x86" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false))); } { name = "windows_x86_64_gnu"; packageId = "windows_x86_64_gnu"; target = { target, features }: (("x86_64" == target."arch" or null) && ("gnu" == target."env" or null) && (!("llvm" == target."abi" or null)) && (!(target."windows_raw_dylib" or false))); } { name = "windows_x86_64_gnullvm"; packageId = "windows_x86_64_gnullvm"; target = { target, features }: (target.name == "x86_64-pc-windows-gnullvm"); } { name = "windows_x86_64_msvc"; packageId = "windows_x86_64_msvc"; target = { target, features }: ((("x86_64" == target."arch" or null) || ("arm64ec" == target."arch" or null)) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false))); } ]; }; "windows_aarch64_gnullvm" = rec { crateName = "windows_aarch64_gnullvm"; version = "0.52.5"; edition = "2021"; sha256 = "0qrjimbj67nnyn7zqy15mzzmqg0mn5gsr2yciqjxm3cb3vbyx23h"; authors = [ "Microsoft" ]; }; "windows_aarch64_msvc" = rec { crateName = "windows_aarch64_msvc"; version = "0.52.5"; edition = "2021"; sha256 = "1dmga8kqlmln2ibckk6mxc9n59vdg8ziqa2zr8awcl720hazv1cr"; authors = [ "Microsoft" ]; }; "windows_i686_gnu" = rec { crateName = "windows_i686_gnu"; version = "0.52.5"; edition = "2021"; sha256 = "0w4np3l6qwlra9s2xpflqrs60qk1pz6ahhn91rr74lvdy4y0gfl8"; authors = [ "Microsoft" ]; }; "windows_i686_gnullvm" = rec { crateName = "windows_i686_gnullvm"; version = "0.52.5"; edition = "2021"; sha256 = "1s9f4gff0cixd86mw3n63rpmsm4pmr4ffndl6s7qa2h35492dx47"; authors = [ "Microsoft" ]; }; "windows_i686_msvc" = rec { crateName = "windows_i686_msvc"; version = "0.52.5"; edition = "2021"; sha256 = "1gw7fklxywgpnwbwg43alb4hm0qjmx72hqrlwy5nanrxs7rjng6v"; authors = [ "Microsoft" ]; }; "windows_x86_64_gnu" = rec { crateName = "windows_x86_64_gnu"; version = "0.52.5"; edition = "2021"; sha256 = "1n8p2mcf3lw6300k77a0knksssmgwb9hynl793mhkzyydgvlchjf"; authors = [ "Microsoft" ]; }; "windows_x86_64_gnullvm" = rec { crateName = "windows_x86_64_gnullvm"; version = "0.52.5"; edition = "2021"; sha256 = "15n56jrh4s5bz66zimavr1rmcaw6wa306myrvmbc6rydhbj9h8l5"; authors = [ "Microsoft" ]; }; "windows_x86_64_msvc" = rec { crateName = "windows_x86_64_msvc"; version = "0.52.5"; edition = "2021"; sha256 = "1w1bn24ap8dp9i85s8mlg8cim2bl2368bd6qyvm0xzqvzmdpxi5y"; authors = [ "Microsoft" ]; }; "winnow" = rec { crateName = "winnow"; version = "0.6.13"; edition = "2021"; sha256 = "189b0mrr9lkckdyr0177hwj1c59igxc2lsl71f4wg8wrqbvfbdar"; dependencies = [ { name = "memchr"; packageId = "memchr"; optional = true; usesDefaultFeatures = false; } ]; features = { "debug" = [ "std" "dep:anstream" "dep:anstyle" "dep:is-terminal" "dep:terminal_size" ]; "default" = [ "std" ]; "simd" = [ "dep:memchr" ]; "std" = [ "alloc" "memchr?/std" ]; "unstable-doc" = [ "alloc" "std" "simd" "unstable-recover" ]; }; resolvedDefaultFeatures = [ "alloc" "default" "std" ]; }; }; # # crate2nix/default.nix (excerpt start) # /* Target (platform) data for conditional dependencies. This corresponds roughly to what buildRustCrate is setting. */ makeDefaultTarget = platform: { name = platform.rust.rustcTarget; unix = platform.isUnix; windows = platform.isWindows; fuchsia = true; test = false; inherit (platform.rust.platform) arch os vendor ; family = platform.rust.platform.target-family; env = "gnu"; endian = if platform.parsed.cpu.significantByte.name == "littleEndian" then "little" else "big"; pointer_width = toString platform.parsed.cpu.bits; debug_assertions = false; } // extraTargetFlags; registryUrl = { registries , url , crate , version , sha256 , }: let dl = registries.${url}.dl; tmpl = [ "{crate}" "{version}" "{prefix}" "{lowerprefix}" "{sha256-checksum}" ]; in with lib.strings; if lib.lists.any (i: hasInfix "{}" dl) tmpl then let prefix = if builtins.stringLength crate == 1 then "1" else if builtins.stringLength crate == 2 then "2" else "${builtins.substring 0 2 crate}/${builtins.substring 2 (builtins.stringLength crate - 2) crate}"; in builtins.replaceStrings tmpl [ crate version prefix (lib.strings.toLower prefix) sha256 ] else "${dl}/${crate}/${version}/download"; # Filters common temp files and build files. # TODO(pkolloch): Substitute with gitignore filter sourceFilter = name: type: let baseName = builtins.baseNameOf (builtins.toString name); in !( # Filter out git baseName == ".gitignore" || (type == "directory" && baseName == ".git") # Filter out build results || ( type == "directory" && ( baseName == "target" || baseName == "_site" || baseName == ".sass-cache" || baseName == ".jekyll-metadata" || baseName == "build-artifacts" ) ) # Filter out nix-build result symlinks || (type == "symlink" && lib.hasPrefix "result" baseName) # Filter out IDE config || (type == "directory" && (baseName == ".idea" || baseName == ".vscode")) || lib.hasSuffix ".iml" baseName # Filter out nix build files || baseName == "Cargo.nix" # Filter out editor backup / swap files. || lib.hasSuffix "~" baseName || builtins.match "^\\.sw[a-z]$$" baseName != null || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null || lib.hasSuffix ".tmp" baseName || lib.hasSuffix ".bak" baseName || baseName == "tests.nix" ); /* Returns a crate which depends on successful test execution of crate given as the second argument. testCrateFlags: list of flags to pass to the test exectuable testInputs: list of packages that should be available during test execution */ crateWithTest = { crate , testCrate , testCrateFlags , testInputs , testPreRun , testPostRun , }: assert builtins.typeOf testCrateFlags == "list"; assert builtins.typeOf testInputs == "list"; assert builtins.typeOf testPreRun == "string"; assert builtins.typeOf testPostRun == "string"; let # override the `crate` so that it will build and execute tests instead of # building the actual lib and bin targets We just have to pass `--test` # to rustc and it will do the right thing. We execute the tests and copy # their log and the test executables to $out for later inspection. test = let drv = testCrate.override (_: { buildTests = true; }); # If the user hasn't set any pre/post commands, we don't want to # insert empty lines. This means that any existing users of crate2nix # don't get a spurious rebuild unless they set these explicitly. testCommand = pkgs.lib.concatStringsSep "\n" ( pkgs.lib.filter (s: s != "") [ testPreRun "$f $testCrateFlags 2>&1 | tee -a $out" testPostRun ] ); in pkgs.stdenvNoCC.mkDerivation { name = "run-tests-${testCrate.name}"; inherit (crate) src; inherit testCrateFlags; buildInputs = testInputs; buildPhase = '' set -e export RUST_BACKTRACE=1 # build outputs testRoot=target/debug mkdir -p $testRoot # executables of the crate # we copy to prevent std::env::current_exe() to resolve to a store location for i in ${crate}/bin/*; do cp "$i" "$testRoot" done chmod +w -R . # test harness executables are suffixed with a hash, like cargo does # this allows to prevent name collision with the main # executables of the crate hash=$(basename $out) for file in ${drv}/tests/*; do f=$testRoot/$(basename $file)-$hash cp $file $f ${testCommand} done ''; }; in pkgs.runCommand "${crate.name}-linked" { inherit (crate) outputs crateName meta; passthru = (crate.passthru or { }) // { inherit test; }; } ( lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) '' echo tested by ${test} '' + '' ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs} '' ); # A restricted overridable version of builtRustCratesWithFeatures. buildRustCrateWithFeatures = { packageId , features ? rootFeatures , crateOverrides ? defaultCrateOverrides , buildRustCrateForPkgsFunc ? null , runTests ? false , testCrateFlags ? [ ] , testInputs ? [ ] , # Any command to run immediatelly before a test is executed. testPreRun ? "" , # Any command run immediatelly after a test is executed. testPostRun ? "" , }: lib.makeOverridable ( { features , crateOverrides , runTests , testCrateFlags , testInputs , testPreRun , testPostRun , }: let buildRustCrateForPkgsFuncOverriden = if buildRustCrateForPkgsFunc != null then buildRustCrateForPkgsFunc else ( if crateOverrides == pkgs.defaultCrateOverrides then buildRustCrateForPkgs else pkgs: (buildRustCrateForPkgs pkgs).override { defaultCrateOverrides = crateOverrides; } ); builtRustCrates = builtRustCratesWithFeatures { inherit packageId features; buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden; runTests = false; }; builtTestRustCrates = builtRustCratesWithFeatures { inherit packageId features; buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden; runTests = true; }; drv = builtRustCrates.crates.${packageId}; testDrv = builtTestRustCrates.crates.${packageId}; derivation = if runTests then crateWithTest { crate = drv; testCrate = testDrv; inherit testCrateFlags testInputs testPreRun testPostRun ; } else drv; in derivation ) { inherit features crateOverrides runTests testCrateFlags testInputs testPreRun testPostRun ; }; /* Returns an attr set with packageId mapped to the result of buildRustCrateForPkgsFunc for the corresponding crate. */ builtRustCratesWithFeatures = { packageId , features , crateConfigs ? crates , buildRustCrateForPkgsFunc , runTests , makeTarget ? makeDefaultTarget , }@args: assert (builtins.isAttrs crateConfigs); assert (builtins.isString packageId); assert (builtins.isList features); assert (builtins.isAttrs (makeTarget stdenv.hostPlatform)); assert (builtins.isBool runTests); let rootPackageId = packageId; mergedFeatures = mergePackageFeatures ( args // { inherit rootPackageId; target = makeTarget stdenv.hostPlatform // { test = runTests; }; } ); # Memoize built packages so that reappearing packages are only built once. builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs; mkBuiltByPackageIdByPkgs = pkgs: let self = { crates = lib.mapAttrs ( packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId ) crateConfigs; target = makeTarget pkgs.stdenv.hostPlatform; build = mkBuiltByPackageIdByPkgs pkgs.buildPackages; }; in self; buildByPackageIdForPkgsImpl = self: pkgs: packageId: let features = mergedFeatures."${packageId}" or [ ]; crateConfig' = crateConfigs."${packageId}"; crateConfig = builtins.removeAttrs crateConfig' [ "resolvedDefaultFeatures" "devDependencies" ]; devDependencies = lib.optionals (runTests && packageId == rootPackageId) ( crateConfig'.devDependencies or [ ] ); dependencies = dependencyDerivations { inherit features; inherit (self) target; buildByPackageId = depPackageId: # proc_macro crates must be compiled for the build architecture if crateConfigs.${depPackageId}.procMacro or false then self.build.crates.${depPackageId} else self.crates.${depPackageId}; dependencies = (crateConfig.dependencies or [ ]) ++ devDependencies; }; buildDependencies = dependencyDerivations { inherit features; inherit (self.build) target; buildByPackageId = depPackageId: self.build.crates.${depPackageId}; dependencies = crateConfig.buildDependencies or [ ]; }; dependenciesWithRenames = let buildDeps = filterEnabledDependencies { inherit features; inherit (self) target; dependencies = crateConfig.dependencies or [ ] ++ devDependencies; }; hostDeps = filterEnabledDependencies { inherit features; inherit (self.build) target; dependencies = crateConfig.buildDependencies or [ ]; }; in lib.filter (d: d ? "rename") (hostDeps ++ buildDeps); # Crate renames have the form: # # { # crate_name = [ # { version = "1.2.3"; rename = "crate_name01"; } # ]; # # ... # } crateRenames = let grouped = lib.groupBy (dependency: dependency.name) dependenciesWithRenames; versionAndRename = dep: let package = crateConfigs."${dep.packageId}"; in { inherit (dep) rename; inherit (package) version; }; in lib.mapAttrs (name: builtins.map versionAndRename) grouped; in buildRustCrateForPkgsFunc pkgs ( crateConfig // { src = crateConfig.src or (fetchurl rec { name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz"; # https://www.pietroalbini.org/blog/downloading-crates-io/ # Not rate-limited, CDN URL. url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate"; sha256 = assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}"); crateConfig.sha256; }); extraRustcOpts = lib.lists.optional (targetFeatures != [ ]) "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}"; inherit features dependencies buildDependencies crateRenames release ; } ); in builtByPackageIdByPkgs; # Returns the actual derivations for the given dependencies. dependencyDerivations = { buildByPackageId , features , dependencies , target , }: assert (builtins.isList features); assert (builtins.isList dependencies); assert (builtins.isAttrs target); let enabledDependencies = filterEnabledDependencies { inherit dependencies features target; }; depDerivation = dependency: buildByPackageId dependency.packageId; in map depDerivation enabledDependencies; /* Returns a sanitized version of val with all values substituted that cannot be serialized as JSON. */ sanitizeForJson = val: if builtins.isAttrs val then lib.mapAttrs (n: sanitizeForJson) val else if builtins.isList val then builtins.map sanitizeForJson val else if builtins.isFunction val then "function" else val; # Returns various tools to debug a crate. debugCrate = { packageId , target ? makeDefaultTarget stdenv.hostPlatform , }: assert (builtins.isString packageId); let debug = rec { # The built tree as passed to buildRustCrate. buildTree = buildRustCrateWithFeatures { buildRustCrateForPkgsFunc = _: lib.id; inherit packageId; }; sanitizedBuildTree = sanitizeForJson buildTree; dependencyTree = sanitizeForJson (buildRustCrateWithFeatures { buildRustCrateForPkgsFunc = _: crate: { "01_crateName" = crate.crateName or false; "02_features" = crate.features or [ ]; "03_dependencies" = crate.dependencies or [ ]; }; inherit packageId; }); mergedPackageFeatures = mergePackageFeatures { features = rootFeatures; inherit packageId target; }; diffedDefaultPackageFeatures = diffDefaultPackageFeatures { inherit packageId target; }; }; in { internal = debug; }; /* Returns differences between cargo default features and crate2nix default features. This is useful for verifying the feature resolution in crate2nix. */ diffDefaultPackageFeatures = { crateConfigs ? crates , packageId , target , }: assert (builtins.isAttrs crateConfigs); let prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; }); mergedFeatures = prefixValues "crate2nix" (mergePackageFeatures { inherit crateConfigs packageId target; features = [ "default" ]; }); configs = prefixValues "cargo" crateConfigs; combined = lib.foldAttrs (a: b: a // b) { } [ mergedFeatures configs ]; onlyInCargo = builtins.attrNames ( lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined ); onlyInCrate2Nix = builtins.attrNames ( lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined ); differentFeatures = lib.filterAttrs ( n: v: (v ? "crate2nix") && (v ? "cargo") && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ]) ) combined; in builtins.toJSON { inherit onlyInCargo onlyInCrate2Nix differentFeatures; }; /* Returns an attrset mapping packageId to the list of enabled features. If multiple paths to a dependency enable different features, the corresponding feature sets are merged. Features in rust are additive. */ mergePackageFeatures = { crateConfigs ? crates , packageId , rootPackageId ? packageId , features ? rootFeatures , dependencyPath ? [ crates.${packageId}.crateName ] , featuresByPackageId ? { } , target , # Adds devDependencies to the crate with rootPackageId. runTests ? false , ... }@args: assert (builtins.isAttrs crateConfigs); assert (builtins.isString packageId); assert (builtins.isString rootPackageId); assert (builtins.isList features); assert (builtins.isList dependencyPath); assert (builtins.isAttrs featuresByPackageId); assert (builtins.isAttrs target); assert (builtins.isBool runTests); let crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}"); expandedFeatures = expandFeatures (crateConfig.features or { }) features; enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures; depWithResolvedFeatures = dependency: let inherit (dependency) packageId; features = dependencyFeatures enabledFeatures dependency; in { inherit packageId features; }; resolveDependencies = cache: path: dependencies: assert (builtins.isAttrs cache); assert (builtins.isList dependencies); let enabledDependencies = filterEnabledDependencies { inherit dependencies target; features = enabledFeatures; }; directDependencies = map depWithResolvedFeatures enabledDependencies; foldOverCache = op: lib.foldl op cache directDependencies; in foldOverCache ( cache: { packageId, features }: let cacheFeatures = cache.${packageId} or [ ]; combinedFeatures = sortedUnique (cacheFeatures ++ features); in if cache ? ${packageId} && cache.${packageId} == combinedFeatures then cache else mergePackageFeatures { features = combinedFeatures; featuresByPackageId = cache; inherit crateConfigs packageId target runTests rootPackageId ; } ); cacheWithSelf = let cacheFeatures = featuresByPackageId.${packageId} or [ ]; combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures); in featuresByPackageId // { "${packageId}" = combinedFeatures; }; cacheWithDependencies = resolveDependencies cacheWithSelf "dep" ( crateConfig.dependencies or [ ] ++ lib.optionals (runTests && packageId == rootPackageId) (crateConfig.devDependencies or [ ]) ); cacheWithAll = resolveDependencies cacheWithDependencies "build" ( crateConfig.buildDependencies or [ ] ); in cacheWithAll; # Returns the enabled dependencies given the enabled features. filterEnabledDependencies = { dependencies , features , target , }: assert (builtins.isList dependencies); assert (builtins.isList features); assert (builtins.isAttrs target); lib.filter ( dep: let targetFunc = dep.target or (features: true); in targetFunc { inherit features target; } && (!(dep.optional or false) || builtins.any (doesFeatureEnableDependency dep) features) ) dependencies; # Returns whether the given feature should enable the given dependency. doesFeatureEnableDependency = dependency: feature: let name = dependency.rename or dependency.name; prefix = "${name}/"; len = builtins.stringLength prefix; startsWithPrefix = builtins.substring 0 len feature == prefix; in feature == name || feature == "dep:" + name || startsWithPrefix; /* Returns the expanded features for the given inputFeatures by applying the rules in featureMap. featureMap is an attribute set which maps feature names to lists of further feature names to enable in case this feature is selected. */ expandFeatures = featureMap: inputFeatures: assert (builtins.isAttrs featureMap); assert (builtins.isList inputFeatures); let expandFeaturesNoCycle = oldSeen: inputFeatures: if inputFeatures != [ ] then let # The feature we're currently expanding. feature = builtins.head inputFeatures; # All the features we've seen/expanded so far, including the one # we're currently processing. seen = oldSeen // { ${feature} = 1; }; # Expand the feature but be careful to not re-introduce a feature # that we've already seen: this can easily cause a cycle, see issue # #209. enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]); in [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables)) # No more features left, nothing to expand to. else [ ]; outFeatures = expandFeaturesNoCycle { } inputFeatures; in sortedUnique outFeatures; /* This function adds optional dependencies as features if they are enabled indirectly by dependency features. This function mimics Cargo's behavior described in a note at: https://doc.rust-lang.org/nightly/cargo/reference/features.html#dependency-features */ enableFeatures = dependencies: features: assert (builtins.isList features); assert (builtins.isList dependencies); let additionalFeatures = lib.concatMap ( dependency: assert (builtins.isAttrs dependency); let enabled = builtins.any (doesFeatureEnableDependency dependency) features; in if (dependency.optional or false) && enabled then [ (dependency.rename or dependency.name) ] else [ ] ) dependencies; in sortedUnique (features ++ additionalFeatures); /* Returns the actual features for the given dependency. features: The features of the crate that refers this dependency. */ dependencyFeatures = features: dependency: assert (builtins.isList features); assert (builtins.isAttrs dependency); let defaultOrNil = if dependency.usesDefaultFeatures or true then [ "default" ] else [ ]; explicitFeatures = dependency.features or [ ]; additionalDependencyFeatures = let name = dependency.rename or dependency.name; stripPrefixMatch = prefix: s: if lib.hasPrefix prefix s then lib.removePrefix prefix s else null; extractFeature = feature: lib.findFirst (f: f != null) null ( map (prefix: stripPrefixMatch prefix feature) [ (name + "/") (name + "?/") ] ); dependencyFeatures = lib.filter (f: f != null) (map extractFeature features); in dependencyFeatures; in defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures; # Sorts and removes duplicates from a list of strings. sortedUnique = features: assert (builtins.isList features); assert (builtins.all builtins.isString features); let outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features; outFeaturesUnique = builtins.attrNames outFeaturesSet; in builtins.sort (a: b: a < b) outFeaturesUnique; deprecationWarning = message: value: if strictDeprecation then builtins.throw "strictDeprecation enabled, aborting: ${message}" else builtins.trace message value; # # crate2nix/default.nix (excerpt end) # }; } ================================================ FILE: crate2nix/Cargo.toml ================================================ [package] name = "crate2nix" version = "0.15.0" authors = ["Peter Kolloch "] edition = "2021" license = "Apache-2.0" description = "crate2nix generates nix (as in NixOS) build files for rust using cargo." repository = "https://github.com/nix-community/crate2nix" homepage = "https://nix-community.github.io/crate2nix/" documentation = "https://nix-community.github.io/crate2nix/" readme = "../README.md" keywords = ["nix", "cargo", "build", "ci", "cache"] resolver = "2" [dependencies] anyhow = "1.0.28" cargo_metadata = "0.18" cargo-platform = "0.1" hex = "0.4" itertools = "0.12" lazy_static = "1" nix-base32 = "0.1" pathdiff = "0.2" structopt = "0.3" semver = { version = "1", features = ["serde"] } serde = { version = "1.0.107", features = ["derive"] } serde_json = { version = "1.0.59", features = ["unbounded_depth"] } tera = { version = "1", default-features = false } toml = "0.8" url = { version = "2", features = ["serde"] } [dev-dependencies] colored-diff = "0.2.2" fs_extra = "1.1" tempdir = "0.3" ================================================ FILE: crate2nix/crate-hashes.json ================================================ {} ================================================ FILE: crate2nix/default-json.nix ================================================ # Build crate2nix from the pre-resolved JSON output, dogfooding the JSON path. { pkgs ? ( import (builtins.fetchTree (import ../nix/flakeInput.nix "nixpkgs")) { } ) , stdenv ? pkgs.stdenv , lib ? pkgs.lib , symlinkJoin ? pkgs.symlinkJoin , makeWrapper ? pkgs.makeWrapper , nix ? pkgs.nix , cargo ? pkgs.cargo , libsecret ? pkgs.libsecret , nix-prefetch-git ? pkgs.nix-prefetch-git , release ? true }: let cargoNix = import ../lib/build-from-json.nix { inherit pkgs lib stdenv; src = ./.; resolvedJson = ./Cargo.json; }; in import ./mk-crate2nix.nix { inherit pkgs stdenv lib symlinkJoin makeWrapper nix cargo libsecret nix-prefetch-git release; rootCrate = cargoNix.rootCrate.build; } ================================================ FILE: crate2nix/default.nix ================================================ # Provided by callPackage or also directly usable via nix-build with defaults. { pkgs ? ( import (builtins.fetchTree (import ../nix/flakeInput.nix "nixpkgs")) { } ) , stdenv ? pkgs.stdenv , lib ? pkgs.lib , symlinkJoin ? pkgs.symlinkJoin , makeWrapper ? pkgs.makeWrapper , darwin ? pkgs.darwin , defaultCrateOverrides ? pkgs.defaultCrateOverrides , nix ? pkgs.nix , cargo ? pkgs.cargo , libsecret ? pkgs.libsecret , callPackage ? pkgs.callPackage , nix-prefetch-git ? pkgs.nix-prefetch-git # Seperate arguements that are NOT filled by callPackage. , cargoNixPath ? ./Cargo.nix , release ? true }: let cargoNix = callPackage cargoNixPath { inherit release; }; withoutTemplates = name: type: let baseName = builtins.baseNameOf (builtins.toString name); in !(baseName == "templates" && type == "directory"); rootCrate = cargoNix.rootCrate.build.override { testCrateFlags = [ "--skip nix_integration_tests" ]; crateOverrides = defaultCrateOverrides // { crate2nix = { src, ... }: { src = if release then src else lib.cleanSourceWith { filter = withoutTemplates; inherit src; }; dontFixup = !release; }; cssparser-macros = attrs: assert builtins.trace "cssparser" true;{ buildInputs = lib.optionals stdenv.isDarwin [ darwin.apple_sdk.frameworks.Security ]; }; }; }; in import ./mk-crate2nix.nix { inherit pkgs stdenv lib symlinkJoin makeWrapper nix cargo libsecret nix-prefetch-git release rootCrate; } ================================================ FILE: crate2nix/deny.toml ================================================ # This template contains all of the possible sections and their default values # Note that all fields that take a lint level have these possible values: # * deny - An error will be produced and the check will fail # * warn - A warning will be produced, but the check will not fail # * allow - No warning or error will be produced, though in some cases a note # will be # The values provided in this template are the default values that will be used # when any section or field is not specified in your own configuration # If 1 or more target triples (and optionally, target_features) are specified, # only the specified targets will be checked when running `cargo deny check`. # This means, if a particular package is only ever used as a target specific # dependency, such as, for example, the `nix` crate only being used via the # `target_family = "unix"` configuration, that only having windows targets in # this list would mean the nix crate, as well as any of its exclusive # dependencies not shared by any other crates, would be ignored, as the target # list here is effectively saying which targets you are building for. targets = [ # The triple can be any string, but only the target triples built in to # rustc (as of 1.40) can be checked against actual config expressions #{ triple = "x86_64-unknown-linux-musl" }, # You can also specify which target_features you promise are enabled for a # particular target. target_features are currently not validated against # the actual valid features supported by the target architecture. #{ triple = "wasm32-unknown-unknown", features = ["atomics"] }, ] # This section is considered when running `cargo deny check advisories` # More documentation for the advisories section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html [advisories] # The path where the advisory database is cloned/fetched into db-path = "~/.cargo/advisory-db" # The url of the advisory database to use db-url = "https://github.com/rustsec/advisory-db" # The lint level for security vulnerabilities vulnerability = "deny" # The lint level for unmaintained crates unmaintained = "warn" # The lint level for crates that have been yanked from their source registry yanked = "warn" # The lint level for crates with security notices. Note that as of # 2019-12-17 there are no security notice advisories in # https://github.com/rustsec/advisory-db notice = "warn" # A list of advisory IDs to ignore. Note that ignored advisories will still # output a note when they are encountered. ignore = [ #"RUSTSEC-0000-0000", ] # Threshold for security vulnerabilities, any vulnerability with a CVSS score # lower than the range specified will be ignored. Note that ignored advisories # will still output a note when they are encountered. # * None - CVSS Score 0.0 # * Low - CVSS Score 0.1 - 3.9 # * Medium - CVSS Score 4.0 - 6.9 # * High - CVSS Score 7.0 - 8.9 # * Critical - CVSS Score 9.0 - 10.0 #severity-threshold = # This section is considered when running `cargo deny check licenses` # More documentation for the licenses section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html [licenses] # The lint level for crates which do not have a detectable license unlicensed = "deny" # List of explictly allowed licenses # See https://spdx.org/licenses/ for list of possible licenses # [possible values: any SPDX 3.7 short identifier (+ optional exception)]. allow = [ "MIT", "Apache-2.0", "BSD-3-Clause", "ISC", #"Apache-2.0 WITH LLVM-exception", ] # List of explictly disallowed licenses # See https://spdx.org/licenses/ for list of possible licenses # [possible values: any SPDX 3.7 short identifier (+ optional exception)]. deny = [ #"Nokia", ] # Lint level for licenses considered copyleft copyleft = "warn" # Blanket approval or denial for OSI-approved or FSF Free/Libre licenses # * both - The license will be approved if it is both OSI-approved *AND* FSF # * either - The license will be approved if it is either OSI-approved *OR* FSF # * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF # * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved # * neither - This predicate is ignored and the default lint level is used allow-osi-fsf-free = "neither" # Lint level used when no other predicates are matched # 1. License isn't in the allow or deny lists # 2. License isn't copyleft # 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither" default = "deny" # The confidence threshold for detecting a license from license text. # The higher the value, the more closely the license text must be to the # canonical license text of a valid SPDX license file. # [possible values: any between 0.0 and 1.0]. confidence-threshold = 0.8 # Allow 1 or more licenses on a per-crate basis, so that particular licenses # aren't accepted for every possible crate as with the normal allow list exceptions = [ # Each entry is the crate and version constraint, and its specific allow # list #{ allow = ["Zlib"], name = "adler32", version = "*" }, ] # Some crates don't have (easily) machine readable licensing information, # adding a clarification entry for it allows you to manually specify the # licensing information #[[licenses.clarify]] # The name of the crate the clarification applies to #name = "ring" # THe optional version constraint for the crate #version = "*" # The SPDX expression for the license requirements of the crate #expression = "MIT AND ISC AND OpenSSL" # One or more files in the crate's source used as the "source of truth" for # the license expression. If the contents match, the clarification will be used # when running the license check, otherwise the clarification will be ignored # and the crate will be checked normally, which may produce warnings or errors # depending on the rest of your configuration #license-files = [ # Each entry is a crate relative path, and the (opaque) hash of its contents #{ path = "LICENSE", hash = 0xbd0eed23 } #] [licenses.private] # If true, ignores workspace crates that aren't published, or are only # published to private registries ignore = false # One or more private registries that you might publish crates to, if a crate # is only published to private registries, and ignore is true, the crate will # not have its license(s) checked registries = [ #"https://sekretz.com/registry ] # This section is considered when running `cargo deny check bans`. # More documentation about the 'bans' section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html [bans] # Lint level for when multiple versions of the same crate are detected multiple-versions = "warn" # The graph highlighting used when creating dotgraphs for crates # with multiple versions # * lowest-version - The path to the lowest versioned duplicate is highlighted # * simplest-path - The path to the version with the fewest edges is highlighted # * all - Both lowest-version and simplest-path are used highlight = "all" # List of crates that are allowed. Use with care! allow = [ #{ name = "ansi_term", version = "=0.11.0" }, ] # List of crates to deny deny = [ # Each entry the name of a crate and a version range. If version is # not specified, all versions will be matched. #{ name = "ansi_term", version = "=0.11.0" }, ] # Certain crates/versions that will be skipped when doing duplicate detection. skip = [ #{ name = "ansi_term", version = "=0.11.0" }, ] # Similarly to `skip` allows you to skip certain crates during duplicate # detection. Unlike skip, it also includes the entire tree of transitive # dependencies starting at the specified crate, up to a certain depth, which is # by default infinite skip-tree = [ #{ name = "ansi_term", version = "=0.11.0", depth = 20 }, ] # This section is considered when running `cargo deny check sources`. # More documentation about the 'sources' section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html [sources] # Lint level for what to happen when a crate from a crate registry that is not # in the allow list is encountered unknown-registry = "warn" # Lint level for what to happen when a crate from a git repository that is not # in the allow list is encountered unknown-git = "warn" # List of URLs for allowed crate registries. Defaults to the crates.io index # if not specified. If it is specified but empty, no registries are allowed. allow-registry = ["https://github.com/rust-lang/crates.io-index"] # List of URLs for allowed Git repositories allow-git = [] ================================================ FILE: crate2nix/flake-module.nix ================================================ { self, inputs, lib, ... }: { flake.overlays.default = final: prev: { crate2nix = prev.callPackage ./default.nix { }; }; perSystem = { pkgs , system , ... }@perSystem: { # imports = [ # inputs.pre-commit-hooks.flakeModule # ]; config.devshells.default = { imports = [ "${inputs.devshell}/extra/language/c.nix" "${inputs.devshell}/extra/language/rust.nix" ]; packages = with pkgs; [ rust-analyzer clippy rustc rustfmt ]; commands = with pkgs; [ { package = cargo; category = "rust"; } ]; language.c = { libraries = lib.optional pkgs.stdenv.isDarwin pkgs.libiconv; }; }; config.pre-commit = { settings.settings.rust.cargoManifestPath = "crate2nix/Cargo.toml"; settings.hooks = { # rust rustfmt.enable = true; # clippy.enable = true; }; }; config.packages.default = pkgs.callPackage ./default.nix { }; config.packages.crate2nix-from-json = pkgs.callPackage ./default-json.nix { }; config.checks = let # Note: This uses the build of the nix-test binary using the stable nixpkgs/crate2nix. # The "unstable" build is tested in the tests.nix checks. nixTestRunner = import "${self}/nix/nix-test-runner" { inherit system; }; in { unit-tests = pkgs.callPackage ./templates/nix/crate2nix/tests/run.nix { inherit nixTestRunner; }; } // (pkgs.callPackage ../tests.nix { }).checks; }; } ================================================ FILE: crate2nix/mk-crate2nix.nix ================================================ # Shared wrapper logic for the crate2nix binary. # Takes the raw rootCrate derivation and wraps it with PATH, completions, etc. { pkgs ? ( import (builtins.fetchTree (import ../nix/flakeInput.nix "nixpkgs")) { } ) , stdenv ? pkgs.stdenv , lib ? pkgs.lib , symlinkJoin ? pkgs.symlinkJoin , makeWrapper ? pkgs.makeWrapper , nix ? pkgs.nix , cargo ? pkgs.cargo , libsecret ? pkgs.libsecret , nix-prefetch-git ? pkgs.nix-prefetch-git , release ? true # The raw rootCrate.build derivation to wrap. , rootCrate }: let crate2nix = rootCrate.overrideAttrs (attrs: { postInstall = lib.optionalString stdenv.isLinux '' patchelf --add-needed ${libsecret}/lib/libsecret-1.so.0 $out/bin/crate2nix ''; }); set_templates = if release then "" else "--set TEMPLATES_DIR ${./templates}"; in symlinkJoin { name = crate2nix.name; paths = [ crate2nix ]; buildInputs = [ makeWrapper cargo ]; meta = { description = "Nix build file generator for rust crates."; longDescription = '' Crate2nix generates nix files from Cargo.toml/lock files so that you can build every crate individually in a nix sandbox. ''; homepage = "https://github.com/nix-community/crate2nix"; license = lib.licenses.asl20; maintainers = [ { github = "kolloch"; githubId = 339354; name = "Peter Kolloch"; } lib.maintainers.andir lib.maintainers.domenkozar ]; mainProgram = "crate2nix"; platforms = lib.platforms.all; }; postBuild = '' # Fallback to built dependencies for cargo and nix-prefetch-url wrapProgram $out/bin/crate2nix ${set_templates}\ --suffix PATH ":" ${lib.makeBinPath [ cargo nix nix-prefetch-git ]} rm -rf $out/lib $out/bin/crate2nix.d mkdir -p \ $out/share/bash-completion/completions \ $out/share/zsh/vendor-completions $out/bin/crate2nix completions -s 'bash' -o $out/share/bash-completion/completions $out/bin/crate2nix completions -s 'zsh' -o $out/share/zsh/vendor-completions ''; } ================================================ FILE: crate2nix/rustfmt.toml ================================================ edition = "2018" reorder_imports = true # merge_imports = true ================================================ FILE: crate2nix/src/command.rs ================================================ //! Utilities for dealing with spawned commands. use anyhow::{bail, format_err, Error}; use std::process::{Child, Stdio}; use std::thread; use std::{ io::{BufRead, Read}, io::{BufReader, Cursor}, sync::mpsc, }; /// Runs the given command with output capturing. /// /// The output will be printed indented if and only if the command does not /// return succesfully. pub fn run(caption: &str, command: &mut std::process::Command) -> Result<(), Error> { eprint!("{}: ", caption); let mut spawned: Child = command .stdin(Stdio::null()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() .map_err(|e| format_err!("while spawning {:?}: {}", command, e))?; let (sender, receiver) = mpsc::channel(); pass_through(spawned.stdout.take().expect("stdout"), sender.clone()); pass_through(spawned.stderr.take().expect("stderr"), sender); let mut out = Vec::::new(); while let Ok(buf) = receiver.recv() { out.extend(buf.iter()); } let status = spawned .wait() .map_err(|e| format_err!("while waiting for the {:?} to finish: {}", command, e))?; if status.success() { eprintln!("done."); return Ok(()); } eprintln!(); eprintln!(" {:?}", command); let line_reader = BufReader::new(Cursor::new(out)); for line in line_reader.lines() { println!( " {}", line.map_err(|e| format_err!("while processing output lines: {}", e))? ); } bail!( "{:?}\n=> exited with: {}", command, status.code().unwrap_or(-1) ); } fn pass_through(mut read: impl Read + Send + 'static, sender: mpsc::Sender>) { thread::spawn(move || { let mut buf = [0; 4096]; while let Ok(n) = read.read(&mut buf) { if n == 0 { break; } if sender.send(Vec::from(&buf[..n])).is_err() { break; } } }); } ================================================ FILE: crate2nix/src/config.rs ================================================ //! Managing the `crate2nix.json` config. use anyhow::{Context, Error}; use serde::{Deserialize, Serialize}; use std::{ collections::BTreeMap, fmt::Display, fs::File, io::{BufReader, BufWriter}, path::Path, }; impl Config { /// Read config from path. pub fn read_from_or_default(path: &Path) -> Result { if !path.exists() { return Ok(Config::default()); } let file = File::open(path).context(format!("while opening {}", path.to_string_lossy()))?; let reader = BufReader::new(file); serde_json::from_reader(reader).context(format!( "while deserializing config: {}", path.to_string_lossy() )) } /// Write config to path. pub fn write_to(&self, path: &Path) -> Result<(), Error> { let file = File::create(path).context(format!("while opening {}", path.to_string_lossy()))?; let writer = BufWriter::new(file); Ok(serde_json::to_writer_pretty(writer, self)?) } } /// The `crate2nix.json` config data. #[derive(Debug, Default, Serialize, Deserialize)] pub struct Config { /// Out of tree sources. pub sources: BTreeMap, } impl Config { /// Add or replace a source. Returns the old source if there was one. pub fn upsert_source( &mut self, explicit_name: Option, source: Source, ) -> Option { let name = explicit_name .or_else(|| source.name().map(|s| s.to_string())) .expect("No name given"); self.sources.insert(name, source) } /// Prints all sources to stdout. pub fn print_sources(&self) { if self.sources.is_empty() { eprintln!("No sources configured.\n"); return; } let max_len = self .sources .keys() .map(|n| n.len()) .max() .unwrap_or_default(); for (name, source) in &self.sources { println!("{:width$} {}", name, source, width = max_len); println!(); println!( "{:width$} crate2nix source add {}", "", source.as_command(name), width = max_len ); println!(); } } } /// An out of tree source. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(tag = "type")] pub enum Source { /// Get the source from crates.io. CratesIo { /// The crate name. name: String, /// The exact crate version to fetch. version: semver::Version, /// The sha256 hash of the source. sha256: String, }, /// Get the source from crates.io. Registry { /// The registry's URL registry: String, /// The crate name. name: String, /// The exact crate version to fetch. version: semver::Version, /// The sha256 hash of the source. sha256: String, }, /// Get the source from git. Git { /// The URL of the git repository. /// /// E.g. https://github.com/kolloch/crate2nix.git url: url::Url, /// The revision hash. rev: String, /// The sha256 of the fetched result. sha256: String, }, /// Get the source from a nix expression. Nix { /// The nixfile to include. #[serde(flatten)] file: NixFile, /// A Nix attribute path which will be resolved against the file. #[serde(skip_serializing_if = "Option::is_none")] attr: Option, }, } /// A nix file path which is either included by `import` or `callPackage`. #[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Eq, Hash)] pub enum NixFile { /// A file path that should be imported. #[serde(rename = "import")] Import(String), /// A file path the should be included by `pkgs.callPackage`. #[serde(rename = "package")] Package(String), } impl Display for NixFile { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Import(path) => write!(f, "import {}", path), Self::Package(path) => write!(f, "pkgs.callPackage {} {{}}", path), } } } impl NixFile { /// Returns the chosen file option as CLI string. pub fn as_command(&self) -> String { match self { Self::Import(path) => format!("--import '{}'", path), Self::Package(path) => format!("--package '{}'", path), } } } impl Source { /// The name of the source. pub fn name(&self) -> Option<&str> { match self { Source::CratesIo { name, .. } => Some(name), Source::Git { url, .. } => { let path = url.path(); let after_last_slash = path.split('/').next_back().unwrap_or(path); let without_dot_git = after_last_slash .strip_suffix(".git") .unwrap_or(after_last_slash); Some(without_dot_git) } Source::Nix { attr: Some(attr), .. } => attr.split('.').next_back().or(if attr.trim().is_empty() { None } else { Some(attr.trim()) }), _ => None, } } } impl Display for Source { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Source::CratesIo { name, version, sha256, } => write!(f, "{} {} from crates.io: {}", name, version, sha256), Source::Registry { name, version, sha256, registry, .. } => write!(f, "{} {} from {}: {}", name, version, registry, sha256), Source::Git { url, rev, sha256 } => write!(f, "{}#{} via git: {}", url, rev, sha256), Source::Nix { file, attr: None } => write!(f, "{}", file), Source::Nix { file, attr: Some(attr), } => write!(f, "({}).{}", file, attr), } } } impl Source { /// Returns a CLI string to reproduce this source. pub fn as_command(&self, name: &str) -> String { match self { Source::CratesIo { name: crate_name, version, .. } => format!("cratesIo --name '{}' '{}' '{}'", name, crate_name, version), Source::Registry { name: crate_name, version, registry, .. } => format!( "registry --registry '{}' --name '{}' '{}' '{}'", registry, name, crate_name, version ), Source::Git { url, rev, .. } => { format!("git --name '{}' '{}' --rev {}", name, url, rev) } Source::Nix { file, attr: None } => { format!("nix --name '{}' {}", name, file.as_command()) } Source::Nix { file, attr: Some(attr), } => format!("nix --name '{}' {} '{}'", name, file.as_command(), attr), } } } ================================================ FILE: crate2nix/src/json_output.rs ================================================ //! Pre-resolved JSON output format. //! //! Converts BuildInfo to a flat JSON structure with dependencies already //! filtered by target platform and optional dep activation. This eliminates //! the O(n*m) feature resolution that the Nix template output requires at //! eval time. use std::collections::{BTreeMap, HashSet}; use serde::{Deserialize, Serialize}; use crate::resolve::{ResolvedDependency, ResolvedSource}; use crate::BuildInfo; /// The pre-resolved workspace, ready for consumption by a thin Nix wrapper. #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ResolvedWorkspace { /// Generator marker so we can identify the file and avoid accidental overwrites. /// Always set to "@generated by crate2nix". pub generator: String, /// Package ID of the root crate, or null for pure workspaces. pub root: Option, /// Workspace member name → packageId. pub workspace_members: BTreeMap, /// Package ID → resolved crate info. pub crates: BTreeMap, } /// A single crate with pre-resolved dependencies. #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ResolvedCrate { /// Crate name (as used in Cargo.toml). pub crate_name: String, /// Crate version. pub version: String, /// Rust edition (e.g. "2021"). pub edition: String, /// SHA-256 hash for crates.io or git sources. #[serde(skip_serializing_if = "Option::is_none")] pub sha256: Option, /// Source information (crates-io, local, git). #[serde(skip_serializing_if = "Option::is_none")] pub source: Option, /// Normal dependencies, already filtered by platform and optional dep activation. #[serde(skip_serializing_if = "Vec::is_empty")] pub dependencies: Vec, /// Build dependencies, already filtered by platform and optional dep activation. #[serde(skip_serializing_if = "Vec::is_empty")] pub build_dependencies: Vec, /// Dev dependencies (for tests/benches/examples). Only populated for /// workspace members since transitive deps' tests are never built. #[serde(skip_serializing_if = "Vec::is_empty", default)] pub dev_dependencies: Vec, /// The resolved features for this crate. #[serde(skip_serializing_if = "Vec::is_empty")] pub resolved_default_features: Vec, /// Whether this is a proc-macro crate. #[serde(skip_serializing_if = "std::ops::Not::not")] pub proc_macro: bool, /// Build script path (relative to crate root), if non-default. #[serde(skip_serializing_if = "Option::is_none")] pub build: Option, /// Library source path (relative to crate root), if non-default. #[serde(skip_serializing_if = "Option::is_none")] pub lib_path: Option, /// Library name, if different from crate name. #[serde(skip_serializing_if = "Option::is_none")] pub lib_name: Option, /// Binary targets. #[serde(skip_serializing_if = "Vec::is_empty")] pub crate_bin: Vec, /// Library crate types (e.g. \["lib"\], \["cdylib", "rlib"\]). #[serde(skip_serializing_if = "Vec::is_empty")] pub lib_crate_types: Vec, /// Native library this crate links to (the `links` field in Cargo.toml). #[serde(skip_serializing_if = "Option::is_none")] pub links: Option, /// Crate authors. #[serde(skip_serializing_if = "Vec::is_empty")] pub authors: Vec, } /// A resolved dependency reference. #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct DepInfo { /// Package name of the dependency. pub name: String, /// Shortened package ID of the resolved dependency. pub package_id: String, /// Rename (extern crate name), if different from package name. #[serde(skip_serializing_if = "Option::is_none")] pub rename: Option, /// Platform condition string (e.g. `cfg(unix)`, `x86_64-unknown-linux-gnu`). /// Absent for unconditional deps. #[serde(skip_serializing_if = "Option::is_none")] pub target: Option, } /// Source type for a crate. #[derive(Debug, Serialize, Deserialize)] #[serde(tag = "type")] #[serde(rename_all = "kebab-case")] pub enum SourceInfo { /// From crates.io. CratesIo, /// Local path dependency. Local { /// Relative or absolute path to the crate directory. path: String, }, /// Git dependency. Git { /// Repository URL. url: String, /// Git revision. rev: String, }, } /// A binary target within a crate. #[derive(Debug, Serialize, Deserialize)] pub struct BinTarget { /// Binary name. pub name: String, /// Source path relative to crate root. pub path: String, } /// Normalize a crate name (hyphens → underscores) as Cargo does. fn normalize_name(name: &str) -> String { name.replace('-', "_") } /// Expand resolved features through the feature map to find all activated /// optional deps. /// /// Returns a set of **effective dep names** — the rename if present, otherwise /// the package name. This handles: /// - `dep:foo` syntax: directly activates the dep /// - `dep_name/feature` syntax: activates the dep /// - Implicit activation: feature name matching an optional dep's effective name /// - Transitive expansion through the feature map fn activated_optional_deps( features_map: &BTreeMap>, dependencies: &[&ResolvedDependency], resolved_features: &[String], ) -> HashSet { // Collect effective names of all optional deps. let optional_dep_effective_names: HashSet = dependencies .iter() .filter(|d| d.optional) .map(|d| { d.rename .as_ref() .map(|r| normalize_name(r)) .unwrap_or_else(|| normalize_name(&d.name)) }) .collect(); let mut seen = HashSet::new(); let mut queue: Vec = resolved_features.to_vec(); let mut activated = HashSet::new(); while let Some(feat) = queue.pop() { if !seen.insert(feat.clone()) { continue; } // "dep:foo" directly activates optional dep "foo" (effective name) if let Some(dep_name) = feat.strip_prefix("dep:") { activated.insert(normalize_name(dep_name)); continue; } // "dep_name/feature" syntax: activates the dep if let Some((dep_part, _feature_part)) = feat.split_once('/') { let normalized = normalize_name(dep_part); if optional_dep_effective_names.contains(&normalized) { activated.insert(normalized); } } // Feature with same name as an optional dep implicitly activates it let normalized_feat = normalize_name(&feat); if optional_dep_effective_names.contains(&normalized_feat) { activated.insert(normalized_feat); } // Follow feature rules if let Some(rules) = features_map.get(&feat) { for rule in rules { queue.push(rule.clone()); } } } activated } /// Filter inactive optional deps and serialize platform conditions as strings. /// /// Optional deps that aren't activated by resolved features are removed. /// Platform conditions are preserved as strings for the Nix consumer to /// evaluate cheaply at eval time. fn resolve_deps(deps: &[ResolvedDependency], activated: &HashSet) -> Vec { deps.iter() .filter(|dep| { // Remove inactive optional deps (the expensive resolution is already done) if dep.optional { let effective = dep .rename .as_ref() .map(|r| normalize_name(r)) .unwrap_or_else(|| normalize_name(&dep.name)); if !activated.contains(&effective) { return false; } } true }) .map(|dep| DepInfo { name: dep.name.clone(), package_id: dep.package_id.repr.clone(), rename: dep.rename.as_ref().map(|r| normalize_name(r)), target: dep.target.as_ref().map(|p| p.to_string()), }) .collect() } fn convert_source(source: &ResolvedSource, workspace_root: &str) -> Option { match source { ResolvedSource::CratesIo(_) => Some(SourceInfo::CratesIo), ResolvedSource::Registry(_) => { // TODO: handle non-crates.io registries Some(SourceInfo::CratesIo) } ResolvedSource::LocalDirectory(local) => { let abs = local.to_string(); let rel = abs .strip_prefix(workspace_root) .and_then(|s| s.strip_prefix('/')) .unwrap_or(&abs); // Strip leading "./" if present let rel = rel.strip_prefix("./").unwrap_or(rel); // Root workspace member has empty relative path, use "." let path = if rel.is_empty() { "." } else { rel }; Some(SourceInfo::Local { path: path.to_string(), }) } ResolvedSource::Git(git) => Some(SourceInfo::Git { url: git.url.to_string(), rev: git.rev.clone(), }), ResolvedSource::Nix(_) => None, } } /// Convert a BuildInfo into a pre-resolved workspace JSON. /// /// Feature expansion and optional dep activation are resolved in Rust. /// Platform conditions are preserved as strings so the output is /// target-independent — the Nix consumer filters by `stdenv.hostPlatform`. /// /// Local source paths are made relative to the workspace root so the /// consumer can reconstruct them as `src + "/${relativePath}"`. pub fn to_resolved_workspace(build_info: &BuildInfo) -> ResolvedWorkspace { let workspace_root = build_info.workspace_root.clone().unwrap_or_default(); let mut crates = BTreeMap::new(); for crate_deriv in &build_info.crates { let id = &crate_deriv.package_id.repr; // All deps (normal + build + dev) for optional dep activation let all_deps: Vec<&ResolvedDependency> = crate_deriv .dependencies .iter() .chain(crate_deriv.build_dependencies.iter()) .chain(crate_deriv.dev_dependencies.iter()) .collect(); // Compute activated optional deps from resolved features let activated = activated_optional_deps( &crate_deriv.features, &all_deps, &crate_deriv.resolved_default_features, ); let dependencies = resolve_deps(&crate_deriv.dependencies, &activated); let build_dependencies = resolve_deps(&crate_deriv.build_dependencies, &activated); // Only emit dev-deps for workspace members — transitive deps' tests // are never built, so their dev-deps would just bloat the JSON. let dev_dependencies = if crate_deriv.is_root_or_workspace_member { resolve_deps(&crate_deriv.dev_dependencies, &activated) } else { vec![] }; let lib_name = crate_deriv.lib.as_ref().map(|l| normalize_name(&l.name)); crates.insert( id.clone(), ResolvedCrate { crate_name: crate_deriv.crate_name.clone(), version: crate_deriv.version.to_string(), edition: crate_deriv.edition.clone(), sha256: crate_deriv.source.sha256().cloned(), source: convert_source(&crate_deriv.source, &workspace_root), dependencies, build_dependencies, dev_dependencies, resolved_default_features: crate_deriv.resolved_default_features.clone(), proc_macro: crate_deriv.proc_macro, build: crate_deriv.build.as_ref().and_then(|b| { let s = b.src_path.to_string_lossy().to_string(); if s == "build.rs" { None } else { Some(s) } }), lib_path: crate_deriv.lib.as_ref().and_then(|l| { let s = l.src_path.to_string_lossy().to_string(); if s == "src/lib.rs" { None } else { Some(s) } }), lib_name: lib_name.as_ref().and_then(|n| { if *n == normalize_name(&crate_deriv.crate_name) { None } else { Some(n.clone()) } }), crate_bin: if crate_deriv.is_root_or_workspace_member { crate_deriv .binaries .iter() .map(|b| BinTarget { name: b.name.clone(), path: b.src_path.to_string_lossy().to_string(), }) .collect() } else { vec![] }, lib_crate_types: crate_deriv.lib_crate_types.clone(), links: crate_deriv.links.clone(), authors: crate_deriv.authors.clone(), }, ); } let workspace_members = build_info .workspace_members .iter() .map(|(name, id)| (name.clone(), id.repr.clone())) .collect(); let root = build_info .root_package_id .as_ref() .map(|id| id.repr.clone()); ResolvedWorkspace { generator: "@generated by crate2nix".to_string(), root, workspace_members, crates, } } #[cfg(test)] mod tests { use super::*; use cargo_platform::{Cfg, CfgExpr, Platform}; #[test] fn dep_syntax_activates_optional_dep() { let features = BTreeMap::from([("default".to_string(), vec!["dep:libbz2-rs-sys".to_string()])]); let dep = ResolvedDependency { name: "libbz2-rs-sys".to_string(), rename: None, package_id: cargo_metadata::PackageId { repr: "libbz2-rs-sys 0.1.0".to_string(), }, target: None, optional: true, uses_default_features: true, features: vec![], }; let deps = vec![&dep]; let activated = activated_optional_deps(&features, &deps, &["default".to_string()]); assert!(activated.contains("libbz2_rs_sys"), "got: {activated:?}"); } #[test] fn renamed_optional_dep_activated_by_effective_name() { let features = BTreeMap::from([( "rustls-0_23".to_string(), vec!["dep:tokio-rustls-026".to_string()], )]); let dep = ResolvedDependency { name: "tokio-rustls".to_string(), rename: Some("tokio-rustls-026".to_string()), package_id: cargo_metadata::PackageId { repr: "tokio-rustls 0.26.0".to_string(), }, target: None, optional: true, uses_default_features: true, features: vec![], }; let deps = vec![&dep]; let activated = activated_optional_deps(&features, &deps, &["rustls-0_23".to_string()]); assert!(activated.contains("tokio_rustls_026"), "got: {activated:?}"); } #[test] fn only_referenced_rename_activated_among_same_package() { let features = BTreeMap::from([ ( "rustls-0_20".to_string(), vec!["dep:tokio-rustls-023".to_string()], ), ( "rustls-0_23".to_string(), vec!["dep:tokio-rustls-026".to_string()], ), ]); let dep1 = ResolvedDependency { name: "tokio-rustls".to_string(), rename: Some("tokio-rustls-023".to_string()), package_id: cargo_metadata::PackageId { repr: "tokio-rustls 0.23.0".to_string(), }, target: None, optional: true, uses_default_features: true, features: vec![], }; let dep2 = ResolvedDependency { name: "tokio-rustls".to_string(), rename: Some("tokio-rustls-026".to_string()), package_id: cargo_metadata::PackageId { repr: "tokio-rustls 0.26.0".to_string(), }, target: None, optional: true, uses_default_features: true, features: vec![], }; let deps = vec![&dep1, &dep2]; let activated = activated_optional_deps(&features, &deps, &["rustls-0_23".to_string()]); assert!( activated.contains("tokio_rustls_026"), "026 should be activated" ); assert!( !activated.contains("tokio_rustls_023"), "023 should NOT be activated" ); } #[test] fn platform_deps_preserved_with_target_string() { let activated = HashSet::new(); let deps = vec![ ResolvedDependency { name: "winapi".to_string(), rename: None, package_id: cargo_metadata::PackageId { repr: "winapi 0.3.0".to_string(), }, target: Some(Platform::Cfg(CfgExpr::Value(Cfg::KeyPair( "target_os".to_string(), "windows".to_string(), )))), optional: false, uses_default_features: true, features: vec![], }, ResolvedDependency { name: "libc".to_string(), rename: None, package_id: cargo_metadata::PackageId { repr: "libc 0.2.0".to_string(), }, target: Some(Platform::Cfg(CfgExpr::Value(Cfg::Name("unix".to_string())))), optional: false, uses_default_features: true, features: vec![], }, ]; let result = resolve_deps(&deps, &activated); // Both deps kept — platform filtering is done by the Nix consumer assert_eq!(result.len(), 2, "got: {result:?}"); assert_eq!( result[0].target.as_deref(), Some("cfg(target_os = \"windows\")") ); assert_eq!(result[1].target.as_deref(), Some("cfg(unix)")); } } ================================================ FILE: crate2nix/src/lib.rs ================================================ //! # crate2nix //! //! Internal library for the crate2nix binary. This is not meant to be used separately, I just enjoy //! writing doc tests ;) //! //! [Repository](https://github.com/kolloch/crate2nix) #![forbid(unsafe_code)] #![deny(missing_docs)] use std::env; use std::path::PathBuf; use std::{ collections::{BTreeMap, HashMap, HashSet, VecDeque}, path::Path, }; use anyhow::format_err; use anyhow::Context; use anyhow::Error; use cargo_metadata::Metadata; use cargo_metadata::PackageId; use metadata::MergedMetadata; use serde::Deserialize; use serde::Serialize; use crate::metadata::IndexedMetadata; use crate::resolve::{CrateDerivation, ResolvedSource}; use itertools::Itertools; use resolve::CratesIoSource; mod command; pub mod config; pub mod json_output; mod lock; mod metadata; pub mod nix_build; mod prefetch; pub mod render; mod resolve; pub mod sources; #[cfg(test)] pub mod test; pub mod util; /// The resolved build info and the input for rendering the build.nix.tera template. #[derive(Debug, Deserialize, Serialize)] pub struct BuildInfo { /// The package ID of the root crate. pub root_package_id: Option, /// Workspaces member package IDs by package names. pub workspace_members: BTreeMap, /// Registries used by the crates. pub registries: BTreeMap, /// Build info for all crates needed for this build. pub crates: Vec, /// For convenience include the source for tests. pub indexed_metadata: IndexedMetadata, /// The generation configuration. pub info: GenerateInfo, /// The generation configuration. pub config: GenerateConfig, /// Workspace root directory path (from cargo metadata). pub workspace_root: Option, } impl BuildInfo { /// Return the `NixBuildInfo` data ready for rendering the nix build file. pub fn for_config(info: &GenerateInfo, config: &GenerateConfig) -> Result { let merged = { let mut metadatas = Vec::new(); for cargo_toml in &config.cargo_toml { metadatas.push(cargo_metadata(config, cargo_toml)?); } metadata::MergedMetadata::merge(metadatas)? }; let indexed_metadata = IndexedMetadata::new_from_merged(&merged).map_err(|e| { format_err!( "while indexing metadata for {:#?}: {}", config .cargo_toml .iter() .map(|p| p.to_string_lossy()) .collect::>(), e ) })?; let mut default_nix = BuildInfo::new(info, config, indexed_metadata)?; default_nix.prune_unneeded_crates(); prefetch_and_fill_crates_sha256(config, &merged, &mut default_nix)?; prefetch_and_fill_registries(config, &mut default_nix)?; Ok(default_nix) } fn prune_unneeded_crates(&mut self) { let mut queue: VecDeque<&PackageId> = self .root_package_id .iter() .chain(self.workspace_members.values()) .collect(); let mut reachable = HashSet::new(); let indexed_crates: BTreeMap<_, _> = self.crates.iter().map(|c| (&c.package_id, c)).collect(); while let Some(next_package_id) = queue.pop_back() { if !reachable.insert(next_package_id.clone()) { continue; } queue.extend( indexed_crates .get(next_package_id) .iter() .flat_map(|c| { c.dependencies .iter() .chain(c.build_dependencies.iter()) .chain(c.dev_dependencies.iter()) }) .map(|d| &d.package_id), ); } self.crates.retain(|c| reachable.contains(&c.package_id)); } fn new( info: &GenerateInfo, config: &GenerateConfig, metadata: IndexedMetadata, ) -> Result { let crate2nix_json = crate::config::Config::read_from_or_default( &config .crate_hashes_json .parent() .expect("crate-hashes.json has parent dir") .join("crate2nix.json"), )?; Ok(BuildInfo { root_package_id: metadata.root.clone(), workspace_members: metadata .workspace_members .iter() .flat_map(|pkg_id| { metadata .pkgs_by_id .get(pkg_id) .map(|pkg| (pkg.name.clone(), pkg_id.clone())) }) .collect(), registries: BTreeMap::new(), crates: metadata .pkgs_by_id .values() .map(|package| { CrateDerivation::resolve(config, &crate2nix_json, &metadata, package) }) .collect::>()?, workspace_root: metadata.workspace_root.clone(), indexed_metadata: metadata, info: info.clone(), config: config.clone(), }) } } /// Call `cargo metadata` and return result. fn cargo_metadata(config: &GenerateConfig, cargo_toml: &Path) -> Result { let mut cmd = cargo_metadata::MetadataCommand::new(); let mut other_options = config.other_metadata_options.clone(); other_options.push("--locked".into()); cmd.manifest_path(cargo_toml).other_options(&*other_options); cmd.exec().map_err(|e| { format_err!( "while retrieving metadata about {}: {}", &cargo_toml.to_string_lossy(), e ) }) } /// Prefetch hashes when necessary. fn prefetch_and_fill_crates_sha256( config: &GenerateConfig, merged: &MergedMetadata, default_nix: &mut BuildInfo, ) -> Result<(), Error> { let mut from_lock_file: HashMap = extract_hashes_from_lockfile(config, merged, default_nix)?; for (_package_id, hash) in from_lock_file.iter_mut() { let bytes = hex::decode(&hash).map_err(|e| format_err!("while decoding '{}': {}", hash, e))?; *hash = nix_base32::to_nix_base32(&bytes); } let prefetched = prefetch::prefetch( config, &from_lock_file, &default_nix.crates, &default_nix.indexed_metadata.id_shortener, ) .map_err(|e| format_err!("while prefetching crates for calculating sha256: {}", e))?; for package in default_nix.crates.iter_mut() { if package.source.sha256().is_none() { if let Some(hash) = prefetched .get( default_nix .indexed_metadata .id_shortener .lengthen_ref(&package.package_id), ) .or_else(|| from_lock_file.get(&package.package_id)) { package.source = package.source.with_sha256(hash.clone()); } } } Ok(()) } /// Prefetch hashes when necessary. fn prefetch_and_fill_registries( config: &GenerateConfig, default_nix: &mut BuildInfo, ) -> Result<(), Error> { default_nix.registries = prefetch::prefetch_registries(config, &default_nix.crates) .map_err(|e| format_err!("while prefetching crates for calculating sha256: {}", e))?; Ok(()) } fn extract_hashes_from_lockfile( config: &GenerateConfig, merged: &MergedMetadata, default_nix: &mut BuildInfo, ) -> Result, Error> { if !config.use_cargo_lock_checksums { return Ok(HashMap::new()); } let mut hashes: HashMap = HashMap::new(); for cargo_toml in &config.cargo_toml { let lock_file_path = cargo_toml.parent().unwrap().join("Cargo.lock"); let lock_file = crate::lock::EncodableResolve::load_lock_file(&lock_file_path)?; lock_file .get_hashes_by_package_id(merged, &mut hashes) .context(format!( "while parsing checksums from Lockfile {}", &lock_file_path.to_string_lossy() ))?; } let hashes_with_shortened_ids: HashMap = hashes .into_iter() .map(|(package_id, hash)| { ( default_nix .indexed_metadata .id_shortener .shorten_owned(package_id), hash, ) }) .collect(); let mut missing_hashes = Vec::new(); for package in default_nix.crates.iter_mut().filter(|c| match &c.source { ResolvedSource::CratesIo(CratesIoSource { sha256, .. }) if sha256.is_none() => { !hashes_with_shortened_ids.contains_key(&c.package_id) } _ => false, }) { missing_hashes.push(format!("{} {}", package.crate_name, package.version)); } if !missing_hashes.is_empty() { eprintln!( "Did not find all crates.io hashes in Cargo.lock. Hashes for e.g. {} are missing.\n\ This is probably a bug.", missing_hashes.iter().take(10).join(", ") ); } Ok(hashes_with_shortened_ids) } /// Some info about the crate2nix invocation. #[derive(Debug, Deserialize, Serialize, Clone)] pub struct GenerateInfo { /// The version of this `crate2nix` instance. pub crate2nix_version: String, /// The arguments that were passed to `crate2nix`. pub crate2nix_arguments: Vec, } impl Default for GenerateInfo { fn default() -> GenerateInfo { GenerateInfo { crate2nix_version: env!("CARGO_PKG_VERSION").to_string(), crate2nix_arguments: env::args().skip(1).collect(), } } } /// Configuration for the default.nix generation. #[derive(Debug, Deserialize, Serialize, Clone)] pub struct GenerateConfig { /// The path to `Cargo.toml`. pub cargo_toml: Vec, /// Whether to inspect `Cargo.lock` for checksums so that we do not need to prefetch them. pub use_cargo_lock_checksums: bool, /// The path of the generated `Cargo.nix` file. pub output: PathBuf, /// The path of the `crate-hashes.json` file which is used to look up hashes and/or store /// prefetched hashes at. pub crate_hashes_json: PathBuf, /// The path of the `registry-hashes.json` file which is used to look up hashes and/or store /// prefetched hashes at. pub registry_hashes_json: PathBuf, /// The nix expression for the nixpkgs path to use. pub nixpkgs_path: String, /// Additional arguments to pass to `cargo metadata`. pub other_metadata_options: Vec, /// Whether to read a `crate-hashes.json` file. pub read_crate_hashes: bool, } ================================================ FILE: crate2nix/src/lock.rs ================================================ //! Code for extracting hashes and more from Cargo.lock use anyhow::{format_err, Error}; use cargo_metadata::PackageId; use serde::{de, ser, Deserialize, Serialize}; use std::collections::{BTreeMap, HashMap}; use std::fmt; use std::path::Path; use std::str::FromStr; use crate::metadata::MergedMetadata; impl EncodableResolve { pub fn load_lock_file(path: &Path) -> Result { let config = &std::fs::read_to_string(path) .map_err(|e| format_err!("while reading lock file {}: {}", path.display(), e))?; Self::load_lock_string(path, config) } pub fn load_lock_string(path: &Path, config: &str) -> Result { let resolve: toml::Value = toml::from_str(config) .map_err(|e| format_err!("while parsing toml from {}: {}", path.display(), e))?; let v: EncodableResolve = resolve .try_into() .map_err(|e| format_err!("unexpected format in {}: {}", path.display(), e))?; Ok(v) } pub fn get_hashes_by_package_id( &self, metadata: &MergedMetadata, hashes: &mut HashMap, ) -> Result<(), Error> { let mut package_id_by_source = HashMap::new(); for p in &metadata.packages { let Some(ref source) = p.source else { // local crate continue; }; let key = (p.name.as_str(), source.repr.as_str(), p.version.to_string()); package_id_by_source.insert(key, &p.id); } for EncodableDependency { name, version, source, checksum, .. } in self.package.iter() { let Some(source) = source.as_ref() else { continue; }; let Some(package_id) = package_id_by_source.get(&(name.as_str(), source.as_str(), version.clone())) else { continue; }; let checksum = match checksum.as_ref() { Some(checksum) if checksum == "" => None, Some(checksum) => Some(checksum), None => { // Retrieve legacy checksums. self.metadata.as_ref().and_then(|metadata| { let checksum_key = format!("checksum {name} {version} ({source})"); metadata.get(&checksum_key) }) } }; if let Some(checksum) = checksum { hashes.insert((*package_id).clone(), checksum.to_owned()); } } Ok(()) } } // // The code below was copied/adjusted from Cargo. // /// The `Cargo.lock` structure. #[derive(Serialize, Deserialize, Debug)] pub struct EncodableResolve { package: Vec, /// `root` is optional to allow backward compatibility. root: Option, metadata: Option>, } #[derive(Serialize, Deserialize, Debug, PartialOrd, Ord, PartialEq, Eq)] pub struct EncodableDependency { name: String, version: String, source: Option, checksum: Option, dependencies: Option>, replace: Option, } #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Clone)] pub struct EncodablePackageId { name: String, version: Option, source: Option, } impl fmt::Display for EncodablePackageId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.name)?; if let Some(s) = &self.version { write!(f, " {}", s)?; } if let Some(s) = &self.source { write!(f, " ({})", s)?; } Ok(()) } } impl FromStr for EncodablePackageId { type Err = anyhow::Error; fn from_str(s: &str) -> Result { let mut s = s.splitn(3, ' '); let name = s.next().unwrap(); let version = s.next(); let source_id = match s.next() { Some(s) => { if s.starts_with('(') && s.ends_with(')') { Some(String::from(&s[1..s.len() - 1])) } else { anyhow::bail!("invalid serialized PackageId") } } None => None, }; Ok(EncodablePackageId { name: name.to_string(), version: version.map(|v| v.to_string()), source: source_id, }) } } impl ser::Serialize for EncodablePackageId { fn serialize(&self, s: S) -> Result where S: ser::Serializer, { s.collect_str(self) } } impl<'de> de::Deserialize<'de> for EncodablePackageId { fn deserialize(d: D) -> Result where D: de::Deserializer<'de>, { String::deserialize(d).and_then(|string| { string .parse::() .map_err(de::Error::custom) }) } } ================================================ FILE: crate2nix/src/main.rs ================================================ use std::path::{Path, PathBuf}; use structopt::clap::ArgGroup; use structopt::StructOpt; use anyhow::format_err; use anyhow::{bail, Error}; use crate2nix::{ config::{Config, NixFile}, render, }; use semver::Version; use serde::Deserialize; use serde::Serialize; use std::str::FromStr; const DEFAULT_OUTPUT: &str = "./Cargo.nix"; const DEFAULT_JSON_OUTPUT: &str = "./Cargo.json"; #[derive(Debug, StructOpt, Deserialize, Serialize)] #[structopt( name = "crate2nix", about = "Nix build file generator for a cargo rust projects." )] pub enum Opt { #[structopt( name = "generate", about = "Generates a Cargo.nix file from a cargo rust project." )] Generate { #[structopt( short = "c", long = "config", parse(from_os_str), help = "The path to the crate2nix.json file (same directory as Cargo.nix ...).", default_value = "./crate2nix.json" )] crate2nix_json: PathBuf, #[structopt( short = "f", long = "cargo-toml", parse(from_os_str), help = "The path to the Cargo.toml of the project." )] cargo_toml: Vec, #[structopt( long = "all-features", help = "Resolve project dependencies with all features enabled. \ This is the default and does not need to be specified. \ Users can choose their sub set of features and evaluation time so \ that one generated build file can be used for different feature selections." )] all_features: bool, #[structopt( long = "default-features", help = "Enables the default default features \ (instead of all features as is the default). \ Often combined with --features to add selected features on top." )] default_features: bool, #[structopt( long = "no-default-features", help = "Disables all features. \ Often combined with --features to reenable selected features." )] no_default_features: bool, #[structopt( long = "features", help = "Resolve project dependencies additionally with these features enabled. \ By default, all features are resolved." )] features: Vec, #[structopt( short = "o", long = "output", help = "The path of the output.nix file. Uses ./Cargo.nix by default." )] output: Option, #[structopt( short = "n", long = "nixpkgs-path", help = "The default path for the nixpkgs to use.", default_value = "" )] nixpkgs_path: String, #[structopt( short = "h", long = "crate-hashes", parse(from_os_str), help = "The path to the crate hash cache file. \ Uses 'crate-hashes.json' in the same directory as the Cargo.nix output by default." )] crate_hashes: Option, #[structopt( short = "r", long = "registry-hashes", parse(from_os_str), help = "The path to the registry hash cache file. \ Uses 'registry-hashes.json' in the same directory as the Cargo.nix output by default." )] registry_hashes: Option, // Mostly useful for testing #[structopt( long = "no-cargo-lock-checksums", help = "(FOR TESTING) Do not use checksums from Cargo.lock." )] no_cargo_lock_checksums: bool, #[structopt( long = "dont-read-crate-hashes", help = "(FOR TESTING) Do not read crate-hashes file. \ If there are any prefetches, their hashes will still be written into crate-hashes.json." )] dont_read_crate_hashes: bool, #[structopt( long = "format", help = "Output format: 'nix' (default, generates Cargo.nix) or 'json' \ (pre-resolved JSON with platform conditions preserved as strings).", default_value = "nix" )] format: String, }, #[structopt(name = "source", about = "Manage out of tree sources for crate2nix.")] Source { #[structopt( short = "c", long = "config", parse(from_os_str), help = "The path to the crate2nix.json file (same directory as Cargo.nix ...).", default_value = "./crate2nix.json" )] crate2nix_json: PathBuf, #[structopt(subcommand)] command: SourceCommands, }, #[structopt( name = "completions", about = "Generates auto-completions for the shell." )] Completions { #[structopt( short = "s", long = "shell", parse(from_str), help = "The shell to generate completions for. Specify 'invalid' to get a list of possibilities.", default_value = "bash" )] shell: String, #[structopt( short = "o", long = "output", help = "The path of the output directory.", default_value = "." )] output: PathBuf, }, } #[derive(Debug, StructOpt, Deserialize, Serialize)] #[structopt(about = "Support for managing out-of-tree sources.")] pub enum SourceCommands { #[structopt(name = "add", about = "Adds source, prefetching it if when necessary.")] Add { #[structopt(subcommand)] command: SourceAddingCommands, }, #[structopt(name = "remove", about = "Removes source.")] Remove { #[structopt(long = "name", help = "The name of the source to remove.")] name: String, }, #[structopt(name = "list", about = "Lists all sources.")] List, #[structopt( name = "fetch", about = "Fetch all sources with nix.\n\ This is usually called automatically and mostly useful for testing." )] Fetch, #[structopt( name = "generate", about = "Generate crate2nix-sources.nix.\n\ This is usually called automatically and mostly useful for testing." )] Generate, } impl SourceCommands { pub fn execute(self, crate2nix_json: &Path) -> Result<(), Error> { match self { SourceCommands::Add { command, .. } => command.execute(crate2nix_json), SourceCommands::List => { let config = Config::read_from_or_default(crate2nix_json)?; config.print_sources(); Ok(()) } SourceCommands::Remove { name } => { let mut config = Config::read_from_or_default(crate2nix_json)?; if config.sources.is_empty() { eprintln!( "No sources configured in {}.", crate2nix_json.to_string_lossy() ); } else { let removed = config.sources.remove(&name); if let Some(removed) = removed { config.write_to(crate2nix_json)?; eprintln!("Removed source\n\t{}", removed); } else { eprintln!("Source '{}' not found among the following sources.\n", name); config.print_sources(); } } Ok(()) } SourceCommands::Fetch => { let sources = crate2nix::sources::FetchedSources::new(crate2nix_json); let output = sources.fetch()?; println!("Fetched sources into {}", output.to_string_lossy()); Ok(()) } SourceCommands::Generate => { let sources = crate2nix::sources::FetchedSources::new(crate2nix_json); sources.regenerate_sources_nix() } } } } #[derive(Debug, StructOpt, Deserialize, Serialize)] pub enum SourceAddingCommands { #[structopt(name = "cratesIo", about = "Adds source from crates.io.")] CratesIo { #[structopt( long = "name", help = "Use this source name instead of the crate name.\n\ The source name is used as a workspaceMember name." )] name: Option, #[structopt(help = "The crate name on crates.io.")] crate_name: String, #[structopt(help = "The full version of the crate.")] crate_version: Version, }, #[structopt( name = "git", about = "Adds git source.\n\ \n\ If you want auto-update support, consider using the \"nix\" source type\n\ and manage the sources with niv.\n\ \n\ See https://github.com/nmattia/niv." )] Git { #[structopt( long = "name", help = "Use this source name instead of the last URL path segment without '.git'.\n\ The source name is used as a workspaceMember name." )] name: Option, /// The URL of the git repository. /// /// E.g. https://github.com/kolloch/crate2nix.git url: url::Url, #[structopt(long = "rev", parse(from_str), help = "The git revision hash.")] rev: String, }, #[structopt( name = "nix", about = "Adds nix attribute from a file as source.\n\ E.g. crate2nix source add --import nix/sources.nix my_crate. \n\ This is the most flexible source type.\n\ Works well with tools like niv which support easy updating.", // We need either an `--import` or a `--package`. group = ArgGroup::with_name("file").required(true), // We need an explicit `--name` or an `attr` to derive the name from. group = ArgGroup::with_name("some_name").multiple(true).required(true), )] Nix { #[structopt( long, help = "The name of this source \n\ if you do not want to use the last element of the attribute path.", group = "some_name" )] name: Option, #[structopt(long, group = "file", help = "A path to `import` in nix.")] import: Option, #[structopt( long, group = "file", help = "A path to call with `pkgs.callPackage` in nix." )] package: Option, #[structopt( help = "The attribute path that leads to the source derivation.", group = "some_name" )] attr: Option, }, } impl SourceAddingCommands { pub fn execute(self, crate2nix_json: &Path) -> Result<(), Error> { let (name, source) = match self { SourceAddingCommands::CratesIo { name, crate_name, crate_version, } => { let source = crate2nix::sources::crates_io_source(crate_name, crate_version)?; (name, source) } SourceAddingCommands::Git { name, url, rev } => { let source = crate2nix::sources::git_io_source(url, rev)?; (name, source) } SourceAddingCommands::Nix { name, import, package, attr, } => { let file = match (import, package) { (Some(import), _) => NixFile::Import(import), (_, Some(package)) => NixFile::Package(package), _ => unreachable!("no file argument given"), }; (name, crate2nix::config::Source::Nix { file, attr }) } }; let mut config = Config::read_from_or_default(crate2nix_json)?; let old_source = config.upsert_source(name, source.clone()); config.write_to(crate2nix_json)?; match old_source { Some(old_source) => { eprintln!( "Updated existing source\n\t{}\nto\n\t{}", old_source, source ); } None => { eprintln!("Added new source: {}", source); } } Ok(()) } } /// Refuse to overwrite a JSON file that wasn't generated by crate2nix. fn check_generated_json(path: impl AsRef) -> Result<(), Error> { let path = path.as_ref(); if !path.exists() { return Ok(()); } let contents = std::fs::read_to_string(path)?; if !contents.contains("@generated by crate2nix") { bail!( "Cowardly refusing to overwrite {} without generated marker.", path.to_string_lossy() ); } Ok(()) } fn main() -> anyhow::Result<()> { let opt = Opt::from_args(); match opt { Opt::Generate { crate2nix_json, mut cargo_toml, output: opt_output, nixpkgs_path, crate_hashes, registry_hashes, all_features, default_features, no_default_features, features, no_cargo_lock_checksums, dont_read_crate_hashes, format, } => { let config = crate2nix::config::Config::read_from_or_default(&crate2nix_json)?; if !config.sources.is_empty() { let fetched_sources = crate2nix::sources::FetchedSources::new(&crate2nix_json); let cargo_tomls = fetched_sources.get_cargo_tomls()?; cargo_toml.extend(cargo_tomls); } if cargo_toml.is_empty() { cargo_toml.push("./Cargo.toml".into()); } let output: PathBuf = opt_output .map(|v| Ok(v) as Result<_, Error>) .unwrap_or_else(|| { if format == "json" { check_generated_json(DEFAULT_JSON_OUTPUT)?; Ok(DEFAULT_JSON_OUTPUT.into()) } else { crate2nix::render::check_generated_by_crate2nix(DEFAULT_OUTPUT)?; Ok(DEFAULT_OUTPUT.into()) } })?; let crate_hashes_json = crate_hashes.unwrap_or_else(|| { output .parent() .expect("Cargo.nix has parent") .join("crate-hashes.json") }); let registry_hashes_json = registry_hashes.unwrap_or_else(|| { output .parent() .expect("Cargo.nix has parent") .join("registry-hashes.json") }); let generate_info = crate2nix::GenerateInfo::default(); let feature_metadata_options = || { let mut options = Vec::new(); if [all_features, default_features, no_default_features] .iter() .filter(|x| **x) .count() > 1 { bail!( "Please specify at most one of \ --all-features, --no-default-features and --default-features." ) } // "cargo metadata" will default to the "default features". // crate2nix defaults to "--all-features" since this allows users to choose // any set of features at evaluation time. let all_features = !no_default_features && !default_features; if no_default_features { options.push("--no-default-features".to_string()); } else if !default_features { assert!(all_features); options.push("--all-features".to_string()); } if !features.is_empty() { if all_features { bail!( "You specified --features but --all-features was already selected. \ Use --no-default-features or --default-features to only select \ some features as a basis and then use --features to add additional \ features on top." ) } options.push("--features".to_string()); options.push(features.join(" ")); } Ok(options) }; let generate_config = crate2nix::GenerateConfig { cargo_toml, output: output.clone(), nixpkgs_path, crate_hashes_json, registry_hashes_json, other_metadata_options: feature_metadata_options()?, use_cargo_lock_checksums: !no_cargo_lock_checksums, read_crate_hashes: !dont_read_crate_hashes, }; let build_info = crate2nix::BuildInfo::for_config(&generate_info, &generate_config)?; match format.as_str() { "json" => { let resolved = crate2nix::json_output::to_resolved_workspace(&build_info); let json = serde_json::to_string_pretty(&resolved)?; std::fs::write(&output, json)?; eprintln!("Generated {} successfully.", output.to_string_lossy()); } "nix" => { render::CARGO_NIX.write_to_file(&output, &build_info)?; } _ => bail!("Unknown format '{}'. Use 'nix' or 'json'.", format), } } Opt::Completions { shell, output } => { let shell = FromStr::from_str(&shell).map_err(|s| format_err!("{}", s))?; Opt::clap().gen_completions(env!("CARGO_PKG_NAME"), shell, output); } Opt::Source { crate2nix_json, command, } => { command.execute(&crate2nix_json)?; } } Ok(()) } ================================================ FILE: crate2nix/src/metadata.rs ================================================ //! Indexing cargo metadata. use std::collections::{BTreeMap, HashMap, HashSet}; use anyhow::format_err; use anyhow::{Error, Result}; use cargo_metadata::Node; use cargo_metadata::Package; use cargo_metadata::PackageId; use cargo_metadata::{Metadata, NodeDep}; use itertools::Itertools; use serde::Deserialize; use serde::Serialize; /// The merged metadata of potentially multiple sources. /// /// Metadata: Package Metadata from Cargo.lock files. /// /// Why "merged" metadata: crate2nix can be used to generate /// builds for multiple projects without combining them into a /// workspace. #[derive(Debug)] pub struct MergedMetadata { workspace_members: Vec, pub(crate) packages: Vec, root: Option, nodes: Vec, /// The workspace root directory (from the first metadata). pub workspace_root: Option, } impl MergedMetadata { pub fn merge(metadatas: Vec) -> Result { assert!(!metadatas.is_empty()); let mut workspace_members = Vec::new(); let mut package_ids = HashSet::new(); let mut packages = Vec::new(); let mut node_package_ids = HashSet::new(); let mut nodes = Vec::new(); // Take workspace_root from the first metadata (primary workspace). let workspace_root = metadatas.first().map(|m| m.workspace_root.to_string()); for metadata in metadatas.into_iter() { let resolve = metadata .resolve .ok_or_else(|| format_err!("no resolve in metadata"))?; if let Some(root) = resolve.root { if metadata.workspace_members != vec![root.clone()] { // Usually, cargo metadata also puts the root into workspace_members. // Therefore, I only saw this warning in unit tests. eprintln!("WARNING: root missing from workspace_members."); } } workspace_members.extend(metadata.workspace_members); packages.extend( metadata .packages .into_iter() .filter(|p| package_ids.insert(p.id.clone())), ); nodes.extend( resolve .nodes .into_iter() .filter(|p| node_package_ids.insert(p.id.clone())), ); } let root = if workspace_members.len() <= 1 { workspace_members.first().cloned() } else { None }; Ok(MergedMetadata { packages, root, workspace_members: workspace_members.into_iter().unique().collect(), nodes, workspace_root, }) } } /// The metadata with maps indexed by {{PackageId}} instead of flat lists. #[derive(Debug, Deserialize, Serialize, Clone)] pub struct IndexedMetadata { pub root: Option, pub workspace_members: Vec, pub pkgs_by_id: BTreeMap, pub nodes_by_id: BTreeMap, pub id_shortener: PackageIdShortener, /// The workspace root directory path. pub workspace_root: Option, } impl IndexedMetadata { pub fn new_from(metadata: Metadata) -> Result { let merged = MergedMetadata::merge(vec![metadata])?; Self::new_from_merged(&merged) } pub fn new_from_merged( MergedMetadata { root, workspace_members, packages, nodes, workspace_root, }: &MergedMetadata, ) -> Result { let id_shortener = PackageIdShortener::new(packages.iter()); let pkgs_by_id: BTreeMap = packages .iter() .map(|pkg| { ( id_shortener.shorten(&pkg.id), id_shortener.shorten_in_package(pkg), ) }) .collect(); let nodes_by_id: BTreeMap = nodes .iter() .map(|node| { ( id_shortener.shorten(&node.id), id_shortener.shorten_in_node(node), ) }) .collect(); Ok(IndexedMetadata { root: root.as_ref().map(|id| id_shortener.shorten(id)), workspace_members: workspace_members .iter() .map(|id| id_shortener.shorten(id)) .collect(), pkgs_by_id, nodes_by_id, id_shortener, workspace_root: workspace_root.clone(), }) } #[cfg(test)] pub fn root_package(&self) -> Option<&Package> { let root = self.root.as_ref()?; self.pkgs_by_id.get(root) } } /// "Shortens" package IDs to potentially remove local paths from /// the IDs. The local paths can make the build file generation /// depend on the local systems path. #[derive(Debug, Deserialize, Serialize, Clone)] pub struct PackageIdShortener { substitution: HashMap, reverse: HashMap, } impl PackageIdShortener { /// Returns a substitution map for shorter package IDs. It falls back to the next /// longer option if it is not unique. The options in order: /// /// * Just the crate name. /// * The crate name and the version. /// /// If the shortening substitution wasn't successful, the package_id is not contained in /// the returned map. #[allow(clippy::needless_lifetimes)] fn new<'a>(all_packages: impl Iterator) -> PackageIdShortener { let mut substitution = HashMap::new(); let mut reverse = HashMap::new(); for (_crate_name, group) in &all_packages .sorted_by_key(|p| &p.name) .group_by(|p| p.name.clone()) { let packages: Vec<_> = group.collect(); enum UniqueComponent { Name, NameVersion, PackageId, } let unique_component = if packages.len() == 1 { UniqueComponent::Name } else if packages.iter().map(|p| &p.version).unique().count() == packages.len() { UniqueComponent::NameVersion } else { eprintln!( "Using same version of crate from different sources: {:#?}", packages.iter().map(|p| &p.id.repr).collect::>() ); UniqueComponent::PackageId }; for p in &packages { let substitute = match unique_component { UniqueComponent::Name => Some(p.name.clone()), UniqueComponent::NameVersion => Some(format!("{} {}", p.name, p.version)), UniqueComponent::PackageId => None, }; if let Some(repr) = substitute { substitution.insert(p.id.clone(), PackageId { repr: repr.clone() }); reverse.insert(PackageId { repr }, p.id.clone()); } } } PackageIdShortener { substitution, reverse, } } pub fn lengthen_ref<'a>(&'a self, package_id: &'a PackageId) -> &'a PackageId { self.reverse.get(package_id).unwrap_or(package_id) } pub fn shorten_ref<'a>(&'a self, package_id: &'a PackageId) -> &'a PackageId { self.substitution.get(package_id).unwrap_or(package_id) } pub fn shorten(&self, package_id: &PackageId) -> PackageId { self.substitution .get(package_id) .cloned() .unwrap_or_else(|| package_id.clone()) } pub fn shorten_owned(&self, package_id: PackageId) -> PackageId { self.substitution .get(&package_id) .cloned() .unwrap_or(package_id) } fn shorten_in_package(&self, package: &Package) -> Package { let mut p = package.clone(); p.id = self.shorten(&package.id); p } fn shorten_in_node(&self, node: &Node) -> Node { let mut n = node.clone(); n.id = self.shorten_owned(n.id); n.dependencies = n .dependencies .into_iter() .map(|id| self.shorten_owned(id)) .collect(); n.deps = n .deps .iter() .map(|dep| self.shorten_in_node_dep(dep)) .collect(); n } fn shorten_in_node_dep(&self, nod_dep: &NodeDep) -> NodeDep { let mut n = nod_dep.clone(); n.pkg = self.shorten_owned(n.pkg); n } } ================================================ FILE: crate2nix/src/nix_build.rs ================================================ //! Code for invoking `nix-build`. use std::io::BufRead; use std::io::Write; use std::path::Path; use std::process::Command; use anyhow::bail; use anyhow::format_err; use anyhow::Error; /// Call `nix build` in the given directory on the `default.nix` in that directory. pub fn nix_build( project_dir: impl AsRef, nix_attr: &str, features: &[&str], ) -> Result<(), Error> { let project_dir_path = project_dir.as_ref(); let project_dir = project_dir_path.to_string_lossy().to_string(); let result = crate::command::run( &format!("Building {}", project_dir), Command::new("nix") .current_dir(&project_dir) .args([ "--show-trace", "build", "-f", "default.nix", nix_attr, "--arg", "rootFeatures", ]) .arg(format!( "[ {} ]", features .iter() .map(|s| crate::render::escape_nix_string(s)) .collect::>() .join(" ") )), ); if result.is_err() { dump_with_lines(project_dir_path.join("default.nix"))?; } result } /// Dump the content of the specified file with line numbers to stdout. pub fn dump_with_lines(file_path: impl AsRef) -> Result<(), Error> { let file_path = file_path.as_ref().to_string_lossy().to_string(); let content = std::io::BufReader::new(std::fs::File::open(file_path)?); let stdout = std::io::stdout(); let mut handle = stdout.lock(); for (idx, line) in content.lines().enumerate() { writeln!(handle, "{:>5}: {}", idx + 1, line?)?; } Ok(()) } /// Run the command at the given path without arguments and capture the output in the return value. pub fn run_cmd(cmd_path: impl AsRef) -> Result { let cmd_path = cmd_path.as_ref().to_string_lossy().to_string(); let output = Command::new(&cmd_path) .output() .map_err(|e| format_err!("while spawning {}: {}", cmd_path, e))?; if !output.status.success() { std::io::stdout().write_all(&output.stdout)?; std::io::stderr().write_all(&output.stderr)?; bail!( "{}\n=> exited with: {}", cmd_path, output.status.code().unwrap_or(-1) ); } String::from_utf8(output.stdout) .map_err(|_e| format_err!("output of {} is not UTF8!", cmd_path)) } ================================================ FILE: crate2nix/src/prefetch.rs ================================================ //! Utilities for calling `nix-prefetch` on packages. use std::io::Write; use std::process::Command; use crate::metadata::PackageIdShortener; use crate::resolve::{CrateDerivation, CratesIoSource, GitSource, RegistrySource, ResolvedSource}; use crate::GenerateConfig; use anyhow::bail; use anyhow::format_err; use anyhow::Error; use cargo_metadata::PackageId; use serde::Deserialize; use std::collections::{BTreeMap, HashMap}; /// Extracts `(name, version)` from a package ID string in either format: /// - Old: `"name version (source)"` → split on spaces, take first two tokens /// - New: `"source#name@version"` → split on `#`, then on `@` /// /// Returns `None` for malformed IDs or new-format IDs without a name (e.g. `"source#version"`). fn parse_package_id_components(repr: &str) -> Option<(String, String)> { if repr.contains(' ') { // Old format: "name version (source)" or "name version" let mut parts = repr.split_whitespace(); let name = parts.next()?; let version = parts.next()?; Some((name.to_string(), version.to_string())) } else if let Some(fragment) = repr.split_once('#').map(|(_, f)| f) { // New format: "source#name@version" let (name, version) = fragment.split_once('@')?; Some((name.to_string(), version.to_string())) } else { None } } /// The source is important because we need to store only hashes for which we performed /// a prefetch. #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum HashSource { Prefetched, Existing, } #[derive(Debug, Clone, PartialEq, Eq)] struct HashWithSource { sha256: String, source: HashSource, } /// A source with all the packages that depend on it and a potentially preexisting hash. #[derive(Debug)] struct SourcePrefetchBundle<'a> { source: &'a ResolvedSource, packages: &'a Vec<&'a CrateDerivation>, hash: Option, } /// Uses `nix-prefetch` to get the hashes of the sources for the given packages if they come from crates.io. /// /// Uses and updates the existing hashes in the `config.crate_hash_json` file. pub fn prefetch( config: &GenerateConfig, from_lock_file: &HashMap, crate_derivations: &[CrateDerivation], id_shortener: &PackageIdShortener, ) -> Result, Error> { let hashes_string: String = if config.read_crate_hashes { std::fs::read_to_string(&config.crate_hashes_json).unwrap_or_else(|_| "{}".to_string()) } else { "{}".to_string() }; let old_prefetched_hashes: BTreeMap = serde_json::from_str(&hashes_string)?; // Build a fallback index keyed by (name, version) so we can match hashes // even when the package ID format differs (old vs new cargo format). let hashes_by_name_version: HashMap<(String, String), &String> = old_prefetched_hashes .iter() .filter_map(|(id, hash)| parse_package_id_components(&id.repr).map(|key| (key, hash))) .collect(); // Only copy used hashes over to the new map. let mut hashes = BTreeMap::::new(); // Multiple packages might be fetched from the same source. // // Usually, a source is only used by one package but e.g. the same git source can be used // by multiple packages. let packages_by_source: HashMap> = { let mut index = HashMap::new(); for package in crate_derivations { index .entry(package.source.without_sha256()) .or_insert_with(Vec::new) .push(package); } index }; // Associate prefetchable sources with existing hashes. let prefetchable_sources: Vec = packages_by_source .iter() .filter(|(source, _)| source.needs_prefetch()) .map(|(source, packages)| { // All the packages have the same source. // So is there any package for which we already know the hash? let hash = packages .iter() .filter_map(|p| { from_lock_file .get(&p.package_id) .map(|hash| HashWithSource { sha256: hash.clone(), source: HashSource::Existing, }) .or_else(|| { old_prefetched_hashes .get(id_shortener.lengthen_ref(&p.package_id)) .map(|hash| HashWithSource { sha256: hash.clone(), source: HashSource::Prefetched, }) }) .or_else(|| { // Fallback: match by (name, version) when the package ID // format differs between the hashes file and cargo metadata. hashes_by_name_version .get(&(p.crate_name.clone(), p.version.to_string())) .map(|hash| HashWithSource { sha256: (*hash).clone(), source: HashSource::Prefetched, }) }) .or_else(|| { // This happens e.g. if the sha256 comes from crate2nix.json. packages .iter() .filter_map(|p| p.source.sha256()) .next() .map(|hash| HashWithSource { sha256: hash.clone(), source: HashSource::Existing, }) }) }) .next(); SourcePrefetchBundle { source, packages, hash, } }) .collect(); let without_hash_num = prefetchable_sources .iter() .filter(|SourcePrefetchBundle { hash, .. }| hash.is_none()) .count(); let mut idx = 1; for SourcePrefetchBundle { source, packages, hash, } in prefetchable_sources { let (sha256, hash_source) = if let Some(HashWithSource { sha256, source }) = hash { (sha256.trim().to_string(), source) } else { eprintln!("Prefetching {:>4}/{}: {}", idx, without_hash_num, source); idx += 1; (source.prefetch()?, HashSource::Prefetched) }; for package in packages { if hash_source == HashSource::Prefetched { hashes.insert( id_shortener.lengthen_ref(&package.package_id).clone(), sha256.clone(), ); } } } if hashes != old_prefetched_hashes { std::fs::write( &config.crate_hashes_json, serde_json::to_vec_pretty(&hashes)?, ) .map_err(|e| { format_err!( "while writing hashes to {}: {}", config.crate_hashes_json.to_str().unwrap_or(""), e ) })?; eprintln!( "Wrote hashes to {}.", config.crate_hashes_json.to_string_lossy() ); } Ok(hashes) } /// Prefetch the config.json file from all the derivation's private registries. pub fn prefetch_registries( config: &GenerateConfig, crate_derivations: &[CrateDerivation], ) -> Result, Error> { let hashes_string: String = if config.read_crate_hashes { std::fs::read_to_string(&config.registry_hashes_json).unwrap_or_else(|_| "{}".to_string()) } else { "{}".to_string() }; let old_prefetched_hashes: BTreeMap = serde_json::from_str(&hashes_string)?; let mut hashes = old_prefetched_hashes.clone(); for package in crate_derivations { let registry = if let ResolvedSource::Registry(RegistrySource { ref registry, .. }) = package.source { registry } else { continue; }; use std::collections::btree_map::Entry; if let Entry::Vacant(e) = hashes.entry(registry.to_string()) { eprintln!("Prefetching {} config", e.key()); let out = get_command_output( "nix-prefetch-url", &[&format!( "{}{}config.json", e.key(), if e.key().ends_with('/') { "" } else { "/" } )], )?; e.insert(out); } } if hashes != old_prefetched_hashes { std::fs::write( &config.registry_hashes_json, serde_json::to_vec_pretty(&hashes)?, ) .map_err(|e| { format_err!( "while writing hashes to {}: {}", config.crate_hashes_json.to_str().unwrap_or(""), e ) })?; eprintln!( "Wrote hashes to {}.", config.registry_hashes_json.to_string_lossy() ); } Ok(hashes) } fn get_command_output(cmd: &str, args: &[&str]) -> Result { let output = Command::new(cmd) .args(args) .output() .map_err(|e| format_err!("While spawning '{} {}': {}", cmd, args.join(" "), e))?; if !output.status.success() { std::io::stdout().write_all(&output.stdout)?; std::io::stderr().write_all(&output.stderr)?; bail!( "{}\n=> exited with: {}", cmd, output.status.code().unwrap_or(-1) ); } String::from_utf8(output.stdout) .map(|s| s.trim().to_string()) .map_err(|_e| format_err!("output of '{} {}' is not UTF8!", cmd, args.join(" "))) } /// A crate source that potentially has a prefetchable hash. pub trait PrefetchableSource: ToString { /// Returns whether we actually need a prefetch. `false` if /// e.g. we already have the hash. fn needs_prefetch(&self) -> bool; /// Prefetches the source and returns the hash. fn prefetch(&self) -> Result; } impl ResolvedSource { fn inner_prefetchable(&self) -> Option<&dyn PrefetchableSource> { match self { ResolvedSource::CratesIo(source) => Some(source), ResolvedSource::Registry(source) => Some(source), ResolvedSource::Git(source) => Some(source), _ => None, } } } impl PrefetchableSource for ResolvedSource { fn needs_prefetch(&self) -> bool { self.inner_prefetchable() .map(|s| s.needs_prefetch()) .unwrap_or(false) } fn prefetch(&self) -> Result { self.inner_prefetchable() .map(|s| s.prefetch()) .unwrap_or_else(|| Err(format_err!("source does not support prefetch: {:?}", self))) } } impl PrefetchableSource for CratesIoSource { fn needs_prefetch(&self) -> bool { self.sha256.is_none() } fn prefetch(&self) -> Result { let args = &[ &self.url(), "--name", &format!("{}-{}", self.name, self.version), ]; get_command_output("nix-prefetch-url", args) } } impl PrefetchableSource for RegistrySource { fn needs_prefetch(&self) -> bool { self.sha256.is_none() } fn prefetch(&self) -> Result { // This is done in two steps, currently only implemented in // the generated Nix. unimplemented!() } } impl PrefetchableSource for GitSource { fn needs_prefetch(&self) -> bool { self.sha256.is_none() } fn prefetch(&self) -> Result { /// A struct used to contain the output returned by `nix-prefetch-git`. /// /// Additional fields are available (e.g., `name`), but we only call `nix-prefetch-git` to obtain /// the nix sha256 for use in calls to `pkgs.fetchgit` in generated `Cargo.nix` files so there's no /// reason to declare the fields here until they are needed. #[derive(Deserialize)] struct NixPrefetchGitInfo { sha256: String, } let mut args = vec![ "--url", self.url.as_str(), "--fetch-submodules", "--rev", &self.rev, ]; // TODO: --branch-name isn't documented in nix-prefetch-git --help // TODO: Consider the case when ref *isn't* a branch. You have to pass // that to `--rev` instead. This seems like limitation of nix-prefetch-git. if let Some(r#ref) = self.r#ref.as_ref() { args.extend_from_slice(&["--branch-name", r#ref]); } let json = get_command_output("nix-prefetch-git", &args)?; let prefetch_info: NixPrefetchGitInfo = serde_json::from_str(&json)?; Ok(prefetch_info.sha256) } } #[cfg(test)] mod tests { use super::parse_package_id_components; #[test] fn old_format_crates_io() { let id = "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)"; assert_eq!( parse_package_id_components(id), Some(("serde".to_string(), "1.0.160".to_string())) ); } #[test] fn old_format_git() { let id = "pkg-config 0.3.33 (git+https://github.com/rust-lang/pkg-config-rs?branch=main#abcdef1)"; assert_eq!( parse_package_id_components(id), Some(("pkg-config".to_string(), "0.3.33".to_string())) ); } #[test] fn new_format_with_name() { let id = "git+https://github.com/example/repo#pkg-config@0.3.33"; assert_eq!( parse_package_id_components(id), Some(("pkg-config".to_string(), "0.3.33".to_string())) ); } #[test] fn new_format_registry() { let id = "registry+https://github.com/rust-lang/crates.io-index#serde@1.0.160"; assert_eq!( parse_package_id_components(id), Some(("serde".to_string(), "1.0.160".to_string())) ); } #[test] fn new_format_without_name() { // "source#version" with no @ means no name — should return None let id = "git+https://github.com/example/repo#0.3.33"; assert_eq!(parse_package_id_components(id), None); } #[test] fn old_format_short_no_source() { let id = "serde 1.0.160"; assert_eq!( parse_package_id_components(id), Some(("serde".to_string(), "1.0.160".to_string())) ); } #[test] fn single_token_returns_none() { let id = "serde"; assert_eq!(parse_package_id_components(id), None); } #[test] fn new_format_version_with_build_metadata() { let id = "registry+https://github.com/rust-lang/crates.io-index#libsqlite3-sys@0.15.0+8.9.1"; assert_eq!( parse_package_id_components(id), Some(("libsqlite3-sys".to_string(), "0.15.0+8.9.1".to_string())) ); } } ================================================ FILE: crate2nix/src/render.rs ================================================ //! "Render" files using tera templates. use std::io::{BufRead, Write}; use std::path::Path; use std::{fs::File, iter}; use crate::{BuildInfo, GenerateInfo}; use anyhow::format_err; use anyhow::{bail, Error}; use cargo_platform::{Cfg, CfgExpr}; use itertools::Itertools; use lazy_static::lazy_static; use serde::Serialize; use std::collections::HashMap; use std::{fmt::Debug, marker::PhantomData, str::FromStr}; use tera::{Context, Tera}; macro_rules! template { ($x:expr) => { Template { template: $x, #[cfg(not(debug_assertions))] content: include_str!(concat!("../templates/", $x)), context: PhantomData, } }; } /// The template for generating Cargo.nix. pub const CARGO_NIX: Template = template!("Cargo.nix.tera"); /// Included in build.nix.tera const DEFAULT_NIX: Template<()> = template!("nix/crate2nix/default.nix"); /// The template for generating a directory with all out of tree sources. pub const SOURCES_NIX: Template = template!("crate2nix-sources.nix.tera"); /// Context argument for the `CARGO_TOML_FOR_WORKSPACE` template. /// /// This is used to render a `Cargo.toml` for a workspace which /// is build via nix. #[derive(Debug, Serialize)] pub struct CargoTomlForWorkspace { /// The generate info for this invocation. pub info: GenerateInfo, /// The symlink to the workspace member dir derivation output. pub workspace_member_dir: String, /// The names of the members of this workspace - which are /// equal to the names of the subdirectory symlinks. pub members: Vec, } /// A predefined template. #[derive(Debug)] pub struct Template { /// Relative path in the templates directory and template name. template: &'static str, /// The whole content of the template file in release builds. #[cfg(not(debug_assertions))] content: &'static str, context: PhantomData, } impl Template { /// Returns the rendered template as a string. pub fn render(&self, context: &C) -> Result { let rendered = TERA .render(self.template, &Context::from_serialize(context)?) .map_err(|e| { format_err!( "while rendering {}: {:#?}\nContext: {:#?}", self.template, e, context ) })?; Ok(rendered .lines() .map(|l| l.trim_end()) .chain(iter::once("\n")) .join("\n")) } /// Writes the rendered template to the given file path. pub fn write_to_file(&self, path: impl AsRef, context: &C) -> Result<(), Error> { let mut output_file = File::create(&path)?; output_file.write_all(self.render(context)?.as_bytes())?; println!( "Generated {} successfully.", path.as_ref().to_string_lossy() ); Ok(()) } } /// Ensures that the given path either does not exist or contains a file that /// was generated by crate2nix. pub fn check_generated_by_crate2nix(path: impl AsRef) -> Result<(), Error> { if !path.as_ref().exists() { return Ok(()); } let reader = std::io::BufReader::new(File::open(path.as_ref())?); let generated = reader.lines().any(|l| { l.map(|l| l.contains("@generated by crate2nix")) .unwrap_or(false) }); if !generated { bail!( "Cowardly refusing to overwrite {} without generated marker.", path.as_ref().to_string_lossy() ); } Ok(()) } trait AbstractTemplate { fn template(&self) -> &'static str; #[cfg(not(debug_assertions))] fn template_content(&self) -> &'static str; } impl AbstractTemplate for Template { fn template(&self) -> &'static str { self.template } #[cfg(not(debug_assertions))] fn template_content(&self) -> &'static str { self.content } } const TEMPLATES: &[&'static dyn AbstractTemplate] = &[&CARGO_NIX, &DEFAULT_NIX, &SOURCES_NIX]; fn create_tera() -> Tera { let mut tera = Tera::default(); // For debug builds, we load the templates from the files during runtime. #[cfg(debug_assertions)] let template_dir = std::env::var("TEMPLATES_DIR") .expect("TEMPLATES_DIR environment variable when running in debug mode"); #[cfg(debug_assertions)] for template in TEMPLATES.iter() { let path = Path::new(&template_dir).join(template.template()); tera.add_template_file(path, Some(template.template())) .expect("adding template to succeed"); } // For release builds, we compile the template definitions into the binary. #[cfg(not(debug_assertions))] tera.add_raw_templates( TEMPLATES .iter() .map(|template| (template.template(), template.template_content())) .collect::>(), ) .expect("adding templats to succeed"); tera.autoescape_on(vec![".nix.tera", ".nix"]); tera.set_escape_fn(escape_nix_string); tera.register_filter("cfg_to_nix_expr", cfg_to_nix_expr_filter); tera } lazy_static! { static ref TERA: Tera = create_tera(); } fn cfg_to_nix_expr_filter( value: &tera::Value, _args: &HashMap, ) -> tera::Result { match value { tera::Value::String(key) => { if key.starts_with("cfg(") && key.ends_with(')') { let cfg = &key[4..key.len() - 1]; let expr = CfgExpr::from_str(cfg).map_err(|e| { tera::Error::msg(format!( "cfg_to_nix_expr_filter: Could not parse '{}': {}", cfg, e )) })?; Ok(tera::Value::String(cfg_to_nix_expr(&expr))) } else { let condition = format!("(target.name == {})", escape_nix_string(key)); Ok(tera::Value::String(condition)) } } _ => Err(tera::Error::msg(format!( "cfg_to_nix_expr_filter: Expected string, got {:?}", value ))), } } /// Renders a config expression to nix code. fn cfg_to_nix_expr(cfg: &CfgExpr) -> String { fn target(target_name: &str) -> String { escape_nix_string(target_name.strip_prefix("target_").unwrap_or(target_name)) } fn render(result: &mut String, cfg: &CfgExpr) { match cfg { CfgExpr::Value(Cfg::Name(name)) => { result.push_str(&format!("(target.{} or false)", target(name))); } CfgExpr::Value(Cfg::KeyPair(key, value)) => { let escaped_value = escape_nix_string(value); result.push_str(&if key == "feature" { format!("(builtins.elem {} features)", escaped_value) } else if key == "target_feature" { format!("(builtins.elem {} targetFeatures)", escaped_value) } else if key == "target_family" { format!("(builtins.elem {} target.{})", escaped_value, target(key)) } else { format!("({} == target.{} or null)", escaped_value, target(key)) }); } CfgExpr::Not(expr) => { result.push_str("(!"); render(result, expr); result.push(')'); } CfgExpr::All(expressions) => { if expressions.is_empty() { result.push_str("true"); } else { result.push('('); render(result, &expressions[0]); for expr in &expressions[1..] { result.push_str(" && "); render(result, expr); } result.push(')'); } } CfgExpr::Any(expressions) => { if expressions.is_empty() { result.push_str("false"); } else { result.push('('); render(result, &expressions[0]); for expr in &expressions[1..] { result.push_str(" || "); render(result, expr); } result.push(')'); } } } } let mut ret = String::new(); render(&mut ret, cfg); ret } #[test] fn test_render_cfg_to_nix_expr() { fn name(value: &str) -> CfgExpr { CfgExpr::Value(Cfg::Name(value.to_string())) } fn kv(key: &str, value: &str) -> CfgExpr { CfgExpr::Value(Cfg::KeyPair(key.to_string(), value.to_string())) } assert_eq!( "(target.\"unix\" or false)", &cfg_to_nix_expr(&name("unix")) ); assert_eq!( "((builtins.elem \"aes\" targetFeatures) && (builtins.elem \"foo\" features))", &cfg_to_nix_expr(&CfgExpr::All(vec![ kv("target_feature", "aes"), kv("feature", "foo") ])) ); assert_eq!( "(builtins.elem \"unix\" target.\"family\")", &cfg_to_nix_expr(&kv("target_family", "unix")) ); assert_eq!( "(\"linux\" == target.\"os\" or null)", &cfg_to_nix_expr(&kv("target_os", "linux")) ); assert_eq!( "(!(\"linux\" == target.\"os\" or null))", &cfg_to_nix_expr(&CfgExpr::Not(Box::new(kv("target_os", "linux")))) ); assert_eq!( "((target.\"unix\" or false) || (\"linux\" == target.\"os\" or null))", &cfg_to_nix_expr(&CfgExpr::Any(vec![name("unix"), kv("target_os", "linux")])) ); assert_eq!( "((target.\"unix\" or false) && (\"linux\" == target.\"os\" or null))", &cfg_to_nix_expr(&CfgExpr::All(vec![name("unix"), kv("target_os", "linux")])) ); assert_eq!("true", &cfg_to_nix_expr(&CfgExpr::All(vec![]))); assert_eq!("false", &cfg_to_nix_expr(&CfgExpr::Any(vec![]))); } /// Escapes a string as a nix string. /// /// ``` /// use crate2nix::render::escape_nix_string; /// assert_eq!("\"abc\"", escape_nix_string("abc")); /// assert_eq!("\"a\\\"bc\"", escape_nix_string("a\"bc")); /// assert_eq!("\"a$bc\"", escape_nix_string("a$bc")); /// assert_eq!("\"a$\"", escape_nix_string("a$")); /// assert_eq!("\"a\\${bc\"", escape_nix_string("a${bc")); /// ``` pub fn escape_nix_string(raw_string: &str) -> String { let mut ret = String::with_capacity(raw_string.len() + 2); ret.push('"'); let mut peekable_chars = raw_string.chars().peekable(); while let Some(c) = peekable_chars.next() { if c == '\\' || c == '"' || (c == '$' && peekable_chars.peek() == Some(&'{')) { ret.push('\\'); } ret.push(c); } ret.push('"'); ret } ================================================ FILE: crate2nix/src/resolve.rs ================================================ //! Resolve dependencies and other data for CrateDerivation. use anyhow::format_err; use anyhow::Error; use cargo_metadata::Node; use cargo_metadata::Package; use cargo_metadata::PackageId; use cargo_metadata::{Dependency, Source}; use cargo_metadata::{DependencyKind, Target}; use cargo_platform::Platform; use pathdiff::diff_paths; use semver::Version; use serde::Deserialize; use serde::Serialize; use serde_json::to_string_pretty; use std::collections::HashMap; use std::convert::Into; use std::path::{Path, PathBuf}; use crate::metadata::IndexedMetadata; #[cfg(test)] use crate::test; use crate::GenerateConfig; use itertools::Itertools; use std::{collections::btree_map::BTreeMap, fmt::Display}; use url::Url; /// All data necessary for creating a derivation for a crate. #[derive(Debug, Deserialize, Serialize)] pub struct CrateDerivation { pub package_id: PackageId, pub crate_name: String, pub edition: String, pub authors: Vec, pub version: Version, /// The name of a native library the package is linking to. pub links: Option, pub source: ResolvedSource, /// The crate types of the lib targets of this crate, e.g. "lib", "dylib", "rlib", ... pub lib_crate_types: Vec, pub dependencies: Vec, pub build_dependencies: Vec, pub dev_dependencies: Vec, /// Feature rules. Which feature (key) enables which other features (values). pub features: BTreeMap>, /// The resolved features for this crate for a default build as returned by cargo. pub resolved_default_features: Vec, /// The build target for the custom build script. pub build: Option, /// The build target for the library. pub lib: Option, pub binaries: Vec, pub proc_macro: bool, /// This derivation builds the root crate or a workspace member. pub is_root_or_workspace_member: bool, } impl CrateDerivation { pub fn resolve( config: &GenerateConfig, crate2nix_json: &crate::config::Config, metadata: &IndexedMetadata, package: &Package, ) -> Result { let resolved_dependencies = ResolvedDependencies::new(metadata, package)?; let build_dependencies = resolved_dependencies.filtered_dependencies(|d| d.kind == DependencyKind::Build); let dependencies = resolved_dependencies.filtered_dependencies(|d| { d.kind == DependencyKind::Normal || d.kind == DependencyKind::Unknown }); let dev_dependencies = resolved_dependencies.filtered_dependencies(|d| d.kind == DependencyKind::Development); let is_root_or_workspace_member = metadata .root .iter() .chain(metadata.workspace_members.iter()) .any(|pkg_id| *pkg_id == package.id); let package_path = package.manifest_path.parent().unwrap_or_else(|| { panic!( "WUUT? No parent directory of manifest at {}?", package.manifest_path.as_str() ) }); // This depends on the non-cananocalized package_path (without symlinks // resolved). let configured_source = if is_root_or_workspace_member { // In the resolved data, we don't have the link to the workspace member // name anymore. So we need to extract it from the path. let configured_source = package_path .file_name() .and_then(|file_name| crate2nix_json.sources.get(file_name).cloned()); if !crate2nix_json.sources.is_empty() && configured_source.is_none() { eprintln!( "warning: Could not find configured source for workspace member {:?}", package_path ); } configured_source } else { None }; let source = if let Some(configured) = configured_source { configured.into() } else { ResolvedSource::new(config, package, package_path)? }; let package_path = package_path.canonicalize().map_err(|e| { format_err!( "while canonicalizing crate path path {}: {}", package_path.as_str(), e ) })?; let lib = package .targets .iter() .find(|t| { t.kind.iter().any(|k| { k == "lib" || k == "cdylib" || k == "dylib" || k == "rlib" || k == "proc-macro" }) }) .and_then(|target| BuildTarget::new(target, &package_path).ok()); let build = package .targets .iter() .find(|t| t.kind.iter().any(|k| k == "custom-build")) .and_then(|target| BuildTarget::new(target, &package_path).ok()); let proc_macro = package .targets .iter() .any(|t| t.kind.iter().any(|k| k == "proc-macro")); let binaries = package .targets .iter() .filter_map(|t| { if t.kind.iter().any(|k| k == "bin") { BuildTarget::new(t, &package_path).ok() } else { None } }) .collect(); Ok(CrateDerivation { crate_name: package.name.clone(), edition: package.edition.to_string(), authors: package.authors.clone(), package_id: package.id.clone(), version: package.version.clone(), links: package.links.clone(), source, features: package .features .iter() .map(|(name, feature_list)| (name.clone(), feature_list.clone())) .collect(), resolved_default_features: metadata .nodes_by_id .get(&package.id) .map(|n| n.features.clone()) .unwrap_or_default(), lib_crate_types: package .targets .iter() .filter(|target| target.kind.iter().any(|kind| kind.ends_with("lib"))) .flat_map(|target| target.crate_types.iter()) .unique() .cloned() .collect(), dependencies, build_dependencies, dev_dependencies, build, lib, proc_macro, binaries, is_root_or_workspace_member, }) } } #[test] pub fn minimal_resolve() { use cargo_metadata::{Metadata, Resolve}; let config = test::generate_config(); let package = test::package("main", "1.2.3"); let node = test::node(&package.id.repr); let mut resolve: Resolve = test::empty_resolve(); resolve.root = Some(package.id.clone()); resolve.nodes = vec![node]; let mut metadata: Metadata = test::empty_metadata(); metadata.workspace_members = vec![package.id.clone()]; metadata.packages = vec![package.clone()]; metadata.resolve = Some(resolve); let indexed = IndexedMetadata::new_from(metadata).unwrap(); println!("indexed: {:#?}", indexed); let root_package = &indexed.root_package().expect("root package"); let crate_derivation = CrateDerivation::resolve( &config, &crate::config::Config::default(), &indexed, root_package, ) .unwrap(); println!("crate_derivation: {:#?}", crate_derivation); assert_eq!(crate_derivation.crate_name, "main"); assert_eq!( crate_derivation.version, semver::Version::parse("1.2.3").unwrap() ); assert!(crate_derivation.is_root_or_workspace_member); let empty: Vec = vec![]; assert_eq!(crate_derivation.lib_crate_types, empty); package.close().unwrap(); } #[test] pub fn configured_source_is_used_instead_of_local_directory() { use std::convert::TryInto; use std::str::FromStr; let mut env = test::MetadataEnv::default(); let config = test::generate_config(); // crate2nix creates a "virtual" workspace which consists of symlinks to the member sources. // The symlinks use the source names and this is how we detect that we use a workspace member // source. // By simulating this layout, we ensure that we do not canonicalize paths at the "wrong" // moment. let simulated_store_path = env.temp_dir(); std::fs::File::create(simulated_store_path.join("Cargo.toml")).expect("File creation failed"); let workspace_with_symlink = env.temp_dir(); std::os::unix::fs::symlink( &simulated_store_path, workspace_with_symlink.join("some_crate"), ) .expect("could not create symlink"); let manifest_path = workspace_with_symlink.join("some_crate").join("Cargo.toml"); let mut main = env.add_package_and_node("main"); main.update_package(|p| p.manifest_path = manifest_path.try_into().unwrap()); main.make_root(); let indexed = env.indexed_metadata(); let root_package = &indexed.root_package().expect("root package"); let mut crate2nix_json = crate::config::Config::default(); let source = crate::config::Source::CratesIo { name: "some_crate".to_string(), version: semver::Version::from_str("1.2.3").unwrap(), sha256: "123".to_string(), }; crate2nix_json.upsert_source(None, source); let crate_derivation = CrateDerivation::resolve(&config, &crate2nix_json, &indexed, root_package).unwrap(); println!("crate_derivation: {:#?}", crate_derivation); assert!(crate_derivation.is_root_or_workspace_member); assert_eq!( crate_derivation.source, ResolvedSource::CratesIo(CratesIoSource { name: "some_crate".to_string(), version: semver::Version::from_str("1.2.3").unwrap(), sha256: Some("123".to_string()), }) ); env.close(); } #[test] pub fn double_crate_with_rename() { let mut env = test::MetadataEnv::default(); let config = test::generate_config(); let mut main = env.add_package_and_node("main"); main.make_root(); main.add_dependency("futures") .version_and_package_id("0.1.0") .update_package_dep(|d| d.rename = Some("futures01".to_string())) .update_node_dep(|n| n.name = "futures01".to_string()); main.add_dependency("futures") .version_and_package_id("0.3.0") .update_package_dep(|d| { d.uses_default_features = false; d.features = vec!["compat".to_string()]; }); let indexed = env.indexed_metadata(); let root_package = &indexed.root_package().expect("root package"); let crate_derivation = CrateDerivation::resolve( &config, &crate::config::Config::default(), &indexed, root_package, ) .unwrap(); println!("crate_derivation: {:#?}", crate_derivation); assert_eq!(crate_derivation.dependencies.len(), 2); env.close(); } /// A build target of a crate. #[derive(Debug, Deserialize, Serialize)] pub struct BuildTarget { /// The name of the build target. pub name: String, /// The relative path of the target source file. pub src_path: PathBuf, /// The crate's features that need to be enabled for this target to be compiled. /// Otherwise, this target is ignored. pub required_features: Vec, } impl BuildTarget { pub fn new(target: &Target, package_path: impl AsRef) -> Result { Ok(BuildTarget { name: target.name.clone(), src_path: target .src_path .canonicalize()? .strip_prefix(&package_path)? .to_path_buf(), required_features: target.required_features.clone(), }) } } /// Specifies how to retrieve the source code. #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)] pub enum ResolvedSource { CratesIo(CratesIoSource), Registry(RegistrySource), Git(GitSource), LocalDirectory(LocalDirectorySource), Nix(NixSource), } impl From for ResolvedSource { fn from(source: crate::config::Source) -> Self { match source { crate::config::Source::Git { url, rev, sha256 } => ResolvedSource::Git(GitSource { url, rev, r#ref: None, sha256: Some(sha256), }), crate::config::Source::CratesIo { name, version, sha256, } => ResolvedSource::CratesIo(CratesIoSource { name, version, sha256: Some(sha256), }), crate::config::Source::Registry { name, version, sha256, registry, } => ResolvedSource::Registry(RegistrySource { name, version, sha256: Some(sha256), registry: registry.parse().unwrap(), }), crate::config::Source::Nix { file, attr } => { ResolvedSource::Nix(NixSource { file, attr }) } } } } #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)] pub struct CratesIoSource { pub name: String, pub version: Version, pub sha256: Option, } #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)] pub struct RegistrySource { pub registry: Url, pub name: String, pub version: Version, pub sha256: Option, } #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)] pub struct GitSource { pub url: Url, pub rev: String, pub r#ref: Option, pub sha256: Option, } #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)] pub struct LocalDirectorySource { path: PathBuf, } #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)] pub struct NixSource { file: crate::config::NixFile, attr: Option, } const GIT_SOURCE_PREFIX: &str = "git+"; impl ResolvedSource { pub fn new( config: &GenerateConfig, package: &Package, package_path: impl AsRef, ) -> Result { match package.source.as_ref() { Some(source) if source.is_crates_io() => { // Will sha256 will be filled later by prefetch_and_fill_crates_sha256. Ok(ResolvedSource::CratesIo(CratesIoSource { name: package.name.clone(), version: package.version.clone(), sha256: None, })) } Some(source) if source.repr.starts_with("sparse+") => { Ok(ResolvedSource::Registry(RegistrySource { registry: source .repr .split_at("sparse+".len()) .1 .to_string() .parse() .unwrap(), name: package.name.clone(), version: package.version.clone(), sha256: None, })) } Some(source) => { ResolvedSource::git_or_local_directory(config, package, &package_path, source) } None => Ok(ResolvedSource::LocalDirectory(LocalDirectorySource { path: ResolvedSource::relative_directory(config, package_path)?, })), } } fn git_or_local_directory( config: &GenerateConfig, package: &Package, package_path: &impl AsRef, source: &Source, ) -> Result { let source_string = source.to_string(); if !source_string.starts_with(GIT_SOURCE_PREFIX) { return ResolvedSource::fallback_to_local_directory( config, package, package_path, "No 'git+' prefix found.", ); } let mut url = url::Url::parse(&source_string[GIT_SOURCE_PREFIX.len()..])?; let mut query_pairs = url.query_pairs(); let branch = query_pairs .find(|(k, _)| k == "branch") .map(|(_, v)| v.to_string()); let rev = if let Some((_, rev)) = query_pairs.find(|(k, _)| k == "rev") { rev.to_string() } else if let Some(rev) = url.fragment() { rev.to_string() } else { return ResolvedSource::fallback_to_local_directory( config, package, package_path, "No git revision found.", ); }; url.set_query(None); url.set_fragment(None); Ok(ResolvedSource::Git(GitSource { url, rev, r#ref: branch, sha256: None, })) } fn fallback_to_local_directory( config: &GenerateConfig, package: &Package, package_path: impl AsRef, warning: &str, ) -> Result { let path = Self::relative_directory(config, package_path)?; eprintln!( "WARNING: {} Falling back to local directory for crate {} with source {}: {}", warning, package.id, package .source .as_ref() .map(std::string::ToString::to_string) .unwrap_or_else(|| "N/A".to_string()), &path.to_string_lossy() ); Ok(ResolvedSource::LocalDirectory(LocalDirectorySource { path, })) } fn relative_directory( config: &GenerateConfig, package_path: impl AsRef, ) -> Result { // Use local directory. This is the local cargo crate directory in the worst case. let mut output_build_file_directory = config .output .parent() .ok_or_else(|| { format_err!( "could not get parent of output file '{}'.", config.output.to_string_lossy() ) })? .to_path_buf(); if output_build_file_directory.is_relative() { // Deal with "empty" path. E.g. the parent of "Cargo.nix" is "". output_build_file_directory = Path::new(".").join(output_build_file_directory); } output_build_file_directory = output_build_file_directory.canonicalize().map_err(|e| { format_err!( "could not canonicalize output file directory '{}': {}", output_build_file_directory.to_string_lossy(), e ) })?; Ok(if package_path.as_ref() == output_build_file_directory { "./.".into() } else { let path = diff_paths(package_path.as_ref(), &output_build_file_directory) .unwrap_or_else(|| package_path.as_ref().to_path_buf()); if &path == "../" { path.join(PathBuf::from(".")) } else if path.starts_with("../") { path } else { PathBuf::from("./").join(path) } }) } pub fn sha256(&self) -> Option<&String> { match self { Self::CratesIo(CratesIoSource { sha256, .. }) | Self::Registry(RegistrySource { sha256, .. }) | Self::Git(GitSource { sha256, .. }) => sha256.as_ref(), _ => None, } } pub fn with_sha256(&self, sha256: String) -> Self { match self { Self::CratesIo(source) => Self::CratesIo(CratesIoSource { sha256: Some(sha256), ..source.clone() }), Self::Registry(source) => Self::Registry(RegistrySource { sha256: Some(sha256), ..source.clone() }), Self::Git(source) => Self::Git(GitSource { sha256: Some(sha256), ..source.clone() }), _ => self.clone(), } } pub fn without_sha256(&self) -> Self { match self { Self::CratesIo(source) => Self::CratesIo(CratesIoSource { sha256: None, ..source.clone() }), Self::Registry(source) => Self::Registry(RegistrySource { sha256: None, ..source.clone() }), Self::Git(source) => Self::Git(GitSource { sha256: None, ..source.clone() }), _ => self.clone(), } } } impl Display for ResolvedSource { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::CratesIo(source) => source.fmt(f), Self::Registry(source) => source.fmt(f), Self::Git(source) => source.fmt(f), Self::LocalDirectory(source) => source.fmt(f), Self::Nix(source) => source.fmt(f), } } } impl CratesIoSource { pub fn url(&self) -> String { // https://www.pietroalbini.org/blog/downloading-crates-io/ // Not rate-limited, CDN URL. format!( "https://static.crates.io/crates/{name}/{name}-{version}.crate", name = self.name, version = self.version ) } } impl RegistrySource { pub fn url(&self) -> String { unimplemented!() } } impl Display for CratesIoSource { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.url()) } } impl Display for RegistrySource { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.url()) } } impl Display for GitSource { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let base = format!("{}#{}", self.url, self.rev); if let Some(branch) = self.r#ref.as_ref() { write!(f, "{} branch: {}", base, branch) } else { write!(f, "{}", base) } } } impl Display for LocalDirectorySource { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.path.to_str().unwrap()) } } impl Display for NixSource { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if let Some(attr) = self.attr.as_ref() { write!(f, "({}).{}", self.file, attr) } else { write!(f, "{}", self.file) } } } /// Normalize a package name such as cargo does. fn normalize_package_name(package_name: &str) -> String { package_name.replace('-', "_") } #[derive(Debug)] /// Helper to retrieve the `ResolvedDependency` structs for a package/crate. /// /// For this, we need to join the information from `Dependency`, which contains /// the dependency requirements as specified in `Cargo.toml`, and `NodeDep` which /// contains the resolved package to use. Unfortunately, there is no simply key /// on which to perform the join in the general case. struct ResolvedDependencies<'a> { package: &'a Package, /// Packages references in the NodeDeps of this package. resolved_packages_by_crate_name: HashMap>, } impl<'a> ResolvedDependencies<'a> { fn new( metadata: &'a IndexedMetadata, package: &'a Package, ) -> Result, Error> { let node: &Node = metadata.nodes_by_id.get(&package.id).ok_or_else(|| { format_err!( "Could not find node for {}.\n-- Package\n{}", &package.id, to_string_pretty(&package).unwrap_or_else(|_| "ERROR".to_string()) ) })?; let mut resolved_packages_by_crate_name: HashMap> = HashMap::new(); for node_dep in &node.deps { let package = metadata.pkgs_by_id.get(&node_dep.pkg).ok_or_else(|| { format_err!( "No matching package for dependency with package id {} in {}.\n-- Package\n{}\n-- Node\n{}", node_dep.pkg, package.id, to_string_pretty(&package).unwrap_or_else(|_| "ERROR".to_string()), to_string_pretty(&node).unwrap_or_else(|_| "ERROR".to_string()), ) })?; let packages = resolved_packages_by_crate_name .entry(normalize_package_name(&package.name)) .or_default(); packages.push(package); } Ok(ResolvedDependencies { package, resolved_packages_by_crate_name, }) } fn filtered_dependencies( &self, filter: impl Fn(&Dependency) -> bool, ) -> Vec { let ResolvedDependencies { package, resolved_packages_by_crate_name, } = self; let mut resolved = package .dependencies .iter() .filter(|package_dep| filter(package_dep)) .flat_map(|package_dep| { let name: String = normalize_package_name(&package_dep.name); let resolved = resolved_packages_by_crate_name .get(&name) .and_then(|packages| { let matches = packages .iter() .filter(|p| package_dep.req.matches(&p.version)) .collect::>(); // Strip prerelease/build info from versions if we // did not find an exact match. // // E.g. "*" does not match a prerelease version in this // library but cargo thinks differently. let matches = if matches.is_empty() { packages .iter() .filter(|p| { let without_metadata = { let mut version = p.version.clone(); version.pre = semver::Prerelease::EMPTY; version.build = semver::BuildMetadata::EMPTY; version }; package_dep.req.matches(&without_metadata) }) .collect() } else { matches }; // It is possible to have multiple packages that match the name and version // requirement of the dependency. In particular if there are multiple // dependencies on the same package via git at different revisions - in // that case `package_dep.req` is set to `*` so we can't use the version // requirement to match the appropriate locked package with the dependency. // Instead it's necessary to compare by source instead. let matches = if matches.len() > 1 { matches .into_iter() .filter(|p| { sources_match(package_dep.source.as_deref(), p.source.as_ref()) .unwrap_or(false) }) .collect() } else { matches }; if matches.len() == 1 { Some(matches[0]) } else if matches.is_empty() { None } else { panic!("Could not find an unambiguous package match for dependency, {}. Candidates are: {}", &package_dep.name, matches.iter().map(|p| &p.id).join(", ")); } }); let dep_package = resolved?; Some(ResolvedDependency { name: package_dep.name.clone(), rename: package_dep.rename.clone(), package_id: dep_package.id.clone(), target: package_dep.target.clone(), optional: package_dep.optional, uses_default_features: package_dep.uses_default_features, features: package_dep.features.clone(), }) }) .collect::>(); resolved.sort_by(|d1, d2| d1.package_id.cmp(&d2.package_id)); resolved } } fn sources_match( dependency_source: Option<&str>, package_source: Option<&Source>, ) -> Result { let Some(dependency_source) = dependency_source else { return Ok(package_source.is_none()); }; let Some(package_source) = package_source else { return Ok(false); // fail if dependency has a source, but package does not }; let dependency = Url::parse(dependency_source)?; let package = Url::parse(&package_source.repr)?; let scheme_matches = dependency.scheme() == package.scheme(); let domain_matches = dependency.domain() == package.domain(); let path_matches = dependency.path() == package.path(); let query_matches = { let package_query = package.query_pairs().collect::>(); dependency.query_pairs().all(|(key, dep_value)| { package_query .get(&key) .is_some_and(|pkg_value| &dep_value == pkg_value) }) }; Ok(scheme_matches && domain_matches && path_matches && query_matches) } /// Converts one type into another by serializing/deserializing it. /// /// Therefore, the output json of `I` must be deserializable to `O`. #[allow(unused)] #[cfg(test)] fn serialize_deserialize(input: &I) -> O where for<'d> O: Deserialize<'d>, { let json_string = serde_json::to_string(input).expect("serialize"); let deserialized: Result = serde_json::from_str(&json_string); deserialized.expect("deserialize") } #[test] pub fn resolved_dependencies_new_with_double_crate() { let mut env = test::MetadataEnv::default(); let mut main = env.add_package_and_node("main"); main.make_root(); main.add_dependency("futures") .version_and_package_id("0.1.0") .update_package_dep(|d| d.rename = Some("futures01".to_string())) .update_node_dep(|n| n.name = "futures01".to_string()); main.add_dependency("futures") .version_and_package_id("0.3.0") .update_package_dep(|d| { d.uses_default_features = false; d.features = vec!["compat".to_string()]; }); let indexed = env.indexed_metadata(); let root_package = &indexed.root_package().expect("root package"); let resolved_deps = ResolvedDependencies::new(&indexed, root_package).unwrap(); assert_eq!( resolved_deps.resolved_packages_by_crate_name.len(), 1, "unexpected packages_by_crate_name: {}", serde_json::to_string_pretty(&resolved_deps.resolved_packages_by_crate_name).unwrap() ); assert!( resolved_deps .resolved_packages_by_crate_name .contains_key("futures"), "unexpected packages_by_crate_name: {}", serde_json::to_string_pretty(&resolved_deps.resolved_packages_by_crate_name).unwrap() ); assert_eq!( resolved_deps .resolved_packages_by_crate_name .get("futures") .unwrap() .len(), 2, "unexpected packages_by_crate_name: {}", serde_json::to_string_pretty(&resolved_deps.resolved_packages_by_crate_name).unwrap() ); env.close(); } #[test] pub fn resolved_dependencies_filtered_dependencies_with_double_crate() { let mut env = test::MetadataEnv::default(); let mut main = env.add_package_and_node("main"); main.make_root(); main.add_dependency("futures") .version_and_package_id("0.1.0") .update_package_dep(|d| d.rename = Some("futures01".to_string())) .update_node_dep(|n| n.name = "futures01".to_string()); main.add_dependency("futures") .version_and_package_id("0.3.0") .update_package_dep(|d| { d.uses_default_features = false; d.features = vec!["compat".to_string()]; }); let indexed = env.indexed_metadata(); let root_package = &indexed.root_package().expect("root package"); let resolved_deps = ResolvedDependencies::new(&indexed, root_package).unwrap(); let filtered_deps = resolved_deps.filtered_dependencies(|d| { d.kind == DependencyKind::Normal || d.kind == DependencyKind::Unknown }); assert_eq!( filtered_deps.len(), 2, "unexpected resolved dependencies: {}", serde_json::to_string_pretty(&filtered_deps).unwrap() ); env.close(); } #[derive(Debug, Deserialize, Serialize)] pub struct ResolvedDependency { pub name: String, /// New name for the dependency if it is renamed. pub rename: Option, pub package_id: PackageId, /// The cfg expression for conditionally enabling the dependency (if any). /// Can also be a target "triplet". pub target: Option, /// Whether this dependency is optional and thus needs to be enabled via a feature. pub optional: bool, /// Whether the crate uses this dependency with default features enabled. pub uses_default_features: bool, /// Extra-enabled features. pub features: Vec, } ================================================ FILE: crate2nix/src/sources.rs ================================================ //! Manage nix-generated Cargo workspaces. use crate::{ config, prefetch::PrefetchableSource, resolve::{CratesIoSource, GitSource, RegistrySource}, }; use anyhow::{bail, format_err, Context, Error}; use semver::Version; use std::{ borrow::Cow, path::{Path, PathBuf}, }; use std::{fs::File, io::BufRead, process::Command, time::SystemTime}; use url::Url; /// Returns the completed Source::CratesIo definition by prefetching the hash. pub fn crates_io_source(name: String, version: Version) -> Result { let prefetchable = CratesIoSource { name: name.clone(), version: version.clone(), sha256: None, }; eprint!("Prefetching {}: ", prefetchable); let sha256 = prefetchable.prefetch()?; eprintln!("done."); Ok(config::Source::CratesIo { name, version, sha256, }) } /// Returns the completed Source::Registry definition by prefetching the hash. pub fn registry_source( registry: String, name: String, version: Version, ) -> Result { let prefetchable = RegistrySource { registry: registry.parse()?, name: name.clone(), version: version.clone(), sha256: None, }; eprint!("Prefetching {}: ", prefetchable); let sha256 = prefetchable.prefetch()?; eprintln!("done."); Ok(config::Source::Registry { registry, name, version, sha256, }) } /// Returns the completed Source::Git definition by prefetching the hash. pub fn git_io_source(url: Url, rev: String) -> Result { let prefetchable = GitSource { url: url.clone(), rev: rev.clone(), r#ref: None, sha256: None, }; eprint!("Prefetching {}: ", prefetchable); let sha256 = prefetchable.prefetch()?; eprintln!("done."); Ok(config::Source::Git { url, rev, sha256 }) } /// Operations on assmebling out-of-tree sources via nix. pub struct FetchedSources<'a> { crate2nix_json_path: Cow<'a, Path>, } const FETCHED_SOURCES: &str = "crate2nix-sources"; impl<'a> FetchedSources<'a> { /// Returns a new CrateConfig for the given path. pub fn new>>(path: P) -> FetchedSources<'a> { FetchedSources { crate2nix_json_path: path.into(), } } fn project_dir(&self) -> PathBuf { self.crate2nix_json_path .parent() .expect("config to have parent") .to_path_buf() } fn sources_nix(&self) -> PathBuf { self.project_dir().join("crate2nix-sources.nix") } /// Create a config-nix if it doesn't exist yet. pub fn regenerate_sources_nix(&self) -> Result<(), Error> { let info = crate::GenerateInfo::default(); if !self.crate2nix_json_path.exists() { bail!( "Did not find config at '{}'.", self.crate2nix_json_path.to_string_lossy() ); } if self.sources_nix().exists() { let reader = std::io::BufReader::new(File::open(self.sources_nix())?); let generated = reader.lines().any(|l| { l.map(|l| l.contains("@generated by crate2nix")) .unwrap_or(false) }); if !generated { bail!("Cowardly refusing to overwrite sources.nix without generated marker."); } } crate::render::SOURCES_NIX.write_to_file(self.sources_nix(), &info)?; Ok(()) } /// Fetches the sources via nix. pub fn fetch(&self) -> Result { self.regenerate_sources_nix() .context("while regenerating crate2nix-sources.nix")?; let fetched_sources_symlink = self.project_dir().join(FETCHED_SOURCES); download_and_link_out_of_tree_sources( self.project_dir(), self.sources_nix(), &fetched_sources_symlink, "fetchedSources", ) .context("while building crate2nix-sources directory")?; Ok(fetched_sources_symlink) } /// Fetches the sources via nix and returns the paths to their Cargo.tomls. pub fn get_cargo_tomls(&self) -> Result, Error> { let fetched_sources_symlink = self.project_dir().join(FETCHED_SOURCES); let last_modified: fn(&std::path::Path) -> Option = |f: &std::path::Path| { std::fs::symlink_metadata(f) .ok() .and_then(|m| m.modified().ok()) }; let has_nix_sources = { let config = crate::config::Config::read_from_or_default(&self.crate2nix_json_path)?; config .sources .values() .any(|s| matches!(s, config::Source::Nix { .. })) }; let outdated = || { let symlink_generated = last_modified(&fetched_sources_symlink).unwrap_or(SystemTime::UNIX_EPOCH); let sources_modified = last_modified(&self.crate2nix_json_path).unwrap_or_else(SystemTime::now); symlink_generated < sources_modified }; if has_nix_sources || outdated() { eprintln!("Fetching sources."); self.fetch()?; } let workspace_member_dir = fetched_sources_symlink; let mut cargo_tomls: Vec = Vec::new(); for entry in std::fs::read_dir(&workspace_member_dir).map_err(|e| { format_err!( "while iterating {} directory: {}", workspace_member_dir.to_string_lossy(), e ) })? { let entry = entry.map_err(|e| { format_err!( "while resolving entry in {} directory: {}", workspace_member_dir.to_string_lossy().as_ref(), e ) })?; let path: PathBuf = entry.path(); if path.is_dir() { let cargo_toml = path.join("Cargo.toml"); if !cargo_toml.exists() { eprintln!( "WARNING: No Cargo.toml found in {}.\n\ This will lead to later failures.", path.to_string_lossy() ); } let cargo_lock = path.join("Cargo.lock"); if !cargo_lock.exists() { eprintln!( "WARNING: No Cargo.lock found in {}.\n\ This will lead to later failures.", path.to_string_lossy() ); } cargo_tomls.push(cargo_toml); } } Ok(cargo_tomls) } } fn download_and_link_out_of_tree_sources( project_dir: impl AsRef, sources_nix: impl AsRef, generated_sources_symlink: impl AsRef, nix_attr: &str, ) -> Result<(), Error> { let project_dir = project_dir.as_ref().to_string_lossy().to_string(); let sources_nix = sources_nix.as_ref().to_string_lossy().to_string(); let caption = format!("Fetching sources via {} {}", sources_nix, nix_attr); crate::command::run( &caption, Command::new("nix").current_dir(&project_dir).args([ "--show-trace", "build", "-f", &sources_nix, nix_attr, "-o", generated_sources_symlink .as_ref() .to_string_lossy() .as_ref(), ]), )?; Ok(()) } ================================================ FILE: crate2nix/src/test.rs ================================================ //! Constructor functions for test data. #![allow(missing_docs)] use cargo_metadata::{Dependency, Metadata, Node, NodeDep, Package, PackageId, Resolve}; use std::path::PathBuf; use tempdir::TempDir; /// Returns bogus crate::GenerateConfig. pub fn generate_config() -> crate::GenerateConfig { crate::GenerateConfig { cargo_toml: vec!["Cargo.toml".into()], crate_hashes_json: "crate-hashes.json".into(), nixpkgs_path: "bogus-nixpkgs-path".into(), other_metadata_options: vec![], output: "Cargo.nix".into(), use_cargo_lock_checksums: true, read_crate_hashes: true, registry_hashes_json: "registry-hashes.json".into(), } } #[derive(Debug)] pub struct MetadataEnv { /// Keep track of temporary directories. temp_dirs: Vec, metadata: Metadata, } impl Drop for MetadataEnv { fn drop(&mut self) { if !self.temp_dirs.is_empty() { eprintln!( "Consider explicitly closing MetadataEnv: {:?}", self.temp_dirs ); } } } impl Default for MetadataEnv { fn default() -> Self { MetadataEnv { temp_dirs: Vec::new(), metadata: empty_metadata(), } } } impl MetadataEnv { pub fn temp_dir(&mut self) -> PathBuf { let temp_dir = TempDir::new("crate2nix_test").expect("temp dir creation failed"); let path = temp_dir.path().to_path_buf(); self.temp_dirs.push(temp_dir); path } fn mut_metadata(&mut self) -> &mut Metadata { &mut self.metadata } fn mut_resolve(&mut self) -> &mut Resolve { self.metadata.resolve.get_or_insert_with(empty_resolve) } pub fn add_package_and_node<'a>(&'a mut self, name: &str) -> PackageAndNode<'a> { let package = package(name, "0.1.0"); let package_idx = self.metadata.packages.len(); self.metadata.packages.push(package.clone()); let nodes = &mut self.mut_resolve().nodes; let node_idx = nodes.len(); nodes.push(node(&package.id.repr)); self.temp_dirs.push(package.crate_dir); PackageAndNode { env: self, package_idx, node_idx, } } pub fn metadata(&self) -> Metadata { self.metadata.clone() } pub fn indexed_metadata(&self) -> crate::IndexedMetadata { crate::IndexedMetadata::new_from(self.metadata()).unwrap() } pub fn close(&mut self) { for temp_dir in self.temp_dirs.drain(..) { temp_dir.close().expect("while closing temp"); } } } #[derive(Debug)] pub struct PackageAndNode<'a> { env: &'a mut MetadataEnv, package_idx: usize, node_idx: usize, } impl<'a> PackageAndNode<'a> { pub fn make_root(&mut self) -> &mut Self { let package_id = self.get_mut_package().id.clone(); self.env .mut_metadata() .workspace_members .push(package_id.clone()); self.env.mut_resolve().root = Some(package_id); self } pub fn get_mut_package(&mut self) -> &mut Package { &mut self.env.metadata.packages[self.package_idx] } pub fn get_package(&self) -> &Package { &self.env.metadata.packages[self.package_idx] } pub fn update_package(&mut self, f: impl FnOnce(&mut Package)) -> &mut Self { f(self.get_mut_package()); self } pub fn get_mut_node(&mut self) -> &mut Node { &mut self.env.mut_resolve().nodes[self.node_idx] } pub fn get_node(&mut self) -> &Node { &self.env.mut_resolve().nodes[self.node_idx] } pub fn update_node(&mut self, f: impl FnOnce(&mut Node)) -> &mut Self { f(self.get_mut_node()); self } pub fn version_and_package_id(&mut self, version: &str) -> &mut Self { let p = self.get_mut_package(); p.version = semver::Version::parse(version).expect("version incorrect"); p.id = crates_io_package_id(&p.name, version); self } pub fn add_dependency<'b>(&'b mut self, name: &str) -> PackageAndNodeDep<'b> { let package_dep_idx = self.get_mut_package().dependencies.len(); let node_dep_idx = self.get_mut_node().dependencies.len(); let mut new_package = self.env.add_package_and_node(name); let pkg_dep = dependency_from_package(new_package.get_mut_package()); let node_dep = node_dep(name, &new_package.get_mut_package().id); let PackageAndNode { package_idx, node_idx, .. } = new_package; self.get_mut_package().dependencies.push(pkg_dep); let node = self.get_mut_node(); node.dependencies.push(node_dep.pkg.clone()); node.deps.push(node_dep); PackageAndNodeDep { package_and_node: PackageAndNode { env: self.env, package_idx, node_idx, }, parent_package_idx: self.package_idx, parent_node_idx: self.node_idx, package_dep_idx, node_dep_idx, } } } #[derive(Debug)] pub struct PackageAndNodeDep<'a> { package_and_node: PackageAndNode<'a>, package_dep_idx: usize, node_dep_idx: usize, parent_package_idx: usize, parent_node_idx: usize, } impl<'a> PackageAndNodeDep<'a> { pub fn update_package_and_node(&mut self, f: impl FnOnce(&mut PackageAndNode)) -> &mut Self { f(&mut self.package_and_node); self } pub fn get_package(&self) -> &Package { self.package_and_node.get_package() } pub fn update_package(&mut self, f: impl FnOnce(&mut Package)) -> &mut Self { self.package_and_node.update_package(f); self } pub fn update_node(&mut self, f: impl FnOnce(&mut Node)) -> &mut Self { self.package_and_node.update_node(f); self } fn get_mut_parent_package(&mut self) -> &mut Package { let parent_package_idx = self.parent_package_idx; &mut self.package_and_node.env.metadata.packages[parent_package_idx] } pub fn get_mut_package_dep(&mut self) -> &mut Dependency { let package_dep_idx = self.package_dep_idx; &mut self.get_mut_parent_package().dependencies[package_dep_idx] } pub fn update_package_dep(&mut self, f: impl FnOnce(&mut Dependency)) -> &mut Self { f(self.get_mut_package_dep()); self } fn get_mut_parent_node(&mut self) -> &mut Node { let parent_node_idx = self.parent_node_idx; &mut self.package_and_node.env.mut_resolve().nodes[parent_node_idx] } pub fn get_mut_node_dep(&mut self) -> &mut NodeDep { let node_dep_idx = self.node_dep_idx; &mut self.get_mut_parent_node().deps[node_dep_idx] } pub fn update_node_dep(&mut self, f: impl FnOnce(&mut NodeDep)) -> &mut Self { f(self.get_mut_node_dep()); self } pub fn version_and_package_id(&mut self, version: &str) -> &mut Self { self.update_package_and_node(|pn| { pn.version_and_package_id(version); }); let pkg_id = self.package_and_node.get_mut_package().id.clone(); self.update_node(|n| n.id = pkg_id.clone()); self.update_package_dep(|d| { d.req = semver::VersionReq::parse(&format!("={}", version)).unwrap(); }); let node_dep_idx = self.node_dep_idx; self.get_mut_parent_node().dependencies[node_dep_idx] = pkg_id.clone(); self.update_node_dep(|n| { n.pkg = pkg_id.clone(); }); self } } #[derive(Debug)] #[must_use = "Please close."] pub struct TestPackage { inner: Package, crate_dir: tempdir::TempDir, } impl TestPackage { pub fn close(self) -> Result<(), std::io::Error> { self.crate_dir.close() } } impl std::ops::Deref for TestPackage { type Target = Package; fn deref(&self) -> &Self::Target { &self.inner } } impl std::ops::DerefMut for TestPackage { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } fn from_value_unwrap(json: impl Fn() -> serde_json::Value) -> T where T: serde::de::DeserializeOwned, { use serde_json::{from_value, to_string_pretty}; from_value(json()).unwrap_or_else(|_| { panic!( "invalid {}: {}", std::any::type_name::(), to_string_pretty(&json()).unwrap() ) }) } /// Return value from given JSON. /// /// If deserialization fails, the error message includes the JSON itself. #[macro_export] macro_rules! from_json { // Hide distracting implementation details from the generated rustdoc. ($($json:tt)+) => { from_value_unwrap(|| serde_json::json!($($json)+)) }; } pub fn crates_io_package_id(name: &str, version: &str) -> PackageId { PackageId { repr: format!( "{} {} (registry+https://github.com/rust-lang/crates.io-index)", name, version ), } } /// Returns a package with minimal bogus data necessary so that /// this is a valid package. pub fn package(name: &str, version: &str) -> TestPackage { semver::Version::parse(version).expect("invalid version"); let crate_dir: TempDir = TempDir::new(&format!("crate2nix_crate_{}_{}", name, version)).expect("test dir creation"); TestPackage { inner: from_json!( { "name": name, "version": version, "id": crates_io_package_id(name, version), "manifest_path": format!("{}/Cargo.toml", crate_dir.path().to_str().unwrap()), "dependencies": [], "targets": [], "features": {}, }), crate_dir, } } pub fn bin_target(package: &TestPackage, file: &str) -> cargo_metadata::Target { from_json!( { "kind": [ "bin" ], "crate_types": [ "bin" ], "name": if file == "main" { &package.name } else { file }, "src_path": format!("{}/src/{}.rs", package.crate_dir.path().to_str().unwrap(), file), "edition": "2018", "doctest": false } ) } pub fn lib_target(package: &TestPackage, file: &str) -> cargo_metadata::Target { from_json!( { "kind": [ "lib" ], "crate_types": [ "lib" ], "name": if file == "lib" { &package.name } else { file }, "src_path": format!("{}/src/{}.rs", package.crate_dir.path().to_str().unwrap(), file), "edition": "2018", "doctest": true } ) } /// Returns a bin package with minimal bogus data necessary so that /// this is a valid package. pub fn bin_package(name: &str, version: &str) -> TestPackage { let mut package = package(name, version); package.targets = vec![bin_target(&package, "main")]; package } /// Returns a lib package with minimal bogus data necessary so that /// this is a valid package. pub fn lib_package(name: &str, version: &str) -> TestPackage { let mut package = package(name, version); package.targets = vec![lib_target(&package, "lib")]; package } pub fn dependency_from_package(package: &Package) -> Dependency { from_json!( { "name": &package.name, "source": &package.source, "req": format!("={}", package.version), "kind": null, "rename": null, "optional": false, "uses_default_features": true, "features": [], "target": null, "registry": null } ) } #[test] pub fn test_package() { println!("{:#?}", package("test", "0.1.0")); } pub fn node(package_id: &str) -> Node { from_json!({ "id": package_id, "dependencies": [], "deps": [], "features": [] } ) } pub fn node_dep(name: &str, package_id: &PackageId) -> NodeDep { from_json!({ "name": name, "pkg": package_id, "dep_kinds": [ { "kind": null, "target": null } ] } ) } pub fn node_with_deps(package_id: &str, deps: Vec) -> Node { let mut node = node(package_id); node.dependencies = deps.iter().map(|d| &d.pkg).cloned().collect(); node.deps = deps; node } #[test] pub fn test_valid_node() { println!("{:#?}", node("bogus package id")); } pub fn empty_resolve() -> Resolve { from_json!({ "nodes": [], }) } #[test] pub fn test_valid_empty_resolve() { println!("{:#?}", empty_resolve()); } pub fn empty_metadata() -> Metadata { from_json!({ "version": 1, "packages": [], "workspace_members": [], "workspace_root": "", "target_directory": "", }) } #[test] pub fn test_valid_empty_metadata() { println!("{:#?}", empty_metadata()); } ================================================ FILE: crate2nix/src/util.rs ================================================ //! Homeless code. Usually abstract and algorithmic. use std::collections::BTreeSet; /// Return all occurrences of each item after the first. /// ``` /// use crate2nix::util::find_duplicates; /// assert_eq!(find_duplicates(Vec::<&str>::new().iter()), Vec::<&&str>::new()); /// assert_eq!(find_duplicates(vec!["a", "b"].iter()), Vec::<&&str>::new()); /// assert_eq!(find_duplicates(vec!["a", "b", "a"].iter()), vec![&"a"]); /// assert_eq!(find_duplicates(vec!["a", "b", "a", "b", "a"].iter()), vec![&"a", &"b", &"a"]); /// ``` pub fn find_duplicates<'a, T: Ord>(source: impl Iterator) -> Vec<&'a T> { let mut seen = BTreeSet::new(); source.filter(|v| !seen.insert(*v)).collect() } ================================================ FILE: crate2nix/templates/Cargo-workspace.toml.tera ================================================ {# Argument: struct crate2nix::BuildInfo Rendered via https://tera.netlify.com #} # This file was @generated by crate2nix {{info.crate2nix_version | safe}} with the command: # {% for arg in info.crate2nix_arguments %} {{arg}}{% endfor %} # See https://github.com/kolloch/crate2nix for more info. [workspace] members = [ {%- for workspace_member in members %} "{{ workspace_member_dir ~ "/" ~ workspace_member }}", {% endfor -%} ] ================================================ FILE: crate2nix/templates/Cargo.nix.tera ================================================ {# Argument: struct crate2nix::BuildInfo Rendered via https://tera.netlify.com #} # This file was @generated by crate2nix {{info.crate2nix_version | safe}} with the command: # {% for arg in info.crate2nix_arguments %} {{arg}}{% endfor %} # See https://github.com/kolloch/crate2nix for more info. { nixpkgs ? {{config.nixpkgs_path | safe}} , pkgs ? import nixpkgs { config = {}; } , fetchurl ? pkgs.fetchurl , lib ? pkgs.lib , stdenv ? pkgs.stdenv , buildRustCrateForPkgs ? pkgs: pkgs.buildRustCrate # This is used as the `crateOverrides` argument for `buildRustCrate`. , defaultCrateOverrides ? pkgs.defaultCrateOverrides # The features to enable for the root_crate or the workspace_members. , rootFeatures ? [ "default" ] # If true, throw errors instead of issueing deprecation warnings. , strictDeprecation ? false # Elements to add to the `-C target-feature=` argument passed to `rustc` # (separated by `,`, prefixed with `+`). # Used for conditional compilation based on CPU feature detection. , targetFeatures ? [] # Additional target attributes for conditional dependencies. # Use this for custom cfg flags that are passed via rustcflags but need to # be known at Nix evaluation time for dependency resolution. # Example: { tracing_unstable = true; } for crates using cfg(tracing_unstable). , extraTargetFlags ? {} # Whether to perform release builds: longer compile times, faster binaries. , release ? true # Additional crate2nix configuration if it exists. , crateConfig ? if builtins.pathExists ./crate-config.nix then pkgs.callPackage ./crate-config.nix {} else {} }: rec { # # "public" attributes that we attempt to keep stable with new versions of crate2nix. # {% if root_package_id -%} rootCrate = rec { packageId = {{root_package_id}}; # Use this attribute to refer to the derivation building your root crate package. # You can override the features with rootCrate.build.override { features = [ "default" "feature1" ... ]; }. build = internal.buildRustCrateWithFeatures { inherit packageId; }; # Debug support which might change between releases. # File a bug if you depend on any for non-debug work! debug = internal.debugCrate { inherit packageId; }; }; {%- endif -%} {% if workspace_members %} # Refer your crate build derivation by name here. # You can override the features with # workspaceMembers."${crateName}".build.override { features = [ "default" "feature1" ... ]; }. workspaceMembers = { {%- for name, pkg_id in workspace_members %} {{name}} = rec { packageId = {{pkg_id}}; build = internal.buildRustCrateWithFeatures { packageId = {{pkg_id}}; }; # Debug support which might change between releases. # File a bug if you depend on any for non-debug work! debug = internal.debugCrate { inherit packageId; }; }; {%- endfor %} }; {%- endif %} {% if registries %} registries = { {%- for name, hash in registries %} {{name}} = builtins.fromJSON (builtins.readFile (fetchurl { url = "{{name | safe}}/config.json"; sha256 = {{hash}}; })); {%- endfor %} }; {%- endif %} # A derivation that joins the outputs of all workspace members together. allWorkspaceMembers = pkgs.symlinkJoin { name = "all-workspace-members"; paths = let members = builtins.attrValues workspaceMembers; in builtins.map (m: m.build) members; }; # # "internal" ("private") attributes that may change in every new version of crate2nix. # internal = rec { # Build and dependency information for crates. # Many of the fields are passed one-to-one to buildRustCrate. # # Noteworthy: # * `dependencies`/`buildDependencies`: similar to the corresponding fields for buildRustCrate. # but with additional information which is used during dependency/feature resolution. # * `resolvedDependencies`: the selected default features reported by cargo - only included for debugging. # * `devDependencies` as of now not used by `buildRustCrate` but used to # inject test dependencies into the build crates = { {%- for crate in crates %} {{crate.package_id}} = rec { crateName = {{crate.crate_name}}; version = {{crate.version}}; edition = {{crate.edition}}; {%- if crate.links %} links = {{crate.links}}; {%- endif %} {%- if crate.binaries|length > 0 and not crate.is_root_or_workspace_member %} crateBin = []; {%- elif crate.binaries|length > 0 and crate.is_root_or_workspace_member %} crateBin = [ {%- set bins_sorted = crate.binaries|sort(attribute="name") -%} {%- for bin in bins_sorted %} { name = {{ bin.name }}; path = {{ bin.src_path }}; requiredFeatures = [ {% for feature in bin.required_features %}{{feature}} {% endfor %}]; } {%- endfor %} ]; {%- endif -%} {%- if crate.source.CratesIo.sha256 %} sha256 = {{crate.source.CratesIo.sha256}}; {%- elif crate.source.Registry.sha256 %} src = fetchurl { name = "{{crate.source.Registry.name | safe}}-{{crate.source.Registry.version | safe}}.tar.gz"; url = registryUrl { inherit registries; url = {{crate.source.Registry.registry}}; crate = {{crate.source.Registry.name}}; version = {{crate.source.Registry.version}}; sha256 = {{crate.source.Registry.sha256}}; }; sha256 = {{crate.source.Registry.sha256}}; }; sha256 = {{crate.source.Registry.sha256}}; {%- elif crate.source.Nix.file.import and crate.source.Nix.attr %} src = (import {{crate.source.Nix.file.import | safe}}).{{crate.source.Nix.attr | safe}}; {%- elif crate.source.Nix.file.package and crate.source.Nix.attr %} src = (pkgs.callPackage {{crate.source.Nix.file.package | safe}} {}).{{crate.source.Nix.attr | safe}}; {%- elif crate.source.Nix.file.import %} src = import {{crate.source.Nix.file.import | safe}}; {%- elif crate.source.Nix.file.package %} src = pkgs.callPackage {{crate.source.Nix.file.package | safe}} {}; {%- elif crate.source.LocalDirectory.path %} src = lib.cleanSourceWith { filter = sourceFilter; src = {{crate.source.LocalDirectory.path | safe}}; }; {%- elif crate.source.Git %} workspace_member = null; src = pkgs.fetchgit { url = {{crate.source.Git.url}}; rev = {{crate.source.Git.rev}}; {%- if crate.source.Git.sha256 %} sha256 = {{ crate.source.Git.sha256 }}; {%- endif %} }; {%- else %} src = builtins.throw ''ERROR: Could not resolve source: {{crate.source | json_encode() | safe}}''; {%- endif -%} {%- if crate.proc_macro %} procMacro = true; {%- endif -%} {%- if crate.build.src_path and crate.build.src_path != "build.rs" -%} {#- This defaults to "build.rs". #} build = {{crate.build.src_path}}; {%- endif -%} {%- if crate.lib and crate.lib.name and crate.lib.name != crate.crate_name -%} {#- This defaults to crateName. #} libName = {{crate.lib.name}}; {%- endif -%} {%- if crate.lib and crate.lib.src_path and crate.lib.src_path != "src/lib.rs" -%} {#- This defaults to empty which triggers some auto-probing. #} libPath = {{crate.lib.src_path}}; {%- endif -%} {%- if not crate.proc_macro -%} {#- Omitting []. -#} {%- if crate.lib_crate_types|length > 0 -%} {#- Omitting [ "lib" ]. -#} {%- if crate.lib_crate_types|length != 1 or crate.lib_crate_types[0] != "lib" -%} type = [{%- for crate_type in crate.lib_crate_types %} {{ crate_type }}{%- endfor %} ]; {%- endif -%} {%- endif -%} {%- endif -%} {%- if crate.authors|length > 0 %} authors = [ {%- for author in crate.authors %} {{author}} {%- endfor %} ]; {%- endif -%} {%- if crate.dependencies|length > 0 %} dependencies = [ {%- for dependency in crate.dependencies %} { name = {{dependency.name}}; packageId = {{dependency.package_id}}; {%- if dependency.rename %} rename = {{dependency.rename}}; {%- endif %} {%- if dependency.optional %} optional = true; {%- endif -%} {%- if not dependency.uses_default_features %} usesDefaultFeatures = false; {%- endif -%} {%- if dependency.target %} target = { target, features }: {{dependency.target | cfg_to_nix_expr | safe}}; {%- endif %} {%- if dependency.features %} features = [ {% for feature in dependency.features %}{{feature}} {% endfor %}]; {%- endif %} } {%- endfor %} ]; {%- endif -%} {%- if crate.build_dependencies|length > 0 %} buildDependencies = [ {%- for dependency in crate.build_dependencies %} { name = {{dependency.name}}; packageId = {{dependency.package_id}}; {%- if dependency.optional %} optional = true; {%- endif -%} {%- if dependency.rename %} rename = {{dependency.rename}}; {%- endif %} {%- if not dependency.uses_default_features %} usesDefaultFeatures = false; {%- endif -%} {%- if dependency.target %} target = { target, features }: {{dependency.target | cfg_to_nix_expr | safe}}; {%- endif %} {%- if dependency.features %} features = [ {% for feature in dependency.features %}{{feature}} {% endfor %}]; {%- endif %} } {%- endfor %} ]; {%- endif -%} {%- if crate.dev_dependencies|length > 0 %} devDependencies = [ {%- for dependency in crate.dev_dependencies %} { name = {{dependency.name}}; packageId = {{dependency.package_id}}; {%- if dependency.optional %} optional = true; {%- endif -%} {%- if dependency.rename %} rename = {{dependency.rename}}; {%- endif %} {%- if not dependency.uses_default_features %} usesDefaultFeatures = false; {%- endif -%} {%- if dependency.target %} target = { target, features }: {{dependency.target | cfg_to_nix_expr | safe}}; {%- endif %} {%- if dependency.features %} features = [ {% for feature in dependency.features %}{{feature}} {% endfor %}]; {%- endif %} } {%- endfor %} ]; {%- endif -%} {#- #} {% if crate.features|length > 0 -%} features = { {%- for feature, features in crate.features -%} {% if features|length > 0 %} {{feature}} = [ {% for feature in features %}{{feature}} {% endfor %}]; {%- endif %} {%- endfor %} }; {%- endif %} {%- if crate.resolved_default_features|length > 0 %} resolvedDefaultFeatures = [ {% for feature in crate.resolved_default_features %}{{feature}} {% endfor %}]; {%- endif %} }; {%- endfor %} }; {% include "nix/crate2nix/default.nix" %} }; } ================================================ FILE: crate2nix/templates/crate2nix-sources.nix.tera ================================================ {# Argument: struct crate2nix::GenerateInfo Rendered via https://tera.netlify.com -#} # Support functions to create a nix generated workspace for out-of-tree sources. # # You do not need to check this in since it will be regenerated every time it is # used by crate2nix. # # This file was @generated by crate2nix {{crate2nix_version | safe}} with the command: # {% for arg in crate2nix_arguments %} {{arg}}{% endfor %} # # See https://github.com/kolloch/crate2nix for more info. { nixpkgs ? , pkgs ? import nixpkgs {} , lib ? pkgs.lib # The path to crate2nix.json. , crate2nixJson ? ./crate2nix.json }: let config = lib.importJSON crate2nixJson; sources = config.sources or (builtins.throw "no sources in ${crate2nixJson}"); in rec { /* An attrset mapping a source name to its source (as a derivation). */ fetchedSourcesByName = lib.mapAttrs internal.sourceFromConfig sources; /* A derivation building a directory symlinking all workspace member sources by their name. */ fetchedSources = let sources = lib.mapAttrsToList (name: path: { inherit name path; }) fetchedSourcesByName; in pkgs.linkFarm "crate2nix-sources" sources; internal = rec { sourceFromConfig = name: { type, ... } @ source: assert builtins.isString name; assert builtins.isString type; if type == "Git" then pkgs.fetchgit { url = source.url; rev = source.rev; sha256 = source.sha256; } else if type == "CratesIo" then downloadFromCratesIo source else if type == "Nix" then resolveNix source else builtins.throw "Unexpected source type '${type}' for source: ${builtins.toJSON source}"; /* Resolves a source configuration of type "Nix". It can either have * a `{ package = ...; ... }` path which will be resolved with pkg.callPackage * or an `{ import = ...; ... }` path which will be imported. Within that context and additional optional `attr` attribute path is resolved. E.g. ```nix { type = "Nix"; import = "./nix/sources.nix"; attr = "myPackage.release"; } ``` */ resolveNix = { type, ... } @ source: assert type == "Nix"; let attrs = if source ? package then pkgs.callPackage (./. + "/${source.package}") {} else if source ? "import" then import (./. + ''/${source."import"}'') else builtins.throw "Neither import nor package in nix source."; attrPath = lib.splitString "." source.attr; sourceDerivation = if source ? attr then lib.attrByPath attrPath (builtins.throw '' Did not find attribute '${source.attr or ""}' in '${source.package or source.import or "missing file"}'. '') attrs else attrs; in sourceDerivation; downloadFromCratesIo = { type, name, version, sha256 }: assert type == "CratesIo"; let archive = pkgs.fetchurl { name = "${name}-${version}.tar.gz"; url = "https://crates.io/api/v1/crates/${name}/${version}/download"; inherit sha256; }; in pkgs.runCommand (lib.removeSuffix ".tar.gz" name) {} '' mkdir -p $out tar -xzf ${archive} --strip-components=1 -C $out ''; }; } ================================================ FILE: crate2nix/templates/nix/crate2nix/default.nix ================================================ # # crate2nix/default.nix (excerpt start) #{# { pkgs , fetchurl , lib , stdenv , buildRustCrate , buildRustCrateForPkgs ? if buildRustCrate != null then lib.warn "`buildRustCrate` is deprecated, use `buildRustCrateForPkgs` instead" (_: buildRustCrate) else pkgs: pkgs.buildRustCrate , defaultCrateOverrides , strictDeprecation ? true , crates ? { } , rootFeatures ? [ ] , targetFeatures ? [ ] , extraTargetFlags ? { } , release ? true , }: rec { # #} /* Target (platform) data for conditional dependencies. This corresponds roughly to what buildRustCrate is setting. */ makeDefaultTarget = platform: { name = platform.rust.rustcTarget; unix = platform.isUnix; windows = platform.isWindows; fuchsia = true; test = false; inherit (platform.rust.platform) arch os vendor ; family = platform.rust.platform.target-family; env = "gnu"; endian = if platform.parsed.cpu.significantByte.name == "littleEndian" then "little" else "big"; pointer_width = toString platform.parsed.cpu.bits; debug_assertions = false; } // extraTargetFlags; registryUrl = { registries , url , crate , version , sha256 , }: let dl = registries.${url}.dl; tmpl = [ "{crate}" "{version}" "{prefix}" "{lowerprefix}" "{sha256-checksum}" ]; in with lib.strings; if lib.lists.any (i: hasInfix "{}" dl) tmpl then let prefix = if builtins.stringLength crate == 1 then "1" else if builtins.stringLength crate == 2 then "2" else "${builtins.substring 0 2 crate}/${builtins.substring 2 (builtins.stringLength crate - 2) crate}"; in builtins.replaceStrings tmpl [ crate version prefix (lib.strings.toLower prefix) sha256 ] else "${dl}/${crate}/${version}/download"; # Filters common temp files and build files. # TODO(pkolloch): Substitute with gitignore filter sourceFilter = name: type: let baseName = builtins.baseNameOf (builtins.toString name); in !( # Filter out git baseName == ".gitignore" || (type == "directory" && baseName == ".git") # Filter out build results || ( type == "directory" && ( baseName == "target" || baseName == "_site" || baseName == ".sass-cache" || baseName == ".jekyll-metadata" || baseName == "build-artifacts" ) ) # Filter out nix-build result symlinks || (type == "symlink" && lib.hasPrefix "result" baseName) # Filter out IDE config || (type == "directory" && (baseName == ".idea" || baseName == ".vscode")) || lib.hasSuffix ".iml" baseName # Filter out nix build files || baseName == "Cargo.nix" # Filter out editor backup / swap files. || lib.hasSuffix "~" baseName || builtins.match "^\\.sw[a-z]$$" baseName != null || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null || lib.hasSuffix ".tmp" baseName || lib.hasSuffix ".bak" baseName || baseName == "tests.nix" ); /* Returns a crate which depends on successful test execution of crate given as the second argument. testCrateFlags: list of flags to pass to the test exectuable testInputs: list of packages that should be available during test execution */ crateWithTest = { crate , testCrate , testCrateFlags , testInputs , testPreRun , testPostRun , }: assert builtins.typeOf testCrateFlags == "list"; assert builtins.typeOf testInputs == "list"; assert builtins.typeOf testPreRun == "string"; assert builtins.typeOf testPostRun == "string"; let # override the `crate` so that it will build and execute tests instead of # building the actual lib and bin targets We just have to pass `--test` # to rustc and it will do the right thing. We execute the tests and copy # their log and the test executables to $out for later inspection. test = let drv = testCrate.override (_: { buildTests = true; }); # If the user hasn't set any pre/post commands, we don't want to # insert empty lines. This means that any existing users of crate2nix # don't get a spurious rebuild unless they set these explicitly. testCommand = pkgs.lib.concatStringsSep "\n" ( pkgs.lib.filter (s: s != "") [ testPreRun "$f $testCrateFlags 2>&1 | tee -a $out" testPostRun ] ); in pkgs.stdenvNoCC.mkDerivation { name = "run-tests-${testCrate.name}"; inherit (crate) src; inherit testCrateFlags; buildInputs = testInputs; buildPhase = '' set -e export RUST_BACKTRACE=1 # build outputs testRoot=target/debug mkdir -p $testRoot # executables of the crate # we copy to prevent std::env::current_exe() to resolve to a store location for i in ${crate}/bin/*; do cp "$i" "$testRoot" done chmod +w -R . # test harness executables are suffixed with a hash, like cargo does # this allows to prevent name collision with the main # executables of the crate hash=$(basename $out) for file in ${drv}/tests/*; do f=$testRoot/$(basename $file)-$hash cp $file $f ${testCommand} done ''; }; in pkgs.runCommand "${crate.name}-linked" { inherit (crate) outputs crateName meta; passthru = (crate.passthru or { }) // { inherit test; }; } ( lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) '' echo tested by ${test} '' + '' ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs} '' ); # A restricted overridable version of builtRustCratesWithFeatures. buildRustCrateWithFeatures = { packageId , features ? rootFeatures , crateOverrides ? defaultCrateOverrides , buildRustCrateForPkgsFunc ? null , runTests ? false , testCrateFlags ? [ ] , testInputs ? [ ] , # Any command to run immediatelly before a test is executed. testPreRun ? "" , # Any command run immediatelly after a test is executed. testPostRun ? "" , }: lib.makeOverridable ( { features , crateOverrides , runTests , testCrateFlags , testInputs , testPreRun , testPostRun , }: let buildRustCrateForPkgsFuncOverriden = if buildRustCrateForPkgsFunc != null then buildRustCrateForPkgsFunc else ( if crateOverrides == pkgs.defaultCrateOverrides then buildRustCrateForPkgs else pkgs: (buildRustCrateForPkgs pkgs).override { defaultCrateOverrides = crateOverrides; } ); builtRustCrates = builtRustCratesWithFeatures { inherit packageId features; buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden; runTests = false; }; builtTestRustCrates = builtRustCratesWithFeatures { inherit packageId features; buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden; runTests = true; }; drv = builtRustCrates.crates.${packageId}; testDrv = builtTestRustCrates.crates.${packageId}; derivation = if runTests then crateWithTest { crate = drv; testCrate = testDrv; inherit testCrateFlags testInputs testPreRun testPostRun ; } else drv; in derivation ) { inherit features crateOverrides runTests testCrateFlags testInputs testPreRun testPostRun ; }; /* Returns an attr set with packageId mapped to the result of buildRustCrateForPkgsFunc for the corresponding crate. */ builtRustCratesWithFeatures = { packageId , features , crateConfigs ? crates , buildRustCrateForPkgsFunc , runTests , makeTarget ? makeDefaultTarget , }@args: assert (builtins.isAttrs crateConfigs); assert (builtins.isString packageId); assert (builtins.isList features); assert (builtins.isAttrs (makeTarget stdenv.hostPlatform)); assert (builtins.isBool runTests); let rootPackageId = packageId; mergedFeatures = mergePackageFeatures ( args // { inherit rootPackageId; target = makeTarget stdenv.hostPlatform // { test = runTests; }; } ); # Memoize built packages so that reappearing packages are only built once. builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs; mkBuiltByPackageIdByPkgs = pkgs: let self = { crates = lib.mapAttrs ( packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId ) crateConfigs; target = makeTarget pkgs.stdenv.hostPlatform; build = mkBuiltByPackageIdByPkgs pkgs.buildPackages; }; in self; buildByPackageIdForPkgsImpl = self: pkgs: packageId: let features = mergedFeatures."${packageId}" or [ ]; crateConfig' = crateConfigs."${packageId}"; crateConfig = builtins.removeAttrs crateConfig' [ "resolvedDefaultFeatures" "devDependencies" ]; devDependencies = lib.optionals (runTests && packageId == rootPackageId) ( crateConfig'.devDependencies or [ ] ); dependencies = dependencyDerivations { inherit features; inherit (self) target; buildByPackageId = depPackageId: # proc_macro crates must be compiled for the build architecture if crateConfigs.${depPackageId}.procMacro or false then self.build.crates.${depPackageId} else self.crates.${depPackageId}; dependencies = (crateConfig.dependencies or [ ]) ++ devDependencies; }; buildDependencies = dependencyDerivations { inherit features; inherit (self.build) target; buildByPackageId = depPackageId: self.build.crates.${depPackageId}; dependencies = crateConfig.buildDependencies or [ ]; }; dependenciesWithRenames = let buildDeps = filterEnabledDependencies { inherit features; inherit (self) target; dependencies = crateConfig.dependencies or [ ] ++ devDependencies; }; hostDeps = filterEnabledDependencies { inherit features; inherit (self.build) target; dependencies = crateConfig.buildDependencies or [ ]; }; in lib.filter (d: d ? "rename") (hostDeps ++ buildDeps); # Crate renames have the form: # # { # crate_name = [ # { version = "1.2.3"; rename = "crate_name01"; } # ]; # # ... # } crateRenames = let grouped = lib.groupBy (dependency: dependency.name) dependenciesWithRenames; versionAndRename = dep: let package = crateConfigs."${dep.packageId}"; in { inherit (dep) rename; inherit (package) version; }; in lib.mapAttrs (name: builtins.map versionAndRename) grouped; in buildRustCrateForPkgsFunc pkgs ( crateConfig // { src = crateConfig.src or (fetchurl rec { name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz"; # https://www.pietroalbini.org/blog/downloading-crates-io/ # Not rate-limited, CDN URL. url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate"; sha256 = assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}"); crateConfig.sha256; }); extraRustcOpts = lib.lists.optional (targetFeatures != [ ]) "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}"; inherit features dependencies buildDependencies crateRenames release ; } ); in builtByPackageIdByPkgs; # Returns the actual derivations for the given dependencies. dependencyDerivations = { buildByPackageId , features , dependencies , target , }: assert (builtins.isList features); assert (builtins.isList dependencies); assert (builtins.isAttrs target); let enabledDependencies = filterEnabledDependencies { inherit dependencies features target; }; depDerivation = dependency: buildByPackageId dependency.packageId; in map depDerivation enabledDependencies; /* Returns a sanitized version of val with all values substituted that cannot be serialized as JSON. */ sanitizeForJson = val: if builtins.isAttrs val then lib.mapAttrs (n: sanitizeForJson) val else if builtins.isList val then builtins.map sanitizeForJson val else if builtins.isFunction val then "function" else val; # Returns various tools to debug a crate. debugCrate = { packageId , target ? makeDefaultTarget stdenv.hostPlatform , }: assert (builtins.isString packageId); let debug = rec { # The built tree as passed to buildRustCrate. buildTree = buildRustCrateWithFeatures { buildRustCrateForPkgsFunc = _: lib.id; inherit packageId; }; sanitizedBuildTree = sanitizeForJson buildTree; dependencyTree = sanitizeForJson (buildRustCrateWithFeatures { buildRustCrateForPkgsFunc = _: crate: { "01_crateName" = crate.crateName or false; "02_features" = crate.features or [ ]; "03_dependencies" = crate.dependencies or [ ]; }; inherit packageId; }); mergedPackageFeatures = mergePackageFeatures { features = rootFeatures; inherit packageId target; }; diffedDefaultPackageFeatures = diffDefaultPackageFeatures { inherit packageId target; }; }; in { internal = debug; }; /* Returns differences between cargo default features and crate2nix default features. This is useful for verifying the feature resolution in crate2nix. */ diffDefaultPackageFeatures = { crateConfigs ? crates , packageId , target , }: assert (builtins.isAttrs crateConfigs); let prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; }); mergedFeatures = prefixValues "crate2nix" (mergePackageFeatures { inherit crateConfigs packageId target; features = [ "default" ]; }); configs = prefixValues "cargo" crateConfigs; combined = lib.foldAttrs (a: b: a // b) { } [ mergedFeatures configs ]; onlyInCargo = builtins.attrNames ( lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined ); onlyInCrate2Nix = builtins.attrNames ( lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined ); differentFeatures = lib.filterAttrs ( n: v: (v ? "crate2nix") && (v ? "cargo") && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ]) ) combined; in builtins.toJSON { inherit onlyInCargo onlyInCrate2Nix differentFeatures; }; /* Returns an attrset mapping packageId to the list of enabled features. If multiple paths to a dependency enable different features, the corresponding feature sets are merged. Features in rust are additive. */ mergePackageFeatures = { crateConfigs ? crates , packageId , rootPackageId ? packageId , features ? rootFeatures , dependencyPath ? [ crates.${packageId}.crateName ] , featuresByPackageId ? { } , target , # Adds devDependencies to the crate with rootPackageId. runTests ? false , ... }@args: assert (builtins.isAttrs crateConfigs); assert (builtins.isString packageId); assert (builtins.isString rootPackageId); assert (builtins.isList features); assert (builtins.isList dependencyPath); assert (builtins.isAttrs featuresByPackageId); assert (builtins.isAttrs target); assert (builtins.isBool runTests); let crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}"); expandedFeatures = expandFeatures (crateConfig.features or { }) features; enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures; depWithResolvedFeatures = dependency: let inherit (dependency) packageId; features = dependencyFeatures enabledFeatures dependency; in { inherit packageId features; }; resolveDependencies = cache: path: dependencies: assert (builtins.isAttrs cache); assert (builtins.isList dependencies); let enabledDependencies = filterEnabledDependencies { inherit dependencies target; features = enabledFeatures; }; directDependencies = map depWithResolvedFeatures enabledDependencies; foldOverCache = op: lib.foldl op cache directDependencies; in foldOverCache ( cache: { packageId, features }: let cacheFeatures = cache.${packageId} or [ ]; combinedFeatures = sortedUnique (cacheFeatures ++ features); in if cache ? ${packageId} && cache.${packageId} == combinedFeatures then cache else mergePackageFeatures { features = combinedFeatures; featuresByPackageId = cache; inherit crateConfigs packageId target runTests rootPackageId ; } ); cacheWithSelf = let cacheFeatures = featuresByPackageId.${packageId} or [ ]; combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures); in featuresByPackageId // { "${packageId}" = combinedFeatures; }; cacheWithDependencies = resolveDependencies cacheWithSelf "dep" ( crateConfig.dependencies or [ ] ++ lib.optionals (runTests && packageId == rootPackageId) (crateConfig.devDependencies or [ ]) ); cacheWithAll = resolveDependencies cacheWithDependencies "build" ( crateConfig.buildDependencies or [ ] ); in cacheWithAll; # Returns the enabled dependencies given the enabled features. filterEnabledDependencies = { dependencies , features , target , }: assert (builtins.isList dependencies); assert (builtins.isList features); assert (builtins.isAttrs target); lib.filter ( dep: let targetFunc = dep.target or (features: true); in targetFunc { inherit features target; } && (!(dep.optional or false) || builtins.any (doesFeatureEnableDependency dep) features) ) dependencies; # Returns whether the given feature should enable the given dependency. doesFeatureEnableDependency = dependency: feature: let name = dependency.rename or dependency.name; prefix = "${name}/"; len = builtins.stringLength prefix; startsWithPrefix = builtins.substring 0 len feature == prefix; in feature == name || feature == "dep:" + name || startsWithPrefix; /* Returns the expanded features for the given inputFeatures by applying the rules in featureMap. featureMap is an attribute set which maps feature names to lists of further feature names to enable in case this feature is selected. */ expandFeatures = featureMap: inputFeatures: assert (builtins.isAttrs featureMap); assert (builtins.isList inputFeatures); let expandFeaturesNoCycle = oldSeen: inputFeatures: if inputFeatures != [ ] then let # The feature we're currently expanding. feature = builtins.head inputFeatures; # All the features we've seen/expanded so far, including the one # we're currently processing. seen = oldSeen // { ${feature} = 1; }; # Expand the feature but be careful to not re-introduce a feature # that we've already seen: this can easily cause a cycle, see issue # #209. enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]); in [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables)) # No more features left, nothing to expand to. else [ ]; outFeatures = expandFeaturesNoCycle { } inputFeatures; in sortedUnique outFeatures; /* This function adds optional dependencies as features if they are enabled indirectly by dependency features. This function mimics Cargo's behavior described in a note at: https://doc.rust-lang.org/nightly/cargo/reference/features.html#dependency-features */ enableFeatures = dependencies: features: assert (builtins.isList features); assert (builtins.isList dependencies); let additionalFeatures = lib.concatMap ( dependency: assert (builtins.isAttrs dependency); let enabled = builtins.any (doesFeatureEnableDependency dependency) features; in if (dependency.optional or false) && enabled then [ (dependency.rename or dependency.name) ] else [ ] ) dependencies; in sortedUnique (features ++ additionalFeatures); /* Returns the actual features for the given dependency. features: The features of the crate that refers this dependency. */ dependencyFeatures = features: dependency: assert (builtins.isList features); assert (builtins.isAttrs dependency); let defaultOrNil = if dependency.usesDefaultFeatures or true then [ "default" ] else [ ]; explicitFeatures = dependency.features or [ ]; additionalDependencyFeatures = let name = dependency.rename or dependency.name; stripPrefixMatch = prefix: s: if lib.hasPrefix prefix s then lib.removePrefix prefix s else null; extractFeature = feature: lib.findFirst (f: f != null) null ( map (prefix: stripPrefixMatch prefix feature) [ (name + "/") (name + "?/") ] ); dependencyFeatures = lib.filter (f: f != null) (map extractFeature features); in dependencyFeatures; in defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures; # Sorts and removes duplicates from a list of strings. sortedUnique = features: assert (builtins.isList features); assert (builtins.all builtins.isString features); let outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features; outFeaturesUnique = builtins.attrNames outFeaturesSet; in builtins.sort (a: b: a < b) outFeaturesUnique; deprecationWarning = message: value: if strictDeprecation then builtins.throw "strictDeprecation enabled, aborting: ${message}" else builtins.trace message value; # # crate2nix/default.nix (excerpt end) #{# } # -#} ================================================ FILE: crate2nix/templates/nix/crate2nix/tests/default.nix ================================================ let pkgs = import ../../../../../nix/nixpkgs.nix { }; in pkgs.callPackage ./tests.nix { } ================================================ FILE: crate2nix/templates/nix/crate2nix/tests/dependencyDerivations.nix ================================================ { lib, crate2nix, stdenv }: let fakeCrates = { "pkg_id1" = "pkg_id1"; "pkg_id2" = "pkg_id2"; "pkg_id3" = "pkg_id3"; }; fakeDependencies = [ { name = "id1"; packageId = "pkg_id1"; } { name = "optional_id2"; packageId = "pkg_id2"; optional = true; } { name = "id3"; packageId = "pkg_id3"; usesDefaultFeatures = false; } ]; dependencyDerivations = features: dependencies: crate2nix.dependencyDerivations { buildByPackageId = p: fakeCrates.${p}; target = crate2nix.makeDefaultTarget stdenv.hostPlatform; inherit features dependencies; }; in { testForDefaultAndIgnored = { expr = dependencyDerivations [ "default" "ignored" ] fakeDependencies; expected = [ "pkg_id1" "pkg_id3" ]; }; testWithOptional = { expr = dependencyDerivations [ "default" "optional_id2" ] fakeDependencies; expected = [ "pkg_id1" "pkg_id2" "pkg_id3" ]; }; testWithDepFeatures = { expr = dependencyDerivations [ "default" "id1/default" "id1/stuff" "id2/ignored_feature" "id3/feature1" ] fakeDependencies; expected = [ "pkg_id1" "pkg_id3" ]; }; } ================================================ FILE: crate2nix/templates/nix/crate2nix/tests/dependencyFeatures.nix ================================================ { lib, crate2nix }: let dependencies = [ { name = "tls"; packageId = "pkgid_tls"; } { name = "extra"; packageId = "pkgid_extra"; } { name = "optional_crate"; packageId = "pkgid_optional"; optional = true; } { name = "optional_crate"; rename = "optional_crate2"; packageId = "pkgid_optional2"; optional = true; } { name = "with_target"; target_cfg = true; optional = false; with_defaults = false; packageId = "pkgid_target"; } ]; in { testStringDependency = { expr = crate2nix.dependencyFeatures [ ] { name = "my_dep"; packageId = "pkg_id"; }; expected = [ "default" ]; }; testWithDefaultUnsetDependency = { expr = crate2nix.dependencyFeatures [ ] { name = "my_dep"; }; expected = [ "default" ]; }; testWithDefaultDependency = { expr = crate2nix.dependencyFeatures [ ] { name = "my_dep"; usesDefaultFeatures = true; }; expected = [ "default" ]; }; testWithDefaultDisabledDependency = { expr = crate2nix.dependencyFeatures [ ] { name = "my_dep"; usesDefaultFeatures = false; }; expected = [ ]; }; testDependencyFeature = { expr = crate2nix.dependencyFeatures [ "my_dep/feature1" ] { name = "my_dep"; packageId = "pkg_id"; }; expected = [ "default" "feature1" ]; }; testDependencyFeatures = { expr = crate2nix.dependencyFeatures [ "irrelevant" "my_dep/feature1" "my_dep/feature2" "my_dep3/irrelevant2" ] { name = "my_dep"; packageId = "pkg_id"; }; expected = [ "default" "feature1" "feature2" ]; }; testDependencyFeatures2 = { expr = crate2nix.dependencyFeatures [ "irrelevant" "my_dep/feature1" "my_dep/feature2" "my_dep3/irrelevant2" ] { name = "my_dep"; usesDefaultFeatures = true; }; expected = [ "default" "feature1" "feature2" ]; }; testDependencyFeaturesWithoutDefault = { expr = crate2nix.dependencyFeatures [ "irrelevant" "my_dep/feature1" "my_dep/feature2" "my_dep3/irrelevant2" ] { name = "my_dep"; usesDefaultFeatures = false; }; expected = [ "feature1" "feature2" ]; }; } ================================================ FILE: crate2nix/templates/nix/crate2nix/tests/enableFeatures.nix ================================================ { lib, crate2nix }: let exampleDependency = { name = "dep"; packageId = "pkgid_dep"; optional = true; }; renamedDependency = { name = "dep"; rename = "dep2"; packageId = "pkgid_dep2"; optional = true; }; in { testWithNoEnabledDependencies = { expr = crate2nix.enableFeatures [ exampleDependency ] [ "default" ]; expected = [ "default" ]; }; testWithDirectlyEnabledDependency = { expr = crate2nix.enableFeatures [ exampleDependency ] [ "default" "dep" ]; expected = [ "default" "dep" ]; }; testWithDirectlyEnabledRenamedDependency = { expr = crate2nix.enableFeatures [ renamedDependency ] [ "default" "dep2" ]; expected = [ "default" "dep2" ]; }; testWithIndirectlyEnabledDependency = { expr = crate2nix.enableFeatures [ exampleDependency ] [ "default" "dep/feat" ]; expected = [ "default" "dep" "dep/feat" ]; }; testWithIndirectlyEnabledRenamedDependency = { expr = crate2nix.enableFeatures [ renamedDependency ] [ "default" "dep2/feat" ]; expected = [ "default" "dep2" "dep2/feat" ]; }; testWithDuplicateDependencies = { expr = crate2nix.enableFeatures [ exampleDependency renamedDependency ] [ "default" "dep/feat" "dep2/feat" ]; expected = [ "default" "dep" "dep/feat" "dep2" "dep2/feat" ]; }; testWithDisabledRenamedDependency = { expr = crate2nix.enableFeatures [ renamedDependency ] [ "default" "dep/feat" ]; expected = [ "default" "dep/feat" ]; }; } ================================================ FILE: crate2nix/templates/nix/crate2nix/tests/expandFeatures.nix ================================================ { lib, crate2nix }: let featureMap = { default = [ "tls" ]; resolvable = [ "feature1" "tls/extra_feature" ]; feature1 = [ ]; extra = [ ]; }; dependencies = [ { name = "tls"; packageId = "pkgid_tls"; } { name = "extra"; packageId = "pkgid_extra"; } { name = "with_target"; target_cfg = true; optional = false; with_defaults = false; packageId = "pkgid_target"; } ]; in { testEmpty = { expr = crate2nix.expandFeatures featureMap [ ]; expected = [ ]; }; testDefault = { expr = crate2nix.expandFeatures featureMap [ "default" ]; expected = [ "default" "tls" ]; }; testDefaultPlus = { expr = crate2nix.expandFeatures featureMap [ "default" "resolvable" ]; expected = [ "default" "feature1" "resolvable" "tls" "tls/extra_feature" ]; }; } ================================================ FILE: crate2nix/templates/nix/crate2nix/tests/packageFeatures.nix ================================================ { lib, crate2nix, stdenv }: let crateConfigs = { "pkg_root" = { crateName = "id1"; features = { "optional_id2" = [ ]; }; dependencies = [ { name = "id1"; packageId = "pkg_id1"; } { name = "optional_id2"; packageId = "pkg_id2"; optional = true; } { name = "id3"; packageId = "pkg_id3"; usesDefaultFeatures = false; } ]; }; "pkg_with_feature_clash" = { dependencies = [ { name = "id1"; packageId = "pkg_id1"; } ]; buildDependencies = [ { name = "id1"; packageId = "pkg_id1"; features = [ "for_build" ]; } ]; }; "pkg_id1" = { crateName = "id1"; features = { "default" = [ ]; }; }; "pkg_id2" = { crateName = "id2"; features = { }; }; "pkg_id3" = { crateName = "id3"; features = { }; }; "pkg_numtest" = { crateName = "numtest"; dependencies = [ { name = "num"; packageId = "pkg_num"; } ]; }; "pkg_num_bigint" = { crateName = "num-bigint"; }; "pkg_num" = { crateName = "num"; dependencies = [ { name = "num-bigint"; packageId = "pkg_num_bigint"; usesDefaultFeatures = false; optional = true; } ]; features = { "default" = [ "std" ]; "std" = [ "num-bigint/std" ]; }; }; }; packageFeatures = packageId: features: crate2nix.mergePackageFeatures { target = crate2nix.makeDefaultTarget stdenv.hostPlatform; runTests = false; rootPackageId = packageId; inherit crateConfigs packageId features; }; in { testNumDependencies = { expr = packageFeatures "pkg_num" [ "default" ]; expected = { "pkg_num" = [ "default" "num-bigint" "num-bigint/std" "std" ]; "pkg_num_bigint" = [ "std" ]; }; }; testNumTestDependencies = { expr = packageFeatures "pkg_numtest" [ "default" ]; expected = { "pkg_numtest" = [ "default" ]; "pkg_num" = [ "default" "num-bigint" "num-bigint/std" "std" ]; "pkg_num_bigint" = [ "std" ]; }; }; testTerminalPackageDependency = { expr = packageFeatures "pkg_id1" [ ]; expected = { "pkg_id1" = [ ]; }; }; testTerminalPackageDependencyWithDefault = { expr = packageFeatures "pkg_id1" [ "default" ]; expected = { "pkg_id1" = [ "default" ]; }; }; testRootPackage = { expr = packageFeatures "pkg_root" [ "default" ]; expected = { "pkg_root" = [ "default" ]; "pkg_id1" = [ "default" ]; "pkg_id3" = [ ]; }; }; testRootPackageWithOptional = { expr = packageFeatures "pkg_root" [ "default" "optional_id2" ]; expected = { "pkg_root" = [ "default" "optional_id2" ]; "pkg_id1" = [ "default" ]; "pkg_id2" = [ "default" ]; "pkg_id3" = [ ]; }; }; testPackageWithFeatureClash = { expr = packageFeatures "pkg_with_feature_clash" [ ]; expected = { "pkg_with_feature_clash" = [ ]; "pkg_id1" = [ "default" "for_build" ]; }; }; } ================================================ FILE: crate2nix/templates/nix/crate2nix/tests/run.nix ================================================ { pkgs , nixTestRunner , }: nixTestRunner.runTests { tests = pkgs.callPackage ./tests.nix { }; } ================================================ FILE: crate2nix/templates/nix/crate2nix/tests/tests.nix ================================================ { pkgs , lib }: let crate2nix = pkgs.callPackage ../default.nix { }; testFiles = [ "dependencyDerivations" "dependencyFeatures" "enableFeatures" "expandFeatures" "packageFeatures" ]; testsInFile = f: let tests = (pkgs.callPackage (./. + "/${f}.nix")) { inherit crate2nix; }; prefixedTests = lib.mapAttrs' (n: v: lib.nameValuePair "${n} in ${f}.nix" (if builtins.isAttrs v then v else { })) tests; in assert builtins.isAttrs prefixedTests; prefixedTests; all = lib.foldl (cum: f: cum // (testsInFile f)) { } testFiles; in all ================================================ FILE: crate2nix/tests/self_build_up_to_date.rs ================================================ use anyhow::{bail, format_err, Error}; use colored_diff::PrettyDifference; use crate2nix::{nix_build::dump_with_lines, render, BuildInfo, GenerateConfig, GenerateInfo}; use serde::Deserialize; use serde::Serialize; use std::io::Write; use std::path::{Path, PathBuf}; use std::process::Command; use std::str::FromStr; #[test] fn self_up_to_date() { let metadata = BuildInfo::for_config( &GenerateInfo { crate2nix_arguments: [ "generate", "-n", "../nix/nixpkgs.nix", "-f", "./crate2nix/Cargo.toml", "-o", "./crate2nix/Cargo.nix", ] .iter() .map(|s| s.to_string()) .collect(), ..GenerateInfo::default() }, &GenerateConfig { cargo_toml: vec![PathBuf::from("./Cargo.toml")], output: PathBuf::from("./Cargo.nix"), nixpkgs_path: "../nix/nixpkgs.nix".to_string(), crate_hashes_json: PathBuf::from("./crate-hashes.json"), other_metadata_options: vec![], use_cargo_lock_checksums: true, read_crate_hashes: true, registry_hashes_json: PathBuf::from("./registry-hashes.json"), }, ) .unwrap(); let rerendered_default_nix = render::CARGO_NIX.render(&metadata).unwrap(); let actual_default_nix = std::fs::read_to_string("./Cargo.nix").unwrap(); assert_eq!( actual_default_nix, rerendered_default_nix, "Pregenerated build files differ, please rerun ./regenerate_cargo_nix.sh.\n{}", PrettyDifference { actual: &actual_default_nix, expected: &rerendered_default_nix } ); if rerendered_default_nix.contains(" /home/") || rerendered_default_nix.contains(".cargo") { dump_with_lines("./Cargo.nix").unwrap(); panic!("Build file contains forbidden strings."); } } #[test] fn pregenerated_up_to_date() { let test_configs = get_test_configs().expect("while running instantiate"); // TODO: Regenerate build files and compare for test_config in test_configs { match test_config.pregenerated_build { Some(pregenerated_build) => { let cargo_nix = PathBuf::from_str(&pregenerated_build) .expect("pregeneratedBuild must be valid path"); assert_up_to_date(cargo_nix.parent().expect("Cargo.nix must be in directory")); } None => println!("Skipping not pregenerated {}", test_config.name), } } } // Assert that the pregenerated build files are up to date, i.e. // the current code would result in the same build file. fn assert_up_to_date(project_dir: &Path) { let cargo_toml = project_dir.join("Cargo.toml"); let output = project_dir.join("Cargo.nix"); println!("Checking pregenerated {}", output.to_str().unwrap()); let config = GenerateConfig { cargo_toml: vec![PathBuf::from("../").join(cargo_toml.clone())], output: PathBuf::from("../").join(output.clone()), nixpkgs_path: "".to_string(), crate_hashes_json: PathBuf::from("../") .join(project_dir) .join("./crate-hashes.json"), other_metadata_options: vec![], use_cargo_lock_checksums: true, read_crate_hashes: true, registry_hashes_json: PathBuf::from("../") .join(project_dir) .join("./registry-hashes.json"), }; let metadata = BuildInfo::for_config( &GenerateInfo { crate2nix_arguments: [ "generate", "-f", cargo_toml.to_str().unwrap(), "-o", output.to_str().unwrap(), ] .iter() .map(|s| s.to_string()) .collect(), ..GenerateInfo::default() }, &config, ) .unwrap(); let rerendered_default_nix = render::CARGO_NIX.render(&metadata).unwrap(); let actual_default_nix = std::fs::read_to_string(&config.output).unwrap(); assert_eq!( actual_default_nix, rerendered_default_nix, "Pregenerated build files differ, please rerun ./regenerate_cargo_nix.sh.\n{}", PrettyDifference { actual: &actual_default_nix, expected: &rerendered_default_nix } ); if rerendered_default_nix.contains(" /home/") || rerendered_default_nix.contains(".cargo") { dump_with_lines("./Cargo.nix").unwrap(); panic!("Build file contains forbidden strings."); } } #[derive(Debug, Serialize, Deserialize)] struct TestConfig { name: String, #[serde(rename = "pregeneratedBuild")] pregenerated_build: Option, } fn get_test_configs() -> Result, Error> { let output = Command::new("nix") .args(["eval", "--json", "-f", "../tests.nix", "buildTestConfigs"]) .output() .map_err(|e| format_err!("while spawning nix: {}", e))?; if !output.status.success() { std::io::stdout().write_all(&output.stdout)?; std::io::stderr().write_all(&output.stderr)?; bail!( "nix-instantiate\n=> exited with: {}", output.status.code().unwrap_or(-1) ); } let json_string = String::from_utf8(output.stdout) .map_err(|_e| format_err!("output of nix-instantiate is not UTF8!"))?; Ok(serde_json::from_str(&json_string)?) } ================================================ FILE: crate2nix.sh ================================================ #!/usr/bin/env bash # Runs crate2nix as defined in this repo with pinned nixpkgs. # # Crate2nix is rebuilt if necessary. set -Eeuo pipefail mydir=$(dirname "$0") nix run "${mydir}" -- "$@" ================================================ FILE: default.nix ================================================ import ./crate2nix/default.nix ================================================ FILE: docs/.gitignore ================================================ # build output dist/ # generated types .astro/ # dependencies node_modules/ # logs npm-debug.log* yarn-debug.log* yarn-error.log* pnpm-debug.log* # environment variables .env .env.production # macOS-specific files .DS_Store # nix result ================================================ FILE: docs/.vscode/extensions.json ================================================ { "recommendations": ["astro-build.astro-vscode"], "unwantedRecommendations": [] } ================================================ FILE: docs/.vscode/launch.json ================================================ { "version": "0.2.0", "configurations": [ { "command": "./node_modules/.bin/astro dev", "name": "Development server", "request": "launch", "type": "node-terminal" } ] } ================================================ FILE: docs/README.md ================================================ # Crate2nix documentation This directory contains the `crate2nix` documentation which is published at [https://nix-community.github.io/crate2nix/](https://nix-community.github.io/crate2nix/). See [these docs](https://nix-community.github.io/crate2nix/contributing/docs/) for contributing to the documentation. ================================================ FILE: docs/astro.config.mjs ================================================ import { defineConfig } from 'astro/config'; import starlight from '@astrojs/starlight'; // https://astro.build/config export default defineConfig({ site: 'https://nix-community.github.io', base: '/crate2nix', integrations: [ starlight({ title: 'crate2nix', social: { github: 'https://github.com/nix-community/crate2nix', }, editLink: { baseUrl: 'https://github.com/nix-community/crate2nix/edit/master/docs/', }, sidebar: [ { label: 'Home', link: '/', }, { label: 'Getting Started', autogenerate: { directory: '10_getting_started' }, }, { label: 'Generating', autogenerate: { directory: '20_generating' }, }, { label: 'Building', autogenerate: { directory: '30_building' }, }, { label: 'Toolchains', autogenerate: { directory: '35_toolchains' }, }, { label: 'External Sources', autogenerate: { directory: '40_external_sources' }, }, { label: 'Contributing', autogenerate: { directory: '50_contributing' }, }, { label: 'Design & Background', autogenerate: { directory: '70_design' }, }, { label: 'Reference', autogenerate: { directory: '90_reference' }, }, ], }), ], }); ================================================ FILE: docs/flake-module.nix ================================================ { perSystem = { config, self', inputs', pkgs, lib, system, ... }: let nodejs = pkgs.nodejs_22; in { devshells.default = { commands = [ { package = nodejs; category = "docs"; } { package = pkgs.markdownlint-cli; category = "docs"; } ]; }; # https://github.com/cachix/pre-commit-hooks.nix/tree/master pre-commit = { settings = { hooks = { markdownlint = { enable = true; # https://github.com/DavidAnson/markdownlint/blob/main/schema/.markdownlint.jsonc settings.configuration = { # MD013/line-length : Line length : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md013.md "MD013" = { "line_length" = 120; "code_block_line_length" = 120; "tables" = false; }; # MD024/no-duplicate-heading : Multiple headings with the same content : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md024.md "MD024" = { # we have multiple Bugfixes/Upgrading headings in the CHANGELOG for consistency. "siblings_only" = true; }; "MD033" = { "allowed_elements" = [ "Card" ]; }; }; }; }; }; }; packages.docs = pkgs.buildNpmPackage { pname = "docs"; version = "0.1.0"; inherit nodejs; src = pkgs.nix-gitignore.gitignoreSource [ ".vscode" "README.md" ".gitignore" "nix" "flake.*" ] ./.; buildInputs = [ pkgs.vips ]; nativeBuildInputs = [ pkgs.pkg-config ]; installPhase = '' runHook preInstall cp -pr --reflink=auto dist $out/ runHook postInstall ''; npmDepsHash = "sha256-hGyNh1JG1P1UGfpVB3YvK5+Eu6RZsinQaAdQp9PARcw="; }; }; } ================================================ FILE: docs/package.json ================================================ { "name": "docs", "type": "module", "version": "0.0.1", "scripts": { "dev": "astro dev", "start": "astro dev", "build": "astro check && astro build", "preview": "astro preview", "astro": "astro" }, "dependencies": { "@astrojs/check": "^0.3.4", "@astrojs/starlight": "^0.15.1", "astro": "^4.0.1", "sharp": "^0.32.5", "typescript": "^5.3.3" }, "overrides": { "@astrojs/sitemap": "3.2.1" } } ================================================ FILE: docs/src/content/config.ts ================================================ import { defineCollection } from 'astro:content'; import { docsSchema, i18nSchema } from '@astrojs/starlight/schema'; export const collections = { docs: defineCollection({ schema: docsSchema() }), i18n: defineCollection({ type: 'data', schema: i18nSchema() }), }; ================================================ FILE: docs/src/content/docs/10_getting_started/10_flake_template_new.md ================================================ --- title: Rust Project via Flake Template description: How to create a new rust project using crate2nix via flakes. sidebar: badge: { text: 'Experimental', variant: 'caution' } --- * *️⃣ Uses [nix flakes](https://zero-to-nix.com/concepts/flakes). * ✅ No need to install `crate2nix`. * ✅ Auto-generates nix from your `Cargo.lock` file. * ⚠️ Uses the [import from derivation](https://nixos.org/manual/nix/stable/language/import-from-derivation) feature from Nix which may lead to disabling build parallelism. * ✅ Uses an overlay to use the rust version specified in `rust-toolchain.toml`. Call this in your project directory: ```bash title="Add flake.nix and other files to your project" nix flake init --template github:nix-community/crate2nix ``` If the directory is empty, it will also create a hello world stub. If you call this from an existing project, make sure to delete any superfluous files. The template expects top-level `Cargo.toml`/`Cargo.lock` files, and a single result binary, otherwise you need to adjust `flake.nix` manually. Continue to use `cargo`/`rust-analyzer` for a fast developer loop and use nix to verify your program hermetically with nix locally and in CI: ```bash title="Building project and running tests" nix build ``` ```bash title="Building & running project" nix run ``` ```bash title="Building & running project with arguments" nix run -- --arg1 x ``` ================================================ FILE: docs/src/content/docs/10_getting_started/20_installing_crate2nix.md ================================================ --- title: Installing crate2nix --- Know what you are doing and want a stable version? Just use the version from [nixpkgs](https://search.nixos.org/packages?channel=unstable&show=crate2nix&from=0&size=50&sort=relevance&type=packages&query=crate2nix). :::note New to nix? [Install nix first](https://github.com/DeterminateSystems/nix-installer). ::: ## Flake-enabled nix ```bash title="Install from nixpkgs in user profile (recommended)" nix profile install nixpkgs#crate2nix crate2nix help ``` ```bash title="Install latest development version" nix profile install github:nix-community/crate2nix crate2nix help ``` ## Traditional (non-flake) ```bash title="Install from nixpkgs (recommended)" nix-channel --update # if you wish nix-env -i -f '' -A crate2nix ``` ```bash title="Install latest development version" nix-env -i -f https://github.com/nix-community/crate2nix/tarball/master ``` ================================================ FILE: docs/src/content/docs/10_getting_started/30_running_crate2nix_without_install.md ================================================ --- title: Running crate2nix without installation --- Know what you are doing and want a stable version? Just use the version from [nixpkgs](https://search.nixos.org/packages?channel=unstable&show=crate2nix&from=0&size=50&sort=relevance&type=packages&query=crate2nix). :::note New to nix? [Install nix first](https://github.com/DeterminateSystems/nix-installer). ::: ## Flake-enabled nix ```bash title="Running from nixpkgs without installation" nix run nixpkgs#crate2nix -- help ``` ```bash title="Running latest development version without installation" nix run github:nix-community/crate2nix -- help ``` ================================================ FILE: docs/src/content/docs/20_generating/10_generating.md ================================================ --- title: Generate Cargo.nix manually --- * ✅ Does not use [import from derivation](https://nixos.org/manual/nix/stable/language/import-from-derivation) feature from Nix which may lead to disabling build parallelism, * 🛠️ but you need to regenerate `Cargo.nix` whenever you change your dependencies or other config affecting `Cargo.lock`. Disregarding whether you use nix with flakes or without, you might want to generate your `Cargo.nix` file manually. That way you avoid using the [import from derivation](https://nixos.org/manual/nix/stable/language/import-from-derivation) feature from nix which might disable build parallelism. Here is a simple example which uses all the defaults and will generate a `Cargo.nix` file. If you [installed crate2nix locally](../20_installing_crate2nix/): ```bash # From the project directory with the Cargo.toml. crate2nix generate ``` If you prefer to run it without installation: ```bash # From the project directory with the Cargo.toml. nix run nixpkgs#crate2nix -- generate ``` Here is a more elaborate example that uses `` as the default `pkgs` path (instead of ``) and specifies both the path to the `Cargo.toml` file (`-f`) and the output (`-o`) file explicitly (usually not needed). ```bash crate2nix generate \ -n '' \ -f /some/project/dir/Cargo.toml \ -o /some/project/dir/Cargo.nix ``` Use `crate2nix help` to show all commands and options. Look at the [./crate2nix/Cargo.nix](https://github.com/nix-community/crate2nix/blob/master/crate2nix/Cargo.nix) file of this project for a non-trivial example. (How meta!) ================================================ FILE: docs/src/content/docs/20_generating/20_auto_generating.mdx ================================================ --- title: Generating Cargo.nix via IFD --- import { LinkCard } from '@astrojs/starlight/components'; * ✅ No need to install `crate2nix`. * ✅ Auto-generates nix from your `Cargo.lock` file. * ⚠️ Uses the [import from derivation](https://nixos.org/manual/nix/stable/language/import-from-derivation) feature from Nix which may lead to disabling build parallelism. There are two commands in the `tools.nix` file: * `generatedCargoNix` will generate a folder containing a `default.nix`, to be used as a `Cargo.nix` file. The argument it takes are: * `name`: required. The name of the project (need to be a valid nix name identifier, so no space are allowed, but dash are.) * `src`: required. The folder that contain the root of the Rust project. * `cargoToml`: optional. The name and path relative to the `src` root of the `Cargo.toml` to use. Default to `Cargo.toml`. * `additionalCargoNixArgs`: optional, additional argument for `crate2nix`, in a list * `appliedCargoNix` take the same argument that `generatedCargoNix`, but also call the generated file with the `pkgs` provided when calling `tools.nix` In a flake with flake-parts: ```nix "tools.${system}.appliedCargoNix" "crate2nix" # flake.nix { # ... inputs = { flake-parts = { url = "github:hercules-ci/flake-parts"; inputs.nixpkgs-lib.follows = "nixpkgs"; }; crate2nix.url = "github:nix-community/crate2nix"; # ... }; outputs = inputs @ { self , nixpkgs , flake-parts , rust-overlay , crate2nix , ... }: flake-parts.lib.mkFlake { inherit inputs; } { systems = [ "x86_64-linux" "aarch64-linux" "x86_64-linux" "aarch64-darwin" ]; perSystem = { system, pkgs, lib, inputs', ... }: let cargoNix = inputs.crate2nix.tools.${system}.appliedCargoNix { name = "rustnix"; src = ./.; }; in rec { checks = { rustnix = cargoNix.rootCrate.build.override { runTests = true; }; }; packages = { rustnix = cargoNix.rootCrate.build; default = packages.rustnix; }; }; }; } ``` Without flakes, `crate2nix` needs to point to the root of the `crate2nix` sources: ```nix # default.nix let crate2nix = ...; crate2nix-tools = pkgs.callPackage "${crate2nix}/tools.nix" {}; generated = crate2nix-tools.generatedCargoNix { name = "test-rust-project"; src = ./.; }; called = pkgs.callPackage "${generated}/default.nix" {}; in called.rootCrate.build ``` ================================================ FILE: docs/src/content/docs/30_building/10_building_binaries.md ================================================ --- title: Building binaries --- ## Single binary crates If your `Cargo.nix` was generated for a single binary crate (i.e. workspace) then the derivation that builds your binary can be accessed via the `rootCrate.build` attribute. Use this command to build it and make the result available in the result directory: ```bash your_crate_name="super_duper" nix build -f Cargo.nix rootCrate.build ./result/bin/${your_crate_name} ``` Within a nix file (e.g. your manually written `default.nix`), you can access the derivation like this: ```nix let cargo_nix = callPackage ./Cargo.nix {}; in cargo_nix.rootCrate.build ``` ## Cargo workspaces with multiple crates If your `Cargo.nix` was generated for a workspace (i.e. not a single binary) then the derivation that builds your binary CANNOT be accessed via the `rootCrate` attribute. There is no single root crate. Instead, you can conveniently access the derivations of all your workspace members through the `workspaceMembers` attribute. Use this command to build one of the workspace members and make the result available in the result directory: ```bash your_crate_name="super_duper" nix build -f Cargo.nix workspaceMembers.${your_crate_name}.build ./result/bin/${your_crate_name} ``` Within a nix file (e.g. your manually written `default.nix`), you can access the derivation like this: ```nix let cargo_nix = callPackage ./Cargo.nix {}; in cargo_nix.workspaceMembers."${your_crate_name}".build ``` ================================================ FILE: docs/src/content/docs/30_building/20_choosing_features.md ================================================ --- title: Choosing enabled features --- The enabled features for a crate now are resolved at build time! That means you can easily override them: 1. There is a "rootFeatures" argument to the generated build file which you can override when calling it from the command line: ```bash nix build -f ....nix --arg rootFeatures '["default" "other"]' rootCrate.build ``` 2. Or when importing the build file with "callPackage": ```nix let cargo_nix = callPackage ./Cargo.nix { rootFeatures = ["default" "other"]; }; crate2nix = cargo_nix.rootCrate.build; in ... ``` 3. Or by overriding them on the rootCrate or workspaceMembers: ```nix let cargo_nix = callPackage ./Cargo.nix {}; crate2nix = cargo_nix.rootCrate.build.override { features = ["default" "other"]; }; in ... ``` Note that only dependencies for the default features are included in the build. If you want full flexibility, you can use `crate2nix generate --all-features` to generate the most general build file. If you want to strip down the generated build file, you may want to use `crate2nix generate --no-default-features --features "feature1 feature2"`. ================================================ FILE: docs/src/content/docs/30_building/30_crateOverrides.md ================================================ --- title: "Using crate overrides" --- You can patch the individual crate derivations with `crateOverrides`, for example to add native library dependencies such as openssl! NixOS comes with [`defaultCrateOverrides`](https://github.com/NixOS/nixpkgs/blob/master/pkgs/build-support/rust/default-crate-overrides.nix) which specifies mostly some additional native `buildInputs` for various popular crates. If you are using a rust crate with native dependencies which is not yet covered, you can add additional `buildInputs` with the `crateOverride` parameter (similar to `features`): ```nix let customBuildRustCrateForPkgs = pkgs: pkgs.buildRustCrate.override { defaultCrateOverrides = pkgs.defaultCrateOverrides // { funky-things = attrs: { buildInputs = [ pkgs.openssl ]; }; }; }; generatedBuild = callPackage ./crate2nix/Cargo.nix { buildRustCrateForPkgs = customBuildRustCrateForPkgs; }; in generatedBuild.rootCrate.build ``` Or obviously you can use the power of nix to add a dependency conditionally: ```nix let customBuildRustCrateForPkgs = pkgs: pkgs.buildRustCrate.override { defaultCrateOverrides = pkgs.defaultCrateOverrides // { cssparser-macros = attrs: { buildInputs = lib.optionals pkgs.stdenv.isDarwin [ pkgs.darwin.apple_sdk.frameworks.Security ]; }; }; }; generatedBuild = callPackage ./crate2nix/Cargo.nix { buildRustCrateForPkgs = customBuildRustCrateForPkgs; }; in generatedBuild.rootCrate.build ``` `crateOverrides` are not restricted to buildInputs however. You should also be able to add patches and the like! (I didn't try that, though.) `crateOverrides` are a feature of the underlying [`buildRustCrate` support in NixOS](https://nixos.org/manual/nixpkgs/stable/#compiling-rust-crates-using-nix-instead-of-cargo) that crate2nix uses. ================================================ FILE: docs/src/content/docs/30_building/40_tests.md ================================================ --- title: Rust tests sidebar: badge: { text: 'Experimental', variant: 'caution' } --- :::tip[Not thaaat experimental] Running tests has been supported by `crate2nix` for a while now but is still marked as experimental. Don't let that scare you too much: we intend to support tests in the future but the interface might slightly change. ::: ## Running rust tests There is some experimental support for running tests of your rust crates. All of the crates in the workspace will have their tests executed. When enabling test execution (`runTests = true;`), failing tests will make the whole build fail unless you explicitly disable this via test hooks: see the section below. ```nix let cargo_nix = callPackage ./Cargo.nix {}; crate2nix = cargo_nix.rootCrate.build.override { runTests = true; testInputs = [ pkgs.cowsay ]; }; in ... ``` `testInputs` is optional and allows passing inputs to the test execution that should be in scope. Defaults to an empty list and is ignored when `runTests` equals `false`. ## Custom pre/post test hooks Want to customize your test execution? Use the `testPreRun` and `testPostRun` crate attributes(next to `runTests` in the example above). `crate2nix` executes the bash snippets in `testPreRun` and `testPostRun` directly before and after the actual test command, and in the same shell. Some example use-cases include: * Setting some environment variable that is needed for the test. * Setting (and then unsetting) the bash `set +e` option to not fail the derivation build even if a test fails. This is quite useful if your tests are not flaky and you want to cache failures. ================================================ FILE: docs/src/content/docs/35_toolchains/10_custom_toolchains.md ================================================ --- title: Custom Toolchains --- One way to use a custom rust toolchain with `crate2nix` is to import nixpkgs with an overlay for `rustc`. Here is an example flake using [fenix]: ```nix { description = "containix"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/24.05"; fenix = { url = "github:nix-community/fenix"; inputs.nixpkgs.follows = "nixpkgs"; }; flake-utils.url = "github:numtide/flake-utils"; crate2nix.url = "github:nix-community/crate2nix"; }; outputs = { self, nixpkgs, flake-utils, fenix, crate2nix, }: flake-utils.lib.eachDefaultSystem ( system: let toolchain = fenix.packages.${system}.stable.defaultToolchain; pkgs = import nixpkgs { inherit system; overlays = [ (final: prev: { rustc = toolchain; cargo = toolchain; }) ]; }; crate2nix' = pkgs.callPackage (import "${crate2nix}/tools.nix") {}; cargoNix = crate2nix'.appliedCargoNix { name = "my-crate"; src = ./.; }; in { packages = { default = cargoNix.rootCrate.build; }; } ); } ``` [fenix]: https://github.com/nix-community/fenix ================================================ FILE: docs/src/content/docs/35_toolchains/20_using_a_rust_overlay.md ================================================ --- title: Using a Rust overlay --- It's possible to use a Rust overlay such as `rust-overlay` with `crate2nix`. This can be used for pinning the Rust toolchain version, or to use a newer toolchain version than is available in Nixpkgs. In a flake with flake-parts: ```nix # flake.nix { # ... inputs = { flake-parts = { url = "github:hercules-ci/flake-parts"; inputs.nixpkgs-lib.follows = "nixpkgs"; }; crate2nix.url = "github:nix-community/crate2nix"; # ... }; outputs = inputs @ { self , nixpkgs , flake-parts , rust-overlay , crate2nix , ... }: flake-parts.lib.mkFlake { inherit inputs; } { systems = [ "x86_64-linux" "aarch64-linux" "x86_64-linux" "aarch64-darwin" ]; perSystem = { system, lib, inputs', ... }: let pkgs = import nixpkgs { inherit system; overlays = [ rust-overlay.overlays.default ]; }; buildRustCrateForPkgs = crate: pkgs.buildRustCrate.override { rustc = pkgs.rust-bin.stable.latest.default; cargo = pkgs.rust-bin.stable.latest.default; }; generatedCargoNix = inputs.crate2nix.tools.${system}.generatedCargoNix { name = "rustnix"; src = ./.; }; cargoNix = import generatedCargoNix { inherit pkgs buildRustCrateForPkgs; }; in rec { checks = { rustnix = cargoNix.rootCrate.build.override { runTests = true; }; }; packages = { rustnix = cargoNix.rootCrate.build; default = packages.rustnix; }; }; }; } ``` ================================================ FILE: docs/src/content/docs/40_external_sources/10_building_fetched_sources.md ================================================ --- title: Building fetched sources --- * ✅ No need to install `crate2nix`. * ✅ Auto-generates nix from your `Cargo.lock` file. * ⚠️ Uses the [import from derivation](https://nixos.org/manual/nix/stable/language/import-from-derivation) feature from Nix which may lead to disabling build parallelism. Do you want to build a rust binary but you cannot simply add the necessary nix-files to the source repository? You don't need to. The `crate2nix` repo itself contains an example where it builds an external repository using the [tools.nix](./31_auto_generating) support: ```nix # nix/nix-test-runner.nix let flakeInput = import ./flakeInput.nix; src = builtins.fetchTree (flakeInput "nix-test-runner"); # Use last pinned crate2nix packages and corresponding nixpkgs to build the # test runner so that it works even if we have broken stuff! crate2nix_stable = builtins.fetchTree (flakeInput "crate2nix_stable"); nixpkgs_stable = builtins.fetchTree (flakeInput "crate2nix_stable.nixpkgs"); in { system ? builtins.currentSystem , pkgs ? import nixpkgs_stable { inherit system; } , tools ? pkgs.callPackage "${crate2nix_stable}/tools.nix" { } }: let nixTestRunner = tools.appliedCargoNix { name = "nix-test-runner"; inherit src; }; in nixTestRunner.rootCrate.build ``` ```nix # nix/nixpkgs.nix let flakeInput = import ./flakeInput.nix; in import (builtins.fetchTree (flakeInput "nixpkgs")) ``` ```nix # flake.nix { # ... inputs = { nixpkgs.url = "nixpkgs"; # ... crate2nix_stable = { url = "github:nix-community/crate2nix/0.12.0"; }; nix-test-runner = { url = "github:stoeffel/nix-test-runner"; flake = false; }; }; # ... } ``` ================================================ FILE: docs/src/content/docs/40_external_sources/20_generating_for_fetched_sources.md ================================================ --- title: Generating Cargo.nix for multiple fetched sources sidebar: badge: { text: 'Experimental', variant: 'caution' } --- NOTE: This is work in progress, see [#102](https://github.com/kolloch/crate2nix/issues/102), the interface might still change. `crate2nix` has convenient support for managing out-of-tree sources: It will manage a nix-generated directory of the source roots for you. `crate2nix` will use the supplied `Cargo.lock` files in the sources to generate the binaries with the versions that the maintainers specified. ## Demo Starting from scratch with an empty directory: ```console ❯ mkdir ripgrep-example ❯ cd ripgrep-example ``` Adding a crates.io dependency by name and version: ```console ❯ crate2nix source add cratesIo ripgrep 12.0.1 Prefetching https://crates.io/api/v1/crates/ripgrep/12.0.1/download: done. Added new source: ripgrep 12.0.1 from crates.io: 1arw9pk1qiih0szd26wq76bc0wwbcmhyyy3d4dnwcflka8kfkikx ``` Adding a git dependency by URL and revision hash: ```console ❯ crate2nix source add git https://github.com/cjbassi/ytop --rev 21cb63f656519b86928fce9522107f78780d6460 Prefetching https://github.com/cjbassi/ytop#21cb63f656519b86928fce9522107f78780d6460: done. Added new source: https://github.com/cjbassi/ytop#21cb63f656519b86928fce9522107f78780d6460 via git: 1si5cq18qn819v28vfgbrx3fmfgzj4h0z00ga2p0px42q62vrs7q ``` Listing all our sources: ```console ❯ crate2nix source list ripgrep ripgrep 12.0.1 from crates.io: 1arw9pk1qiih0szd26wq76bc0wwbcmhyyy3d4dnwcflka8kfkikx crate2nix source add cratesIo --name 'ripgrep' 'ripgrep' '12.0.1' ytop https://github.com/cjbassi/ytop#21cb63f656519b86928fce9522107f78780d6460 via git: 1si5cq18qn819v28vfgbrx3fmfgzj4h0z00ga2p0px42q62vrs7q crate2nix source add git --name 'ytop' 'https://github.com/cjbassi/ytop' --rev 21cb63f656519b86928fce9522107f78780d6460 ``` This also shows the commands with which you could recreate your sources for convenience. Generating `Cargo.nix`, prefetching some indirect git dependencies: ```console ❯ crate2nix generate Fetching sources. Generated ./crate2nix-sources.nix successfully. Fetching sources via ./crate2nix-sources.nix fetchedSources: done. Prefetching 1/2: https://github.com/rust-psutil/rust-psutil#6abe2de4409672c3f42b69db2b1ba45d73e78ee4 Prefetching 2/2: https://github.com/cjbassi/tui-rs#aff0a4c40aff6e0962a2a935d4b21065298e329c Wrote hashes to ./crate-hashes.json. Generated ./Cargo.nix successfully. ``` Building the binaries: ```console ❯ nix build -f Cargo.nix workspaceMembers.ripgrep workspaceMembers.ytop [93 built, 0.0 MiB DL] ``` Running the binaries: ```console ❯ ./result-1/bin/ytop --version ytop 0.5.1 ❯ ./result/bin/rg --version ripgrep 12.0.1 -SIMD -AVX (compiled) +SIMD +AVX (runtime) ``` ## Managing sources in crate2nix.json Here are some examples for adding sources: ```console crate2nix source add cratesIo ripgrep 12.0.1 crate2nix source add git https://github.com/kolloch/crate2nix.git --rev 0832e5ac0a2c53a7a99b9b0b2ff2d51828e5cb60 crate2nix source add nix --import ./nix/sources.nix my_crate ``` If `crate2nix.json` does not exist yet, it will be created. As always, you can have a look at all available options and commands by using `crate2nix source --help`, `crate2nix source add --help` and so forth. `crate2nix source list` shows already configured sources. `crate2nix source remove ripgrep` removes the source named `ripgrep`. ## crate2nix generate `crate2nix generate` will generally just do the right thing and pick up crate2nix.json file if necessary: * Build a `crate-sources` directory with all of your sources. It uses a generated `crate-sources.nix` in your project directory. * Call `cargo metadata` individually for all sources and concatenate the results. ## What to check into version control For building the binaries, `Cargo.nix` is sufficient. But everyone regenerating it will appreciate if you also check in * `crate2nix.json` (the source configuration) * `crate-hashes.json` (the hashes for packages not in the lockfile) The generated `crate-sources*` files should go into `.gitignore` or similar: ```crate-sources*``` * `crate-sources.nix` is the temporary nix expression to download the sources. * `crate-sources` is the temporary result symlink to the downloaded sources. ## Feature resolution Note that features in `crate2nix` are resolved at build time so that every dependency is build only with the features necessary for the specific binary. This is probably what you want because it can prevent build problems. Even though, features in rust are meant to be additive, in reality they are often not. ================================================ FILE: docs/src/content/docs/50_contributing/00_intro.md ================================================ --- title: Intro description: About contributing to crate2nix. --- Contributions in the form of documentation and bug fixes are highly welcome. Please start a discussion with us maintainers before working on larger features. Feature ideas are also welcome -- just know that this is a pure hobby side project and we might not allocate a lot of bandwidth to this. Therefore, important bug fixes are always prioritized. :::tip[⚖️ Important Legal Notice] By submitting a pull request, you agree to license your changes via all the current licenses of the project. ::: ## 🥳 CHANGELOG Add your change to the CHANGELOG. * **New Features**: Let people know about new features. * **Breaking Changes**: Let people know about breaking changes. Of course, please keep them rare and if at all possible warn people for a few releases before implementing them. * **Recognition**: That way you ensure that your contribution is recognized in the next release notes 🤗. ================================================ FILE: docs/src/content/docs/50_contributing/05_code.md ================================================ --- title: Nix/Rust/Tera Code description: Some pointers for contributing code successfully. --- Read [Project Overview & Terminology](/crate2nix/70_design/10_structure_and_phases/) for a first overview. We really appreciate tests for all new features. Please run `./run_tests.sh` before submitting a pull request and/or react to test failures in the pull request. ## Running tests `run_tests.sh` will regenerate build files AND run cargo test for you. It will call out to nix to build the sample projects -- so a considerable number of dependencies will be fetched and built. Consecutive runs are much faster. ### Running an individual integration test :::tip[Sample projects] Many of these just build a sample project in the [sample_projects folder](https://github.com/nix-community/crate2nix/tree/master/sample_projects). ::: To run just an individual test (in this example "bin_with_lib_dep"): ```shell nix-build \ -o ./crate2nix/target/nix-results ./tests.nix -A checks.bin_with_lib_dep ``` If you want to run a test that is currently marked as skip, you can do so with: ```shell nix-build \ -o ./crate2nix/target/nix-results ./tests.nix -A checks.empty_cross.forceSkipped ``` ### Regenerating Cargo.nix files for tests If you change `crate2nix` such that it will produce a different output, you may need to regenerate some of the Cargo.toml files. Not all `Cargo.toml` files can be generated during test time because crate2nix needs to vendor the dependencies for this to work and support for git submodules is not working yet, see [#101](https://github.com/nix-community/crate2nix/issues/101). `regenerate_cargo_nix.sh` should do what you want and is run as part of `run_tests.sh` (see below). ### Updating `crate-hashes.json` `regenerate_cargo_nix.sh` does not automatically refetch any references if you update a `Cargo.lock` file of a sample_project. You need to call `crate2nix generate` manually. `crate-hashes.json` will be updated, the generated Cargo.nix will be overwritten by `./regenerate_cargo_nix.sh`. ## Hacking on `buildRustCrate` in nixpkgs Since `crate2nix` heavily depends on `buildRustCrate` in `nixpkgs`, it makes sense to hack on them together. To be able to provide pull requests, you probably want to fork [nixpkgs](https://github.com/NixOS/nixpkgs) first. Once you have done that, clone that fork into some local directory (separate from crate2nix). ### Overriding nixpkgs for everything Now you can run the integration tests of `crate2nix` against that version of nixpkgs. Let's assume you are in the `crate2nix` project directory and you cloned `nixpkgs` to a sibling directory: ```shell nix-build --arg nixpkgs ../nixpkgs -o ./crate2nix/target/nix-results ./tests.nix checks ``` Or run just an individual test (in this example "bin_with_lib_dep"): ```shell nix-build --arg nixpkgs ../nixpkgs \ -o ./crate2nix/target/nix-results ./tests.nix -A checks.bin_with_lib_dep ``` (The "-o" argument is just to avoid a lot of top-level result directories.) ### Overriding nixpkgs for buildTests The problems is that this method will rebuild everything with your nixpkgs, including `crate2nix` itself. That can become severely annoying if you want to iterate on one special test case. If you are fixing an issue in `buildRustCrate` that you can reproduce with a `buildTest` in `tests.nix`, then there is a much better way. ```shell nix-build --arg buildTestNixpkgs ../nixpkgs \ -o ./crate2nix/target/nix-results ./tests.nix -A checks.bin_with_lib_dep ``` Notice `--arg buildTestNixpkgs` instead of `--arg nixpkgs`. That will not rebuild `crate2nix` itself with your nixpkgs but it will use `buildRustCrate` from your `nixpkgs` for all `buildTests`. ================================================ FILE: docs/src/content/docs/50_contributing/10_docs.md ================================================ --- title: Documentation description: How to contribute to the documentation. --- * You learned something about `crate2nix` that isn't documented or described in a bad way? * You noticed some bad grammar? * You spotted some overcomplicated wording? * You want to spice up the layout? 🚀 Awesome. Contribute by improving the documentation! If you are not sure about something, make a rough draft or explain what you want to do, submit a PR and ask for feedback early. ## ⚡ Editing directly in GitHub :::note Only recommended for small changes. ::: If you want to edit an already existing page, you can click on the "Edit page" link at the bottom of the page. It will lead you to in-place editing in GitHub. This will eventually allow you to create a pull request with your changes. Check for any "build errors" -- there is some linting that is run across the markdown files and it is rather strict. ## 💻 Editing the documentation in a local copy `crate2nix` uses Starlight and Astro to provide this GitHub page. All the docs are in the docs folder of the source repo. ### 🥇 Recommended workflow * **Enter a dev shell**: `nix develop` or `direnv allow` if you use [direnv](https://direnv.net/). * **Enter docs directory**: `cd docs` * **Install dependencies**: `npm install` * **Preview your docs continuously**: `npm run dev` * **Edit Loop** * **Edit**: Make your changes. * **Preview**: Preview your changes [locally](http://localhost:4321/crate2nix/). * **Build in nix**: `git add` your changes. * **Lint**: `pre-commit` run local lints. * **CHANGELOG**: Add yourself to the changelog to get some well-deserved recognition in the release notes! * **Build in nix**: run `nix flake check`. * **Create your pull request** ### 🧞 Using NPM Commands All commands are run from the docs folder of the project, from a terminal: | Command | Action | | :------------------------ | :----------------------------------------------- | | `nix develop` | Enter a dev shell if you haven't enabled [direnv](https://direnv.net/) | | `npm install` | Installs dependencies | | `npm run dev` | Starts local dev server at `localhost:4321` ` | | `npm run build` | Build your production site to `./dist/` | | `npm run preview` | Preview your build locally, before deploying | | `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | | `npm run astro -- --help` | Get help using the Astro CLI | ### 🧞 Linting, checking, building | Command | Action | | :------------------------ | :----------------------------------------------- | | `nix develop` | Enter a dev shell if you haven't enabled [direnv](https://direnv.net/) | | `nix build .#docs` | Building docs with nix | | `nix flake check` | Running build AND lints | | `pre-commit` | Running pre-commit lints without committing. | ## 🚀 Docs Project Structure Inside of your Astro + Starlight project, you'll see the following folders and files: ```text . ├── public/ ├── src/ │ ├── assets/ │ ├── content/ │ │ ├── docs/ │ │ └── config.ts │ └── env.d.ts ├── astro.config.mjs ├── package.json └── tsconfig.json ``` Starlight looks for `.md` or `.mdx` files in the `src/content/docs/` directory. Each file is exposed as a route based on its file name. Images can be added to `src/assets/` and embedded in Markdown with a relative link. Static assets, like favicons, can be placed in the `public/` directory. ## 👀 Want to learn more about Starlight/Astro? Check out [Starlight’s docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat). ## 👀 Want to learn more about structuring documentation? And help me with it 😀 * Read [about the Diátaxis framework](https://diataxis.fr/). ================================================ FILE: docs/src/content/docs/50_contributing/90_release.md ================================================ --- title: Creating a release --- :::tip[Target Audience] Maintainers of crate2nix ::: See [RELEASE.md](https://github.com/nix-community/crate2nix/blob/master/RELEASE.md) in the repository root. ================================================ FILE: docs/src/content/docs/70_design/10_structure_and_phases.md ================================================ --- title: Project Overview & Terminology --- If you want to hack on this, it is useful to know that build file generation is broken up into multiple phases: 1. **cargo metadata**: Calling `cargo metadata` via the `cargo_metadata` crate. 2. **indexing metadata**: Indexing the metadata by package ID to enable easy joining of "Node" and "Package" information, resulting in `metadata::IndexedMetadata`. 3. **resolving**: Using the indexed metadata to actually resolve the dependencies and join all needed build information into `resolve::CrateDerivation`. 4. **pre-fetching**: Pre-fetching crates.io packages to determine their sha256, see `prefetch` module. 5. **rendering**: Rendering the data via the `build.nix.tera` template, see `render` module. ================================================ FILE: docs/src/content/docs/70_design/50_tools_nix.mdx ================================================ --- title: tools.nix --- import { LinkCard } from '@astrojs/starlight/components'; The `tools.nix` file contain the necessary code to generate the `Cargo.nix` file during evaluation time, which guarantee to always have `Cargo.nix` file up-to-date in regard to the `Cargo.lock`. The generated file is imported automatically in Nix code via the [import from derivation feature](https://nixos.org/manual/nix/stable/language/import-from-derivation), and can then be used like a normal `Cargo.nix` file. Internally, this work by reading the `Cargo.lock` file with Nix, using the locked version and hash present in it to fetch them without introducing impurities. The fetched dependencies are then used to generate a vendored folder, and the appropriate configuration is generated so that the dependencies are fetched from here. `crate2nix` is then called in a derivation that will generate the `Cargo.nix` file offline, which can later be imported. ================================================ FILE: docs/src/content/docs/70_design/90_inspiration.md ================================================ --- title: Inspiration & History description: Where did crate2nix come from? --- [carnix](https://nest.pijul.com/pmeunier/carnix:master) was already widely used in NixOS itself and also built projects at crate granularity! Unfortunately, it failed to generate correct builds for [@kolloch's](https://github.com/kolloch) rust projects. After some attempts to fix `carnix` itself, he gave up and started `crate2nix`. That said, big kudos for all the work on `buildRustCrate` and showing the way! The [NixOS Wiki Rust Page](https://nixos.wiki/wiki/Rust#Packaging_Rust_projects_with_nix) contains a nice overview over currently available approaches for building rust projects with nix. ## Other Related Projects * [naersk](https://github.com/nmattia/naersk/) uses cargo to drive the entire build. It builds all dependencies in one derivation and the crate itself in another. Since it relies on hashes from the Cargo.lock file, I don't know how it handles git dependencies with sub modules. * [tenx-tech/cargo2nix](https://github.com/tenx-tech/cargo2nix): I haven't used it so take it with a grain of salt but I think * it uses its own build logic instead of `buildRustCrate` but still builds each crate in its own derivation. * it has some support for cross building (which is quite weak in crate2nix). * [cargo-raze](https://github.com/google/cargo-raze) generates `BUILD` files for bazel. ================================================ FILE: docs/src/content/docs/90_reference/10_runtime_dependencies.md ================================================ --- title: Runtime Dependencies --- crate2nix uses `cargo metadata` at runtime when generating `Cargo.nix`. Depending on the situation it also calls out to * `nix-prefetch-url` (e.g. for git dependencies), * `nix` (e.g. for out of tree functionality). The default package appends the nixpkgs default versions of all runtime dependencies to the path, so that they should never be missing. ================================================ FILE: docs/src/content/docs/90_reference/20_known_restrictions.md ================================================ --- title: Known Restrictions --- `crate2nix` makes heavy use of `buildRustCrate` in `nixpkgs`. So we potentially depend on features in a recent version of `nixpkgs`. Check [nix/sources.json](https://github.com/nix-community/crate2nix/blob/master/nix/sources.json) for the version of nixpkgs that `crate2nix` is tested against. If you feel limited by these restrictions, please do not hesitate to file an issue! That gives me a feeling of what is worth working on. * There is only experimental support for running tests ~~Before 0.7.x: No support for building and running tests, see [nixpkgs, issue 59177](https://github.com/NixOS/nixpkgs/issues/59177).~~ * Target-specific features do not work automatically, see [#129](https://github.com/nix-community/crate2nix/issues/129). You should be able to enable the required features manually, however. * A crate will only have access to its own source directory during build time and not e.g. to other directories in the same workspace. See [crate2nix, issue 17](https://github.com/nix-community/crate2nix/issues/17). I used to consider this "works as intended" but now I think that we should use the "workspaceMember" argument of buildRustCrate for this. * It does translates target strings to nix expressions. The support should be reasonable but probably not complete - please let me know if you hit problems. ~~Before 0.2.x: Filters all dependencies for the *hard-coded "Linux x86_64" target platform*. Again, it should be quite easy to support more platforms. To do so completely and at build time (vs build generation time) might be more involved.~~ Former restrictions, now supported: * ~~Before 0.8.x: Since cargo exposes local paths in package IDs, the generated build file also contain them as part of an "opaque" ID. They are not interpreted as paths but maybe you do not want to expose local paths in there...~~ The full opaque package ID will only be used if you have the same package with the same version multiple times. That should be very rare. * ~~Before 0.6.x: [Renamed crates](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#renaming-dependencies-in-cargotoml) with an explicit `package` name don't work yet.~~ * Git sources are now also supported. Starting with 0.7 sub modules also work. Finding crates in arbitrary sub directories of git sources (which cargo supports!)is not supported, see #53. * ~~Before 0.4.x: Only *default crate features* are supported. It should be easy to support a different feature set at build generation time since we can simply pass this set to `cargo metadata`. Feature selection during build time is out of scope for now.~~ ================================================ FILE: docs/src/content/docs/90_reference/90_CHANGELOG.md ================================================ --- title: CHANGELOG description: A list of all major changes per version. --- ## 0.15.0 (2026-01-28) ### New features * [#366](https://github.com/nix-community/crate2nix/pull/366): Support for private registries. Thank you, @P-E-Meunier! * [#390](https://github.com/nix-community/crate2nix/pull/390): Allow selected toolchain in `generatedCargoNix`. Thank you, @bengsparks! * [#416](https://github.com/nix-community/crate2nix/pull/416): Add `extraTargetFlags` parameter for custom cfg conditions. ### Fixes * [#357](https://github.com/nix-community/crate2nix/pull/357): Use `mkDerivation` with `src` instead of `runCommand` for test derivation. * [#372](https://github.com/nix-community/crate2nix/pull/372): Fix build-dependencies resolution when cross-compiling. Thank you, @pnmadelaine! * [#375](https://github.com/nix-community/crate2nix/pull/375): Fix cargo tests and clippy warnings. Thank you, @pnmadelaine! * [#388](https://github.com/nix-community/crate2nix/pull/388): Respect `fetchurl` passed via top-level Cargo.nix. Thank you, @weihanglo! * [#391](https://github.com/nix-community/crate2nix/pull/391): Fix incorrect resolution of aliased git dependencies when multiple versions of the same package are referenced. Thank you, @hallettj! * [#407](https://github.com/nix-community/crate2nix/pull/407): Inherit the main crate's `meta` when building with tests. Thank you, @jrobsonchase! * Fix parsing checksums from v1 manifests with latest cargo. Thank you, @hallettj! * [#394](https://github.com/nix-community/crate2nix/pull/394): Fix handling of git dependencies with wildcard workspace members and subcrates in nested directories. Thank you, @Pacman99! ### Documentation * [#359](https://github.com/nix-community/crate2nix/issues/359): Document using `rust-overlay`. * [#417](https://github.com/nix-community/crate2nix/pull/417): Document custom toolchains. Thank you, @qknight! ### Internal * [#414](https://github.com/nix-community/crate2nix/pull/414): Update flake inputs. Thank you, @jrobsonchase! ## 0.14.x - 0.14.1 (2024-06-30) Maintenance release with cross-compilation fixes, documentation improvements and removal of old workarounds. * [#352](https://github.com/nix-community/crate2nix/pull/352): * `rust.lib` -> `stdenv.hostPlatform.rust` * Always filter `src` using `filterSource` * Get rid of `dontStrip` for Darwin as it's no longer needed * Remove no longer needed `unsafeDiscardStringContext` workaround * [#351](https://github.com/nix-community/crate2nix/pull/351): Set `--extra-experimental-features flakes` in `regenerate_cargo_nix.sh`. * [#350](https://github.com/nix-community/crate2nix/pull/350): Document `targetFeatures` better. ## 0.13.x - 0.14.0 (2024-04-11) [340](https://github.com/nix-community/crate2nix/issues/340) make `crate2nix` compatible with the lock file changes from rust 1.77.x. [Move sources into /build/sources](https://github.com/nix-community/crate2nix/commit/15656bb6cb15f55ee3344bf4362e6489feb93db6) to maximize compatibility. ## 0.12.x - 0.13.0 The usual internal version updates but there is more! ### Documentation as Github Page The old README.md had become very long and hard to navigate. Check out the new and shiny page at [https://nix-community.github.io/crate2nix/](https://nix-community.github.io/crate2nix/)! * Create new [Github Page](https://nix-community.github.io/crate2nix/). [@kolloch](https://github.com/kolloch/) * Move most of the old content there. [@kolloch](https://github.com/kolloch/) ### Export `tools` as flake attribute Do you like to use [import from derivation](https://nixos.org/manual/nix/stable/language/import-from-derivation) so that you do not have to regenerate `Cargo.nix` on every dependency change? The related convenience functions are now also available via the flake attribute "tools": ```nix # ... perSystem = { system, pkgs, lib, inputs', ... }: let cargoNix = inputs.crate2nix.tools.${system}.appliedCargoNix { name = "rustnix"; src = ./.; }; in rec { packages.default = cargoNix.rootCrate.build; }; # ... ``` Check out the [documentation](https://nix-community.github.io/crate2nix/20_generating/20_auto_generating/). ### Flakify the crate2nix build itself Convert the pre-flake infrastructure step-by step to [nix flakes](https://nix.dev/concepts/flakes.html), while attempting to preserve compatibility for non-flake users. This is more of an internal change and should not affect the usage of crate2nix yet but a more flake friendly interface is planned. * Convert flake.nix to [flake.parts](https://flake.parts). [@kolloch](https://github.com/kolloch/) * Use [devshell](https://github.com/numtide/devshell) for devShell. [@kolloch](https://github.com/kolloch/) * Provide `shell.nix` via [flake-compat](https://github.com/edolstra/flake-compat). [@kolloch](https://github.com/kolloch/) * Provide an "old-school" `pkgs.callPackage`-compatible `default.nix` for the `crate2nix` binary. [@kolloch](https://github.com/kolloch/) Tests and some utilities are still working flake-less but use the flake inputs by default. ## 0.11.x - 0.12.0 * [Cross compilation fixes](https://github.com/nix-community/crate2nix/pull/309). Thank you, @flokli! * Propagate crate links attribute. Thank you, @lblasc! * Determine target vendor via nixpkgs function. Thank you, @jordanisaacs! * Initial flake.nix for crate2nix. Thank you, @vleni! ## 0.10.x - 0.11.0 ### Support `foo?/bar` feature selections [Conditional features](https://doc.rust-lang.org/cargo/reference/features.html#dependency-features) are now supported. The feature selection `foo?/bar` will only enable the `bar` feature of the `foo` dependency if that dependency would be enabled anyway (non-optional, or enabled by a different feature selection). ### Remove `buildRustCrate` parameter This was previously deprecated. ### Restore `callPackage` not raw `import` as the recommended method With `buildRustCrate` removed, this is now possible without running into issues. ### Finish renamed dependency support Previously only renamed `dependencies` and `build-dependencies` worked. Now renamed `dev-dependencies` work also. Thank you @Fuuzetsu! ### Better cross and platform-specific compilation support * Rust-style rather than Nixpkgs-style configs are used for `[target."some-config"]` conditional Cargo sections. * The nixpkgs Rust "lib" is used to implement the above and deduplicate other `cfg` reosolution. * Support custom `cfg(target_family = "whatever")` dependencies. ### Fix IFD support library (`tools.nix`) for Nixpkkgs 22.11 * See the comment inside there for details. ## 0.9.x - 0.10.0 Help needed! I don't have the resources to meaningfully advance this project. Thank you for all the valuable contributions but I'd appreciate a co-maintainer who is motivated to put in some time into reviewing PRs, updating docs, fixing bugs, .. Warning: This release does not work on Mac OS with a recent `nixpkgs`. Help wanted. Warning: the `buildRustCrate` argument will be removed soon. ### New "calling convention" The recommended convention to use `Cargo.nix` files generated by `crate2nix` has changed. Instead of `callPackage` you know should use import: ```nix let cargo_nix = import ./Cargo.nix { inherit pkgs; }; in cargo_nix.workspaceMembers."${your_crate_name}".build ``` The reason is that the deprecated `buildRustPackage` argument gets automatically supplied from `pkgs.buildRustPackage` by `pkgs.callPackage`. This causes problems when cross compiling. This was debugged by @symphorien, thank you! ### New: Enable optional create feature if sub feature was enabled Enable a feature with the same name of an optional dependency, if the optional dependency is indirectly enabled by enabling one of its features. This mimicks the behavior of Cargo which is explained in [this note](https://doc.rust-lang.org/nightly/cargo/reference/features.html#dependency-features). Thank you @pandaman64! This fixes the following isues: * [#146](https://github.com/kolloch/crate2nix/issues/146) Features fail to contain optional dependencies that got enabled indirectly * [#182](https://github.com/kolloch/crate2nix/issues/182) Can't build crate with dependency num 0.4.0 ### New: Customize test commands Want to customize your test execution? Use the `testPreRun` and `testPostRun` crate attributes (next to `runTests`, see [README.md](./README.md#running-rust-tests)). `crate2nix` executes the bash snippets in `testPreRun` and `testPostRun` directly before and after the actual test command, and in the same shell. Some example use-cases include: * Setting some environment variable that is needed for the test. * Setting (and then unsetting) the bash `set +e` option to not fail the derivation build even if a test fails. This is quite useful if your tests are not flaky and you want to cache failures. Thank you @Fuuzetsu! ## 0.8.x - 0.9.0 Help needed! I don't have the resources to meaningfully advance this project. Thank you for all the valuable contributions but I'd appreciate a co-maintainer who is motivated to put in some time into reviewing PRs, updating docs, fixing bugs, .. ### Breaking changes * Remove long deprecated `root_crate` and `workspace_members` aliases in the generated `Cargo.nix` files. ### Enhancements * [Issue #83](https://github.com/kolloch/crate2nix/issues/83) Supporting depending on different versions of the same crate! * Some strides towards cross-compilation support. Docs missing and would be appreciated! Thanks, @Ericson2314, @lopsided98! * [Experimental out-of-tree support](./out-of-tree-sources.md) -- with no time to work further on it :( ### Under the hood * Test execution is now closer to what `cargo test` does. Thank you, @symphorien! * Better `direnv` support in the source. Thanks, @Mic92! * Better support for upcoming nix 3.0. Thanks, @balsoft! * tests: avoid building two versions of the same crate. Thanks, @andir! * Remove usages of deprecated `stdenv.lib`. Thanks, @cole-h! ## 0.7.x - 0.8.0 Organizational: @andir is now an additional official maintainer of `crate2nix`. Welcome! Breaking change: * If you are building crates with git dependencies, you will now need to update to a recent version of `nixpkgs-unstable`. On the upside, crates in sub directories of git repositories will now work! New features: * [Issue #53](https://github.com/kolloch/crate2nix/issues/53): Search for matching Cargo.toml for git dependencies. * Running tests is now documented in the README. * Add `testInputs` to test execution. This allows users to add `buildInputs` to the test execution. This might be required as during test execution external programs will be called on generated output or in cases where the rust application is just a wrapper around some other tool (in some way or another). * [Issue #47](https://github.com/kolloch/crate2nix/issues/47) Shorter package IDs: Use the crate name or the crate name together with the version as package ID if that is unique. That shortens the generated files, makes them more readable and also strips local paths from the package IDs in most cases. The last point means that you do not have an unncessary diff in you generated Cargo.nix because someone else was regenerating it. Under the hood: * Trimmed down the dependency tree of `crate2nix` itself by disabling some features. * At least some smoke tests for debug functionality. If you are interested in hacking on `crate2nix` and `buildRustCrate`: There are now some convenience flags for the `./run_tests.sh` script for offline mode and running the tests against your own modified version of `nixpkgs`. Check out the README section "Hacking on `buildRustCrate` in nixpkgs". ## 0.6.x - 0.7.1 New features and improvements: * Use hashes from Cargo.lock instead of prefetching when available. This should work for any crates.io dependency. :) * Follow up to [Issue #22](https://github.com/kolloch/crate2nix/issues/22) (and others) - Handling of "renamed crates". Thanks a lot,@andir! * Support for multiple binaries from the same crate. Thank you, @kristoff3r! * [Issue #34](https://github.com/kolloch/crate2nix/issues/34) - Support for git prefetching so that repositories with sub modules now work. Thank you, @cpcloud! * [Issue #65](https://github.com/kolloch/crate2nix/issues/65) - Reexpose feature selection parameters from `cargo metadata`. This allows to include dependencies in the generated build file which are not used for the default features. Or to exclude dependencies for features that you do not care about. * [Issue #67](https://github.com/kolloch/crate2nix/issues/67) - Support for additional lib types - in particular, `cdylib`. Thank you, @andir! Write a rust library that is used from C code :) * [Issue #18](https://github.com/kolloch/crate2nix/issues/18) - Optional crate unavailable Allows building packages that have multiple versions of the same dependency (with different targets). In particular the flate2 package now builds. Thank you, @cchalmers! * [Issue #37](https://github.com/kolloch/crate2nix/issues/37) - Conditional target expressions for dependencies can now also depend on features. Thank you, @cpcloud! * [Issue #42](https://github.com/kolloch/crate2nix/issues/42) - Some efficiency improvements to prevent stack overflows for projects with huge dependency trees. Thank you, @jamii! * [Issue #90](https://github.com/kolloch/crate2nix/issues/90) There is a follow up to this: @nagisa was seeing super-linear instantiation counts and provided a flamegraph. @andir proposed a [likely fix in nixpkgs](https://github.com/NixOS/nixpkgs/pull/79816). Thank you! * Add fuchsia as an unsupported target (ef945396fcb700322b5b5f497a5d243950ed2513 ). Thank you, @jamii! * [Issue #94](https://github.com/kolloch/crate2nix/issues/94): The `defaultCrateOverrides` argument to the build file has finally the desired effect again. * [#75](https://github.com/kolloch/crate2nix/issues/75): Cleanly separate internal API by `internal.` attribute path element. Formally, this is no breaking change if it only effects private API but still. I will mitigate by allowing the old paths for a release and issue a warning. Thank you to everyone who contributed with awesomely detailed issues, PRs or otherwise. You are amazing! Please let me know if I forgot something or forgot to give someone appropriate credit. For contributors: * `./run_tests.sh` now makes it easier to prepare your pull requests for review. * Build artifacts for linux are now properly pushed to [eigenvalue.cachix.org](https://eigenvalue.cachix.org/). Adding that cache with cachix will speed up your installations and builds. And it speeds up our CI builds via github actions. Shout out to @domenkozar and other cachix contributors. * @alyssais contributed some fixes to the developer scripts, thank you! Experimental and still undocumented: * `cargo test`-like test running support! Thank you very much for your great work, @andir! Heads up! Feel free to discuss these planned changes in future releases with me: * [#77](https://github.com/kolloch/crate2nix/issues/77): New/better override behavior that also allows overriding `buildRustCrate` more easily. * [#82](https://github.com/kolloch/crate2nix/issues/82): Use a new file name for `crate-hashes.json` every time to prevent merge issues. * [#102](https://github.com/kolloch/crate2nix/issues/102): Convenient support for out-of-tree sources (e.g. for nixpkgs) ## 0.6.0 - 0.6.1 Backported escaping fix for target expressions. ## 0.5.1 - 0.6.0 * [Issue #22](https://github.com/kolloch/crate2nix/issues/22) - Support renamed crates. Fixed in `buildRustCrate` in nixpkgs and in `crate2nix` by PR #24 @danieldk, thanks a lot! This is especially awesome because the popular `rand` crate recently made use of the "renamed crates" feature and therefore could not be build by `buildRustCrate`/`crate2nix` anymore. * [Issue #15](https://github.com/kolloch/crate2nix/issues/15) - Support "overrideCrates" argument for modifying the derivation for a crate by name. Common use case, adding additional buildInputs. NixOS comes with `[defaultCrateOverrides](https://github.com/NixOS/nixpkgs/blob/master/pkgs/build-support/rust/default-crate-overrides.nix)` for some common packages already. This is what I use in `default.nix` of `crate2nix` to add a conditional dependency under Mac OS: ```nix cargo_nix.rootCrate.build.override { crateOverrides = defaultCrateOverrides // { cssparser-macros = attrs: { buildInputs = stdenv.lib.optionals stdenv.isDarwin [darwin.apple_sdk.frameworks.Security]; }; }; }; ``` Many thanks to @Profpatsch for pointing to the problem and analyzing it. * [Issue #43](https://github.com/kolloch/crate2nix/issues/43) - Support conditional dependencies using "test" configuration: ```toml [target.'cfg(test)'.dependencies] tracing = { version = "0.1.5", features = ["log"] } ``` When building with `crate2nix`, dependencies will not lead to an error anymore and will simply be ignored (since we do not have test support yet). Thanks to @andir for the nice minimal example! Infrastructure: * I moved the integration tests to tests.nix, they were in rust code before. * I also now build every push with github actions, and cachix/cachix-action. A suggestion from @vbrandl in #44. Unfortunately, the rust crates are not cached yet, I think, because they are not in the closure of the result. The cachix caches is called "eigenvalue" for now (I might change that in the future). ## 0.5.0 - 0.5.1 Don't use ´Cargo.toml´ but ´Cargo.nix´ as default output! Thank you, @tilpner! ## 0.4.0 - 0.5.0 ### Upgrading * The default output file changed to "./Cargo.toml". Specify "-o ./default.nix" explicitly to retain the old default. ### Resolved issues * [Issue #10](https://github.com/kolloch/crate2nix/issues/10) - Changing default for `-n`/`--nixpkgs-path` to `""` so that it works by default on NixOS AND Mac OS. * [Issue #11](https://github.com/kolloch/crate2nix/issues/11) - Adding optional dependencies if any of their features is enabled. * [Issue #14](https://github.com/kolloch/crate2nix/issues/14) - Only overwrite output if explicitly specified as output file. ## 0.3.1 - 0.4.0 ### Upgrading Please change references to `root_crate` to `rootCrate.build` and references to `workspace_members.${crateName}` to `workspaceMembers.${crateName}.build`. The camel case attribute names are in line with the nixos style guide. The `.build` suffix allows future versions of `crate2nix` to add other convenient features such as source tarball packages, docker image derivations, ... The old aliases still work but are deprecated. ### Dynamic feature resolution The enabled features for a crate now are resolved at build time! That means you can easily override them: 1. There is a "rootFeatures" argument to the generated build file which you can override when calling it from the command line: ```bash nix build -f ....nix --arg rootFeatures '["default" "other"]' rootCrate.build ``` 2. Or when importing the build file with "callPackage": ```nix let cargo_nix = callPackage ./Cargo.nix { rootFeatures = ["default" "other"]; }; crate2nix = cargo_nix.rootCrate.build; in ...; ``` 3. Or by overriding them on the rootCrate or workspaceMembers: ```nix let cargo_nix = callPackage ./Cargo.nix {}; crate2nix = cargo_nix.rootCrate.build.override { features = ["default" "other"]; }; in ...; ``` ### Internal: nix test runner For this release, I needed substantial amount of nix code so I created some nix unit tests. They are invoked by `cargo test` like all other tests and live in the [./templates/nix/crate2nix/tests](./templates/nix/crate2nix/tests) directory. ### Feedback: What is needed for a 1.0 release? I would really appreciate your thoughts. Please add comments to issue [#8](https://github.com/kolloch/crate2nix/issues/8). ## 0.3.0 - 0.3.1 ### Bugfixes * Issue [#5](https://github.com/kolloch/crate2nix/issues/5): Support `libPath` for proc-macro crates. Thank you @nuxeh for reporting this bug! Again :) ## 0.2.1 - 0.3.0 ### Bugfixes * Issue [#4](https://github.com/kolloch/crate2nix/issues/4): Support for `libName` != `crateName`. Thank you @nuxeh for reporting this bug! ### Support for dependencies with git sources Example: ```toml [dependencies] "crate2nix" = { git = "https://github.com/kolloch/crate2nix" } ``` ## 0.2.0 - 0.2.1 * Added comments to the generated nix build file to indicate which attributes are public and unlikely to change. ## 0.1.0 - 0.2.0 ### Bugfixes * While the command line help said that the "crate hashes" would be stored in a file called "crate-hashes.json", it actually used the file "crate_hashes.json" by default. This release uses the documented name which means that after the update `nix-prefetch-url` might run again. * Issue [#3](https://github.com/kolloch/crate2nix/issues/3): Do not depend on local channel configuration for shell anymore. Instead, we use a recent nixos-unstable because we still need a fix that's not in stable. ### Workspace Support If `crate2nix` is applied to a workspace, the resulting nix-file will contain a top-level "workspace_members" attribute set that refers the corresponding top-level crate derivations by name. ### Target-specific dependencies "cfg(...)" expressions and target triplets such as "i686-pc-windows-gnu" are compiled into nix expressions. Support should be reasonable but incomplete because e.g. it does not work for processor features. Please let me know if this causes problems for you! ## 0.1.0 Initial public release. ================================================ FILE: docs/src/content/docs/index.mdx ================================================ --- title: Welcome to crate2nix description: What is crate2nix? --- import { Card, CardGrid } from '@astrojs/starlight/components'; import { Icon } from '@astrojs/starlight/components'; `crate2nix` builds your [cargo](https://crates.io/)-based [rust](https://www.rust-lang.org/) project crate-by-crate with [nix](https://nixos.org/nix/). You can * save time by only rebuilding changed crates hermetically in CI, and * use `cargo`/`rust-analyzer` locally for a fast developing loop. **Dependency tree from cargo**: It uses [cargo_metadata](https://github.com/oli-obk/cargo_metadata) to obtain the dependency tree from cargo. Therefore, it will use the exact same library versions as cargo and respect any locked down version in `Cargo.lock`. **Smart caching**: It caches the builds of individual crates so that nix rebuilds exactly the crates that need to be rebuilt. Compare that to docker layers... **Nix ecosystem goodness**: You can use all things that make the nix/NixOS ecosystem great, e.g. distributed/remote builds, build minimal docker images, deploy your binary as a service to the cloud, ... **Out of the box support for libraries with non-rust dependencies**: It builds on top of the `buildRustCrate` function from [NixOS](https://nixos.org/) so that native dependencies of many rust libraries are already correctly fetched when needed. If your library with native dependencies is not yet supported, you can customize `defaultCrateOverrides` / `crateOverrides`, see below. **Optional Import From Derivation**: Optional ability to generate the derived `Cargo.nix` during evaluation time so it does no need to be committed. **Easy to understand nix template**: The actual nix code is generated via `crate2nix/templates/build.nix.tera` so you can fix/improve the nix code without knowing rust if all the data is already there. ================================================ FILE: docs/src/env.d.ts ================================================ /// /// ================================================ FILE: docs/tsconfig.json ================================================ { "extends": "astro/tsconfigs/strict" } ================================================ FILE: flake.nix ================================================ { description = '' crate2nix generates [nix](https://nixos.org/nix/) build files for [rust](https://www.rust-lang.org/) crates using [cargo](https://crates.io/). ''; nixConfig = { extra-trusted-public-keys = "eigenvalue.cachix.org-1:ykerQDDa55PGxU25CETy9wF6uVDpadGGXYrFNJA3TUs="; extra-substituters = "https://eigenvalue.cachix.org"; allow-import-from-derivation = true; }; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; flake-compat.url = "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"; flake-parts = { url = "github:hercules-ci/flake-parts"; inputs.nixpkgs-lib.follows = "nixpkgs"; }; # Development devshell = { url = "github:numtide/devshell"; inputs.nixpkgs.follows = "nixpkgs"; }; nix-test-runner = { url = "github:stoeffel/nix-test-runner"; flake = false; }; cachix = { url = "github:cachix/cachix/latest"; # we only want the binary, so we don't do this # inputs.nixpkgs.follows = "nixpkgs"; inputs.devenv.follows = ""; inputs.flake-compat.follows = ""; }; pre-commit-hooks = { url = "github:cachix/pre-commit-hooks.nix"; inputs.nixpkgs.follows = "nixpkgs"; inputs.flake-compat.follows = "flake-compat"; }; }; outputs = inputs@{ nixpkgs, self, flake-parts, devshell, pre-commit-hooks, ... }: flake-parts.lib.mkFlake { inherit inputs; } { systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" ]; imports = [ ./nix/devshell/flake-module.nix ./nix/pre-commit/flake-module.nix ./nix/perSystem-tools/flake-module.nix ./crate2nix/flake-module.nix ./docs/flake-module.nix ]; flake = { lib, ... }: { config.templates = rec { default = flake-binary; flake-binary = { path = ./templates/flake-binary; description = "An example of crate2nix"; }; }; config.lib = { tools = import ./tools.nix; }; options.lib = lib.mkOption { description = '' nix libraries exported by crate2nix. ''; type = lib.types.submoduleWith { modules = [ { options.tools = lib.mkOption { description = '' Prefer the perSystem "tools" option which has the libary already applied for the correct system. Export the crate2nix/tools.nix function as property. To use it, call it with pkgs.callPackage. ''; type = lib.types.functionTo lib.types.attrs; }; } ]; }; }; }; perSystem = { config, pkgs, ... }: { formatter = pkgs.nixpkgs-fmt; checks = config.packages; }; }; } ================================================ FILE: lib/build-from-json.nix ================================================ # Build a Rust workspace from pre-resolved JSON (generated by crate2nix --format json). # # All dependency resolution, feature expansion, and optional dep activation is # done in Rust by crate2nix. This file wires the pre-resolved data to # buildRustCrate and does cheap platform filtering at eval time. # # Usage: # let # cargoNix = import ./build-from-json.nix { # inherit pkgs; # src = ./.; # resolvedJson = ./Cargo.json; # }; # in cargoNix.workspaceMembers.my-crate.build { pkgs ? import { } , lib ? pkgs.lib , stdenv ? pkgs.stdenv , # Workspace source root (must match the workspace that generated the JSON) src , # Path to the pre-resolved JSON file resolvedJson , # Optional: function to create buildRustCrate for a given pkgs buildRustCrateForPkgs ? pkgs: pkgs.buildRustCrate , # Optional: default crate overrides defaultCrateOverrides ? pkgs.defaultCrateOverrides , }: let resolved = builtins.fromJSON (builtins.readFile resolvedJson); # Source resolution: given a crate's source info, produce a src path. # Local paths in the JSON are relative to workspace root. resolveSrc = crateInfo: let source = crateInfo.source or null; sourceType = if source == null then "local" else source.type or "local"; relPath = if source == null then "." else source.path or "."; in if sourceType == "local" then if relPath == "." then src else src + "/${relPath}" else if sourceType == "crates-io" then pkgs.fetchurl { name = "${crateInfo.crateName}-${crateInfo.version}.tar.gz"; url = "https://static.crates.io/crates/${crateInfo.crateName}/${crateInfo.crateName}-${crateInfo.version}.crate"; sha256 = crateInfo.sha256; } else if sourceType == "git" then builtins.fetchGit { url = source.url; rev = source.rev; } else src; # Platform matching: evaluate cfg() strings against stdenv.hostPlatform. # This is the cheap O(deps) filtering that stays in Nix. platformMatches = platform: targetPlatform: let p = targetPlatform; rustTarget = p.rust.rustcTargetSpec or p.rust.rustcTarget or ""; in if platform == null then true else if lib.hasPrefix "cfg(" platform then evalCfg (lib.removePrefix "cfg(" (lib.removeSuffix ")" platform)) p else # Plain target triple platform == rustTarget; # Evaluate a cfg expression body (the part inside cfg(...)). # Handles: not(...), all(...), any(...), key = "value", name evalCfg = expr: p: let stripped = lib.strings.trim expr; in if lib.hasPrefix "not(" stripped then !(evalCfg (lib.removePrefix "not(" (lib.removeSuffix ")" stripped)) p) else if lib.hasPrefix "all(" stripped then lib.all (e: evalCfg e p) (splitCfgArgs (lib.removePrefix "all(" (lib.removeSuffix ")" stripped))) else if lib.hasPrefix "any(" stripped then lib.any (e: evalCfg e p) (splitCfgArgs (lib.removePrefix "any(" (lib.removeSuffix ")" stripped))) else if lib.hasInfix "=" stripped then let parts = lib.splitString "=" stripped; key = lib.strings.trim (builtins.head parts); rawVal = lib.strings.trim (lib.concatStringsSep "=" (builtins.tail parts)); val = lib.removePrefix "\"" (lib.removeSuffix "\"" rawVal); in evalCfgKeyValue key val p else # Bare name like "unix" or "windows" evalCfgName stripped p; evalCfgName = name: p: if name == "unix" then p.isUnix or false else if name == "windows" then p.isWindows or false else false; evalCfgKeyValue = key: val: p: if key == "target_os" then (p.parsed.kernel.name or "") == val || (val == "macos" && (p.parsed.kernel.name or "") == "darwin") || (val == "linux" && p.isLinux or false) || (val == "windows" && p.isWindows or false) else if key == "target_arch" then (p.parsed.cpu.name or "") == val else if key == "target_vendor" then (p.parsed.vendor.name or p.vendor or "") == val else if key == "target_env" then (p.parsed.abi.name or "") == val || (val == "gnu" && p.isGnu or false) || (val == "musl" && p.isMusl or false) else if key == "target_family" then (val == "unix" && p.isUnix or false) || (val == "windows" && p.isWindows or false) else if key == "target_endian" then (val == "little" && p.isLittleEndian or false) || (val == "big" && p.isBigEndian or false) else if key == "target_pointer_width" then (val == "64" && p.is64bit or false) || (val == "32" && p.is32bit or false) else false; # Split comma-separated cfg arguments, respecting parentheses nesting. splitCfgArgs = s: let chars = lib.stringToCharacters s; result = builtins.foldl' ( acc: c: if c == "(" then acc // { depth = acc.depth + 1; current = acc.current + c; } else if c == ")" then acc // { depth = acc.depth - 1; current = acc.current + c; } else if c == "," && acc.depth == 0 then acc // { parts = acc.parts ++ [ acc.current ]; current = ""; } else acc // { current = acc.current + c; } ) { parts = [ ]; current = ""; depth = 0; } chars; in result.parts ++ (if result.current != "" then [ result.current ] else [ ]); # Filter deps for the given platform filterDeps = deps: targetPlatform: builtins.filter ( dep: platformMatches (dep.target or null) targetPlatform ) deps; # Build a crate graph. When testRootPackageId is non-null, the crate with # that ID gets its devDependencies merged into dependencies and buildTests # set so buildRustCrate compiles test targets instead of lib/bin. mkBuiltByPackageIdByPkgs = { testRootPackageId ? null }: let go = cratePkgs: let buildRustCrate = let base = buildRustCrateForPkgs cratePkgs; in if defaultCrateOverrides != pkgs.defaultCrateOverrides then base.override { defaultCrateOverrides = defaultCrateOverrides; } else base; self = { crates = lib.mapAttrs ( packageId: _: buildCrate self cratePkgs buildRustCrate testRootPackageId packageId ) resolved.crates; build = go cratePkgs.buildPackages; }; in self; in go pkgs; buildCrate = self: cratePkgs: buildRustCrate: testRootPackageId: packageId: let crateInfo = resolved.crates.${packageId}; targetPlatform = cratePkgs.stdenv.hostPlatform; isTestRoot = testRootPackageId != null && packageId == testRootPackageId; # Resolve a regular dependency. Proc-macro crates must be built for # the build platform since they execute as compiler plugins. depDrv = dep: let depCrateInfo = resolved.crates.${dep.packageId} or null; in if depCrateInfo != null && (depCrateInfo.procMacro or false) then self.build.crates.${dep.packageId} else self.crates.${dep.packageId}; # Build-script dependencies run on the build platform, so all of # them must be built for that platform (not just proc-macros). buildDepDrv = dep: self.build.crates.${dep.packageId}; # Dev-deps only merge for the crate under test, not its transitive deps. # This mirrors the template mode's `packageId == rootPackageId` guard. devDeps = lib.optionals isTestRoot (crateInfo.devDependencies or [ ]); normalDeps = (crateInfo.dependencies or [ ]) ++ devDeps; dependencies = map depDrv (filterDeps normalDeps targetPlatform); buildDependencies = map buildDepDrv (filterDeps (crateInfo.buildDependencies or [ ]) targetPlatform); allDeps = filterDeps ( normalDeps ++ (crateInfo.buildDependencies or [ ]) ) targetPlatform; renamedDeps = lib.filter (d: d ? rename && d.rename != null) allDeps; crateRenames = let grouped = lib.groupBy (dep: dep.name) renamedDeps; versionAndRename = dep: { inherit (dep) rename; version = (resolved.crates.${dep.packageId}).version; }; in lib.mapAttrs (_name: builtins.map versionAndRename) grouped; crateSrc = resolveSrc crateInfo; in buildRustCrate ( { crateName = crateInfo.crateName; version = crateInfo.version; edition = crateInfo.edition or "2021"; sha256 = crateInfo.sha256 or ""; src = crateSrc; authors = crateInfo.authors or [ ]; inherit dependencies buildDependencies crateRenames; features = crateInfo.resolvedDefaultFeatures or [ ]; procMacro = crateInfo.procMacro or false; crateBin = crateInfo.crateBin or [ ]; } // lib.optionalAttrs isTestRoot { buildTests = true; } // lib.optionalAttrs ((crateInfo.build or null) != null) { build = crateInfo.build; } // lib.optionalAttrs ((crateInfo.libPath or null) != null) { libPath = crateInfo.libPath; } // lib.optionalAttrs ((crateInfo.libName or null) != null) { libName = crateInfo.libName; } // lib.optionalAttrs ((crateInfo.links or null) != null) { links = crateInfo.links; } // lib.optionalAttrs (crateInfo.libCrateTypes or [ ] != [ ]) { type = crateInfo.libCrateTypes; } ); builtCrates = mkBuiltByPackageIdByPkgs { }; # Builds the test binaries for a single workspace member, merging its # devDependencies into the dependency set. Transitive deps are shared # with the normal build graph since only the root crate differs. buildTestsFor = packageId: (mkBuiltByPackageIdByPkgs { testRootPackageId = packageId; }).crates.${packageId}; in { workspaceMembers = lib.mapAttrs ( name: packageId: { inherit packageId; build = builtCrates.crates.${packageId}; buildTests = buildTestsFor packageId; } ) resolved.workspaceMembers; rootCrate = if resolved.root != null then { packageId = resolved.root; build = builtCrates.crates.${resolved.root}; buildTests = buildTestsFor resolved.root; } else null; allWorkspaceMembers = pkgs.symlinkJoin { name = "all-workspace-members"; paths = lib.mapAttrsToList ( _name: packageId: builtCrates.crates.${packageId} ) resolved.workspaceMembers; }; inherit resolved; inherit builtCrates; } ================================================ FILE: nix/devshell/flake-module.nix ================================================ { inputs , ... }: { imports = [ inputs.devshell.flakeModule ]; config.perSystem = { config , system , inputs' , lib , pkgs , ... } @ perSystem: { devshells.default = { packages = with pkgs; [ nil jq niv coreutils gnugrep util-linux cacert ]; commands = with pkgs; [ { package = gitMinimal; } { package = prek; } { package = nixpkgs-fmt; category = "nix"; } { package = nix; category = "nix"; } { package = nix-prefetch-git; category = "nix"; } { inherit (callPackage ../nix-test-runner { }) package; name = "nix-test"; category = "nix"; help = "nix test runner for unit tests."; } { package = inputs'.cachix.packages.default; category = "nix"; } ]; env = [ { name = "IN_CRATE2NIX_SHELL"; value = "1"; } { name = "NIX_PATH"; value = "nixpkgs=${inputs.nixpkgs}"; } ]; devshell.startup.pre-commit.text = '' ${perSystem.config.pre-commit.installationScript} ''; }; }; } ================================================ FILE: nix/flakeInput.nix ================================================ # Get a locked flake input value that can be given to `builtins.fetchTree` or to # `builtins.getFlake`. For example, # # pkgs = import (builtins.fetchTree (flakeInput "nixpkgs")) { } # # This function can also be used to get inputs of inputs using dot-separated # paths. For example, # # pkgs = import (builtins.fetchTree (flakeInput "crate2nix_stable.nixpkgs")) { } # # Gets the nixpkgs input of the crate2nix_stable input. let flakeLock = builtins.fromJSON (builtins.readFile ../flake.lock); flakeInputNodeOf = parentNode: inputName: let inputNodeName = builtins.getAttr inputName parentNode.inputs; in builtins.getAttr inputNodeName flakeLock.nodes; rootNode = let rootName = flakeLock.root; in builtins.getAttr rootName flakeLock.nodes; in name: let parts = builtins.split "[.]" name; inputNames = builtins.filter builtins.isString parts; flakeNode = builtins.foldl' flakeInputNodeOf rootNode inputNames; in flakeNode.locked ================================================ FILE: nix/nix-test-runner/README.md ================================================ # nix-test-runner This is a mostly-vendored version of the nix source from [NixTest](https://github.com/stoeffel/nix-test-runner), mainly to work around the `lib ? (import { }).lib` argument in the upstream `runTests.nix`. ================================================ FILE: nix/nix-test-runner/default.nix ================================================ let # Use last stable crate2nix release to build the test runner so that it # works even if the current branch has broken stuff. # Pinned directly instead of via flake input to avoid bloating the # dependency graph (see https://github.com/nix-community/crate2nix/issues/371). crate2nix_stable = builtins.fetchTree { type = "github"; owner = "nix-community"; repo = "crate2nix"; rev = "7c33e664668faecf7655fa53861d7a80c9e464a2"; # 0.15.0 narHash = "sha256-SUuruvw1/moNzCZosHaa60QMTL+L9huWdsCBN6XZIic="; }; nixpkgs_stable = builtins.fetchTree { type = "github"; owner = "NixOS"; repo = "nixpkgs"; rev = "addf7cf5f383a3101ecfba091b98d0a1263dc9b8"; # crate2nix 0.15.0's nixpkgs narHash = "sha256-hM20uyap1a0M9d344I692r+ik4gTMyj60cQWO+hAYP8="; }; in { system ? builtins.currentSystem , pkgs ? import nixpkgs_stable { inherit system; } , crate2nixTools ? pkgs.callPackage "${crate2nix_stable}/tools.nix" { } , nix-test-runner ? pkgs.callPackage ./package.nix { inherit crate2nixTools; } , lib ? pkgs.lib }: rec { package = nix-test-runner; /* Runs your nix tests from a file or an expression and outputs a pretty diff if they fail. Type: runTests attrSet -> derivation Example: runTests { testFile = ./examples/failing.nix; } => returns a derivation that will show a failure diff. runTests { tests = { testFailed = { expr = builtins.add 1 1; expected = 1; }; }; } => returns a derivation that will show a failure diff. You need to pass one of the following arguments: testFile - the nix file to import that evaluates to the nix expressions. tests - the nix expression containing the tests. Takes precedence. Optional arguments: name - used in the derivation(s) produced (for the test results as JSON etc.). alwaysPretty - also print pretty results for passing tests. If there are no failures, returns a derivation with an empty output. */ runTests = { name ? if testFile != null then "nix-tests-${builtins.baseNameOf testFile}" else "nix-tests" , testFile ? null , tests ? import testFile , alwaysPretty ? false }: let result = testResult { inherit tests lib; }; debugTestOrigin = if testFile != null then "${name} imported from ${toString testFile}" else name; resultJson = pkgs.writeTextFile { name = "${name}-result.json"; text = builtins.toJSON result; }; failed = result.failed or [ ]; allGood = failed == [ ]; in if allGood then ( if alwaysPretty then pkgs.runCommandLocal "${name}-passed" { } '' echo -e "\e[1mPASSED\e[0m: ${debugTestOrigin}" touch $out '' else pkgs.runCommandLocal "${name}-passed" { } '' echo -e "\e[1mPASSED\e[0m: ${debugTestOrigin}" echo "" ( set -x ${nix-test-runner}/bin/nix-test --skip-run ${resultJson} | tee $out ) '' ) else pkgs.runCommandLocal "${name}-failed" { } '' echo -e "\e[1m\e[31mFAILED\e[0m: ${debugTestOrigin}" echo "" ( set -x ${nix-test-runner}/bin/nix-test --skip-run ${resultJson} ) ''; /* Returns the prettified test results as processed by nix-test-runner. */ testResult = import ./runTest.nix; } ================================================ FILE: nix/nix-test-runner/package.nix ================================================ let flakeInput = import ../flakeInput.nix; src = builtins.fetchTree (flakeInput "nix-test-runner"); in { pkgs , crate2nixTools }: let nixTestRunner = crate2nixTools.appliedCargoNix { name = "nix-test-runner"; inherit src; }; in nixTestRunner.rootCrate.build ================================================ FILE: nix/nix-test-runner/runTest.nix ================================================ { testFile ? null , tests ? import testFile , lib }: with builtins; let testNames = map (t: { passedTest = t; }) (attrNames tests); failed = map (t: { failedTest = t.name; expected = prettyVal t.expected; result = prettyVal t.result; }) (lib.debug.runTests tests); failedTests = map (f: f.failedTest) failed; passed = filter (t: !lib.elem t.passedTest failedTests) testNames; result = { inherit passed failed; }; prettyVal = let modify = v: let pr = f: { __pretty = f; val = v; }; in if lib.isDerivation v then pr (drv: "<δ:${drv.name}:${concatStringsSep "," (attrNames drv)}>") else if [ ] == v then pr (lib.const "[]") else if lib.isList v then pr (l: "[ ${toString (map go l)} ]") else if lib.isAttrs v then pr (a: "{ ${ concatStringsSep " " (lib.attrValues (lib.mapAttrs (n: v: "${n} = ${go v};") v)) } }") else v; go = x: lib.generators.toPretty { allowPrettyValues = true; } (modify x); in go; in result ================================================ FILE: nix/nixpkgs.nix ================================================ let flakeInput = import ./flakeInput.nix; in import (builtins.fetchTree (flakeInput "nixpkgs")) ================================================ FILE: nix/perSystem-tools/flake-module.nix ================================================ { # provider perSystem transposition.tools = { }; perSystem = { config, pkgs, lib, ... }: { options.tools = lib.mkOption { description = '' Library functions to generate the `Cargo.nix` build file automatically. ''; type = lib.types.submoduleWith { modules = [ { options.generatedCargoNix = lib.mkOption { description = '' Returns a derivation containing the generated `Cargo.nix` file which can be called with `pkgs.callPackage`. name: will be part of the derivation name src: the source that is needed to build the crate, usually the crate/workspace root directory cargoToml: Path to the Cargo.toml file relative to src, "Cargo.toml" by default. ''; type = lib.types.functionTo lib.types.package; }; options.appliedCargoNix = lib.mkOption { description = '' Applies the default arguments from pkgs to the generated `Cargo.nix` file. name: will be part of the derivation name src: the source that is needed to build the crate, usually the crate/workspace root directory cargoToml: Path to the Cargo.toml file relative to src, "Cargo.toml" by default. ''; type = lib.types.functionTo lib.types.attrs; }; } ]; }; }; config.tools = let tools = pkgs.callPackage ../../tools.nix { }; in { inherit (tools) generatedCargoNix appliedCargoNix; }; config.checks = { toolsGeneratedCargoNix_crate2nix = let cargoNixBuilder = config.tools.generatedCargoNix { name = "crate2nix"; src = ../../crate2nix; }; cargoNix = pkgs.callPackage cargoNixBuilder { }; in cargoNix.rootCrate.build; toolsAppliedCargoNix_crate2nix = let cargoNix = config.tools.appliedCargoNix { name = "crate2nix"; src = ../../crate2nix; }; in cargoNix.rootCrate.build; }; }; } ================================================ FILE: nix/pre-commit/flake-module.nix ================================================ { self, inputs, ... }: { imports = [ inputs.pre-commit-hooks.flakeModule ]; config.perSystem = { config , system , pkgs , ... } @ perSystem: { # https://github.com/cachix/pre-commit-hooks.nix/tree/master pre-commit = { check.enable = true; settings.package = pkgs.prek; settings.hooks = { # # lint shell scripts # # TODO: https://github.com/NixOS/nix/issues/8761 # shellcheck.enable = true; # # nix format # # TODO: need to preformat things accordingly and potentially use another formatter # nixpkgs-fmt.enable = true; }; }; }; } ================================================ FILE: nix-test.sh ================================================ #!/usr/bin/env bash # Executes the pinned nix-test-runner. # # Example: ./nix-test-runner.sh ./unit-tests.nix set -Eeuo pipefail top=$(dirname "$0") if [ -z "${IN_CRATE2NIX_SHELL:-}" ]; then echo "=== Entering $top/shell.nix" exec nix-shell --pure "$top/shell.nix" --run "$(printf "%q " $0 "$@")" fi nix-test "$@" ================================================ FILE: nixpkgs-fmt.sh ================================================ #!/usr/bin/env bash # Executes nixpkgs-fmt from the pinned nixpkgs # # Example: ./nixpkgs-fmt.sh ./tests.nix set -Eeuo pipefail top=$(dirname "$0") if [ -z "${IN_CRATE2NIX_SHELL:-}" ]; then echo "=== Entering $top/shell.nix" exec nix-shell --pure "$top/shell.nix" --run "$(printf "%q " $0 "$@")" fi nixpkgs-fmt "$@" ================================================ FILE: out-of-tree-sources.md ================================================ # Out of tree sources See [github page](https://nix-community.github.io/crate2nix/00_guides/71_generating_for_fetched_sources/). ================================================ FILE: regenerate_cargo_nix.sh ================================================ #!/usr/bin/env bash set -Eeuo pipefail top="$(readlink -f "$(dirname "$0")")" if [ -z "${IN_CRATE2NIX_SHELL:-}" ]; then exec nix-shell --extra-experimental-features flakes --pure "$top/shell.nix" --run "$(printf "%q " $0 "$@")" fi options=$(getopt -o '' --long offline,no-cargo-build -- "$@") [ $? -eq 0 ] || { echo "Incorrect options provided. Available:" echo " --offline Enable offline friendly operations with out substituters" echo " --no-cargo-build Skip local cargo build." >&2 exit 1 } eval set -- "$options" NIX_OPTIONS="--option log-lines 100 --show-trace" NO_CARGO_BUILD="" while true; do case "$1" in --no-cargo-build) NO_CARGO_BUILD=1 ;; --offline) NIX_OPTIONS="--option substitute false" ;; --) shift break ;; esac shift done echo "================ Regenerating ./Cargo.nix ==================" cd "${top}" function noisily { set -x "$@" { set +x; } 2>/dev/null return $? } if [ -z "${NO_CARGO_BUILD}" ]; then (cd crate2nix; noisily ../cargo.sh run -- generate -n ../nix/nixpkgs.nix \ -f ./Cargo.toml -o ./Cargo.nix) ||\ { echo "Bootstrap regeneration of ./Cargo.nix failed." >&2 ; exit 1; } else echo "Skipping because of --no-cargo-build" fi noisily nix-build --extra-experimental-features flakes --arg release false $NIX_OPTIONS crate2nix=$(nix-build --extra-experimental-features flakes --arg release false $NIX_OPTIONS)/bin/crate2nix noisily "$crate2nix" generate -n ../nix/nixpkgs.nix \ -f ./crate2nix/Cargo.toml -o ./crate2nix/Cargo.nix || \ { echo "Regeneration of ./Cargo.nix failed." >&2 ; exit 1; } nix-instantiate --extra-experimental-features flakes tests.nix --eval --strict --json -A buildTestConfigs | \ jq -r .[].pregeneratedBuild | \ while read cargo_nix; do if [ "$cargo_nix" = "null" ]; then continue fi dir=$(dirname "$cargo_nix") echo "=============== Regenerating ${cargo_nix} ================" noisily "$crate2nix" generate -f "$dir/Cargo.toml" -o "$cargo_nix" ||\ { echo "Regeneration of ${cargo_nix} failed." >&2 ; exit 1; } done ================================================ FILE: run_tests.sh ================================================ #!/usr/bin/env bash # # Regenerates build files and runs tests on them. # Please use this to validate your pull requests! # set -Eeuo pipefail top="$(dirname "$0")" top="$(cd "$top"; pwd)" if [ -z "${IN_CRATE2NIX_SHELL:-}" ] || [ "${IN_NIX_SHELL:-}" = "impure" ]; then export CACHIX CACHIX="$(which cachix 2>/dev/null || echo "")" echo -e "\e[1m=== Entering $top/shell.nix\e[0m" >&2 exec nix-shell --keep CACHIX --pure "$top/shell.nix" --run "$(printf "%q " "$0" "$@")" fi echo -e "\e[1m=== Parsing opts for $top/run_tests.sh\e[0m" >&2 options=$(getopt -o '' --long offline,build-test-nixpkgs:,no-cargo-build -- "$@") || { echo "" >&2 echo "Available options:" >&2 echo " --offline Enable offline friendly operations with out substituters" >&2 echo " --build-test-nixpkgs Path to nixpkgs used for the build tests." >&2 echo " --no-cargo-build Skip local cargo build." >&2 exit 1 } eval set -- "$options" NIX_OPTIONS=( --option log-lines 100 --show-trace ) REGENERATE_OPTIONS=() NIX_TESTS_OPTIONS=( --out-link ./target/nix-result ) NO_CARGO_BUILD="" while true; do case "$1" in --no-cargo-build) REGENERATE_OPTIONS+=( --no-cargo-build ) NO_CARGO_BUILD="1" ;; --offline) NIX_OPTIONS+=( --option substitute false ) REGENERATE_OPTIONS+=( --offline ) CACHIX="" ;; --build-test-nixpkgs) shift NIX_TESTS_OPTIONS+=( --arg buildTestNixpkgs "$1" ) ;; --) shift break ;; esac shift done # Add other files when we adopt nixpkgs-fmt for them. cd "$top" echo -e "\e[1m=== Reformatting nix code\e[0m" >&2 ./nixpkgs-fmt.sh \ ./{,nix/}{,*/}*.nix \ ./crate2nix/templates/nix/crate2nix/{*.nix,tests/*.nix} \ ./sample_projects/*/[[:lower:]]*.nix cd "$top"/crate2nix echo "=== Reformatting rust code" >&2 ../cargo.sh fmt cd "$top"/crate2nix echo -e "\e[1m=== Running cargo clippy\e[0m" >&2 if [ -z "${NO_CARGO_BUILD}" ]; then ../cargo.sh clippy || { echo "==================" >&2 echo "$top/cargo.sh clippy: FAILED" >&2 exit 2 } else echo "Skipping because of --no-cargo-build" fi ../regenerate_cargo_nix.sh "${REGENERATE_OPTIONS[@]}" || { echo "==================" >&2 echo "$top/regenerate_cargo_nix.sh ${REGENERATE_OPTIONS[*]}: FAILED" >&2 exit 3 } echo -e "\e[1m=== Running cargo test\e[0m" >&2 if [ -z "${NO_CARGO_BUILD}" ]; then ../cargo.sh test || { echo "==================" >&2 echo "$top/cargo.sh test: FAILED" >&2 exit 4 } else echo "Skipping because of --no-cargo-build" fi cd "$top" echo -e "\e[1m=== Running 'nix flake check' (= Running Integration Tests)\e[0m" >&2 rm -rf target/nix-result* nix flake check -L "${NIX_OPTIONS[@]}" || { echo "==================" >&2 echo "cd $top" >&2 echo "nix flake check -L \\" >&2 echo " ${NIX_OPTIONS[*]}" >&2 echo "=> FAILED" >&2 exit 5 } echo -e "\e[1m=== Checking for uncomitted changes\e[0m" >&2 if test -n "$(git status --porcelain)"; then echo "" git --no-pager diff HEAD echo "" git --no-pager diff --stat HEAD echo "" git status --porcelain echo "" echo "!!! repository has uncomitted changes" >&2 echo "Otherwise, things look good :)" exit 6 fi # Crude hack: check if we have the right to push to the cache cd "$top" if test -n "${CACHIX:-}" && test -r ~/.config/cachix/cachix.dhall &&\ grep -q '"eigenvalue"' ~/.config/cachix/cachix.dhall; then echo -e "\e[1m=== Pushing artifacts to eigenvalue.cachix.org \e[0m" >&2 # we filter for "rust_" to exclude some things that are in the # nixos cache anyways nix-store -q -R --include-outputs "$(nix-store -q -d target/nix-result* | grep -v crates.io)" |\ grep -e "-rust_" |\ $CACHIX push eigenvalue fi echo -e "\e[1m=== SUCCESS (run_tests.sh) \e[0m" >&2 ================================================ FILE: sample_projects/aliased-dependencies/Cargo.toml ================================================ [package] name = "aliased-dependencies" version = "0.1.0" edition = "2021" [dependencies] hello = { package = "ludndev-hello-world", git = "https://github.com/ludndev/rustlang-hello-world-lib.git", tag = "v0.1.1" } hello_v010 = { package = "ludndev-hello-world", git = "https://github.com/ludndev/rustlang-hello-world-lib", tag = "v0.1.0" } hello_path = { package = "ludndev-hello-world", path = "./hello" } ================================================ FILE: sample_projects/aliased-dependencies/crate-hashes.json ================================================ { "git+https://github.com/ludndev/rustlang-hello-world-lib.git?tag=v0.1.1#ludndev-hello-world@0.1.1": "0ayzrhha2kl636qy0v3k1m29rf9hwy6d9pfyv5v68rrlqfsqn954", "git+https://github.com/ludndev/rustlang-hello-world-lib?tag=v0.1.0#ludndev-hello-world@0.1.0": "0ab12fsfk45wasqnpv7xmcs59vgqy1i3a6ly16054q0mmnzwjz5x" } ================================================ FILE: sample_projects/aliased-dependencies/hello/Cargo.toml ================================================ [package] name = "ludndev-hello-world" version = "0.0.1" edition = "2021" ================================================ FILE: sample_projects/aliased-dependencies/hello/src/lib.rs ================================================ pub fn greet() -> String { "hello".to_string() } ================================================ FILE: sample_projects/aliased-dependencies/src/main.rs ================================================ pub fn main() { println!("{}", hello::greet("")); println!("{}", hello_v010::greet("")); println!("{}", hello_path::greet()); } ================================================ FILE: sample_projects/bin/Cargo.toml ================================================ [package] name = "hello_world_bin" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" [dependencies] [target.'cfg(never_happens)'.dependencies] cfg-if = "1" [target.'cfg(doesnt_exist = "should pass")'.dependencies] cfg-if = "1" ================================================ FILE: sample_projects/bin/crate-hashes.json ================================================ {} ================================================ FILE: sample_projects/bin/src/main.rs ================================================ fn main() { println!("Hello, world!"); } ================================================ FILE: sample_projects/bin_required_features/Cargo.toml ================================================ [package] name = "bin_required_features" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [[bin]] name = "bin_required_features" required-features = ["compilemainbinary"] [[bin]] name = "bin_not_to_be_compiled" required-features = ["afeature"] [features] afeature = [] compilemainbinary = [] ================================================ FILE: sample_projects/bin_required_features/src/bin/bin_not_to_be_compiled.rs ================================================ pub fn main() { compile_error!("This binary shouldn’t be compiled, as it depend on \"afeature\", which souldn't be enabled!"); } ================================================ FILE: sample_projects/bin_required_features/src/main.rs ================================================ fn main() { println!("Hello from bin_required_features default binary"); } ================================================ FILE: sample_projects/bin_with_default_features/Cargo.toml ================================================ [package] name = "bin_with_default_features" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" [dependencies] "hello_world_lib" = { path = "../lib", optional = true} [features] default = ["use_lib"] use_lib = ["hello_world_lib"] do_not_activate = [] ================================================ FILE: sample_projects/bin_with_default_features/crate-hashes.json ================================================ {} ================================================ FILE: sample_projects/bin_with_default_features/override-root-features.nix ================================================ { pkgs ? import ../../nix/nixpkgs.nix { config = { }; }, generatedCargoNix }: let basePackage = pkgs.callPackage generatedCargoNix { rootFeatures = [ "default" "do_not_activate" ]; }; build = basePackage.rootCrate.build; in build ================================================ FILE: sample_projects/bin_with_default_features/src/main.rs ================================================ #[cfg(feature = "use_lib")] use renamed_hello_world_lib; fn main() { #[cfg(feature = "do_not_activate")] let str = ", do_not_activate"; #[cfg(not(feature = "do_not_activate"))] let str = ""; #[cfg(feature = "use_lib")] renamed_hello_world_lib::hello_world( &format!("bin_with_default_features{}", str)); } ================================================ FILE: sample_projects/bin_with_dep_features/Cargo.toml ================================================ [package] name = "bin_with_dep_features" version = "0.1.0" authors = ["Faye Duxovni "] edition = "2018" [dependencies] "hello_world_lib" = { path = "../lib", optional = true } [features] default = ["use_lib"] use_lib = ["dep:hello_world_lib"] ================================================ FILE: sample_projects/bin_with_dep_features/src/main.rs ================================================ #[cfg(feature = "use_lib")] use renamed_hello_world_lib; fn main() { #[cfg(feature = "use_lib")] renamed_hello_world_lib::hello_world("bin_with_dep_features"); } ================================================ FILE: sample_projects/bin_with_git_branch_dep/Cargo.toml ================================================ [package] name = "bin_with_lib_git_dep" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" [dependencies.nix-base32] git = "https://github.com/kolloch/nix-base32" branch = "branch-for-test" ================================================ FILE: sample_projects/bin_with_git_branch_dep/crate-hashes.json ================================================ { "git+https://github.com/kolloch/nix-base32?branch=branch-for-test#0.1.2-alpha.0": "011f945b48xkilkqbvbsxazspz5z23ka0s90ms4jiqjbhiwll1nw" } ================================================ FILE: sample_projects/bin_with_git_branch_dep/src/main.rs ================================================ fn main() { nix_base32::to_nix_base32(&[1,2,3]); println!("Hello world from bin_with_git_branch_dep!"); } ================================================ FILE: sample_projects/bin_with_git_submodule_dep/Cargo.nix ================================================ # This file was @generated by crate2nix 0.15.0 with the command: # "generate" "-f" "sample_projects/bin_with_git_submodule_dep/Cargo.toml" "-o" "sample_projects/bin_with_git_submodule_dep/Cargo.nix" # See https://github.com/kolloch/crate2nix for more info. { nixpkgs ? , pkgs ? import nixpkgs { config = {}; } , fetchurl ? pkgs.fetchurl , lib ? pkgs.lib , stdenv ? pkgs.stdenv , buildRustCrateForPkgs ? pkgs: pkgs.buildRustCrate # This is used as the `crateOverrides` argument for `buildRustCrate`. , defaultCrateOverrides ? pkgs.defaultCrateOverrides # The features to enable for the root_crate or the workspace_members. , rootFeatures ? [ "default" ] # If true, throw errors instead of issueing deprecation warnings. , strictDeprecation ? false # Elements to add to the `-C target-feature=` argument passed to `rustc` # (separated by `,`, prefixed with `+`). # Used for conditional compilation based on CPU feature detection. , targetFeatures ? [] # Additional target attributes for conditional dependencies. # Use this for custom cfg flags that are passed via rustcflags but need to # be known at Nix evaluation time for dependency resolution. # Example: { tracing_unstable = true; } for crates using cfg(tracing_unstable). , extraTargetFlags ? {} # Whether to perform release builds: longer compile times, faster binaries. , release ? true # Additional crate2nix configuration if it exists. , crateConfig ? if builtins.pathExists ./crate-config.nix then pkgs.callPackage ./crate-config.nix {} else {} }: rec { # # "public" attributes that we attempt to keep stable with new versions of crate2nix. # rootCrate = rec { packageId = "bin_with_git_submodule_dep"; # Use this attribute to refer to the derivation building your root crate package. # You can override the features with rootCrate.build.override { features = [ "default" "feature1" ... ]; }. build = internal.buildRustCrateWithFeatures { inherit packageId; }; # Debug support which might change between releases. # File a bug if you depend on any for non-debug work! debug = internal.debugCrate { inherit packageId; }; }; # Refer your crate build derivation by name here. # You can override the features with # workspaceMembers."${crateName}".build.override { features = [ "default" "feature1" ... ]; }. workspaceMembers = { "bin_with_git_submodule_dep" = rec { packageId = "bin_with_git_submodule_dep"; build = internal.buildRustCrateWithFeatures { packageId = "bin_with_git_submodule_dep"; }; # Debug support which might change between releases. # File a bug if you depend on any for non-debug work! debug = internal.debugCrate { inherit packageId; }; }; }; # A derivation that joins the outputs of all workspace members together. allWorkspaceMembers = pkgs.symlinkJoin { name = "all-workspace-members"; paths = let members = builtins.attrValues workspaceMembers; in builtins.map (m: m.build) members; }; # # "internal" ("private") attributes that may change in every new version of crate2nix. # internal = rec { # Build and dependency information for crates. # Many of the fields are passed one-to-one to buildRustCrate. # # Noteworthy: # * `dependencies`/`buildDependencies`: similar to the corresponding fields for buildRustCrate. # but with additional information which is used during dependency/feature resolution. # * `resolvedDependencies`: the selected default features reported by cargo - only included for debugging. # * `devDependencies` as of now not used by `buildRustCrate` but used to # inject test dependencies into the build crates = { "aho-corasick" = rec { crateName = "aho-corasick"; version = "1.1.2"; edition = "2021"; sha256 = "1w510wnixvlgimkx1zjbvlxh6xps2vjgfqgwf5a6adlbjp5rv5mj"; libName = "aho_corasick"; authors = [ "Andrew Gallant " ]; dependencies = [ { name = "memchr"; packageId = "memchr"; optional = true; usesDefaultFeatures = false; } ]; features = { "default" = [ "std" "perf-literal" ]; "logging" = [ "dep:log" ]; "perf-literal" = [ "dep:memchr" ]; "std" = [ "memchr?/std" ]; }; resolvedDefaultFeatures = [ "default" "perf-literal" "std" ]; }; "bin_with_git_submodule_dep" = rec { crateName = "bin_with_git_submodule_dep"; version = "0.1.0"; edition = "2018"; crateBin = [ { name = "bin_with_git_submodule_dep"; path = "src/main.rs"; requiredFeatures = [ ]; } ]; src = lib.cleanSourceWith { filter = sourceFilter; src = ./.; }; authors = [ "Phillip Cloud " ]; dependencies = [ { name = "rocksdb"; packageId = "rocksdb"; usesDefaultFeatures = false; } ]; }; "bindgen" = rec { crateName = "bindgen"; version = "0.69.1"; edition = "2018"; sha256 = "1hkrccfri0223b2r5cvacy83ld6s76n2m68518bsfilrhk1ypz4z"; libPath = "lib.rs"; authors = [ "Jyun-Yan You " "Emilio Cobos Álvarez " "Nick Fitzgerald " "The Servo project developers" ]; dependencies = [ { name = "bitflags"; packageId = "bitflags"; } { name = "cexpr"; packageId = "cexpr"; } { name = "clang-sys"; packageId = "clang-sys"; features = [ "clang_6_0" ]; } { name = "lazy_static"; packageId = "lazy_static"; } { name = "lazycell"; packageId = "lazycell"; } { name = "peeking_take_while"; packageId = "peeking_take_while"; } { name = "proc-macro2"; packageId = "proc-macro2"; usesDefaultFeatures = false; } { name = "quote"; packageId = "quote"; usesDefaultFeatures = false; } { name = "regex"; packageId = "regex"; usesDefaultFeatures = false; features = [ "std" "unicode" ]; } { name = "rustc-hash"; packageId = "rustc-hash"; } { name = "shlex"; packageId = "shlex"; } { name = "syn"; packageId = "syn"; features = [ "full" "extra-traits" "visit-mut" ]; } ]; features = { "default" = [ "logging" "prettyplease" "runtime" "which-rustfmt" ]; "experimental" = [ "dep:annotate-snippets" ]; "logging" = [ "dep:log" ]; "prettyplease" = [ "dep:prettyplease" ]; "runtime" = [ "clang-sys/runtime" ]; "static" = [ "clang-sys/static" ]; "which-rustfmt" = [ "dep:which" ]; }; resolvedDefaultFeatures = [ "runtime" ]; }; "bitflags" = rec { crateName = "bitflags"; version = "2.4.1"; edition = "2021"; sha256 = "01ryy3kd671b0ll4bhdvhsz67vwz1lz53fz504injrd7wpv64xrj"; authors = [ "The Rust Project Developers" ]; features = { "arbitrary" = [ "dep:arbitrary" ]; "bytemuck" = [ "dep:bytemuck" ]; "compiler_builtins" = [ "dep:compiler_builtins" ]; "core" = [ "dep:core" ]; "rustc-dep-of-std" = [ "core" "compiler_builtins" ]; "serde" = [ "dep:serde" ]; }; }; "bzip2-sys" = rec { crateName = "bzip2-sys"; version = "0.1.11+1.0.8"; edition = "2015"; links = "bzip2"; sha256 = "1p2crnv8d8gpz5c2vlvzl0j55i3yqg5bi0kwsl1531x77xgraskk"; libName = "bzip2_sys"; libPath = "lib.rs"; authors = [ "Alex Crichton " ]; dependencies = [ { name = "libc"; packageId = "libc"; } ]; buildDependencies = [ { name = "cc"; packageId = "cc"; } { name = "pkg-config"; packageId = "pkg-config"; } ]; features = { }; resolvedDefaultFeatures = [ "static" ]; }; "cc" = rec { crateName = "cc"; version = "1.0.83"; edition = "2018"; crateBin = []; sha256 = "1l643zidlb5iy1dskc5ggqs4wqa29a02f44piczqc8zcnsq4y5zi"; authors = [ "Alex Crichton " ]; dependencies = [ { name = "jobserver"; packageId = "jobserver"; optional = true; } { name = "libc"; packageId = "libc"; usesDefaultFeatures = false; target = { target, features }: (target."unix" or false); } ]; features = { "jobserver" = [ "dep:jobserver" ]; "parallel" = [ "jobserver" ]; }; resolvedDefaultFeatures = [ "jobserver" "parallel" ]; }; "cexpr" = rec { crateName = "cexpr"; version = "0.6.0"; edition = "2018"; sha256 = "0rl77bwhs5p979ih4r0202cn5jrfsrbgrksp40lkfz5vk1x3ib3g"; authors = [ "Jethro Beekman " ]; dependencies = [ { name = "nom"; packageId = "nom"; usesDefaultFeatures = false; features = [ "std" ]; } ]; }; "cfg-if" = rec { crateName = "cfg-if"; version = "1.0.0"; edition = "2018"; sha256 = "1za0vb97n4brpzpv8lsbnzmq5r8f2b0cpqqr0sy8h5bn751xxwds"; libName = "cfg_if"; authors = [ "Alex Crichton " ]; features = { "compiler_builtins" = [ "dep:compiler_builtins" ]; "core" = [ "dep:core" ]; "rustc-dep-of-std" = [ "core" "compiler_builtins" ]; }; }; "clang-sys" = rec { crateName = "clang-sys"; version = "1.7.0"; edition = "2015"; links = "clang"; sha256 = "1lb9ffil7bidvpsfg38wkkfj55946v82ia07ss4ikkp39cxkllk7"; libName = "clang_sys"; authors = [ "Kyle Mayes " ]; dependencies = [ { name = "glob"; packageId = "glob"; } { name = "libc"; packageId = "libc"; usesDefaultFeatures = false; } { name = "libloading"; packageId = "libloading"; optional = true; } ]; buildDependencies = [ { name = "glob"; packageId = "glob"; } ]; devDependencies = [ { name = "glob"; packageId = "glob"; } ]; features = { "clang_10_0" = [ "clang_9_0" ]; "clang_11_0" = [ "clang_10_0" ]; "clang_12_0" = [ "clang_11_0" ]; "clang_13_0" = [ "clang_12_0" ]; "clang_14_0" = [ "clang_13_0" ]; "clang_15_0" = [ "clang_14_0" ]; "clang_16_0" = [ "clang_15_0" ]; "clang_17_0" = [ "clang_16_0" ]; "clang_3_6" = [ "clang_3_5" ]; "clang_3_7" = [ "clang_3_6" ]; "clang_3_8" = [ "clang_3_7" ]; "clang_3_9" = [ "clang_3_8" ]; "clang_4_0" = [ "clang_3_9" ]; "clang_5_0" = [ "clang_4_0" ]; "clang_6_0" = [ "clang_5_0" ]; "clang_7_0" = [ "clang_6_0" ]; "clang_8_0" = [ "clang_7_0" ]; "clang_9_0" = [ "clang_8_0" ]; "libloading" = [ "dep:libloading" ]; "runtime" = [ "libloading" ]; }; resolvedDefaultFeatures = [ "clang_3_5" "clang_3_6" "clang_3_7" "clang_3_8" "clang_3_9" "clang_4_0" "clang_5_0" "clang_6_0" "libloading" "runtime" ]; }; "glob" = rec { crateName = "glob"; version = "0.3.1"; edition = "2015"; sha256 = "16zca52nglanv23q5qrwd5jinw3d3as5ylya6y1pbx47vkxvrynj"; authors = [ "The Rust Project Developers" ]; }; "jobserver" = rec { crateName = "jobserver"; version = "0.1.27"; edition = "2018"; sha256 = "0z9w6vfqwbr6hfk9yaw7kydlh6f7k39xdlszxlh39in4acwzcdwc"; authors = [ "Alex Crichton " ]; dependencies = [ { name = "libc"; packageId = "libc"; target = { target, features }: (target."unix" or false); } ]; }; "lazy_static" = rec { crateName = "lazy_static"; version = "1.4.0"; edition = "2015"; sha256 = "0in6ikhw8mgl33wjv6q6xfrb5b9jr16q8ygjy803fay4zcisvaz2"; authors = [ "Marvin Löbel " ]; features = { "spin" = [ "dep:spin" ]; "spin_no_std" = [ "spin" ]; }; }; "lazycell" = rec { crateName = "lazycell"; version = "1.3.0"; edition = "2015"; sha256 = "0m8gw7dn30i0zjjpjdyf6pc16c34nl71lpv461mix50x3p70h3c3"; authors = [ "Alex Crichton " "Nikita Pekin " ]; features = { "clippy" = [ "dep:clippy" ]; "nightly-testing" = [ "clippy" "nightly" ]; "serde" = [ "dep:serde" ]; }; }; "libc" = rec { crateName = "libc"; version = "0.2.152"; edition = "2015"; sha256 = "1rsnma7hnw22w7jh9yqg43slddvfbnfzrvm3s7s4kinbj1jvzqqk"; authors = [ "The Rust Project Developers" ]; features = { "default" = [ "std" ]; "rustc-dep-of-std" = [ "align" "rustc-std-workspace-core" ]; "rustc-std-workspace-core" = [ "dep:rustc-std-workspace-core" ]; "use_std" = [ "std" ]; }; resolvedDefaultFeatures = [ "default" "std" ]; }; "libloading" = rec { crateName = "libloading"; version = "0.8.1"; edition = "2015"; sha256 = "0q812zvfag4m803ak640znl6cf8ngdd0ilzky498r6pwvmvbcwf5"; authors = [ "Simonas Kazlauskas " ]; dependencies = [ { name = "cfg-if"; packageId = "cfg-if"; target = { target, features }: (target."unix" or false); } { name = "windows-sys"; packageId = "windows-sys"; target = { target, features }: (target."windows" or false); features = [ "Win32_Foundation" "Win32_System_Diagnostics_Debug" "Win32_System_LibraryLoader" ]; } ]; }; "librocksdb-sys" = rec { crateName = "librocksdb-sys"; version = "0.15.0+8.9.1"; edition = "2018"; links = "rocksdb"; workspace_member = null; src = pkgs.fetchgit { url = "https://github.com/rust-rocksdb/rust-rocksdb"; rev = "66f04df013b6e6bd42b5a8c353406e09a7c7da2a"; sha256 = "1rchvjrjamdaznx26gy4bmjj10rrf00mgc1wvkc489r9z1nh4h1h"; }; libName = "librocksdb_sys"; authors = [ "Karl Hobley " "Arkadiy Paronyan " ]; dependencies = [ { name = "bzip2-sys"; packageId = "bzip2-sys"; optional = true; usesDefaultFeatures = false; } { name = "libc"; packageId = "libc"; } { name = "libz-sys"; packageId = "libz-sys"; optional = true; usesDefaultFeatures = false; } ]; buildDependencies = [ { name = "bindgen"; packageId = "bindgen"; usesDefaultFeatures = false; features = [ "runtime" ]; } { name = "cc"; packageId = "cc"; features = [ "parallel" ]; } { name = "glob"; packageId = "glob"; } ]; features = { "bzip2" = [ "bzip2-sys" ]; "bzip2-sys" = [ "dep:bzip2-sys" ]; "default" = [ "static" ]; "io-uring" = [ "pkg-config" ]; "jemalloc" = [ "tikv-jemalloc-sys" ]; "libz-sys" = [ "dep:libz-sys" ]; "lz4" = [ "lz4-sys" ]; "lz4-sys" = [ "dep:lz4-sys" ]; "pkg-config" = [ "dep:pkg-config" ]; "static" = [ "libz-sys?/static" "bzip2-sys?/static" ]; "tikv-jemalloc-sys" = [ "dep:tikv-jemalloc-sys" ]; "zlib" = [ "libz-sys" ]; "zstd" = [ "zstd-sys" ]; "zstd-sys" = [ "dep:zstd-sys" ]; }; resolvedDefaultFeatures = [ "default" "static" ]; }; "libz-sys" = rec { crateName = "libz-sys"; version = "1.1.12"; edition = "2018"; links = "z"; sha256 = "0yqahz2m5g44mpgfdy0k53hpfkfs5rfiv3a1y7p766ijbsr3fwfr"; libName = "libz_sys"; authors = [ "Alex Crichton " "Josh Triplett " "Sebastian Thiel " ]; buildDependencies = [ { name = "cc"; packageId = "cc"; } { name = "pkg-config"; packageId = "pkg-config"; } { name = "vcpkg"; packageId = "vcpkg"; } ]; features = { "cmake" = [ "dep:cmake" ]; "default" = [ "libc" "stock-zlib" ]; "libc" = [ "dep:libc" ]; "zlib-ng" = [ "libc" "cmake" ]; }; resolvedDefaultFeatures = [ "static" ]; }; "memchr" = rec { crateName = "memchr"; version = "2.7.1"; edition = "2021"; sha256 = "0jf1kicqa4vs9lyzj4v4y1p90q0dh87hvhsdd5xvhnp527sw8gaj"; authors = [ "Andrew Gallant " "bluss" ]; features = { "compiler_builtins" = [ "dep:compiler_builtins" ]; "core" = [ "dep:core" ]; "default" = [ "std" ]; "logging" = [ "dep:log" ]; "rustc-dep-of-std" = [ "core" "compiler_builtins" ]; "std" = [ "alloc" ]; "use_std" = [ "std" ]; }; resolvedDefaultFeatures = [ "alloc" "default" "std" ]; }; "minimal-lexical" = rec { crateName = "minimal-lexical"; version = "0.2.1"; edition = "2018"; sha256 = "16ppc5g84aijpri4jzv14rvcnslvlpphbszc7zzp6vfkddf4qdb8"; libName = "minimal_lexical"; authors = [ "Alex Huszagh " ]; features = { "default" = [ "std" ]; }; resolvedDefaultFeatures = [ "std" ]; }; "nom" = rec { crateName = "nom"; version = "7.1.3"; edition = "2018"; sha256 = "0jha9901wxam390jcf5pfa0qqfrgh8li787jx2ip0yk5b8y9hwyj"; authors = [ "contact@geoffroycouprie.com" ]; dependencies = [ { name = "memchr"; packageId = "memchr"; usesDefaultFeatures = false; } { name = "minimal-lexical"; packageId = "minimal-lexical"; usesDefaultFeatures = false; } ]; features = { "default" = [ "std" ]; "std" = [ "alloc" "memchr/std" "minimal-lexical/std" ]; }; resolvedDefaultFeatures = [ "alloc" "std" ]; }; "peeking_take_while" = rec { crateName = "peeking_take_while"; version = "0.1.2"; edition = "2015"; sha256 = "16bhqr6rdyrp12zv381cxaaqqd0pwysvm1q8h2ygihvypvfprc8r"; authors = [ "Nick Fitzgerald " ]; }; "pkg-config" = rec { crateName = "pkg-config"; version = "0.3.28"; edition = "2015"; sha256 = "16kgffwncx5hsppsdf54z6jnjkhwywqy601cxk3rqncyi9zmilv9"; libName = "pkg_config"; authors = [ "Alex Crichton " ]; }; "proc-macro2" = rec { crateName = "proc-macro2"; version = "1.0.76"; edition = "2021"; sha256 = "136cp0fgl6rg5ljm3b1xpc0bn0lyvagzzmxvbxgk5hxml36mdz4m"; libName = "proc_macro2"; authors = [ "David Tolnay " "Alex Crichton " ]; dependencies = [ { name = "unicode-ident"; packageId = "unicode-ident"; } ]; features = { "default" = [ "proc-macro" ]; }; resolvedDefaultFeatures = [ "proc-macro" ]; }; "quote" = rec { crateName = "quote"; version = "1.0.35"; edition = "2018"; sha256 = "1vv8r2ncaz4pqdr78x7f138ka595sp2ncr1sa2plm4zxbsmwj7i9"; authors = [ "David Tolnay " ]; dependencies = [ { name = "proc-macro2"; packageId = "proc-macro2"; usesDefaultFeatures = false; } ]; features = { "default" = [ "proc-macro" ]; "proc-macro" = [ "proc-macro2/proc-macro" ]; }; resolvedDefaultFeatures = [ "proc-macro" ]; }; "regex" = rec { crateName = "regex"; version = "1.10.2"; edition = "2021"; sha256 = "0hxkd814n4irind8im5c9am221ri6bprx49nc7yxv02ykhd9a2rq"; authors = [ "The Rust Project Developers" "Andrew Gallant " ]; dependencies = [ { name = "aho-corasick"; packageId = "aho-corasick"; optional = true; } { name = "memchr"; packageId = "memchr"; optional = true; } { name = "regex-automata"; packageId = "regex-automata"; usesDefaultFeatures = false; features = [ "alloc" "syntax" "meta" "nfa-pikevm" ]; } { name = "regex-syntax"; packageId = "regex-syntax"; usesDefaultFeatures = false; } ]; features = { "default" = [ "std" "perf" "unicode" "regex-syntax/default" ]; "logging" = [ "aho-corasick?/logging" "memchr?/logging" "regex-automata/logging" ]; "perf" = [ "perf-cache" "perf-dfa" "perf-onepass" "perf-backtrack" "perf-inline" "perf-literal" ]; "perf-backtrack" = [ "regex-automata/nfa-backtrack" ]; "perf-dfa" = [ "regex-automata/hybrid" ]; "perf-dfa-full" = [ "regex-automata/dfa-build" "regex-automata/dfa-search" ]; "perf-inline" = [ "regex-automata/perf-inline" ]; "perf-literal" = [ "dep:aho-corasick" "dep:memchr" "regex-automata/perf-literal" ]; "perf-onepass" = [ "regex-automata/dfa-onepass" ]; "std" = [ "aho-corasick?/std" "memchr?/std" "regex-automata/std" "regex-syntax/std" ]; "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "regex-automata/unicode" "regex-syntax/unicode" ]; "unicode-age" = [ "regex-automata/unicode-age" "regex-syntax/unicode-age" ]; "unicode-bool" = [ "regex-automata/unicode-bool" "regex-syntax/unicode-bool" ]; "unicode-case" = [ "regex-automata/unicode-case" "regex-syntax/unicode-case" ]; "unicode-gencat" = [ "regex-automata/unicode-gencat" "regex-syntax/unicode-gencat" ]; "unicode-perl" = [ "regex-automata/unicode-perl" "regex-automata/unicode-word-boundary" "regex-syntax/unicode-perl" ]; "unicode-script" = [ "regex-automata/unicode-script" "regex-syntax/unicode-script" ]; "unicode-segment" = [ "regex-automata/unicode-segment" "regex-syntax/unicode-segment" ]; "unstable" = [ "pattern" ]; "use_std" = [ "std" ]; }; resolvedDefaultFeatures = [ "std" "unicode" "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ]; }; "regex-automata" = rec { crateName = "regex-automata"; version = "0.4.3"; edition = "2021"; sha256 = "0gs8q9yhd3kcg4pr00ag4viqxnh5l7jpyb9fsfr8hzh451w4r02z"; libName = "regex_automata"; authors = [ "The Rust Project Developers" "Andrew Gallant " ]; dependencies = [ { name = "aho-corasick"; packageId = "aho-corasick"; optional = true; usesDefaultFeatures = false; } { name = "memchr"; packageId = "memchr"; optional = true; usesDefaultFeatures = false; } { name = "regex-syntax"; packageId = "regex-syntax"; optional = true; usesDefaultFeatures = false; } ]; features = { "default" = [ "std" "syntax" "perf" "unicode" "meta" "nfa" "dfa" "hybrid" ]; "dfa" = [ "dfa-build" "dfa-search" "dfa-onepass" ]; "dfa-build" = [ "nfa-thompson" "dfa-search" ]; "dfa-onepass" = [ "nfa-thompson" ]; "hybrid" = [ "alloc" "nfa-thompson" ]; "internal-instrument" = [ "internal-instrument-pikevm" ]; "internal-instrument-pikevm" = [ "logging" "std" ]; "logging" = [ "dep:log" "aho-corasick?/logging" "memchr?/logging" ]; "meta" = [ "syntax" "nfa-pikevm" ]; "nfa" = [ "nfa-thompson" "nfa-pikevm" "nfa-backtrack" ]; "nfa-backtrack" = [ "nfa-thompson" ]; "nfa-pikevm" = [ "nfa-thompson" ]; "nfa-thompson" = [ "alloc" ]; "perf" = [ "perf-inline" "perf-literal" ]; "perf-literal" = [ "perf-literal-substring" "perf-literal-multisubstring" ]; "perf-literal-multisubstring" = [ "std" "dep:aho-corasick" ]; "perf-literal-substring" = [ "aho-corasick?/perf-literal" "dep:memchr" ]; "std" = [ "regex-syntax?/std" "memchr?/std" "aho-corasick?/std" "alloc" ]; "syntax" = [ "dep:regex-syntax" "alloc" ]; "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "unicode-word-boundary" "regex-syntax?/unicode" ]; "unicode-age" = [ "regex-syntax?/unicode-age" ]; "unicode-bool" = [ "regex-syntax?/unicode-bool" ]; "unicode-case" = [ "regex-syntax?/unicode-case" ]; "unicode-gencat" = [ "regex-syntax?/unicode-gencat" ]; "unicode-perl" = [ "regex-syntax?/unicode-perl" ]; "unicode-script" = [ "regex-syntax?/unicode-script" ]; "unicode-segment" = [ "regex-syntax?/unicode-segment" ]; }; resolvedDefaultFeatures = [ "alloc" "meta" "nfa-pikevm" "nfa-thompson" "std" "syntax" "unicode" "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "unicode-word-boundary" ]; }; "regex-syntax" = rec { crateName = "regex-syntax"; version = "0.8.2"; edition = "2021"; sha256 = "17rd2s8xbiyf6lb4aj2nfi44zqlj98g2ays8zzj2vfs743k79360"; libName = "regex_syntax"; authors = [ "The Rust Project Developers" "Andrew Gallant " ]; features = { "arbitrary" = [ "dep:arbitrary" ]; "default" = [ "std" "unicode" ]; "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ]; }; resolvedDefaultFeatures = [ "std" "unicode" "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" ]; }; "rocksdb" = rec { crateName = "rocksdb"; version = "0.21.0"; edition = "2018"; workspace_member = null; src = pkgs.fetchgit { url = "https://github.com/rust-rocksdb/rust-rocksdb"; rev = "66f04df013b6e6bd42b5a8c353406e09a7c7da2a"; sha256 = "1rchvjrjamdaznx26gy4bmjj10rrf00mgc1wvkc489r9z1nh4h1h"; }; authors = [ "Tyler Neely " "David Greenberg " ]; dependencies = [ { name = "libc"; packageId = "libc"; } { name = "librocksdb-sys"; packageId = "librocksdb-sys"; } ]; features = { "bzip2" = [ "librocksdb-sys/bzip2" ]; "default" = [ "snappy" "lz4" "zstd" "zlib" "bzip2" ]; "io-uring" = [ "librocksdb-sys/io-uring" ]; "jemalloc" = [ "librocksdb-sys/jemalloc" ]; "lz4" = [ "librocksdb-sys/lz4" ]; "rtti" = [ "librocksdb-sys/rtti" ]; "serde" = [ "dep:serde" ]; "serde1" = [ "serde" ]; "snappy" = [ "librocksdb-sys/snappy" ]; "zlib" = [ "librocksdb-sys/zlib" ]; "zstd" = [ "librocksdb-sys/zstd" ]; }; }; "rustc-hash" = rec { crateName = "rustc-hash"; version = "1.1.0"; edition = "2015"; sha256 = "1qkc5khrmv5pqi5l5ca9p5nl5hs742cagrndhbrlk3dhlrx3zm08"; libName = "rustc_hash"; authors = [ "The Rust Project Developers" ]; features = { "default" = [ "std" ]; }; resolvedDefaultFeatures = [ "default" "std" ]; }; "shlex" = rec { crateName = "shlex"; version = "1.2.0"; edition = "2015"; sha256 = "1033pj9dyb76nm5yv597nnvj3zpvr2aw9rm5wy0gah3dk99f1km7"; authors = [ "comex " "Fenhl " ]; features = { "default" = [ "std" ]; }; resolvedDefaultFeatures = [ "default" "std" ]; }; "syn" = rec { crateName = "syn"; version = "2.0.48"; edition = "2021"; sha256 = "0gqgfygmrxmp8q32lia9p294kdd501ybn6kn2h4gqza0irik2d8g"; authors = [ "David Tolnay " ]; dependencies = [ { name = "proc-macro2"; packageId = "proc-macro2"; usesDefaultFeatures = false; } { name = "quote"; packageId = "quote"; optional = true; usesDefaultFeatures = false; } { name = "unicode-ident"; packageId = "unicode-ident"; } ]; features = { "default" = [ "derive" "parsing" "printing" "clone-impls" "proc-macro" ]; "printing" = [ "quote" ]; "proc-macro" = [ "proc-macro2/proc-macro" "quote/proc-macro" ]; "quote" = [ "dep:quote" ]; "test" = [ "syn-test-suite/all-features" ]; }; resolvedDefaultFeatures = [ "clone-impls" "default" "derive" "extra-traits" "full" "parsing" "printing" "proc-macro" "quote" "visit-mut" ]; }; "unicode-ident" = rec { crateName = "unicode-ident"; version = "1.0.12"; edition = "2018"; sha256 = "0jzf1znfpb2gx8nr8mvmyqs1crnv79l57nxnbiszc7xf7ynbjm1k"; libName = "unicode_ident"; authors = [ "David Tolnay " ]; }; "vcpkg" = rec { crateName = "vcpkg"; version = "0.2.15"; edition = "2015"; sha256 = "09i4nf5y8lig6xgj3f7fyrvzd3nlaw4znrihw8psidvv5yk4xkdc"; authors = [ "Jim McGrath " ]; }; "windows-sys" = rec { crateName = "windows-sys"; version = "0.48.0"; edition = "2018"; sha256 = "1aan23v5gs7gya1lc46hqn9mdh8yph3fhxmhxlw36pn6pqc28zb7"; libName = "windows_sys"; authors = [ "Microsoft" ]; dependencies = [ { name = "windows-targets"; packageId = "windows-targets"; } ]; features = { "Wdk_System" = [ "Wdk" ]; "Wdk_System_OfflineRegistry" = [ "Wdk_System" ]; "Win32_Data" = [ "Win32" ]; "Win32_Data_HtmlHelp" = [ "Win32_Data" ]; "Win32_Data_RightsManagement" = [ "Win32_Data" ]; "Win32_Data_Xml" = [ "Win32_Data" ]; "Win32_Data_Xml_MsXml" = [ "Win32_Data_Xml" ]; "Win32_Data_Xml_XmlLite" = [ "Win32_Data_Xml" ]; "Win32_Devices" = [ "Win32" ]; "Win32_Devices_AllJoyn" = [ "Win32_Devices" ]; "Win32_Devices_BiometricFramework" = [ "Win32_Devices" ]; "Win32_Devices_Bluetooth" = [ "Win32_Devices" ]; "Win32_Devices_Communication" = [ "Win32_Devices" ]; "Win32_Devices_DeviceAccess" = [ "Win32_Devices" ]; "Win32_Devices_DeviceAndDriverInstallation" = [ "Win32_Devices" ]; "Win32_Devices_DeviceQuery" = [ "Win32_Devices" ]; "Win32_Devices_Display" = [ "Win32_Devices" ]; "Win32_Devices_Enumeration" = [ "Win32_Devices" ]; "Win32_Devices_Enumeration_Pnp" = [ "Win32_Devices_Enumeration" ]; "Win32_Devices_Fax" = [ "Win32_Devices" ]; "Win32_Devices_FunctionDiscovery" = [ "Win32_Devices" ]; "Win32_Devices_Geolocation" = [ "Win32_Devices" ]; "Win32_Devices_HumanInterfaceDevice" = [ "Win32_Devices" ]; "Win32_Devices_ImageAcquisition" = [ "Win32_Devices" ]; "Win32_Devices_PortableDevices" = [ "Win32_Devices" ]; "Win32_Devices_Properties" = [ "Win32_Devices" ]; "Win32_Devices_Pwm" = [ "Win32_Devices" ]; "Win32_Devices_Sensors" = [ "Win32_Devices" ]; "Win32_Devices_SerialCommunication" = [ "Win32_Devices" ]; "Win32_Devices_Tapi" = [ "Win32_Devices" ]; "Win32_Devices_Usb" = [ "Win32_Devices" ]; "Win32_Devices_WebServicesOnDevices" = [ "Win32_Devices" ]; "Win32_Foundation" = [ "Win32" ]; "Win32_Gaming" = [ "Win32" ]; "Win32_Globalization" = [ "Win32" ]; "Win32_Graphics" = [ "Win32" ]; "Win32_Graphics_Dwm" = [ "Win32_Graphics" ]; "Win32_Graphics_Gdi" = [ "Win32_Graphics" ]; "Win32_Graphics_Hlsl" = [ "Win32_Graphics" ]; "Win32_Graphics_OpenGL" = [ "Win32_Graphics" ]; "Win32_Graphics_Printing" = [ "Win32_Graphics" ]; "Win32_Graphics_Printing_PrintTicket" = [ "Win32_Graphics_Printing" ]; "Win32_Management" = [ "Win32" ]; "Win32_Management_MobileDeviceManagementRegistration" = [ "Win32_Management" ]; "Win32_Media" = [ "Win32" ]; "Win32_Media_Audio" = [ "Win32_Media" ]; "Win32_Media_Audio_Apo" = [ "Win32_Media_Audio" ]; "Win32_Media_Audio_DirectMusic" = [ "Win32_Media_Audio" ]; "Win32_Media_Audio_Endpoints" = [ "Win32_Media_Audio" ]; "Win32_Media_Audio_XAudio2" = [ "Win32_Media_Audio" ]; "Win32_Media_DeviceManager" = [ "Win32_Media" ]; "Win32_Media_DxMediaObjects" = [ "Win32_Media" ]; "Win32_Media_KernelStreaming" = [ "Win32_Media" ]; "Win32_Media_LibrarySharingServices" = [ "Win32_Media" ]; "Win32_Media_MediaPlayer" = [ "Win32_Media" ]; "Win32_Media_Multimedia" = [ "Win32_Media" ]; "Win32_Media_Speech" = [ "Win32_Media" ]; "Win32_Media_Streaming" = [ "Win32_Media" ]; "Win32_Media_WindowsMediaFormat" = [ "Win32_Media" ]; "Win32_NetworkManagement" = [ "Win32" ]; "Win32_NetworkManagement_Dhcp" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_Dns" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_InternetConnectionWizard" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_IpHelper" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_MobileBroadband" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_Multicast" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_Ndis" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_NetBios" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_NetManagement" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_NetShell" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_NetworkDiagnosticsFramework" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_NetworkPolicyServer" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_P2P" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_QoS" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_Rras" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_Snmp" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_WNet" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_WebDav" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_WiFi" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_WindowsConnectNow" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_WindowsConnectionManager" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_WindowsFilteringPlatform" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_WindowsFirewall" = [ "Win32_NetworkManagement" ]; "Win32_NetworkManagement_WindowsNetworkVirtualization" = [ "Win32_NetworkManagement" ]; "Win32_Networking" = [ "Win32" ]; "Win32_Networking_ActiveDirectory" = [ "Win32_Networking" ]; "Win32_Networking_BackgroundIntelligentTransferService" = [ "Win32_Networking" ]; "Win32_Networking_Clustering" = [ "Win32_Networking" ]; "Win32_Networking_HttpServer" = [ "Win32_Networking" ]; "Win32_Networking_Ldap" = [ "Win32_Networking" ]; "Win32_Networking_NetworkListManager" = [ "Win32_Networking" ]; "Win32_Networking_RemoteDifferentialCompression" = [ "Win32_Networking" ]; "Win32_Networking_WebSocket" = [ "Win32_Networking" ]; "Win32_Networking_WinHttp" = [ "Win32_Networking" ]; "Win32_Networking_WinInet" = [ "Win32_Networking" ]; "Win32_Networking_WinSock" = [ "Win32_Networking" ]; "Win32_Networking_WindowsWebServices" = [ "Win32_Networking" ]; "Win32_Security" = [ "Win32" ]; "Win32_Security_AppLocker" = [ "Win32_Security" ]; "Win32_Security_Authentication" = [ "Win32_Security" ]; "Win32_Security_Authentication_Identity" = [ "Win32_Security_Authentication" ]; "Win32_Security_Authentication_Identity_Provider" = [ "Win32_Security_Authentication_Identity" ]; "Win32_Security_Authorization" = [ "Win32_Security" ]; "Win32_Security_Authorization_UI" = [ "Win32_Security_Authorization" ]; "Win32_Security_ConfigurationSnapin" = [ "Win32_Security" ]; "Win32_Security_Credentials" = [ "Win32_Security" ]; "Win32_Security_Cryptography" = [ "Win32_Security" ]; "Win32_Security_Cryptography_Catalog" = [ "Win32_Security_Cryptography" ]; "Win32_Security_Cryptography_Certificates" = [ "Win32_Security_Cryptography" ]; "Win32_Security_Cryptography_Sip" = [ "Win32_Security_Cryptography" ]; "Win32_Security_Cryptography_UI" = [ "Win32_Security_Cryptography" ]; "Win32_Security_DiagnosticDataQuery" = [ "Win32_Security" ]; "Win32_Security_DirectoryServices" = [ "Win32_Security" ]; "Win32_Security_EnterpriseData" = [ "Win32_Security" ]; "Win32_Security_ExtensibleAuthenticationProtocol" = [ "Win32_Security" ]; "Win32_Security_Isolation" = [ "Win32_Security" ]; "Win32_Security_LicenseProtection" = [ "Win32_Security" ]; "Win32_Security_NetworkAccessProtection" = [ "Win32_Security" ]; "Win32_Security_Tpm" = [ "Win32_Security" ]; "Win32_Security_WinTrust" = [ "Win32_Security" ]; "Win32_Security_WinWlx" = [ "Win32_Security" ]; "Win32_Storage" = [ "Win32" ]; "Win32_Storage_Cabinets" = [ "Win32_Storage" ]; "Win32_Storage_CloudFilters" = [ "Win32_Storage" ]; "Win32_Storage_Compression" = [ "Win32_Storage" ]; "Win32_Storage_DataDeduplication" = [ "Win32_Storage" ]; "Win32_Storage_DistributedFileSystem" = [ "Win32_Storage" ]; "Win32_Storage_EnhancedStorage" = [ "Win32_Storage" ]; "Win32_Storage_FileHistory" = [ "Win32_Storage" ]; "Win32_Storage_FileServerResourceManager" = [ "Win32_Storage" ]; "Win32_Storage_FileSystem" = [ "Win32_Storage" ]; "Win32_Storage_Imapi" = [ "Win32_Storage" ]; "Win32_Storage_IndexServer" = [ "Win32_Storage" ]; "Win32_Storage_InstallableFileSystems" = [ "Win32_Storage" ]; "Win32_Storage_IscsiDisc" = [ "Win32_Storage" ]; "Win32_Storage_Jet" = [ "Win32_Storage" ]; "Win32_Storage_OfflineFiles" = [ "Win32_Storage" ]; "Win32_Storage_OperationRecorder" = [ "Win32_Storage" ]; "Win32_Storage_Packaging" = [ "Win32_Storage" ]; "Win32_Storage_Packaging_Appx" = [ "Win32_Storage_Packaging" ]; "Win32_Storage_Packaging_Opc" = [ "Win32_Storage_Packaging" ]; "Win32_Storage_ProjectedFileSystem" = [ "Win32_Storage" ]; "Win32_Storage_StructuredStorage" = [ "Win32_Storage" ]; "Win32_Storage_Vhd" = [ "Win32_Storage" ]; "Win32_Storage_VirtualDiskService" = [ "Win32_Storage" ]; "Win32_Storage_Vss" = [ "Win32_Storage" ]; "Win32_Storage_Xps" = [ "Win32_Storage" ]; "Win32_Storage_Xps_Printing" = [ "Win32_Storage_Xps" ]; "Win32_System" = [ "Win32" ]; "Win32_System_AddressBook" = [ "Win32_System" ]; "Win32_System_Antimalware" = [ "Win32_System" ]; "Win32_System_ApplicationInstallationAndServicing" = [ "Win32_System" ]; "Win32_System_ApplicationVerifier" = [ "Win32_System" ]; "Win32_System_AssessmentTool" = [ "Win32_System" ]; "Win32_System_ClrHosting" = [ "Win32_System" ]; "Win32_System_Com" = [ "Win32_System" ]; "Win32_System_Com_CallObj" = [ "Win32_System_Com" ]; "Win32_System_Com_ChannelCredentials" = [ "Win32_System_Com" ]; "Win32_System_Com_Events" = [ "Win32_System_Com" ]; "Win32_System_Com_Marshal" = [ "Win32_System_Com" ]; "Win32_System_Com_StructuredStorage" = [ "Win32_System_Com" ]; "Win32_System_Com_UI" = [ "Win32_System_Com" ]; "Win32_System_Com_Urlmon" = [ "Win32_System_Com" ]; "Win32_System_ComponentServices" = [ "Win32_System" ]; "Win32_System_Console" = [ "Win32_System" ]; "Win32_System_Contacts" = [ "Win32_System" ]; "Win32_System_CorrelationVector" = [ "Win32_System" ]; "Win32_System_DataExchange" = [ "Win32_System" ]; "Win32_System_DeploymentServices" = [ "Win32_System" ]; "Win32_System_DesktopSharing" = [ "Win32_System" ]; "Win32_System_DeveloperLicensing" = [ "Win32_System" ]; "Win32_System_Diagnostics" = [ "Win32_System" ]; "Win32_System_Diagnostics_Ceip" = [ "Win32_System_Diagnostics" ]; "Win32_System_Diagnostics_ClrProfiling" = [ "Win32_System_Diagnostics" ]; "Win32_System_Diagnostics_Debug" = [ "Win32_System_Diagnostics" ]; "Win32_System_Diagnostics_Debug_ActiveScript" = [ "Win32_System_Diagnostics_Debug" ]; "Win32_System_Diagnostics_Debug_Extensions" = [ "Win32_System_Diagnostics_Debug" ]; "Win32_System_Diagnostics_Etw" = [ "Win32_System_Diagnostics" ]; "Win32_System_Diagnostics_ProcessSnapshotting" = [ "Win32_System_Diagnostics" ]; "Win32_System_Diagnostics_ToolHelp" = [ "Win32_System_Diagnostics" ]; "Win32_System_DistributedTransactionCoordinator" = [ "Win32_System" ]; "Win32_System_Environment" = [ "Win32_System" ]; "Win32_System_ErrorReporting" = [ "Win32_System" ]; "Win32_System_EventCollector" = [ "Win32_System" ]; "Win32_System_EventLog" = [ "Win32_System" ]; "Win32_System_EventNotificationService" = [ "Win32_System" ]; "Win32_System_GroupPolicy" = [ "Win32_System" ]; "Win32_System_HostCompute" = [ "Win32_System" ]; "Win32_System_HostComputeNetwork" = [ "Win32_System" ]; "Win32_System_HostComputeSystem" = [ "Win32_System" ]; "Win32_System_Hypervisor" = [ "Win32_System" ]; "Win32_System_IO" = [ "Win32_System" ]; "Win32_System_Iis" = [ "Win32_System" ]; "Win32_System_Ioctl" = [ "Win32_System" ]; "Win32_System_JobObjects" = [ "Win32_System" ]; "Win32_System_Js" = [ "Win32_System" ]; "Win32_System_Kernel" = [ "Win32_System" ]; "Win32_System_LibraryLoader" = [ "Win32_System" ]; "Win32_System_Mailslots" = [ "Win32_System" ]; "Win32_System_Mapi" = [ "Win32_System" ]; "Win32_System_Memory" = [ "Win32_System" ]; "Win32_System_Memory_NonVolatile" = [ "Win32_System_Memory" ]; "Win32_System_MessageQueuing" = [ "Win32_System" ]; "Win32_System_MixedReality" = [ "Win32_System" ]; "Win32_System_Mmc" = [ "Win32_System" ]; "Win32_System_Ole" = [ "Win32_System" ]; "Win32_System_ParentalControls" = [ "Win32_System" ]; "Win32_System_PasswordManagement" = [ "Win32_System" ]; "Win32_System_Performance" = [ "Win32_System" ]; "Win32_System_Performance_HardwareCounterProfiling" = [ "Win32_System_Performance" ]; "Win32_System_Pipes" = [ "Win32_System" ]; "Win32_System_Power" = [ "Win32_System" ]; "Win32_System_ProcessStatus" = [ "Win32_System" ]; "Win32_System_RealTimeCommunications" = [ "Win32_System" ]; "Win32_System_Recovery" = [ "Win32_System" ]; "Win32_System_Registry" = [ "Win32_System" ]; "Win32_System_RemoteAssistance" = [ "Win32_System" ]; "Win32_System_RemoteDesktop" = [ "Win32_System" ]; "Win32_System_RemoteManagement" = [ "Win32_System" ]; "Win32_System_RestartManager" = [ "Win32_System" ]; "Win32_System_Restore" = [ "Win32_System" ]; "Win32_System_Rpc" = [ "Win32_System" ]; "Win32_System_Search" = [ "Win32_System" ]; "Win32_System_Search_Common" = [ "Win32_System_Search" ]; "Win32_System_SecurityCenter" = [ "Win32_System" ]; "Win32_System_ServerBackup" = [ "Win32_System" ]; "Win32_System_Services" = [ "Win32_System" ]; "Win32_System_SettingsManagementInfrastructure" = [ "Win32_System" ]; "Win32_System_SetupAndMigration" = [ "Win32_System" ]; "Win32_System_Shutdown" = [ "Win32_System" ]; "Win32_System_StationsAndDesktops" = [ "Win32_System" ]; "Win32_System_SubsystemForLinux" = [ "Win32_System" ]; "Win32_System_SystemInformation" = [ "Win32_System" ]; "Win32_System_SystemServices" = [ "Win32_System" ]; "Win32_System_TaskScheduler" = [ "Win32_System" ]; "Win32_System_Threading" = [ "Win32_System" ]; "Win32_System_Time" = [ "Win32_System" ]; "Win32_System_TpmBaseServices" = [ "Win32_System" ]; "Win32_System_UpdateAgent" = [ "Win32_System" ]; "Win32_System_UpdateAssessment" = [ "Win32_System" ]; "Win32_System_UserAccessLogging" = [ "Win32_System" ]; "Win32_System_VirtualDosMachines" = [ "Win32_System" ]; "Win32_System_WindowsProgramming" = [ "Win32_System" ]; "Win32_System_WindowsSync" = [ "Win32_System" ]; "Win32_System_Wmi" = [ "Win32_System" ]; "Win32_UI" = [ "Win32" ]; "Win32_UI_Accessibility" = [ "Win32_UI" ]; "Win32_UI_Animation" = [ "Win32_UI" ]; "Win32_UI_ColorSystem" = [ "Win32_UI" ]; "Win32_UI_Controls" = [ "Win32_UI" ]; "Win32_UI_Controls_Dialogs" = [ "Win32_UI_Controls" ]; "Win32_UI_Controls_RichEdit" = [ "Win32_UI_Controls" ]; "Win32_UI_HiDpi" = [ "Win32_UI" ]; "Win32_UI_Input" = [ "Win32_UI" ]; "Win32_UI_Input_Ime" = [ "Win32_UI_Input" ]; "Win32_UI_Input_Ink" = [ "Win32_UI_Input" ]; "Win32_UI_Input_KeyboardAndMouse" = [ "Win32_UI_Input" ]; "Win32_UI_Input_Pointer" = [ "Win32_UI_Input" ]; "Win32_UI_Input_Radial" = [ "Win32_UI_Input" ]; "Win32_UI_Input_Touch" = [ "Win32_UI_Input" ]; "Win32_UI_Input_XboxController" = [ "Win32_UI_Input" ]; "Win32_UI_InteractionContext" = [ "Win32_UI" ]; "Win32_UI_LegacyWindowsEnvironmentFeatures" = [ "Win32_UI" ]; "Win32_UI_Magnification" = [ "Win32_UI" ]; "Win32_UI_Notifications" = [ "Win32_UI" ]; "Win32_UI_Ribbon" = [ "Win32_UI" ]; "Win32_UI_Shell" = [ "Win32_UI" ]; "Win32_UI_Shell_Common" = [ "Win32_UI_Shell" ]; "Win32_UI_Shell_PropertiesSystem" = [ "Win32_UI_Shell" ]; "Win32_UI_TabletPC" = [ "Win32_UI" ]; "Win32_UI_TextServices" = [ "Win32_UI" ]; "Win32_UI_WindowsAndMessaging" = [ "Win32_UI" ]; "Win32_UI_Wpf" = [ "Win32_UI" ]; "Win32_Web" = [ "Win32" ]; "Win32_Web_InternetExplorer" = [ "Win32_Web" ]; }; resolvedDefaultFeatures = [ "Win32" "Win32_Foundation" "Win32_System" "Win32_System_Diagnostics" "Win32_System_Diagnostics_Debug" "Win32_System_LibraryLoader" "default" ]; }; "windows-targets" = rec { crateName = "windows-targets"; version = "0.48.5"; edition = "2018"; sha256 = "034ljxqshifs1lan89xwpcy1hp0lhdh4b5n0d2z4fwjx2piacbws"; libName = "windows_targets"; authors = [ "Microsoft" ]; dependencies = [ { name = "windows_aarch64_gnullvm"; packageId = "windows_aarch64_gnullvm"; target = { target, features }: (target.name == "aarch64-pc-windows-gnullvm"); } { name = "windows_aarch64_msvc"; packageId = "windows_aarch64_msvc"; target = { target, features }: (("aarch64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false))); } { name = "windows_i686_gnu"; packageId = "windows_i686_gnu"; target = { target, features }: (("x86" == target."arch" or null) && ("gnu" == target."env" or null) && (!(target."windows_raw_dylib" or false))); } { name = "windows_i686_msvc"; packageId = "windows_i686_msvc"; target = { target, features }: (("x86" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false))); } { name = "windows_x86_64_gnu"; packageId = "windows_x86_64_gnu"; target = { target, features }: (("x86_64" == target."arch" or null) && ("gnu" == target."env" or null) && (!("llvm" == target."abi" or null)) && (!(target."windows_raw_dylib" or false))); } { name = "windows_x86_64_gnullvm"; packageId = "windows_x86_64_gnullvm"; target = { target, features }: (target.name == "x86_64-pc-windows-gnullvm"); } { name = "windows_x86_64_msvc"; packageId = "windows_x86_64_msvc"; target = { target, features }: (("x86_64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false))); } ]; }; "windows_aarch64_gnullvm" = rec { crateName = "windows_aarch64_gnullvm"; version = "0.48.5"; edition = "2018"; sha256 = "1n05v7qblg1ci3i567inc7xrkmywczxrs1z3lj3rkkxw18py6f1b"; authors = [ "Microsoft" ]; }; "windows_aarch64_msvc" = rec { crateName = "windows_aarch64_msvc"; version = "0.48.5"; edition = "2018"; sha256 = "1g5l4ry968p73g6bg6jgyvy9lb8fyhcs54067yzxpcpkf44k2dfw"; authors = [ "Microsoft" ]; }; "windows_i686_gnu" = rec { crateName = "windows_i686_gnu"; version = "0.48.5"; edition = "2018"; sha256 = "0gklnglwd9ilqx7ac3cn8hbhkraqisd0n83jxzf9837nvvkiand7"; authors = [ "Microsoft" ]; }; "windows_i686_msvc" = rec { crateName = "windows_i686_msvc"; version = "0.48.5"; edition = "2018"; sha256 = "01m4rik437dl9rdf0ndnm2syh10hizvq0dajdkv2fjqcywrw4mcg"; authors = [ "Microsoft" ]; }; "windows_x86_64_gnu" = rec { crateName = "windows_x86_64_gnu"; version = "0.48.5"; edition = "2018"; sha256 = "13kiqqcvz2vnyxzydjh73hwgigsdr2z1xpzx313kxll34nyhmm2k"; authors = [ "Microsoft" ]; }; "windows_x86_64_gnullvm" = rec { crateName = "windows_x86_64_gnullvm"; version = "0.48.5"; edition = "2018"; sha256 = "1k24810wfbgz8k48c2yknqjmiigmql6kk3knmddkv8k8g1v54yqb"; authors = [ "Microsoft" ]; }; "windows_x86_64_msvc" = rec { crateName = "windows_x86_64_msvc"; version = "0.48.5"; edition = "2018"; sha256 = "0f4mdp895kkjh9zv8dxvn4pc10xr7839lf5pa9l0193i2pkgr57d"; authors = [ "Microsoft" ]; }; }; # # crate2nix/default.nix (excerpt start) # /* Target (platform) data for conditional dependencies. This corresponds roughly to what buildRustCrate is setting. */ makeDefaultTarget = platform: { name = platform.rust.rustcTarget; unix = platform.isUnix; windows = platform.isWindows; fuchsia = true; test = false; inherit (platform.rust.platform) arch os vendor ; family = platform.rust.platform.target-family; env = "gnu"; endian = if platform.parsed.cpu.significantByte.name == "littleEndian" then "little" else "big"; pointer_width = toString platform.parsed.cpu.bits; debug_assertions = false; } // extraTargetFlags; registryUrl = { registries , url , crate , version , sha256 , }: let dl = registries.${url}.dl; tmpl = [ "{crate}" "{version}" "{prefix}" "{lowerprefix}" "{sha256-checksum}" ]; in with lib.strings; if lib.lists.any (i: hasInfix "{}" dl) tmpl then let prefix = if builtins.stringLength crate == 1 then "1" else if builtins.stringLength crate == 2 then "2" else "${builtins.substring 0 2 crate}/${builtins.substring 2 (builtins.stringLength crate - 2) crate}"; in builtins.replaceStrings tmpl [ crate version prefix (lib.strings.toLower prefix) sha256 ] else "${dl}/${crate}/${version}/download"; # Filters common temp files and build files. # TODO(pkolloch): Substitute with gitignore filter sourceFilter = name: type: let baseName = builtins.baseNameOf (builtins.toString name); in !( # Filter out git baseName == ".gitignore" || (type == "directory" && baseName == ".git") # Filter out build results || ( type == "directory" && ( baseName == "target" || baseName == "_site" || baseName == ".sass-cache" || baseName == ".jekyll-metadata" || baseName == "build-artifacts" ) ) # Filter out nix-build result symlinks || (type == "symlink" && lib.hasPrefix "result" baseName) # Filter out IDE config || (type == "directory" && (baseName == ".idea" || baseName == ".vscode")) || lib.hasSuffix ".iml" baseName # Filter out nix build files || baseName == "Cargo.nix" # Filter out editor backup / swap files. || lib.hasSuffix "~" baseName || builtins.match "^\\.sw[a-z]$$" baseName != null || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null || lib.hasSuffix ".tmp" baseName || lib.hasSuffix ".bak" baseName || baseName == "tests.nix" ); /* Returns a crate which depends on successful test execution of crate given as the second argument. testCrateFlags: list of flags to pass to the test exectuable testInputs: list of packages that should be available during test execution */ crateWithTest = { crate , testCrate , testCrateFlags , testInputs , testPreRun , testPostRun , }: assert builtins.typeOf testCrateFlags == "list"; assert builtins.typeOf testInputs == "list"; assert builtins.typeOf testPreRun == "string"; assert builtins.typeOf testPostRun == "string"; let # override the `crate` so that it will build and execute tests instead of # building the actual lib and bin targets We just have to pass `--test` # to rustc and it will do the right thing. We execute the tests and copy # their log and the test executables to $out for later inspection. test = let drv = testCrate.override (_: { buildTests = true; }); # If the user hasn't set any pre/post commands, we don't want to # insert empty lines. This means that any existing users of crate2nix # don't get a spurious rebuild unless they set these explicitly. testCommand = pkgs.lib.concatStringsSep "\n" ( pkgs.lib.filter (s: s != "") [ testPreRun "$f $testCrateFlags 2>&1 | tee -a $out" testPostRun ] ); in pkgs.stdenvNoCC.mkDerivation { name = "run-tests-${testCrate.name}"; inherit (crate) src; inherit testCrateFlags; buildInputs = testInputs; buildPhase = '' set -e export RUST_BACKTRACE=1 # build outputs testRoot=target/debug mkdir -p $testRoot # executables of the crate # we copy to prevent std::env::current_exe() to resolve to a store location for i in ${crate}/bin/*; do cp "$i" "$testRoot" done chmod +w -R . # test harness executables are suffixed with a hash, like cargo does # this allows to prevent name collision with the main # executables of the crate hash=$(basename $out) for file in ${drv}/tests/*; do f=$testRoot/$(basename $file)-$hash cp $file $f ${testCommand} done ''; }; in pkgs.runCommand "${crate.name}-linked" { inherit (crate) outputs crateName meta; passthru = (crate.passthru or { }) // { inherit test; }; } ( lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) '' echo tested by ${test} '' + '' ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs} '' ); # A restricted overridable version of builtRustCratesWithFeatures. buildRustCrateWithFeatures = { packageId , features ? rootFeatures , crateOverrides ? defaultCrateOverrides , buildRustCrateForPkgsFunc ? null , runTests ? false , testCrateFlags ? [ ] , testInputs ? [ ] , # Any command to run immediatelly before a test is executed. testPreRun ? "" , # Any command run immediatelly after a test is executed. testPostRun ? "" , }: lib.makeOverridable ( { features , crateOverrides , runTests , testCrateFlags , testInputs , testPreRun , testPostRun , }: let buildRustCrateForPkgsFuncOverriden = if buildRustCrateForPkgsFunc != null then buildRustCrateForPkgsFunc else ( if crateOverrides == pkgs.defaultCrateOverrides then buildRustCrateForPkgs else pkgs: (buildRustCrateForPkgs pkgs).override { defaultCrateOverrides = crateOverrides; } ); builtRustCrates = builtRustCratesWithFeatures { inherit packageId features; buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden; runTests = false; }; builtTestRustCrates = builtRustCratesWithFeatures { inherit packageId features; buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden; runTests = true; }; drv = builtRustCrates.crates.${packageId}; testDrv = builtTestRustCrates.crates.${packageId}; derivation = if runTests then crateWithTest { crate = drv; testCrate = testDrv; inherit testCrateFlags testInputs testPreRun testPostRun ; } else drv; in derivation ) { inherit features crateOverrides runTests testCrateFlags testInputs testPreRun testPostRun ; }; /* Returns an attr set with packageId mapped to the result of buildRustCrateForPkgsFunc for the corresponding crate. */ builtRustCratesWithFeatures = { packageId , features , crateConfigs ? crates , buildRustCrateForPkgsFunc , runTests , makeTarget ? makeDefaultTarget , }@args: assert (builtins.isAttrs crateConfigs); assert (builtins.isString packageId); assert (builtins.isList features); assert (builtins.isAttrs (makeTarget stdenv.hostPlatform)); assert (builtins.isBool runTests); let rootPackageId = packageId; mergedFeatures = mergePackageFeatures ( args // { inherit rootPackageId; target = makeTarget stdenv.hostPlatform // { test = runTests; }; } ); # Memoize built packages so that reappearing packages are only built once. builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs; mkBuiltByPackageIdByPkgs = pkgs: let self = { crates = lib.mapAttrs ( packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId ) crateConfigs; target = makeTarget pkgs.stdenv.hostPlatform; build = mkBuiltByPackageIdByPkgs pkgs.buildPackages; }; in self; buildByPackageIdForPkgsImpl = self: pkgs: packageId: let features = mergedFeatures."${packageId}" or [ ]; crateConfig' = crateConfigs."${packageId}"; crateConfig = builtins.removeAttrs crateConfig' [ "resolvedDefaultFeatures" "devDependencies" ]; devDependencies = lib.optionals (runTests && packageId == rootPackageId) ( crateConfig'.devDependencies or [ ] ); dependencies = dependencyDerivations { inherit features; inherit (self) target; buildByPackageId = depPackageId: # proc_macro crates must be compiled for the build architecture if crateConfigs.${depPackageId}.procMacro or false then self.build.crates.${depPackageId} else self.crates.${depPackageId}; dependencies = (crateConfig.dependencies or [ ]) ++ devDependencies; }; buildDependencies = dependencyDerivations { inherit features; inherit (self.build) target; buildByPackageId = depPackageId: self.build.crates.${depPackageId}; dependencies = crateConfig.buildDependencies or [ ]; }; dependenciesWithRenames = let buildDeps = filterEnabledDependencies { inherit features; inherit (self) target; dependencies = crateConfig.dependencies or [ ] ++ devDependencies; }; hostDeps = filterEnabledDependencies { inherit features; inherit (self.build) target; dependencies = crateConfig.buildDependencies or [ ]; }; in lib.filter (d: d ? "rename") (hostDeps ++ buildDeps); # Crate renames have the form: # # { # crate_name = [ # { version = "1.2.3"; rename = "crate_name01"; } # ]; # # ... # } crateRenames = let grouped = lib.groupBy (dependency: dependency.name) dependenciesWithRenames; versionAndRename = dep: let package = crateConfigs."${dep.packageId}"; in { inherit (dep) rename; inherit (package) version; }; in lib.mapAttrs (name: builtins.map versionAndRename) grouped; in buildRustCrateForPkgsFunc pkgs ( crateConfig // { src = crateConfig.src or (fetchurl rec { name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz"; # https://www.pietroalbini.org/blog/downloading-crates-io/ # Not rate-limited, CDN URL. url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate"; sha256 = assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}"); crateConfig.sha256; }); extraRustcOpts = lib.lists.optional (targetFeatures != [ ]) "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}"; inherit features dependencies buildDependencies crateRenames release ; } ); in builtByPackageIdByPkgs; # Returns the actual derivations for the given dependencies. dependencyDerivations = { buildByPackageId , features , dependencies , target , }: assert (builtins.isList features); assert (builtins.isList dependencies); assert (builtins.isAttrs target); let enabledDependencies = filterEnabledDependencies { inherit dependencies features target; }; depDerivation = dependency: buildByPackageId dependency.packageId; in map depDerivation enabledDependencies; /* Returns a sanitized version of val with all values substituted that cannot be serialized as JSON. */ sanitizeForJson = val: if builtins.isAttrs val then lib.mapAttrs (n: sanitizeForJson) val else if builtins.isList val then builtins.map sanitizeForJson val else if builtins.isFunction val then "function" else val; # Returns various tools to debug a crate. debugCrate = { packageId , target ? makeDefaultTarget stdenv.hostPlatform , }: assert (builtins.isString packageId); let debug = rec { # The built tree as passed to buildRustCrate. buildTree = buildRustCrateWithFeatures { buildRustCrateForPkgsFunc = _: lib.id; inherit packageId; }; sanitizedBuildTree = sanitizeForJson buildTree; dependencyTree = sanitizeForJson (buildRustCrateWithFeatures { buildRustCrateForPkgsFunc = _: crate: { "01_crateName" = crate.crateName or false; "02_features" = crate.features or [ ]; "03_dependencies" = crate.dependencies or [ ]; }; inherit packageId; }); mergedPackageFeatures = mergePackageFeatures { features = rootFeatures; inherit packageId target; }; diffedDefaultPackageFeatures = diffDefaultPackageFeatures { inherit packageId target; }; }; in { internal = debug; }; /* Returns differences between cargo default features and crate2nix default features. This is useful for verifying the feature resolution in crate2nix. */ diffDefaultPackageFeatures = { crateConfigs ? crates , packageId , target , }: assert (builtins.isAttrs crateConfigs); let prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; }); mergedFeatures = prefixValues "crate2nix" (mergePackageFeatures { inherit crateConfigs packageId target; features = [ "default" ]; }); configs = prefixValues "cargo" crateConfigs; combined = lib.foldAttrs (a: b: a // b) { } [ mergedFeatures configs ]; onlyInCargo = builtins.attrNames ( lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined ); onlyInCrate2Nix = builtins.attrNames ( lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined ); differentFeatures = lib.filterAttrs ( n: v: (v ? "crate2nix") && (v ? "cargo") && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ]) ) combined; in builtins.toJSON { inherit onlyInCargo onlyInCrate2Nix differentFeatures; }; /* Returns an attrset mapping packageId to the list of enabled features. If multiple paths to a dependency enable different features, the corresponding feature sets are merged. Features in rust are additive. */ mergePackageFeatures = { crateConfigs ? crates , packageId , rootPackageId ? packageId , features ? rootFeatures , dependencyPath ? [ crates.${packageId}.crateName ] , featuresByPackageId ? { } , target , # Adds devDependencies to the crate with rootPackageId. runTests ? false , ... }@args: assert (builtins.isAttrs crateConfigs); assert (builtins.isString packageId); assert (builtins.isString rootPackageId); assert (builtins.isList features); assert (builtins.isList dependencyPath); assert (builtins.isAttrs featuresByPackageId); assert (builtins.isAttrs target); assert (builtins.isBool runTests); let crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}"); expandedFeatures = expandFeatures (crateConfig.features or { }) features; enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures; depWithResolvedFeatures = dependency: let inherit (dependency) packageId; features = dependencyFeatures enabledFeatures dependency; in { inherit packageId features; }; resolveDependencies = cache: path: dependencies: assert (builtins.isAttrs cache); assert (builtins.isList dependencies); let enabledDependencies = filterEnabledDependencies { inherit dependencies target; features = enabledFeatures; }; directDependencies = map depWithResolvedFeatures enabledDependencies; foldOverCache = op: lib.foldl op cache directDependencies; in foldOverCache ( cache: { packageId, features }: let cacheFeatures = cache.${packageId} or [ ]; combinedFeatures = sortedUnique (cacheFeatures ++ features); in if cache ? ${packageId} && cache.${packageId} == combinedFeatures then cache else mergePackageFeatures { features = combinedFeatures; featuresByPackageId = cache; inherit crateConfigs packageId target runTests rootPackageId ; } ); cacheWithSelf = let cacheFeatures = featuresByPackageId.${packageId} or [ ]; combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures); in featuresByPackageId // { "${packageId}" = combinedFeatures; }; cacheWithDependencies = resolveDependencies cacheWithSelf "dep" ( crateConfig.dependencies or [ ] ++ lib.optionals (runTests && packageId == rootPackageId) (crateConfig.devDependencies or [ ]) ); cacheWithAll = resolveDependencies cacheWithDependencies "build" ( crateConfig.buildDependencies or [ ] ); in cacheWithAll; # Returns the enabled dependencies given the enabled features. filterEnabledDependencies = { dependencies , features , target , }: assert (builtins.isList dependencies); assert (builtins.isList features); assert (builtins.isAttrs target); lib.filter ( dep: let targetFunc = dep.target or (features: true); in targetFunc { inherit features target; } && (!(dep.optional or false) || builtins.any (doesFeatureEnableDependency dep) features) ) dependencies; # Returns whether the given feature should enable the given dependency. doesFeatureEnableDependency = dependency: feature: let name = dependency.rename or dependency.name; prefix = "${name}/"; len = builtins.stringLength prefix; startsWithPrefix = builtins.substring 0 len feature == prefix; in feature == name || feature == "dep:" + name || startsWithPrefix; /* Returns the expanded features for the given inputFeatures by applying the rules in featureMap. featureMap is an attribute set which maps feature names to lists of further feature names to enable in case this feature is selected. */ expandFeatures = featureMap: inputFeatures: assert (builtins.isAttrs featureMap); assert (builtins.isList inputFeatures); let expandFeaturesNoCycle = oldSeen: inputFeatures: if inputFeatures != [ ] then let # The feature we're currently expanding. feature = builtins.head inputFeatures; # All the features we've seen/expanded so far, including the one # we're currently processing. seen = oldSeen // { ${feature} = 1; }; # Expand the feature but be careful to not re-introduce a feature # that we've already seen: this can easily cause a cycle, see issue # #209. enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]); in [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables)) # No more features left, nothing to expand to. else [ ]; outFeatures = expandFeaturesNoCycle { } inputFeatures; in sortedUnique outFeatures; /* This function adds optional dependencies as features if they are enabled indirectly by dependency features. This function mimics Cargo's behavior described in a note at: https://doc.rust-lang.org/nightly/cargo/reference/features.html#dependency-features */ enableFeatures = dependencies: features: assert (builtins.isList features); assert (builtins.isList dependencies); let additionalFeatures = lib.concatMap ( dependency: assert (builtins.isAttrs dependency); let enabled = builtins.any (doesFeatureEnableDependency dependency) features; in if (dependency.optional or false) && enabled then [ (dependency.rename or dependency.name) ] else [ ] ) dependencies; in sortedUnique (features ++ additionalFeatures); /* Returns the actual features for the given dependency. features: The features of the crate that refers this dependency. */ dependencyFeatures = features: dependency: assert (builtins.isList features); assert (builtins.isAttrs dependency); let defaultOrNil = if dependency.usesDefaultFeatures or true then [ "default" ] else [ ]; explicitFeatures = dependency.features or [ ]; additionalDependencyFeatures = let name = dependency.rename or dependency.name; stripPrefixMatch = prefix: s: if lib.hasPrefix prefix s then lib.removePrefix prefix s else null; extractFeature = feature: lib.findFirst (f: f != null) null ( map (prefix: stripPrefixMatch prefix feature) [ (name + "/") (name + "?/") ] ); dependencyFeatures = lib.filter (f: f != null) (map extractFeature features); in dependencyFeatures; in defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures; # Sorts and removes duplicates from a list of strings. sortedUnique = features: assert (builtins.isList features); assert (builtins.all builtins.isString features); let outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features; outFeaturesUnique = builtins.attrNames outFeaturesSet; in builtins.sort (a: b: a < b) outFeaturesUnique; deprecationWarning = message: value: if strictDeprecation then builtins.throw "strictDeprecation enabled, aborting: ${message}" else builtins.trace message value; # # crate2nix/default.nix (excerpt end) # }; } ================================================ FILE: sample_projects/bin_with_git_submodule_dep/Cargo.toml ================================================ [package] name = "bin_with_git_submodule_dep" version = "0.1.0" authors = ["Phillip Cloud "] edition = "2018" [dependencies.rocksdb] git = "https://github.com/rust-rocksdb/rust-rocksdb" default-features = false ================================================ FILE: sample_projects/bin_with_git_submodule_dep/crate-hashes.json ================================================ { "git+https://github.com/rust-rocksdb/rust-rocksdb#librocksdb-sys@0.15.0+8.9.1": "1rchvjrjamdaznx26gy4bmjj10rrf00mgc1wvkc489r9z1nh4h1h", "git+https://github.com/rust-rocksdb/rust-rocksdb#rocksdb@0.21.0": "1rchvjrjamdaznx26gy4bmjj10rrf00mgc1wvkc489r9z1nh4h1h" } ================================================ FILE: sample_projects/bin_with_git_submodule_dep/default-with-customBuildRustCrate.nix ================================================ { pkgs ? import ../../nix/nixpkgs.nix { config = { }; } , generatedCargoNix ? ./Cargo.nix }: let customBuildRustCrate = pkgs: pkgs.buildRustCrate.override { defaultCrateOverrides = pkgs.defaultCrateOverrides // { librocksdb-sys = attrs: with pkgs; { src = attrs.src + "/librocksdb-sys"; buildInputs = [ clang rocksdb ]; LIBCLANG_PATH = "${clang.cc.lib}/lib"; ROCKSDB_LIB_DIR = "${rocksdb}/lib/"; }; }; }; basePackage = pkgs.callPackage generatedCargoNix { buildRustCrateForPkgs = customBuildRustCrate; }; submodulePackage = basePackage.rootCrate.build; in submodulePackage ================================================ FILE: sample_projects/bin_with_git_submodule_dep/default-with-customBuildRustCrateForPkgs.nix ================================================ { pkgs ? import ../../nix/nixpkgs.nix { config = { }; } , generatedCargoNix ? ./Cargo.nix }: let customBuildRustCrateForPkgs = pkgs: pkgs.buildRustCrate.override { defaultCrateOverrides = pkgs.defaultCrateOverrides // { librocksdb-sys = attrs: with pkgs; { src = attrs.src + "/librocksdb-sys"; buildInputs = [ clang rocksdb ]; LIBCLANG_PATH = "${clang.cc.lib}/lib"; ROCKSDB_LIB_DIR = "${rocksdb}/lib/"; }; }; }; basePackage = pkgs.callPackage generatedCargoNix { buildRustCrateForPkgs = customBuildRustCrateForPkgs; }; submodulePackage = basePackage.rootCrate.build; in submodulePackage ================================================ FILE: sample_projects/bin_with_git_submodule_dep/default.nix ================================================ { pkgs ? import ../../nix/nixpkgs.nix { config = { }; } , generatedCargoNix ? ./Cargo.nix }: let basePackage = pkgs.callPackage generatedCargoNix { defaultCrateOverrides = pkgs.defaultCrateOverrides // { librocksdb-sys = attrs: with pkgs; { src = attrs.src + "/librocksdb-sys"; buildInputs = [ clang rocksdb ]; LIBCLANG_PATH = "${clang.cc.lib}/lib"; ROCKSDB_LIB_DIR = "${rocksdb}/lib/"; }; }; }; submodulePackage = basePackage.rootCrate.build; in submodulePackage ================================================ FILE: sample_projects/bin_with_git_submodule_dep/src/main.rs ================================================ fn main() { println!("Hello world from with_git_submodule_dep!"); } ================================================ FILE: sample_projects/bin_with_lib_dep/Cargo.toml ================================================ [package] name = "hello_world_with_dep" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" [dependencies] "hello_world_lib" = { path = "../lib"} # Doesn't make sense but reproduces bug that filters # this dev dependency by accident. [dev-dependencies] "hello_world_lib" = { path = "../lib"} ================================================ FILE: sample_projects/bin_with_lib_dep/crate-hashes.json ================================================ {} ================================================ FILE: sample_projects/bin_with_lib_dep/src/main.rs ================================================ fn main() { renamed_hello_world_lib::hello_world("bin_with_lib_dep"); } ================================================ FILE: sample_projects/bin_with_lib_git_dep/Cargo.toml ================================================ [package] name = "bin_with_lib_git_dep" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" [dependencies] "nix-base32" = { git = "https://github.com/kolloch/nix-base32", rev="42f5544e51187f0c7535d453fcffb4b524c99eb2" } ================================================ FILE: sample_projects/bin_with_lib_git_dep/crate-hashes.json ================================================ { "git+https://github.com/kolloch/nix-base32?rev=42f5544e51187f0c7535d453fcffb4b524c99eb2#0.1.2-alpha.0": "011f945b48xkilkqbvbsxazspz5z23ka0s90ms4jiqjbhiwll1nw" } ================================================ FILE: sample_projects/bin_with_lib_git_dep/src/main.rs ================================================ fn main() { nix_base32::to_nix_base32(&[1,2,3]); println!("Hello world from bin_with_lib_git_dep!"); } ================================================ FILE: sample_projects/bin_with_rerenamed_lib_dep/Cargo.toml ================================================ [package] name = "bin_with_rerenamed_lib_dep" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" [dependencies] "new_name_hello_world_lib" = { package = "hello_world_lib", path = "../lib"} "feature_hello_world_lib_and_bin" = { package = "hello_world_lib_and_bin", path = "../lib_and_bin", optional = true} [features] default = [ "enable_renamed_crate" ] enable_renamed_crate = [ "feature_hello_world_lib_and_bin" ] ================================================ FILE: sample_projects/bin_with_rerenamed_lib_dep/crate-hashes.json ================================================ {} ================================================ FILE: sample_projects/bin_with_rerenamed_lib_dep/src/main.rs ================================================ fn main() { new_name_hello_world_lib::hello_world("bin_with_rerenamed_lib_dep"); } ================================================ FILE: sample_projects/cdylib/Cargo.toml ================================================ [package] name = "cdylib" version = "0.1.0" authors = ["Andreas Rammhold "] edition = "2018" [lib] crate-type = ["cdylib"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] ================================================ FILE: sample_projects/cdylib/src/lib.rs ================================================ #[no_mangle] pub extern "C" fn some_function() { println!("cdylib test"); } ================================================ FILE: sample_projects/cdylib/test.nix ================================================ { pkgs ? import ../../nix/nixpkgs.nix { config = { }; }, generatedCargoNix }: let basePackage = pkgs.callPackage generatedCargoNix { }; lib = basePackage.rootCrate.build.lib; src = pkgs.writeText "main.c" '' extern void some_function(void); int main(int argc, char** argv) { some_function(); } ''; in pkgs.runCommandCC "link-cdylib" { crateName = "cdylib"; } '' mkdir -p $out/bin cc -L${lib}/lib/ -lcdylib ${src} -o $out/bin/cdylib '' ================================================ FILE: sample_projects/cfg-test/.gitignore ================================================ /target **/*.rs.bk ================================================ FILE: sample_projects/cfg-test/Cargo.toml ================================================ [package] name = "cfg-test" version = "0.1.0" authors = ["Andreas Rammhold "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] [target.'cfg(test)'.dependencies] tracing = { version = "0.1.5", features = ["log"] } ================================================ FILE: sample_projects/cfg-test/crate-hashes.json ================================================ {} ================================================ FILE: sample_projects/cfg-test/snowflake.txt ================================================ ❄️ ================================================ FILE: sample_projects/cfg-test/src/lib.rs ================================================ #[cfg(test)] #[test] fn lib_test() { println!("lib test executed"); } ================================================ FILE: sample_projects/cfg-test/src/main.rs ================================================ fn main() { println!("Hello, cfg-test!"); } ================================================ FILE: sample_projects/cfg-test/test.nix ================================================ { pkgs ? import ../../nix/nixpkgs.nix { config = { }; } , generatedCargoNix ? ./Cargo.nix { } }: let instantiatedBuild = pkgs.callPackage generatedCargoNix { }; in instantiatedBuild.rootCrate.build.override { runTests = true; testInputs = [ pkgs.cowsay ]; } ================================================ FILE: sample_projects/cfg-test/tests/echo_foo_test.rs ================================================ #[test] fn echo_foo_test() { println!("echo_foo_test"); } #[test] fn in_source_dir() { let path = std::path::Path::new("./snowflake.txt"); assert!(path.exists()) } #[test] fn exec_cowsay() { std::process::Command::new("cowsay").spawn().unwrap(); } ================================================ FILE: sample_projects/codegen/Cargo.nix ================================================ # This file was @generated by crate2nix 0.15.0 with the command: # "generate" "-f" "sample_projects/codegen/Cargo.toml" "-o" "sample_projects/codegen/Cargo.nix" # See https://github.com/kolloch/crate2nix for more info. { nixpkgs ? , pkgs ? import nixpkgs { config = {}; } , fetchurl ? pkgs.fetchurl , lib ? pkgs.lib , stdenv ? pkgs.stdenv , buildRustCrateForPkgs ? pkgs: pkgs.buildRustCrate # This is used as the `crateOverrides` argument for `buildRustCrate`. , defaultCrateOverrides ? pkgs.defaultCrateOverrides # The features to enable for the root_crate or the workspace_members. , rootFeatures ? [ "default" ] # If true, throw errors instead of issueing deprecation warnings. , strictDeprecation ? false # Elements to add to the `-C target-feature=` argument passed to `rustc` # (separated by `,`, prefixed with `+`). # Used for conditional compilation based on CPU feature detection. , targetFeatures ? [] # Additional target attributes for conditional dependencies. # Use this for custom cfg flags that are passed via rustcflags but need to # be known at Nix evaluation time for dependency resolution. # Example: { tracing_unstable = true; } for crates using cfg(tracing_unstable). , extraTargetFlags ? {} # Whether to perform release builds: longer compile times, faster binaries. , release ? true # Additional crate2nix configuration if it exists. , crateConfig ? if builtins.pathExists ./crate-config.nix then pkgs.callPackage ./crate-config.nix {} else {} }: rec { # # "public" attributes that we attempt to keep stable with new versions of crate2nix. # rootCrate = rec { packageId = "codegen"; # Use this attribute to refer to the derivation building your root crate package. # You can override the features with rootCrate.build.override { features = [ "default" "feature1" ... ]; }. build = internal.buildRustCrateWithFeatures { inherit packageId; }; # Debug support which might change between releases. # File a bug if you depend on any for non-debug work! debug = internal.debugCrate { inherit packageId; }; }; # Refer your crate build derivation by name here. # You can override the features with # workspaceMembers."${crateName}".build.override { features = [ "default" "feature1" ... ]; }. workspaceMembers = { "codegen" = rec { packageId = "codegen"; build = internal.buildRustCrateWithFeatures { packageId = "codegen"; }; # Debug support which might change between releases. # File a bug if you depend on any for non-debug work! debug = internal.debugCrate { inherit packageId; }; }; }; # A derivation that joins the outputs of all workspace members together. allWorkspaceMembers = pkgs.symlinkJoin { name = "all-workspace-members"; paths = let members = builtins.attrValues workspaceMembers; in builtins.map (m: m.build) members; }; # # "internal" ("private") attributes that may change in every new version of crate2nix. # internal = rec { # Build and dependency information for crates. # Many of the fields are passed one-to-one to buildRustCrate. # # Noteworthy: # * `dependencies`/`buildDependencies`: similar to the corresponding fields for buildRustCrate. # but with additional information which is used during dependency/feature resolution. # * `resolvedDependencies`: the selected default features reported by cargo - only included for debugging. # * `devDependencies` as of now not used by `buildRustCrate` but used to # inject test dependencies into the build crates = { "ansi_term" = rec { crateName = "ansi_term"; version = "0.12.1"; edition = "2015"; sha256 = "1ljmkbilxgmhavxvxqa7qvm6f3fjggi7q2l3a72q9x0cxjvrnanm"; authors = [ "ogham@bsago.me" "Ryan Scheel (Havvy) " "Josh Triplett " ]; dependencies = [ { name = "winapi"; packageId = "winapi"; target = { target, features }: ("windows" == target."os" or null); features = [ "consoleapi" "errhandlingapi" "fileapi" "handleapi" "processenv" ]; } ]; features = { "derive_serde_style" = [ "serde" ]; "serde" = [ "dep:serde" ]; }; }; "atty" = rec { crateName = "atty"; version = "0.2.14"; edition = "2015"; sha256 = "1s7yslcs6a28c5vz7jwj63lkfgyx8mx99fdirlhi9lbhhzhrpcyr"; authors = [ "softprops " ]; dependencies = [ { name = "hermit-abi"; packageId = "hermit-abi"; target = { target, features }: ("hermit" == target."os" or null); } { name = "libc"; packageId = "libc"; usesDefaultFeatures = false; target = { target, features }: (target."unix" or false); } { name = "winapi"; packageId = "winapi"; target = { target, features }: (target."windows" or false); features = [ "consoleapi" "processenv" "minwinbase" "minwindef" "winbase" ]; } ]; }; "bitflags" = rec { crateName = "bitflags"; version = "1.3.2"; edition = "2018"; sha256 = "12ki6w8gn1ldq7yz9y680llwk5gmrhrzszaa17g1sbrw2r2qvwxy"; authors = [ "The Rust Project Developers" ]; features = { "compiler_builtins" = [ "dep:compiler_builtins" ]; "core" = [ "dep:core" ]; "rustc-dep-of-std" = [ "core" "compiler_builtins" ]; }; resolvedDefaultFeatures = [ "default" ]; }; "clap" = rec { crateName = "clap"; version = "2.34.0"; edition = "2018"; sha256 = "071q5d8jfwbazi6zhik9xwpacx5i6kb2vkzy060vhf0c3120aqd0"; authors = [ "Kevin K. " ]; dependencies = [ { name = "ansi_term"; packageId = "ansi_term"; optional = true; target = { target, features }: (!(target."windows" or false)); } { name = "atty"; packageId = "atty"; optional = true; } { name = "bitflags"; packageId = "bitflags"; } { name = "strsim"; packageId = "strsim"; optional = true; } { name = "textwrap"; packageId = "textwrap"; } { name = "unicode-width"; packageId = "unicode-width"; } { name = "vec_map"; packageId = "vec_map"; optional = true; } ]; features = { "ansi_term" = [ "dep:ansi_term" ]; "atty" = [ "dep:atty" ]; "clippy" = [ "dep:clippy" ]; "color" = [ "ansi_term" "atty" ]; "default" = [ "suggestions" "color" "vec_map" ]; "doc" = [ "yaml" ]; "strsim" = [ "dep:strsim" ]; "suggestions" = [ "strsim" ]; "term_size" = [ "dep:term_size" ]; "vec_map" = [ "dep:vec_map" ]; "wrap_help" = [ "term_size" "textwrap/term_size" ]; "yaml" = [ "yaml-rust" ]; "yaml-rust" = [ "dep:yaml-rust" ]; }; resolvedDefaultFeatures = [ "ansi_term" "atty" "color" "default" "strsim" "suggestions" "vec_map" ]; }; "codegen" = rec { crateName = "codegen"; version = "0.1.0"; edition = "2018"; crateBin = [ { name = "codegen"; path = "src/main.rs"; requiredFeatures = [ ]; } ]; src = lib.cleanSourceWith { filter = sourceFilter; src = ./.; }; authors = [ "Peter Kolloch " ]; dependencies = [ { name = "dbus"; packageId = "dbus"; } ]; buildDependencies = [ { name = "dbus-codegen"; packageId = "dbus-codegen"; } ]; }; "dbus" = rec { crateName = "dbus"; version = "0.9.7"; edition = "2018"; workspace_member = null; src = pkgs.fetchgit { url = "https://github.com/diwic/dbus-rs.git"; rev = "618262f5e3217cdd173d46d705bbac26c5141e21"; sha256 = "0gvhz2knd1k799l7ssh4rdm5qw0vhazzr3bxpmlgq7fhy6hjazrs"; }; authors = [ "David Henningsson " ]; dependencies = [ { name = "libc"; packageId = "libc"; } { name = "libdbus-sys"; packageId = "libdbus-sys"; } { name = "winapi"; packageId = "winapi"; target = { target, features }: (target."windows" or false); features = [ "winsock2" ]; } ]; features = { "futures" = [ "futures-util" "futures-channel" ]; "futures-channel" = [ "dep:futures-channel" ]; "futures-executor" = [ "dep:futures-executor" ]; "futures-util" = [ "dep:futures-util" ]; "vendored" = [ "libdbus-sys/vendored" ]; }; }; "dbus-codegen" = rec { crateName = "dbus-codegen"; version = "0.10.0"; edition = "2018"; crateBin = []; workspace_member = null; src = pkgs.fetchgit { url = "https://github.com/diwic/dbus-rs.git"; rev = "618262f5e3217cdd173d46d705bbac26c5141e21"; sha256 = "0gvhz2knd1k799l7ssh4rdm5qw0vhazzr3bxpmlgq7fhy6hjazrs"; }; libName = "dbus_codegen"; authors = [ "David Henningsson " ]; dependencies = [ { name = "clap"; packageId = "clap"; } { name = "dbus"; packageId = "dbus"; optional = true; } { name = "xml-rs"; packageId = "xml-rs"; } ]; features = { "dbus" = [ "dep:dbus" ]; "dbus-crossroads" = [ "dep:dbus-crossroads" ]; "dbus-tree" = [ "dep:dbus-tree" ]; "default" = [ "dbus" ]; }; resolvedDefaultFeatures = [ "dbus" "default" ]; }; "hermit-abi" = rec { crateName = "hermit-abi"; version = "0.1.19"; edition = "2018"; sha256 = "0cxcm8093nf5fyn114w8vxbrbcyvv91d4015rdnlgfll7cs6gd32"; libName = "hermit_abi"; authors = [ "Stefan Lankes" ]; dependencies = [ { name = "libc"; packageId = "libc"; usesDefaultFeatures = false; } ]; features = { "compiler_builtins" = [ "dep:compiler_builtins" ]; "core" = [ "dep:core" ]; "rustc-dep-of-std" = [ "core" "compiler_builtins/rustc-dep-of-std" "libc/rustc-dep-of-std" ]; }; resolvedDefaultFeatures = [ "default" ]; }; "libc" = rec { crateName = "libc"; version = "0.2.152"; edition = "2015"; sha256 = "1rsnma7hnw22w7jh9yqg43slddvfbnfzrvm3s7s4kinbj1jvzqqk"; authors = [ "The Rust Project Developers" ]; features = { "default" = [ "std" ]; "rustc-dep-of-std" = [ "align" "rustc-std-workspace-core" ]; "rustc-std-workspace-core" = [ "dep:rustc-std-workspace-core" ]; "use_std" = [ "std" ]; }; resolvedDefaultFeatures = [ "default" "std" ]; }; "libdbus-sys" = rec { crateName = "libdbus-sys"; version = "0.2.5"; edition = "2015"; links = "dbus"; workspace_member = null; src = pkgs.fetchgit { url = "https://github.com/diwic/dbus-rs.git"; rev = "618262f5e3217cdd173d46d705bbac26c5141e21"; sha256 = "0gvhz2knd1k799l7ssh4rdm5qw0vhazzr3bxpmlgq7fhy6hjazrs"; }; libName = "libdbus_sys"; authors = [ "David Henningsson " ]; buildDependencies = [ { name = "pkg-config"; packageId = "pkg-config"; optional = true; } ]; features = { "cc" = [ "dep:cc" ]; "default" = [ "pkg-config" ]; "pkg-config" = [ "dep:pkg-config" ]; "vendored" = [ "cc" ]; }; resolvedDefaultFeatures = [ "default" "pkg-config" ]; }; "pkg-config" = rec { crateName = "pkg-config"; version = "0.3.28"; edition = "2015"; sha256 = "16kgffwncx5hsppsdf54z6jnjkhwywqy601cxk3rqncyi9zmilv9"; libName = "pkg_config"; authors = [ "Alex Crichton " ]; }; "strsim" = rec { crateName = "strsim"; version = "0.8.0"; edition = "2015"; sha256 = "0sjsm7hrvjdifz661pjxq5w4hf190hx53fra8dfvamacvff139cf"; authors = [ "Danny Guo " ]; }; "textwrap" = rec { crateName = "textwrap"; version = "0.11.0"; edition = "2015"; sha256 = "0q5hky03ik3y50s9sz25r438bc4nwhqc6dqwynv4wylc807n29nk"; authors = [ "Martin Geisler " ]; dependencies = [ { name = "unicode-width"; packageId = "unicode-width"; } ]; features = { "hyphenation" = [ "dep:hyphenation" ]; "term_size" = [ "dep:term_size" ]; }; }; "unicode-width" = rec { crateName = "unicode-width"; version = "0.1.11"; edition = "2015"; sha256 = "11ds4ydhg8g7l06rlmh712q41qsrd0j0h00n1jm74kww3kqk65z5"; libName = "unicode_width"; authors = [ "kwantam " "Manish Goregaokar " ]; features = { "compiler_builtins" = [ "dep:compiler_builtins" ]; "core" = [ "dep:core" ]; "rustc-dep-of-std" = [ "std" "core" "compiler_builtins" ]; "std" = [ "dep:std" ]; }; resolvedDefaultFeatures = [ "default" ]; }; "vec_map" = rec { crateName = "vec_map"; version = "0.8.2"; edition = "2015"; sha256 = "1481w9g1dw9rxp3l6snkdqihzyrd2f8vispzqmwjwsdyhw8xzggi"; authors = [ "Alex Crichton " "Jorge Aparicio " "Alexis Beingessner " "Brian Anderson <>" "tbu- <>" "Manish Goregaokar <>" "Aaron Turon " "Adolfo Ochagavía <>" "Niko Matsakis <>" "Steven Fackler <>" "Chase Southwood " "Eduard Burtescu <>" "Florian Wilkens <>" "Félix Raimundo <>" "Tibor Benke <>" "Markus Siemens " "Josh Branchaud " "Huon Wilson " "Corey Farwell " "Aaron Liblong <>" "Nick Cameron " "Patrick Walton " "Felix S Klock II <>" "Andrew Paseltiner " "Sean McArthur " "Vadim Petrochenkov <>" ]; features = { "eders" = [ "serde" ]; "serde" = [ "dep:serde" ]; }; }; "winapi" = rec { crateName = "winapi"; version = "0.3.9"; edition = "2015"; sha256 = "06gl025x418lchw1wxj64ycr7gha83m44cjr5sarhynd9xkrm0sw"; authors = [ "Peter Atashian " ]; dependencies = [ { name = "winapi-i686-pc-windows-gnu"; packageId = "winapi-i686-pc-windows-gnu"; target = { target, features }: (target.name == "i686-pc-windows-gnu"); } { name = "winapi-x86_64-pc-windows-gnu"; packageId = "winapi-x86_64-pc-windows-gnu"; target = { target, features }: (target.name == "x86_64-pc-windows-gnu"); } ]; features = { "debug" = [ "impl-debug" ]; }; resolvedDefaultFeatures = [ "consoleapi" "errhandlingapi" "fileapi" "handleapi" "minwinbase" "minwindef" "processenv" "winbase" "winsock2" ]; }; "winapi-i686-pc-windows-gnu" = rec { crateName = "winapi-i686-pc-windows-gnu"; version = "0.4.0"; edition = "2015"; sha256 = "1dmpa6mvcvzz16zg6d5vrfy4bxgg541wxrcip7cnshi06v38ffxc"; libName = "winapi_i686_pc_windows_gnu"; authors = [ "Peter Atashian " ]; }; "winapi-x86_64-pc-windows-gnu" = rec { crateName = "winapi-x86_64-pc-windows-gnu"; version = "0.4.0"; edition = "2015"; sha256 = "0gqq64czqb64kskjryj8isp62m2sgvx25yyj3kpc2myh85w24bki"; libName = "winapi_x86_64_pc_windows_gnu"; authors = [ "Peter Atashian " ]; }; "xml-rs" = rec { crateName = "xml-rs"; version = "0.8.19"; edition = "2021"; crateBin = []; sha256 = "0nnpvk3fv32hgh7vs9gbg2swmzxx5yz73f4b7rak7q39q2x9rjqg"; libName = "xml"; authors = [ "Vladimir Matveev " ]; }; }; # # crate2nix/default.nix (excerpt start) # /* Target (platform) data for conditional dependencies. This corresponds roughly to what buildRustCrate is setting. */ makeDefaultTarget = platform: { name = platform.rust.rustcTarget; unix = platform.isUnix; windows = platform.isWindows; fuchsia = true; test = false; inherit (platform.rust.platform) arch os vendor ; family = platform.rust.platform.target-family; env = "gnu"; endian = if platform.parsed.cpu.significantByte.name == "littleEndian" then "little" else "big"; pointer_width = toString platform.parsed.cpu.bits; debug_assertions = false; } // extraTargetFlags; registryUrl = { registries , url , crate , version , sha256 , }: let dl = registries.${url}.dl; tmpl = [ "{crate}" "{version}" "{prefix}" "{lowerprefix}" "{sha256-checksum}" ]; in with lib.strings; if lib.lists.any (i: hasInfix "{}" dl) tmpl then let prefix = if builtins.stringLength crate == 1 then "1" else if builtins.stringLength crate == 2 then "2" else "${builtins.substring 0 2 crate}/${builtins.substring 2 (builtins.stringLength crate - 2) crate}"; in builtins.replaceStrings tmpl [ crate version prefix (lib.strings.toLower prefix) sha256 ] else "${dl}/${crate}/${version}/download"; # Filters common temp files and build files. # TODO(pkolloch): Substitute with gitignore filter sourceFilter = name: type: let baseName = builtins.baseNameOf (builtins.toString name); in !( # Filter out git baseName == ".gitignore" || (type == "directory" && baseName == ".git") # Filter out build results || ( type == "directory" && ( baseName == "target" || baseName == "_site" || baseName == ".sass-cache" || baseName == ".jekyll-metadata" || baseName == "build-artifacts" ) ) # Filter out nix-build result symlinks || (type == "symlink" && lib.hasPrefix "result" baseName) # Filter out IDE config || (type == "directory" && (baseName == ".idea" || baseName == ".vscode")) || lib.hasSuffix ".iml" baseName # Filter out nix build files || baseName == "Cargo.nix" # Filter out editor backup / swap files. || lib.hasSuffix "~" baseName || builtins.match "^\\.sw[a-z]$$" baseName != null || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null || lib.hasSuffix ".tmp" baseName || lib.hasSuffix ".bak" baseName || baseName == "tests.nix" ); /* Returns a crate which depends on successful test execution of crate given as the second argument. testCrateFlags: list of flags to pass to the test exectuable testInputs: list of packages that should be available during test execution */ crateWithTest = { crate , testCrate , testCrateFlags , testInputs , testPreRun , testPostRun , }: assert builtins.typeOf testCrateFlags == "list"; assert builtins.typeOf testInputs == "list"; assert builtins.typeOf testPreRun == "string"; assert builtins.typeOf testPostRun == "string"; let # override the `crate` so that it will build and execute tests instead of # building the actual lib and bin targets We just have to pass `--test` # to rustc and it will do the right thing. We execute the tests and copy # their log and the test executables to $out for later inspection. test = let drv = testCrate.override (_: { buildTests = true; }); # If the user hasn't set any pre/post commands, we don't want to # insert empty lines. This means that any existing users of crate2nix # don't get a spurious rebuild unless they set these explicitly. testCommand = pkgs.lib.concatStringsSep "\n" ( pkgs.lib.filter (s: s != "") [ testPreRun "$f $testCrateFlags 2>&1 | tee -a $out" testPostRun ] ); in pkgs.stdenvNoCC.mkDerivation { name = "run-tests-${testCrate.name}"; inherit (crate) src; inherit testCrateFlags; buildInputs = testInputs; buildPhase = '' set -e export RUST_BACKTRACE=1 # build outputs testRoot=target/debug mkdir -p $testRoot # executables of the crate # we copy to prevent std::env::current_exe() to resolve to a store location for i in ${crate}/bin/*; do cp "$i" "$testRoot" done chmod +w -R . # test harness executables are suffixed with a hash, like cargo does # this allows to prevent name collision with the main # executables of the crate hash=$(basename $out) for file in ${drv}/tests/*; do f=$testRoot/$(basename $file)-$hash cp $file $f ${testCommand} done ''; }; in pkgs.runCommand "${crate.name}-linked" { inherit (crate) outputs crateName meta; passthru = (crate.passthru or { }) // { inherit test; }; } ( lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) '' echo tested by ${test} '' + '' ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs} '' ); # A restricted overridable version of builtRustCratesWithFeatures. buildRustCrateWithFeatures = { packageId , features ? rootFeatures , crateOverrides ? defaultCrateOverrides , buildRustCrateForPkgsFunc ? null , runTests ? false , testCrateFlags ? [ ] , testInputs ? [ ] , # Any command to run immediatelly before a test is executed. testPreRun ? "" , # Any command run immediatelly after a test is executed. testPostRun ? "" , }: lib.makeOverridable ( { features , crateOverrides , runTests , testCrateFlags , testInputs , testPreRun , testPostRun , }: let buildRustCrateForPkgsFuncOverriden = if buildRustCrateForPkgsFunc != null then buildRustCrateForPkgsFunc else ( if crateOverrides == pkgs.defaultCrateOverrides then buildRustCrateForPkgs else pkgs: (buildRustCrateForPkgs pkgs).override { defaultCrateOverrides = crateOverrides; } ); builtRustCrates = builtRustCratesWithFeatures { inherit packageId features; buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden; runTests = false; }; builtTestRustCrates = builtRustCratesWithFeatures { inherit packageId features; buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden; runTests = true; }; drv = builtRustCrates.crates.${packageId}; testDrv = builtTestRustCrates.crates.${packageId}; derivation = if runTests then crateWithTest { crate = drv; testCrate = testDrv; inherit testCrateFlags testInputs testPreRun testPostRun ; } else drv; in derivation ) { inherit features crateOverrides runTests testCrateFlags testInputs testPreRun testPostRun ; }; /* Returns an attr set with packageId mapped to the result of buildRustCrateForPkgsFunc for the corresponding crate. */ builtRustCratesWithFeatures = { packageId , features , crateConfigs ? crates , buildRustCrateForPkgsFunc , runTests , makeTarget ? makeDefaultTarget , }@args: assert (builtins.isAttrs crateConfigs); assert (builtins.isString packageId); assert (builtins.isList features); assert (builtins.isAttrs (makeTarget stdenv.hostPlatform)); assert (builtins.isBool runTests); let rootPackageId = packageId; mergedFeatures = mergePackageFeatures ( args // { inherit rootPackageId; target = makeTarget stdenv.hostPlatform // { test = runTests; }; } ); # Memoize built packages so that reappearing packages are only built once. builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs; mkBuiltByPackageIdByPkgs = pkgs: let self = { crates = lib.mapAttrs ( packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId ) crateConfigs; target = makeTarget pkgs.stdenv.hostPlatform; build = mkBuiltByPackageIdByPkgs pkgs.buildPackages; }; in self; buildByPackageIdForPkgsImpl = self: pkgs: packageId: let features = mergedFeatures."${packageId}" or [ ]; crateConfig' = crateConfigs."${packageId}"; crateConfig = builtins.removeAttrs crateConfig' [ "resolvedDefaultFeatures" "devDependencies" ]; devDependencies = lib.optionals (runTests && packageId == rootPackageId) ( crateConfig'.devDependencies or [ ] ); dependencies = dependencyDerivations { inherit features; inherit (self) target; buildByPackageId = depPackageId: # proc_macro crates must be compiled for the build architecture if crateConfigs.${depPackageId}.procMacro or false then self.build.crates.${depPackageId} else self.crates.${depPackageId}; dependencies = (crateConfig.dependencies or [ ]) ++ devDependencies; }; buildDependencies = dependencyDerivations { inherit features; inherit (self.build) target; buildByPackageId = depPackageId: self.build.crates.${depPackageId}; dependencies = crateConfig.buildDependencies or [ ]; }; dependenciesWithRenames = let buildDeps = filterEnabledDependencies { inherit features; inherit (self) target; dependencies = crateConfig.dependencies or [ ] ++ devDependencies; }; hostDeps = filterEnabledDependencies { inherit features; inherit (self.build) target; dependencies = crateConfig.buildDependencies or [ ]; }; in lib.filter (d: d ? "rename") (hostDeps ++ buildDeps); # Crate renames have the form: # # { # crate_name = [ # { version = "1.2.3"; rename = "crate_name01"; } # ]; # # ... # } crateRenames = let grouped = lib.groupBy (dependency: dependency.name) dependenciesWithRenames; versionAndRename = dep: let package = crateConfigs."${dep.packageId}"; in { inherit (dep) rename; inherit (package) version; }; in lib.mapAttrs (name: builtins.map versionAndRename) grouped; in buildRustCrateForPkgsFunc pkgs ( crateConfig // { src = crateConfig.src or (fetchurl rec { name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz"; # https://www.pietroalbini.org/blog/downloading-crates-io/ # Not rate-limited, CDN URL. url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate"; sha256 = assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}"); crateConfig.sha256; }); extraRustcOpts = lib.lists.optional (targetFeatures != [ ]) "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}"; inherit features dependencies buildDependencies crateRenames release ; } ); in builtByPackageIdByPkgs; # Returns the actual derivations for the given dependencies. dependencyDerivations = { buildByPackageId , features , dependencies , target , }: assert (builtins.isList features); assert (builtins.isList dependencies); assert (builtins.isAttrs target); let enabledDependencies = filterEnabledDependencies { inherit dependencies features target; }; depDerivation = dependency: buildByPackageId dependency.packageId; in map depDerivation enabledDependencies; /* Returns a sanitized version of val with all values substituted that cannot be serialized as JSON. */ sanitizeForJson = val: if builtins.isAttrs val then lib.mapAttrs (n: sanitizeForJson) val else if builtins.isList val then builtins.map sanitizeForJson val else if builtins.isFunction val then "function" else val; # Returns various tools to debug a crate. debugCrate = { packageId , target ? makeDefaultTarget stdenv.hostPlatform , }: assert (builtins.isString packageId); let debug = rec { # The built tree as passed to buildRustCrate. buildTree = buildRustCrateWithFeatures { buildRustCrateForPkgsFunc = _: lib.id; inherit packageId; }; sanitizedBuildTree = sanitizeForJson buildTree; dependencyTree = sanitizeForJson (buildRustCrateWithFeatures { buildRustCrateForPkgsFunc = _: crate: { "01_crateName" = crate.crateName or false; "02_features" = crate.features or [ ]; "03_dependencies" = crate.dependencies or [ ]; }; inherit packageId; }); mergedPackageFeatures = mergePackageFeatures { features = rootFeatures; inherit packageId target; }; diffedDefaultPackageFeatures = diffDefaultPackageFeatures { inherit packageId target; }; }; in { internal = debug; }; /* Returns differences between cargo default features and crate2nix default features. This is useful for verifying the feature resolution in crate2nix. */ diffDefaultPackageFeatures = { crateConfigs ? crates , packageId , target , }: assert (builtins.isAttrs crateConfigs); let prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; }); mergedFeatures = prefixValues "crate2nix" (mergePackageFeatures { inherit crateConfigs packageId target; features = [ "default" ]; }); configs = prefixValues "cargo" crateConfigs; combined = lib.foldAttrs (a: b: a // b) { } [ mergedFeatures configs ]; onlyInCargo = builtins.attrNames ( lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined ); onlyInCrate2Nix = builtins.attrNames ( lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined ); differentFeatures = lib.filterAttrs ( n: v: (v ? "crate2nix") && (v ? "cargo") && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ]) ) combined; in builtins.toJSON { inherit onlyInCargo onlyInCrate2Nix differentFeatures; }; /* Returns an attrset mapping packageId to the list of enabled features. If multiple paths to a dependency enable different features, the corresponding feature sets are merged. Features in rust are additive. */ mergePackageFeatures = { crateConfigs ? crates , packageId , rootPackageId ? packageId , features ? rootFeatures , dependencyPath ? [ crates.${packageId}.crateName ] , featuresByPackageId ? { } , target , # Adds devDependencies to the crate with rootPackageId. runTests ? false , ... }@args: assert (builtins.isAttrs crateConfigs); assert (builtins.isString packageId); assert (builtins.isString rootPackageId); assert (builtins.isList features); assert (builtins.isList dependencyPath); assert (builtins.isAttrs featuresByPackageId); assert (builtins.isAttrs target); assert (builtins.isBool runTests); let crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}"); expandedFeatures = expandFeatures (crateConfig.features or { }) features; enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures; depWithResolvedFeatures = dependency: let inherit (dependency) packageId; features = dependencyFeatures enabledFeatures dependency; in { inherit packageId features; }; resolveDependencies = cache: path: dependencies: assert (builtins.isAttrs cache); assert (builtins.isList dependencies); let enabledDependencies = filterEnabledDependencies { inherit dependencies target; features = enabledFeatures; }; directDependencies = map depWithResolvedFeatures enabledDependencies; foldOverCache = op: lib.foldl op cache directDependencies; in foldOverCache ( cache: { packageId, features }: let cacheFeatures = cache.${packageId} or [ ]; combinedFeatures = sortedUnique (cacheFeatures ++ features); in if cache ? ${packageId} && cache.${packageId} == combinedFeatures then cache else mergePackageFeatures { features = combinedFeatures; featuresByPackageId = cache; inherit crateConfigs packageId target runTests rootPackageId ; } ); cacheWithSelf = let cacheFeatures = featuresByPackageId.${packageId} or [ ]; combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures); in featuresByPackageId // { "${packageId}" = combinedFeatures; }; cacheWithDependencies = resolveDependencies cacheWithSelf "dep" ( crateConfig.dependencies or [ ] ++ lib.optionals (runTests && packageId == rootPackageId) (crateConfig.devDependencies or [ ]) ); cacheWithAll = resolveDependencies cacheWithDependencies "build" ( crateConfig.buildDependencies or [ ] ); in cacheWithAll; # Returns the enabled dependencies given the enabled features. filterEnabledDependencies = { dependencies , features , target , }: assert (builtins.isList dependencies); assert (builtins.isList features); assert (builtins.isAttrs target); lib.filter ( dep: let targetFunc = dep.target or (features: true); in targetFunc { inherit features target; } && (!(dep.optional or false) || builtins.any (doesFeatureEnableDependency dep) features) ) dependencies; # Returns whether the given feature should enable the given dependency. doesFeatureEnableDependency = dependency: feature: let name = dependency.rename or dependency.name; prefix = "${name}/"; len = builtins.stringLength prefix; startsWithPrefix = builtins.substring 0 len feature == prefix; in feature == name || feature == "dep:" + name || startsWithPrefix; /* Returns the expanded features for the given inputFeatures by applying the rules in featureMap. featureMap is an attribute set which maps feature names to lists of further feature names to enable in case this feature is selected. */ expandFeatures = featureMap: inputFeatures: assert (builtins.isAttrs featureMap); assert (builtins.isList inputFeatures); let expandFeaturesNoCycle = oldSeen: inputFeatures: if inputFeatures != [ ] then let # The feature we're currently expanding. feature = builtins.head inputFeatures; # All the features we've seen/expanded so far, including the one # we're currently processing. seen = oldSeen // { ${feature} = 1; }; # Expand the feature but be careful to not re-introduce a feature # that we've already seen: this can easily cause a cycle, see issue # #209. enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]); in [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables)) # No more features left, nothing to expand to. else [ ]; outFeatures = expandFeaturesNoCycle { } inputFeatures; in sortedUnique outFeatures; /* This function adds optional dependencies as features if they are enabled indirectly by dependency features. This function mimics Cargo's behavior described in a note at: https://doc.rust-lang.org/nightly/cargo/reference/features.html#dependency-features */ enableFeatures = dependencies: features: assert (builtins.isList features); assert (builtins.isList dependencies); let additionalFeatures = lib.concatMap ( dependency: assert (builtins.isAttrs dependency); let enabled = builtins.any (doesFeatureEnableDependency dependency) features; in if (dependency.optional or false) && enabled then [ (dependency.rename or dependency.name) ] else [ ] ) dependencies; in sortedUnique (features ++ additionalFeatures); /* Returns the actual features for the given dependency. features: The features of the crate that refers this dependency. */ dependencyFeatures = features: dependency: assert (builtins.isList features); assert (builtins.isAttrs dependency); let defaultOrNil = if dependency.usesDefaultFeatures or true then [ "default" ] else [ ]; explicitFeatures = dependency.features or [ ]; additionalDependencyFeatures = let name = dependency.rename or dependency.name; stripPrefixMatch = prefix: s: if lib.hasPrefix prefix s then lib.removePrefix prefix s else null; extractFeature = feature: lib.findFirst (f: f != null) null ( map (prefix: stripPrefixMatch prefix feature) [ (name + "/") (name + "?/") ] ); dependencyFeatures = lib.filter (f: f != null) (map extractFeature features); in dependencyFeatures; in defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures; # Sorts and removes duplicates from a list of strings. sortedUnique = features: assert (builtins.isList features); assert (builtins.all builtins.isString features); let outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features; outFeaturesUnique = builtins.attrNames outFeaturesSet; in builtins.sort (a: b: a < b) outFeaturesUnique; deprecationWarning = message: value: if strictDeprecation then builtins.throw "strictDeprecation enabled, aborting: ${message}" else builtins.trace message value; # # crate2nix/default.nix (excerpt end) # }; } ================================================ FILE: sample_projects/codegen/Cargo.toml ================================================ [package] name = "codegen" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" build = "build.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] dbus = { git = "https://github.com/diwic/dbus-rs.git" } [build-dependencies] dbus-codegen = { git = "https://github.com/diwic/dbus-rs" } ================================================ FILE: sample_projects/codegen/build.rs ================================================ // src/build.rs use std::env; use std::fs::File; use std::io::Write; use std::path::Path; use dbus_codegen; fn main() { let out_dir = env::var("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("hello.rs"); let mut f = File::create(&dest_path).unwrap(); f.write_all( b" pub fn message() -> &'static str { \"Hello, World!\" } ", ) .unwrap(); } ================================================ FILE: sample_projects/codegen/crate-hashes.json ================================================ { "git+https://github.com/diwic/dbus-rs.git#dbus-codegen@0.10.0": "0gvhz2knd1k799l7ssh4rdm5qw0vhazzr3bxpmlgq7fhy6hjazrs", "git+https://github.com/diwic/dbus-rs.git#dbus@0.9.7": "0gvhz2knd1k799l7ssh4rdm5qw0vhazzr3bxpmlgq7fhy6hjazrs", "git+https://github.com/diwic/dbus-rs.git#libdbus-sys@0.2.5": "0gvhz2knd1k799l7ssh4rdm5qw0vhazzr3bxpmlgq7fhy6hjazrs" } ================================================ FILE: sample_projects/codegen/src/main.rs ================================================ // src/main.rs include!(concat!(env!("OUT_DIR"), "/hello.rs")); fn main() { println!("{}", message()); } ================================================ FILE: sample_projects/conditional_features/Cargo.toml ================================================ [package] name = "conditional_features" version = "0.1.0" edition = "2021" [dependencies.optional] optional = true path = "./optional" [features] hello = ["optional"] foo = ["optional?/foo"] allow-build = ["optional?/allow-build"] ================================================ FILE: sample_projects/conditional_features/optional/Cargo.toml ================================================ [package] name = "optional" version = "0.1.0" edition = "2021" [features] foo = [] allow-build = [] ================================================ FILE: sample_projects/conditional_features/optional/src/lib.rs ================================================ #[cfg(not(feature = "allow-build"))] compile_error!("allow-build feature is not enabled, refusing to build"); pub fn maybe_foo() -> &'static str { #[cfg(feature = "foo")] { "foo" } #[cfg(not(feature = "foo"))] { "not foo" } } ================================================ FILE: sample_projects/conditional_features/src/main.rs ================================================ fn main() { #[cfg(feature = "hello")] println!("Hello, {}!", optional::maybe_foo()); #[cfg(all(not(feature = "hello"), feature = "foo"))] println!("Bye, foo!"); #[cfg(all(not(feature = "hello"), not(feature = "foo")))] println!("Bye, not foo!"); } ================================================ FILE: sample_projects/cross_compile_build_dependencies/Cargo.toml ================================================ [package] name = "cross_compile_build_dependencies" version = "1.0.0" edition = "2021" [build-dependencies] alice.path = "./alice" ================================================ FILE: sample_projects/cross_compile_build_dependencies/alice/Cargo.toml ================================================ [package] name = "alice" version = "1.0.0" edition = "2021" [target.'cfg(unix)'.dependencies] bob.path = "./bob" ================================================ FILE: sample_projects/cross_compile_build_dependencies/alice/bob/Cargo.toml ================================================ [package] name = "bob" version = "1.0.0" edition = "2021" ================================================ FILE: sample_projects/cross_compile_build_dependencies/alice/bob/src/lib.rs ================================================ ================================================ FILE: sample_projects/cross_compile_build_dependencies/alice/src/lib.rs ================================================ #[cfg(unix)] pub use bob; ================================================ FILE: sample_projects/cross_compile_build_dependencies/default.nix ================================================ { generatedCargoNix , pkgs ? import ../../nix/nixpkgs.nix { crossSystem.config = "wasm32-unknown-none"; } }: (pkgs.callPackage generatedCargoNix { }).rootCrate.build ================================================ FILE: sample_projects/cross_compile_build_dependencies/src/main.rs ================================================ fn main() {} ================================================ FILE: sample_projects/dependency_issue_65/Cargo.toml ================================================ [package] name = "dependency_issue_65" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] [dependencies.diesel] features = ["r2d2", "chrono"] version = "*" [features] default = ["postgres"] postgres = ["diesel/postgres"] sqlite = ["diesel/sqlite"] ================================================ FILE: sample_projects/dependency_issue_65/default.nix ================================================ { pkgs ? import ../../nix/nixpkgs.nix { config = { }; } , generatedCargoNix ? ./Cargo.nix { } }: let cargo_nix = pkgs.callPackage generatedCargoNix { }; bin = cargo_nix.rootCrate.build.override { features = [ "sqlite" ]; }; in bin ================================================ FILE: sample_projects/dependency_issue_65/src/main.rs ================================================ fn main() { println!("Hello, dependency_issue_65!"); } ================================================ FILE: sample_projects/empty_cross/Cargo.toml ================================================ [package] name = "empty_cross" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] ================================================ FILE: sample_projects/empty_cross/build.rs ================================================ #![cfg(not(target_os = "none"))] fn main() { } ================================================ FILE: sample_projects/empty_cross/default.nix ================================================ { pkgs ? import ../../nix/nixpkgs.nix { config = { }; } , generatedCargoNix ? ./Cargo.nix { } }: let pkgs0 = pkgs; in let pkgs = import pkgs0.path { config = { }; crossSystem = { config = "riscv32-unknown-none-elf"; rustc = { config = "riscv32i-unknown-none-elf"; }; }; }; inherit (pkgs) lib; instantiatedBuild = pkgs.callPackage generatedCargoNix { defaultCrateOverrides = pkgs.defaultCrateOverrides; buildRustCrateForPkgs = pkgs: let isBareMetal = pkgs.stdenv.hostPlatform.parsed.kernel.name == "none"; # Don't need other tools stdenvBase = if isBareMetal then pkgs.stdenvNoCC else pkgs.stdenv; stdenv = if stdenvBase.hostPlatform.extensions ? sharedLibrary then stdenvBase else lib.recursiveUpdate stdenvBase { # This is used in buildRustCrate. Should probably be optional there. hostPlatform.extensions.sharedLibrary = ""; }; fun = pkgs.buildRustCrate.override { inherit stdenv; # Don't bother with cross compiler since we don't need stdlib inherit (pkgs.buildPackages.buildPackages) rust rustc cargo; }; in args: fun (args // lib.optionalAttrs isBareMetal { RUSTC_BOOTSTRAP = true; # Needed for std for build.rs nativeBuildInputs = args.nativeBuildInputs or [ ] ++ lib.optional pkgs.stdenv.buildPlatform.isDarwin pkgs.buildPackages.libiconv; }); }; in instantiatedBuild.rootCrate.build ================================================ FILE: sample_projects/empty_cross/src/lib.rs ================================================ #![feature(no_core)] #![no_core] #![cfg_attr(not(target_os = "none"), feature(rustc_attrs))] #[cfg(not(target_os = "none"))] #[rustc_builtin_macro] macro_rules! compile_error { ($msg:expr $(,)?) => {{ /* compiler built-in */ }}; } #[cfg(not(target_os = "none"))] compile_error!{ "Didn't cross compile!" } ================================================ FILE: sample_projects/future_util_multi_version/Cargo.toml ================================================ [package] name = "future_util_multi_version" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] futures = { version = "0.3", default-features = false, features = ["compat"] } futures01 = { package = "futures", version = "0.1" } ================================================ FILE: sample_projects/future_util_multi_version/src/main.rs ================================================ use std::pin::Pin; use std::task::{Context, Poll}; struct StupidFuture01; impl futures01::Future for StupidFuture01 { type Item = (); type Error = String; fn poll(&mut self) -> futures01::Poll { Ok(futures01::Async::NotReady) } } struct StupidFuture; impl futures::Future for StupidFuture { type Output = (); fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { Poll::Pending } } fn main() { { use futures01::Future; let mut future01 = StupidFuture01; assert_eq!(future01.poll(), Ok(futures01::Async::NotReady)); } let _future = StupidFuture; println!("Hello, world!"); } ================================================ FILE: sample_projects/futures_compat/Cargo.toml ================================================ [package] # See bug https://github.com/kolloch/crate2nix/issues/56 name = "futures_compat" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] futures-preview = { version = "0.3.0-alpha.17", features = ["compat"] } ================================================ FILE: sample_projects/futures_compat/src/main.rs ================================================ const CRATE_NAME: &'static str = env!("CARGO_PKG_NAME"); fn main() { println!("Hello, {}!", CRATE_NAME); } ================================================ FILE: sample_projects/integration_test/Cargo.json ================================================ { "generator": "@generated by crate2nix", "root": "integration_test", "workspaceMembers": { "integration_test": "integration_test" }, "crates": { "cli_test_dir": { "crateName": "cli_test_dir", "version": "0.1.7", "edition": "2015", "sha256": "06fmd96s0fb0nya0h45mmmm769jd5q4bdpv7nzsd8f4mllw37iib", "source": { "type": "crates-io" }, "libCrateTypes": [ "lib" ], "authors": [ "Eric Kidd " ] }, "integration_test": { "crateName": "integration_test", "version": "0.1.0", "edition": "2018", "source": { "type": "local", "path": "." }, "devDependencies": [ { "name": "cli_test_dir", "packageId": "cli_test_dir" } ], "crateBin": [ { "name": "integration_test", "path": "src/main.rs" } ], "authors": [ "Symphorien Gibol " ] } } } ================================================ FILE: sample_projects/integration_test/Cargo.toml ================================================ [package] name = "integration_test" version = "0.1.0" authors = ["Symphorien Gibol "] edition = "2018" [dev-dependencies] cli_test_dir = "0.1" ================================================ FILE: sample_projects/integration_test/src/main.rs ================================================ /// a lousy implementation of cat fn main() { let args = std::env::args().collect::>(); if args.len() != 2 { println!("expected one argument"); } else { let path = std::path::PathBuf::from(&args[1]); let content = std::fs::read_to_string(path).unwrap(); print!("{}", content); } } ================================================ FILE: sample_projects/integration_test/test-json.nix ================================================ # PR #453: test that JSON-mode buildTests wires dev-dependencies. { pkgs ? import ../../nix/nixpkgs.nix { config = { }; } , generatedCargoNix ? null }: let cargoNix = import ../../lib/build-from-json.nix { inherit pkgs; src = ./.; resolvedJson = ./Cargo.json; }; in cargoNix.rootCrate.buildTests ================================================ FILE: sample_projects/integration_test/test.nix ================================================ { pkgs ? import ../../nix/nixpkgs.nix { config = { }; } , generatedCargoNix ? ./Cargo.nix }: let instantiatedBuild = pkgs.callPackage generatedCargoNix { }; in instantiatedBuild.rootCrate.build.override { runTests = true; } ================================================ FILE: sample_projects/integration_test/tests/hello ================================================ hello world ================================================ FILE: sample_projects/integration_test/tests/integration_test.rs ================================================ use cli_test_dir::*; #[test] fn read_source_file() { let testdir = TestDir::new("integration_test", "temp"); let source_file = testdir.src_path("tests/hello"); let output = testdir.cmd() .arg(source_file) .output() .expect_success(); let string = String::from_utf8_lossy(&output.stdout); assert_eq!(string, "hello world\n"); } #[test] fn write_output_file() { let testdir = TestDir::new("integration_test", "temp"); testdir.create_file("input.txt", "yay"); let output = testdir.cmd() .arg("input.txt") .output() .expect_success(); let string = String::from_utf8_lossy(&output.stdout); assert_eq!(string, "yay"); } ================================================ FILE: sample_projects/lib/Cargo.toml ================================================ [package] name = "hello_world_lib" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" [lib] # Regression test for issue #4 name = "renamed_hello_world_lib" [dependencies] ================================================ FILE: sample_projects/lib/src/lib.rs ================================================ pub fn hello_world(name: &str) { println!("Hello, {}!", name); } ================================================ FILE: sample_projects/lib_and_bin/Cargo.toml ================================================ [package] name = "hello_world_lib_and_bin" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" [dependencies] ================================================ FILE: sample_projects/lib_and_bin/crate-hashes.json ================================================ {} ================================================ FILE: sample_projects/lib_and_bin/src/lib.rs ================================================ pub fn hello_world() { println!("Hello, lib_and_bin!"); } ================================================ FILE: sample_projects/lib_and_bin/src/main.rs ================================================ use hello_world_lib_and_bin::hello_world; fn main() { hello_world(); } ================================================ FILE: sample_projects/multiple_bin/Cargo.toml ================================================ [package] name = "multiple_bin" version = "0.1.0" authors = ["Kristoffer Søholm "] edition = "2018" [[bin]] name = "bin1" path = "src/bin1.rs" [[bin]] name = "bin2" path = "src/bin2.rs" [dependencies] ================================================ FILE: sample_projects/multiple_bin/src/bin1.rs ================================================ fn main() { println!("Hello, world! from bin1"); } ================================================ FILE: sample_projects/multiple_bin/src/bin2.rs ================================================ fn main() { println!("Hello, world! from bin2"); } ================================================ FILE: sample_projects/multiple_bin/src/main.rs ================================================ fn main() { println!("Hello, world!"); } ================================================ FILE: sample_projects/nix_workspaces/.gitignore ================================================ workspace.nix workspace_members ================================================ FILE: sample_projects/nix_workspaces/Cargo.toml ================================================ # This file was @generated by crate2nix 0.9.0-alpha.1 with the command: # generate # See https://github.com/kolloch/crate2nix for more info. [workspace] members = [ "workspace_members/navi", "workspace_members/ytop", "workspace_members/ripgrep12", ] ================================================ FILE: sample_projects/nix_workspaces/nix/sources.nix ================================================ # This file has been generated by Niv. let # # The fetchers. fetch_ fetches specs of type . # fetch_file = pkgs: spec: if spec.builtin or true then builtins_fetchurl { inherit (spec) url sha256; } else pkgs.fetchurl { inherit (spec) url sha256; }; fetch_tarball = pkgs: spec: if spec.builtin or true then builtins_fetchTarball { inherit (spec) url sha256; } else pkgs.fetchzip { inherit (spec) url sha256; }; fetch_git = spec: builtins.fetchGit { url = spec.repo; inherit (spec) rev ref; }; fetch_builtin-tarball = spec: builtins.trace '' WARNING: The niv type "builtin-tarball" will soon be deprecated. You should instead use `builtin = true`. $ niv modify -a type=tarball -a builtin=true '' builtins_fetchTarball { inherit (spec) url sha256; }; fetch_builtin-url = spec: builtins.trace '' WARNING: The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`. $ niv modify -a type=file -a builtin=true '' (builtins_fetchurl { inherit (spec) url sha256; }); # # Various helpers # # The set of packages used when specs are fetched using non-builtins. mkPkgs = sources: let sourcesNixpkgs = import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { }; hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath; hasThisAsNixpkgsPath = == ./.; in if builtins.hasAttr "nixpkgs" sources then sourcesNixpkgs else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then import { } else abort '' Please specify either (through -I or NIX_PATH=nixpkgs=...) or add a package called "nixpkgs" to your sources.json. ''; # The actual fetching function. fetch = pkgs: name: spec: if ! builtins.hasAttr "type" spec then abort "ERROR: niv spec ${name} does not have a 'type' attribute" else if spec.type == "file" then fetch_file pkgs spec else if spec.type == "tarball" then fetch_tarball pkgs spec else if spec.type == "git" then fetch_git spec else if spec.type == "builtin-tarball" then fetch_builtin-tarball spec else if spec.type == "builtin-url" then fetch_builtin-url spec else abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}"; # Ports of functions for older nix versions # a Nix version of mapAttrs if the built-in doesn't exist mapAttrs = builtins.mapAttrs or ( f: set: with builtins; listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set)) ); # fetchTarball version that is compatible between all the versions of Nix builtins_fetchTarball = { url, sha256 }@attrs: let inherit (builtins) lessThan nixVersion fetchTarball; in if lessThan nixVersion "1.12" then fetchTarball { inherit url; } else fetchTarball attrs; # fetchurl version that is compatible between all the versions of Nix builtins_fetchurl = { url, sha256 }@attrs: let inherit (builtins) lessThan nixVersion fetchurl; in if lessThan nixVersion "1.12" then fetchurl { inherit url; } else fetchurl attrs; # Create the final "sources" from the config mkSources = config: mapAttrs ( name: spec: if builtins.hasAttr "outPath" spec then abort "The values in sources.json should not have an 'outPath' attribute" else spec // { outPath = fetch config.pkgs name spec; } ) config.sources; # The "config" used by the fetchers mkConfig = { sourcesFile ? ./sources.json , sources ? builtins.fromJSON (builtins.readFile sourcesFile) , pkgs ? mkPkgs sources }: rec { # The sources, i.e. the attribute set of spec name to spec inherit sources; # The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers inherit pkgs; }; in mkSources (mkConfig { }) // { __functor = _: settings: mkSources (mkConfig settings); } ================================================ FILE: sample_projects/numtest/Cargo.toml ================================================ [package] name = "numtest" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] num = "0.2.0" ================================================ FILE: sample_projects/numtest/crate-hashes.json ================================================ {} ================================================ FILE: sample_projects/numtest/src/main.rs ================================================ fn main() { println!("Hello from numtest, world!"); } ================================================ FILE: sample_projects/numtest_new_cargo_lock/Cargo.toml ================================================ [package] name = "numtest" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] num = "0.2.0" ================================================ FILE: sample_projects/numtest_new_cargo_lock/src/main.rs ================================================ fn main() { println!("Hello from numtest, world!"); } ================================================ FILE: sample_projects/renamed_build_deps/Cargo.toml ================================================ [package] name = "renamed_build_deps" version = "0.1.0" authors = ["Andreas Rammhold "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] [build-dependencies] which_crate = {package = "which", version = "3.0", default-features = false} ================================================ FILE: sample_projects/renamed_build_deps/build.rs ================================================ extern crate which_crate; fn main() {} ================================================ FILE: sample_projects/renamed_build_deps/src/main.rs ================================================ fn main() { println!("Hello, renamed_build_deps!"); } ================================================ FILE: sample_projects/renamed_dev_deps/Cargo.toml ================================================ [package] name = "renamed_dev_deps" version = "0.1.0" authors = ["John Ericson "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] # Checks that renames in dev-dependencies work. [dev-dependencies] futures01 = { package = "futures", version = "0.1", default-features = false } ================================================ FILE: sample_projects/renamed_dev_deps/src/lib.rs ================================================ #[cfg(test)] mod test { extern crate futures01; #[allow(unused)] use futures01::Future; #[test] fn ran_a_test() { } } ================================================ FILE: sample_projects/renamed_dev_deps/test.nix ================================================ { pkgs ? import ../../nix/nixpkgs.nix { config = { }; } , generatedCargoNix ? ./Cargo.nix { } }: let instantiatedBuild = pkgs.callPackage generatedCargoNix { }; in instantiatedBuild.rootCrate.build.override { runTests = true; } ================================================ FILE: sample_projects/renamed_dev_deps/tests.rs ================================================ extern crate futures01; use futures01::Future; #[test] fn ran_a_test() { } ================================================ FILE: sample_projects/renaming/Cargo.toml ================================================ [package] name = "renaming" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diesel = { version = "1.4", features = ["postgres", "uuid"] } ================================================ FILE: sample_projects/renaming/src/main.rs ================================================ fn main() { println!("Hello, world!"); } ================================================ FILE: sample_projects/simple_dep/Cargo.toml ================================================ [package] name = "simple_dep" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] nix-base32 = "*" ================================================ FILE: sample_projects/simple_dep/src/main.rs ================================================ fn main() { println!("Hello, simple_dep!"); } ================================================ FILE: sample_projects/sub_dir_crates/Cargo.nix ================================================ # This file was @generated by crate2nix 0.15.0 with the command: # "generate" "-f" "sample_projects/sub_dir_crates/Cargo.toml" "-o" "sample_projects/sub_dir_crates/Cargo.nix" # See https://github.com/kolloch/crate2nix for more info. { nixpkgs ? , pkgs ? import nixpkgs { config = {}; } , fetchurl ? pkgs.fetchurl , lib ? pkgs.lib , stdenv ? pkgs.stdenv , buildRustCrateForPkgs ? pkgs: pkgs.buildRustCrate # This is used as the `crateOverrides` argument for `buildRustCrate`. , defaultCrateOverrides ? pkgs.defaultCrateOverrides # The features to enable for the root_crate or the workspace_members. , rootFeatures ? [ "default" ] # If true, throw errors instead of issueing deprecation warnings. , strictDeprecation ? false # Elements to add to the `-C target-feature=` argument passed to `rustc` # (separated by `,`, prefixed with `+`). # Used for conditional compilation based on CPU feature detection. , targetFeatures ? [] # Additional target attributes for conditional dependencies. # Use this for custom cfg flags that are passed via rustcflags but need to # be known at Nix evaluation time for dependency resolution. # Example: { tracing_unstable = true; } for crates using cfg(tracing_unstable). , extraTargetFlags ? {} # Whether to perform release builds: longer compile times, faster binaries. , release ? true # Additional crate2nix configuration if it exists. , crateConfig ? if builtins.pathExists ./crate-config.nix then pkgs.callPackage ./crate-config.nix {} else {} }: rec { # # "public" attributes that we attempt to keep stable with new versions of crate2nix. # rootCrate = rec { packageId = "sub_dir_crates"; # Use this attribute to refer to the derivation building your root crate package. # You can override the features with rootCrate.build.override { features = [ "default" "feature1" ... ]; }. build = internal.buildRustCrateWithFeatures { inherit packageId; }; # Debug support which might change between releases. # File a bug if you depend on any for non-debug work! debug = internal.debugCrate { inherit packageId; }; }; # Refer your crate build derivation by name here. # You can override the features with # workspaceMembers."${crateName}".build.override { features = [ "default" "feature1" ... ]; }. workspaceMembers = { "sub_dir_crates" = rec { packageId = "sub_dir_crates"; build = internal.buildRustCrateWithFeatures { packageId = "sub_dir_crates"; }; # Debug support which might change between releases. # File a bug if you depend on any for non-debug work! debug = internal.debugCrate { inherit packageId; }; }; }; # A derivation that joins the outputs of all workspace members together. allWorkspaceMembers = pkgs.symlinkJoin { name = "all-workspace-members"; paths = let members = builtins.attrValues workspaceMembers; in builtins.map (m: m.build) members; }; # # "internal" ("private") attributes that may change in every new version of crate2nix. # internal = rec { # Build and dependency information for crates. # Many of the fields are passed one-to-one to buildRustCrate. # # Noteworthy: # * `dependencies`/`buildDependencies`: similar to the corresponding fields for buildRustCrate. # but with additional information which is used during dependency/feature resolution. # * `resolvedDependencies`: the selected default features reported by cargo - only included for debugging. # * `devDependencies` as of now not used by `buildRustCrate` but used to # inject test dependencies into the build crates = { "lib1" = rec { crateName = "lib1"; version = "0.1.0"; edition = "2018"; workspace_member = null; src = pkgs.fetchgit { url = "https://github.com/kolloch/with_sub_crates.git"; rev = "f8ad2b98ff0eb5fea4962f55e3ced5b0b5afe973"; sha256 = "0nlw7rg28p6bya040cbipq4jdcdp4h3q9shdjygfk2xkva9bjl8w"; }; authors = [ "Peter Kolloch " ]; }; "lib2" = rec { crateName = "lib2"; version = "0.1.0"; edition = "2018"; workspace_member = null; src = pkgs.fetchgit { url = "https://github.com/kolloch/with_sub_crates.git"; rev = "f8ad2b98ff0eb5fea4962f55e3ced5b0b5afe973"; sha256 = "0nlw7rg28p6bya040cbipq4jdcdp4h3q9shdjygfk2xkva9bjl8w"; }; authors = [ "Peter Kolloch " ]; }; "sub_dir_crates" = rec { crateName = "sub_dir_crates"; version = "0.1.0"; edition = "2018"; crateBin = [ { name = "sub_dir_crates"; path = "src/main.rs"; requiredFeatures = [ ]; } ]; src = lib.cleanSourceWith { filter = sourceFilter; src = ./.; }; authors = [ "Peter Kolloch " ]; dependencies = [ { name = "lib1"; packageId = "lib1"; } { name = "lib2"; packageId = "lib2"; } ]; }; }; # # crate2nix/default.nix (excerpt start) # /* Target (platform) data for conditional dependencies. This corresponds roughly to what buildRustCrate is setting. */ makeDefaultTarget = platform: { name = platform.rust.rustcTarget; unix = platform.isUnix; windows = platform.isWindows; fuchsia = true; test = false; inherit (platform.rust.platform) arch os vendor ; family = platform.rust.platform.target-family; env = "gnu"; endian = if platform.parsed.cpu.significantByte.name == "littleEndian" then "little" else "big"; pointer_width = toString platform.parsed.cpu.bits; debug_assertions = false; } // extraTargetFlags; registryUrl = { registries , url , crate , version , sha256 , }: let dl = registries.${url}.dl; tmpl = [ "{crate}" "{version}" "{prefix}" "{lowerprefix}" "{sha256-checksum}" ]; in with lib.strings; if lib.lists.any (i: hasInfix "{}" dl) tmpl then let prefix = if builtins.stringLength crate == 1 then "1" else if builtins.stringLength crate == 2 then "2" else "${builtins.substring 0 2 crate}/${builtins.substring 2 (builtins.stringLength crate - 2) crate}"; in builtins.replaceStrings tmpl [ crate version prefix (lib.strings.toLower prefix) sha256 ] else "${dl}/${crate}/${version}/download"; # Filters common temp files and build files. # TODO(pkolloch): Substitute with gitignore filter sourceFilter = name: type: let baseName = builtins.baseNameOf (builtins.toString name); in !( # Filter out git baseName == ".gitignore" || (type == "directory" && baseName == ".git") # Filter out build results || ( type == "directory" && ( baseName == "target" || baseName == "_site" || baseName == ".sass-cache" || baseName == ".jekyll-metadata" || baseName == "build-artifacts" ) ) # Filter out nix-build result symlinks || (type == "symlink" && lib.hasPrefix "result" baseName) # Filter out IDE config || (type == "directory" && (baseName == ".idea" || baseName == ".vscode")) || lib.hasSuffix ".iml" baseName # Filter out nix build files || baseName == "Cargo.nix" # Filter out editor backup / swap files. || lib.hasSuffix "~" baseName || builtins.match "^\\.sw[a-z]$$" baseName != null || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null || lib.hasSuffix ".tmp" baseName || lib.hasSuffix ".bak" baseName || baseName == "tests.nix" ); /* Returns a crate which depends on successful test execution of crate given as the second argument. testCrateFlags: list of flags to pass to the test exectuable testInputs: list of packages that should be available during test execution */ crateWithTest = { crate , testCrate , testCrateFlags , testInputs , testPreRun , testPostRun , }: assert builtins.typeOf testCrateFlags == "list"; assert builtins.typeOf testInputs == "list"; assert builtins.typeOf testPreRun == "string"; assert builtins.typeOf testPostRun == "string"; let # override the `crate` so that it will build and execute tests instead of # building the actual lib and bin targets We just have to pass `--test` # to rustc and it will do the right thing. We execute the tests and copy # their log and the test executables to $out for later inspection. test = let drv = testCrate.override (_: { buildTests = true; }); # If the user hasn't set any pre/post commands, we don't want to # insert empty lines. This means that any existing users of crate2nix # don't get a spurious rebuild unless they set these explicitly. testCommand = pkgs.lib.concatStringsSep "\n" ( pkgs.lib.filter (s: s != "") [ testPreRun "$f $testCrateFlags 2>&1 | tee -a $out" testPostRun ] ); in pkgs.stdenvNoCC.mkDerivation { name = "run-tests-${testCrate.name}"; inherit (crate) src; inherit testCrateFlags; buildInputs = testInputs; buildPhase = '' set -e export RUST_BACKTRACE=1 # build outputs testRoot=target/debug mkdir -p $testRoot # executables of the crate # we copy to prevent std::env::current_exe() to resolve to a store location for i in ${crate}/bin/*; do cp "$i" "$testRoot" done chmod +w -R . # test harness executables are suffixed with a hash, like cargo does # this allows to prevent name collision with the main # executables of the crate hash=$(basename $out) for file in ${drv}/tests/*; do f=$testRoot/$(basename $file)-$hash cp $file $f ${testCommand} done ''; }; in pkgs.runCommand "${crate.name}-linked" { inherit (crate) outputs crateName meta; passthru = (crate.passthru or { }) // { inherit test; }; } ( lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) '' echo tested by ${test} '' + '' ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs} '' ); # A restricted overridable version of builtRustCratesWithFeatures. buildRustCrateWithFeatures = { packageId , features ? rootFeatures , crateOverrides ? defaultCrateOverrides , buildRustCrateForPkgsFunc ? null , runTests ? false , testCrateFlags ? [ ] , testInputs ? [ ] , # Any command to run immediatelly before a test is executed. testPreRun ? "" , # Any command run immediatelly after a test is executed. testPostRun ? "" , }: lib.makeOverridable ( { features , crateOverrides , runTests , testCrateFlags , testInputs , testPreRun , testPostRun , }: let buildRustCrateForPkgsFuncOverriden = if buildRustCrateForPkgsFunc != null then buildRustCrateForPkgsFunc else ( if crateOverrides == pkgs.defaultCrateOverrides then buildRustCrateForPkgs else pkgs: (buildRustCrateForPkgs pkgs).override { defaultCrateOverrides = crateOverrides; } ); builtRustCrates = builtRustCratesWithFeatures { inherit packageId features; buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden; runTests = false; }; builtTestRustCrates = builtRustCratesWithFeatures { inherit packageId features; buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden; runTests = true; }; drv = builtRustCrates.crates.${packageId}; testDrv = builtTestRustCrates.crates.${packageId}; derivation = if runTests then crateWithTest { crate = drv; testCrate = testDrv; inherit testCrateFlags testInputs testPreRun testPostRun ; } else drv; in derivation ) { inherit features crateOverrides runTests testCrateFlags testInputs testPreRun testPostRun ; }; /* Returns an attr set with packageId mapped to the result of buildRustCrateForPkgsFunc for the corresponding crate. */ builtRustCratesWithFeatures = { packageId , features , crateConfigs ? crates , buildRustCrateForPkgsFunc , runTests , makeTarget ? makeDefaultTarget , }@args: assert (builtins.isAttrs crateConfigs); assert (builtins.isString packageId); assert (builtins.isList features); assert (builtins.isAttrs (makeTarget stdenv.hostPlatform)); assert (builtins.isBool runTests); let rootPackageId = packageId; mergedFeatures = mergePackageFeatures ( args // { inherit rootPackageId; target = makeTarget stdenv.hostPlatform // { test = runTests; }; } ); # Memoize built packages so that reappearing packages are only built once. builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs; mkBuiltByPackageIdByPkgs = pkgs: let self = { crates = lib.mapAttrs ( packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId ) crateConfigs; target = makeTarget pkgs.stdenv.hostPlatform; build = mkBuiltByPackageIdByPkgs pkgs.buildPackages; }; in self; buildByPackageIdForPkgsImpl = self: pkgs: packageId: let features = mergedFeatures."${packageId}" or [ ]; crateConfig' = crateConfigs."${packageId}"; crateConfig = builtins.removeAttrs crateConfig' [ "resolvedDefaultFeatures" "devDependencies" ]; devDependencies = lib.optionals (runTests && packageId == rootPackageId) ( crateConfig'.devDependencies or [ ] ); dependencies = dependencyDerivations { inherit features; inherit (self) target; buildByPackageId = depPackageId: # proc_macro crates must be compiled for the build architecture if crateConfigs.${depPackageId}.procMacro or false then self.build.crates.${depPackageId} else self.crates.${depPackageId}; dependencies = (crateConfig.dependencies or [ ]) ++ devDependencies; }; buildDependencies = dependencyDerivations { inherit features; inherit (self.build) target; buildByPackageId = depPackageId: self.build.crates.${depPackageId}; dependencies = crateConfig.buildDependencies or [ ]; }; dependenciesWithRenames = let buildDeps = filterEnabledDependencies { inherit features; inherit (self) target; dependencies = crateConfig.dependencies or [ ] ++ devDependencies; }; hostDeps = filterEnabledDependencies { inherit features; inherit (self.build) target; dependencies = crateConfig.buildDependencies or [ ]; }; in lib.filter (d: d ? "rename") (hostDeps ++ buildDeps); # Crate renames have the form: # # { # crate_name = [ # { version = "1.2.3"; rename = "crate_name01"; } # ]; # # ... # } crateRenames = let grouped = lib.groupBy (dependency: dependency.name) dependenciesWithRenames; versionAndRename = dep: let package = crateConfigs."${dep.packageId}"; in { inherit (dep) rename; inherit (package) version; }; in lib.mapAttrs (name: builtins.map versionAndRename) grouped; in buildRustCrateForPkgsFunc pkgs ( crateConfig // { src = crateConfig.src or (fetchurl rec { name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz"; # https://www.pietroalbini.org/blog/downloading-crates-io/ # Not rate-limited, CDN URL. url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate"; sha256 = assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}"); crateConfig.sha256; }); extraRustcOpts = lib.lists.optional (targetFeatures != [ ]) "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}"; inherit features dependencies buildDependencies crateRenames release ; } ); in builtByPackageIdByPkgs; # Returns the actual derivations for the given dependencies. dependencyDerivations = { buildByPackageId , features , dependencies , target , }: assert (builtins.isList features); assert (builtins.isList dependencies); assert (builtins.isAttrs target); let enabledDependencies = filterEnabledDependencies { inherit dependencies features target; }; depDerivation = dependency: buildByPackageId dependency.packageId; in map depDerivation enabledDependencies; /* Returns a sanitized version of val with all values substituted that cannot be serialized as JSON. */ sanitizeForJson = val: if builtins.isAttrs val then lib.mapAttrs (n: sanitizeForJson) val else if builtins.isList val then builtins.map sanitizeForJson val else if builtins.isFunction val then "function" else val; # Returns various tools to debug a crate. debugCrate = { packageId , target ? makeDefaultTarget stdenv.hostPlatform , }: assert (builtins.isString packageId); let debug = rec { # The built tree as passed to buildRustCrate. buildTree = buildRustCrateWithFeatures { buildRustCrateForPkgsFunc = _: lib.id; inherit packageId; }; sanitizedBuildTree = sanitizeForJson buildTree; dependencyTree = sanitizeForJson (buildRustCrateWithFeatures { buildRustCrateForPkgsFunc = _: crate: { "01_crateName" = crate.crateName or false; "02_features" = crate.features or [ ]; "03_dependencies" = crate.dependencies or [ ]; }; inherit packageId; }); mergedPackageFeatures = mergePackageFeatures { features = rootFeatures; inherit packageId target; }; diffedDefaultPackageFeatures = diffDefaultPackageFeatures { inherit packageId target; }; }; in { internal = debug; }; /* Returns differences between cargo default features and crate2nix default features. This is useful for verifying the feature resolution in crate2nix. */ diffDefaultPackageFeatures = { crateConfigs ? crates , packageId , target , }: assert (builtins.isAttrs crateConfigs); let prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; }); mergedFeatures = prefixValues "crate2nix" (mergePackageFeatures { inherit crateConfigs packageId target; features = [ "default" ]; }); configs = prefixValues "cargo" crateConfigs; combined = lib.foldAttrs (a: b: a // b) { } [ mergedFeatures configs ]; onlyInCargo = builtins.attrNames ( lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined ); onlyInCrate2Nix = builtins.attrNames ( lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined ); differentFeatures = lib.filterAttrs ( n: v: (v ? "crate2nix") && (v ? "cargo") && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ]) ) combined; in builtins.toJSON { inherit onlyInCargo onlyInCrate2Nix differentFeatures; }; /* Returns an attrset mapping packageId to the list of enabled features. If multiple paths to a dependency enable different features, the corresponding feature sets are merged. Features in rust are additive. */ mergePackageFeatures = { crateConfigs ? crates , packageId , rootPackageId ? packageId , features ? rootFeatures , dependencyPath ? [ crates.${packageId}.crateName ] , featuresByPackageId ? { } , target , # Adds devDependencies to the crate with rootPackageId. runTests ? false , ... }@args: assert (builtins.isAttrs crateConfigs); assert (builtins.isString packageId); assert (builtins.isString rootPackageId); assert (builtins.isList features); assert (builtins.isList dependencyPath); assert (builtins.isAttrs featuresByPackageId); assert (builtins.isAttrs target); assert (builtins.isBool runTests); let crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}"); expandedFeatures = expandFeatures (crateConfig.features or { }) features; enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures; depWithResolvedFeatures = dependency: let inherit (dependency) packageId; features = dependencyFeatures enabledFeatures dependency; in { inherit packageId features; }; resolveDependencies = cache: path: dependencies: assert (builtins.isAttrs cache); assert (builtins.isList dependencies); let enabledDependencies = filterEnabledDependencies { inherit dependencies target; features = enabledFeatures; }; directDependencies = map depWithResolvedFeatures enabledDependencies; foldOverCache = op: lib.foldl op cache directDependencies; in foldOverCache ( cache: { packageId, features }: let cacheFeatures = cache.${packageId} or [ ]; combinedFeatures = sortedUnique (cacheFeatures ++ features); in if cache ? ${packageId} && cache.${packageId} == combinedFeatures then cache else mergePackageFeatures { features = combinedFeatures; featuresByPackageId = cache; inherit crateConfigs packageId target runTests rootPackageId ; } ); cacheWithSelf = let cacheFeatures = featuresByPackageId.${packageId} or [ ]; combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures); in featuresByPackageId // { "${packageId}" = combinedFeatures; }; cacheWithDependencies = resolveDependencies cacheWithSelf "dep" ( crateConfig.dependencies or [ ] ++ lib.optionals (runTests && packageId == rootPackageId) (crateConfig.devDependencies or [ ]) ); cacheWithAll = resolveDependencies cacheWithDependencies "build" ( crateConfig.buildDependencies or [ ] ); in cacheWithAll; # Returns the enabled dependencies given the enabled features. filterEnabledDependencies = { dependencies , features , target , }: assert (builtins.isList dependencies); assert (builtins.isList features); assert (builtins.isAttrs target); lib.filter ( dep: let targetFunc = dep.target or (features: true); in targetFunc { inherit features target; } && (!(dep.optional or false) || builtins.any (doesFeatureEnableDependency dep) features) ) dependencies; # Returns whether the given feature should enable the given dependency. doesFeatureEnableDependency = dependency: feature: let name = dependency.rename or dependency.name; prefix = "${name}/"; len = builtins.stringLength prefix; startsWithPrefix = builtins.substring 0 len feature == prefix; in feature == name || feature == "dep:" + name || startsWithPrefix; /* Returns the expanded features for the given inputFeatures by applying the rules in featureMap. featureMap is an attribute set which maps feature names to lists of further feature names to enable in case this feature is selected. */ expandFeatures = featureMap: inputFeatures: assert (builtins.isAttrs featureMap); assert (builtins.isList inputFeatures); let expandFeaturesNoCycle = oldSeen: inputFeatures: if inputFeatures != [ ] then let # The feature we're currently expanding. feature = builtins.head inputFeatures; # All the features we've seen/expanded so far, including the one # we're currently processing. seen = oldSeen // { ${feature} = 1; }; # Expand the feature but be careful to not re-introduce a feature # that we've already seen: this can easily cause a cycle, see issue # #209. enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]); in [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables)) # No more features left, nothing to expand to. else [ ]; outFeatures = expandFeaturesNoCycle { } inputFeatures; in sortedUnique outFeatures; /* This function adds optional dependencies as features if they are enabled indirectly by dependency features. This function mimics Cargo's behavior described in a note at: https://doc.rust-lang.org/nightly/cargo/reference/features.html#dependency-features */ enableFeatures = dependencies: features: assert (builtins.isList features); assert (builtins.isList dependencies); let additionalFeatures = lib.concatMap ( dependency: assert (builtins.isAttrs dependency); let enabled = builtins.any (doesFeatureEnableDependency dependency) features; in if (dependency.optional or false) && enabled then [ (dependency.rename or dependency.name) ] else [ ] ) dependencies; in sortedUnique (features ++ additionalFeatures); /* Returns the actual features for the given dependency. features: The features of the crate that refers this dependency. */ dependencyFeatures = features: dependency: assert (builtins.isList features); assert (builtins.isAttrs dependency); let defaultOrNil = if dependency.usesDefaultFeatures or true then [ "default" ] else [ ]; explicitFeatures = dependency.features or [ ]; additionalDependencyFeatures = let name = dependency.rename or dependency.name; stripPrefixMatch = prefix: s: if lib.hasPrefix prefix s then lib.removePrefix prefix s else null; extractFeature = feature: lib.findFirst (f: f != null) null ( map (prefix: stripPrefixMatch prefix feature) [ (name + "/") (name + "?/") ] ); dependencyFeatures = lib.filter (f: f != null) (map extractFeature features); in dependencyFeatures; in defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures; # Sorts and removes duplicates from a list of strings. sortedUnique = features: assert (builtins.isList features); assert (builtins.all builtins.isString features); let outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features; outFeaturesUnique = builtins.attrNames outFeaturesSet; in builtins.sort (a: b: a < b) outFeaturesUnique; deprecationWarning = message: value: if strictDeprecation then builtins.throw "strictDeprecation enabled, aborting: ${message}" else builtins.trace message value; # # crate2nix/default.nix (excerpt end) # }; } ================================================ FILE: sample_projects/sub_dir_crates/Cargo.toml ================================================ [package] name = "sub_dir_crates" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] lib1 = { git = "https://github.com/kolloch/with_sub_crates.git", rev="f8ad2b98ff0eb5fea4962f55e3ced5b0b5afe973" } lib2 = { git = "https://github.com/kolloch/with_sub_crates.git", rev="f8ad2b98ff0eb5fea4962f55e3ced5b0b5afe973" } ================================================ FILE: sample_projects/sub_dir_crates/crate-hashes.json ================================================ { "git+https://github.com/kolloch/with_sub_crates.git?rev=f8ad2b98ff0eb5fea4962f55e3ced5b0b5afe973#lib1@0.1.0": "0nlw7rg28p6bya040cbipq4jdcdp4h3q9shdjygfk2xkva9bjl8w", "git+https://github.com/kolloch/with_sub_crates.git?rev=f8ad2b98ff0eb5fea4962f55e3ced5b0b5afe973#lib2@0.1.0": "0nlw7rg28p6bya040cbipq4jdcdp4h3q9shdjygfk2xkva9bjl8w" } ================================================ FILE: sample_projects/sub_dir_crates/src/main.rs ================================================ fn main() { println!("main with {} {}", lib1::test(), lib2::test()); } ================================================ FILE: sample_projects/test_flag_passing/Cargo.toml ================================================ [package] name = "test_flag_passing" version = "0.1.0" edition = "2018" ================================================ FILE: sample_projects/test_flag_passing/src/lib.rs ================================================ #[test] fn this_must_be_skipped() { panic!("hello darkness, my old friend"); } #[test] fn this_must_run() { } ================================================ FILE: sample_projects/test_flag_passing/src/main.rs ================================================ fn main() { println!("Banana is a veggie and tomato is a fruit"); } ================================================ FILE: sample_projects/test_flag_passing/test.nix ================================================ { pkgs ? import ../../nix/nixpkgs.nix { config = { }; } , generatedCargoNix ? ./Cargo.nix { } }: let instantiatedBuild = pkgs.callPackage generatedCargoNix { }; in instantiatedBuild.rootCrate.build.override { runTests = true; testCrateFlags = [ "--skip" "this_must_be_skipped" ]; } ================================================ FILE: sample_projects/with_problematic_crates/Cargo.toml ================================================ [package] name = "with_problematic_crates" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" [dependencies] # There was a bug in buildRustCrate for this one... tera = "*" # Introduced some escaping around the generated target.os expression for # dragonfly nix = "*" # Regression test for issue #4 html5ever = "*" cssparser = "*" # Other popular application creates actix-web ="*" diesel = "*" crossbeam-epoch = "*" crossbeam-deque = "*" crossbeam-channel = "*" env_logger = "*" parking_lot ="*" error-chain = "*" hyper = { version = "*", features = ["full"] } tokio = { version = "*", features = ["full"] } mime = "*" tokio-threadpool = "*" # tokio-executor + tracing triggers the use of cfg(feature = "...") tokio-executor = "*" # FIXME: using default features causes 2 versions of lazy_static to be found tracing = "*" # Most popular on crates.io cfg-if = "*" unicode-xid = "*" rand = "*" syn = "*" libc = "*" quote = "*" proc-macro2 = "*" serde = "*" bitflags = "*" ================================================ FILE: sample_projects/with_problematic_crates/crate-hashes.json ================================================ {} ================================================ FILE: sample_projects/with_problematic_crates/default.nix ================================================ { pkgs ? import ../../nix/nixpkgs.nix { config = { }; } , stdenv ? pkgs.stdenv , lib ? pkgs.lib , generatedCargoNix }: let generatedBuild = pkgs.callPackage generatedCargoNix { defaultCrateOverrides = pkgs.defaultCrateOverrides // { pest_generator = attrs: { buildInputs = (attrs.buildInputs or [ ]) ++ lib.optionals stdenv.isDarwin (with pkgs.darwin.apple_sdk.frameworks; [ Security ]); }; cssparser-macros = attrs: { buildInputs = (attrs.buildInputs or [ ]) ++ lib.optionals stdenv.isDarwin (with pkgs.darwin.apple_sdk.frameworks; [ Security ]); }; }; }; in generatedBuild.rootCrate.build ================================================ FILE: sample_projects/with_problematic_crates/src/main.rs ================================================ fn main() { println!("Hello, with_problematic_crates!"); } ================================================ FILE: sample_projects/workspace_with_nondefault_lib/Cargo.toml ================================================ [workspace] members = [ "crates/main", "crates/somelib" ] ================================================ FILE: sample_projects/workspace_with_nondefault_lib/crates/main/Cargo.toml ================================================ [package] name = "main" version = "0.1.0" authors = ["Stefan Junker "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] somelib = { path = "../somelib" } ================================================ FILE: sample_projects/workspace_with_nondefault_lib/crates/main/src/main.rs ================================================ fn main() { println!("Hello, {}", somelib::get_info()); } ================================================ FILE: sample_projects/workspace_with_nondefault_lib/crates/somelib/Cargo.toml ================================================ [package] name = "somelib" version = "0.1.0" authors = ["Stefan Junker "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] name = "somelib" crate-type = [ "cdylib", "rlib" ] path = "src/somelib.rs" ================================================ FILE: sample_projects/workspace_with_nondefault_lib/crates/somelib/src/somelib.rs ================================================ pub fn get_info() -> &'static str { "workspace_with_nondefault_lib" } ================================================ FILE: sample_workspace/Cargo.toml ================================================ [workspace] members = [ "bin", "bin_with_default_features", "lib_and_bin", "bin_with_lib_dep", "bin_with_cond_lib_dep", "with_tera", ] ================================================ FILE: sample_workspace/bin/Cargo.toml ================================================ [package] name = "hello_world_bin" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" [dependencies] ================================================ FILE: sample_workspace/bin/src/main.rs ================================================ fn main() { println!("Hello, world!"); } ================================================ FILE: sample_workspace/bin_with_cond_lib_dep/Cargo.toml ================================================ [package] name = "hello_world_with_cond_dep" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" [target.'cfg(target_family = "unix")'.dependencies] "hello_world_lib" = { path = "../lib"} ================================================ FILE: sample_workspace/bin_with_cond_lib_dep/src/main.rs ================================================ fn main() { hello_world_lib::hello_world("bin_with_cond_lib_dep"); } ================================================ FILE: sample_workspace/bin_with_default_features/Cargo.toml ================================================ [package] name = "bin_with_default_features" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" [dependencies] "hello_world_lib" = { path = "../lib", optional = true} [features] default = ["use_lib"] use_lib = ["hello_world_lib"] do_not_activate = [] ================================================ FILE: sample_workspace/bin_with_default_features/src/main.rs ================================================ #[cfg(feature = "use_lib")] use hello_world_lib; fn main() { #[cfg(feature = "use_lib")] hello_world_lib::hello_world("bin_with_default_features"); #[cfg(feature = "do_not_activate")] println!("COMPILED with do_not_activate"); } ================================================ FILE: sample_workspace/bin_with_lib_dep/Cargo.toml ================================================ [package] name = "hello_world_with_dep" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" [dependencies] "hello_world_lib" = { path = "../lib"} # Doesn't make sense but reproduces bug that filters # this dev dependency by accident. [dev-dependencies] "hello_world_lib" = { path = "../lib"} ================================================ FILE: sample_workspace/bin_with_lib_dep/src/main.rs ================================================ fn main() { #[cfg(target_family = "unix")] hello_world_lib::hello_world("bin_with_lib_dep"); } ================================================ FILE: sample_workspace/crate-hashes.json ================================================ {} ================================================ FILE: sample_workspace/lib/Cargo.toml ================================================ [package] name = "hello_world_lib" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" [dependencies] ================================================ FILE: sample_workspace/lib/src/lib.rs ================================================ pub fn hello_world(name: &str) { println!("Hello, {}!", name); } ================================================ FILE: sample_workspace/lib_and_bin/Cargo.toml ================================================ [package] name = "hello_world_lib_and_bin" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" [dependencies] ================================================ FILE: sample_workspace/lib_and_bin/src/lib.rs ================================================ pub fn hello_world() { println!("Hello, lib_and_bin!"); } ================================================ FILE: sample_workspace/lib_and_bin/src/main.rs ================================================ use hello_world_lib_and_bin::hello_world; fn main() { hello_world(); } ================================================ FILE: sample_workspace/with_tera/Cargo.toml ================================================ [package] name = "with_tera" version = "0.1.0" authors = ["Peter Kolloch "] edition = "2018" [dependencies] tera = "1.0.0-beta.2" ================================================ FILE: sample_workspace/with_tera/src/main.rs ================================================ fn main() { println!("Hello, with_tera!"); } ================================================ FILE: shell.nix ================================================ (import ( let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in fetchTarball { url = lock.nodes.flake-compat.locked.url or "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; sha256 = lock.nodes.flake-compat.locked.narHash; } ) { src = ./.; } ).shellNix ================================================ FILE: templates/flake-binary/.envrc ================================================ #!/usr/bin/env bash # ^ make editor happy # # Use https://direnv.net/ to automatically load the dev shell. # if ! has nix_direnv_version || ! nix_direnv_version 3.0.4; then source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.4/direnvrc" "sha256-DzlYZ33mWF/Gs8DDeyjr8mnVmQGx7ASYqA5WlxwvBG4=" fi watch_file nix/** watch_file -- **/*.nix # Adding files to git includes them in a flake # But it is also a bit much reloading. # watch_file .git/index .git/HEAD use flake . --show-trace ================================================ FILE: templates/flake-binary/.gitignore ================================================ target **/*.rs.bk .idea *.iml /result* *.log *~ # cachix tmp file store-path-pre-build # Devenv .devenv* devenv.local.nix # direnv .direnv # pre-commit .pre-commit-config.yaml template/flake.lock ================================================ FILE: templates/flake-binary/Cargo.toml ================================================ [package] name = "rustnix" description = "An example project about rust and nix" publish = false version = "0.1.0" edition = "2021" [dependencies] colored = "2.0.4" ================================================ FILE: templates/flake-binary/flake.nix ================================================ { description = "Rust-Nix"; inputs = { flake-parts = { url = "github:hercules-ci/flake-parts"; inputs.nixpkgs-lib.follows = "nixpkgs"; }; rust-overlay.url = "github:oxalica/rust-overlay"; crate2nix.url = "github:nix-community/crate2nix"; # Development devshell = { url = "github:numtide/devshell"; inputs.nixpkgs.follows = "nixpkgs"; }; }; nixConfig = { extra-trusted-public-keys = "eigenvalue.cachix.org-1:ykerQDDa55PGxU25CETy9wF6uVDpadGGXYrFNJA3TUs="; extra-substituters = "https://eigenvalue.cachix.org"; allow-import-from-derivation = true; }; outputs = inputs @ { self , nixpkgs , flake-parts , rust-overlay , crate2nix , ... }: flake-parts.lib.mkFlake { inherit inputs; } { systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; imports = [ ./nix/rust-overlay/flake-module.nix ./nix/devshell/flake-module.nix ]; perSystem = { system, pkgs, lib, inputs', ... }: let # If you dislike IFD, you can also generate it with `crate2nix generate` # on each dependency change and import it here with `import ./Cargo.nix`. cargoNix = inputs.crate2nix.tools.${system}.appliedCargoNix { name = "rustnix"; src = ./.; }; in rec { checks = { rustnix = cargoNix.rootCrate.build.override { runTests = true; }; }; packages = { rustnix = cargoNix.rootCrate.build; default = packages.rustnix; inherit (pkgs) rust-toolchain; rust-toolchain-versions = pkgs.writeScriptBin "rust-toolchain-versions" '' ${pkgs.rust-toolchain}/bin/cargo --version ${pkgs.rust-toolchain}/bin/rustc --version ''; }; }; }; } ================================================ FILE: templates/flake-binary/nix/devshell/flake-module.nix ================================================ { inputs, lib, ... }: { imports = [ inputs.devshell.flakeModule ]; config.perSystem = { pkgs , ... }: { config.devshells.default = { imports = [ "${inputs.devshell}/extra/language/c.nix" # "${inputs.devshell}/extra/language/rust.nix" ]; commands = with pkgs; [ { package = rust-toolchain; category = "rust"; } ]; language.c = { libraries = lib.optional pkgs.stdenv.isDarwin pkgs.libiconv; }; }; }; } ================================================ FILE: templates/flake-binary/nix/rust-overlay/flake-module.nix ================================================ { inputs, ... }: let overlays = [ (import inputs.rust-overlay) (self: super: assert !(super ? rust-toolchain); rec { rust-toolchain = super.rust-bin.fromRustupToolchainFile ../../rust-toolchain.toml; # buildRustCrate/crate2nix depend on this. rustc = rust-toolchain; cargo = rust-toolchain; }) ]; in { perSystem = { system, ... }: { _module.args.pkgs = import inputs.nixpkgs { inherit system overlays; config = { }; }; }; } ================================================ FILE: templates/flake-binary/rust-toolchain.toml ================================================ [toolchain] channel = "stable" ================================================ FILE: templates/flake-binary/src/main.rs ================================================ use colored::Colorize; fn main() { println!("{}", "Hello Nix flake!".cyan()); } ================================================ FILE: tests.nix ================================================ { nixpkgs ? ./nix/nixpkgs.nix , pkgs ? import nixpkgs { config = { }; } # Path to nixpkgs for running/building the integration tests # created with the "buildTest" function (e.g. those in the buildTestConfigs array) # and not for building crate2nix etc itself. , buildTestNixpkgs ? nixpkgs , buildTestPkgs ? import buildTestNixpkgs { system = pkgs.stdenv.hostPlatform.system; config = { }; } , lib ? pkgs.lib , stdenv ? pkgs.stdenv }: let crate2nix = pkgs.callPackage ./default.nix { }; tools = pkgs.callPackage ./tools.nix { }; buildTest = { name , src , cargoToml ? "Cargo.toml" , features ? [ "default" ] , skip ? false , expectedOutput ? null , expectedTestOutputs ? [ ] , pregeneratedBuild ? null , additionalCargoNixArgs ? [ ] , customBuild ? null , derivationAttrPath ? [ "rootCrate" ] , # Substitute nix-prefetch-url during generation. nixPrefetchUrl ? '' echo ./tests.nix: NO URL FETCH ALLOWED: "'$*'" >&2 exit 1 '' , # Substitute nix-prefetch-git during generation. nixPrefetchGit ? '' echo ./tests.nix: NO GIT FETCH ALLOWED: "'$*'" >&2 exit 1 '' }: let generatedCargoNix = if builtins.isNull pregeneratedBuild then let autoGeneratedCargoNix = tools.generatedCargoNix { name = "buildTest_test_${name}"; inherit src cargoToml additionalCargoNixArgs; }; in autoGeneratedCargoNix.overrideAttrs ( oldAttrs: { buildInputs = oldAttrs.buildInputs ++ [ (pkgs.writeShellScriptBin "nix-prefetch-url" nixPrefetchUrl) (pkgs.writeShellScriptBin "nix-prefetch-git" nixPrefetchGit) ]; } ) else ./. + "/${pregeneratedBuild}"; derivationAttr = lib.attrByPath derivationAttrPath null (buildTestPkgs.callPackage generatedCargoNix { release = true; }); derivation = if builtins.isNull customBuild then derivationAttr.build.override { inherit features; } else buildTestPkgs.callPackage (./. + "/${customBuild}") { inherit generatedCargoNix; }; debug = derivationAttr.debug.internal; # We could easily use a mapAttrs here but then the error # output is aweful if things go wrong :( debugFile = attrName: pkgs.writeTextFile { name = "${name}_${attrName}.json"; text = builtins.toJSON debug."${attrName}"; }; testDerivation = pkgs.stdenv.mkDerivation { name = "${name}_buildTest"; phases = [ "buildPhase" ]; buildInputs = [ derivation ]; inherit derivation generatedCargoNix; sanitizedBuildTree = debugFile "sanitizedBuildTree"; buildPhase = # Need tests if there is expected test output assert lib.length expectedTestOutputs > 0 -> derivation ? test; '' echo === DEBUG INFO echo ${debugFile "sanitizedBuildTree"} echo ${debugFile "dependencyTree"} echo ${debugFile "mergedPackageFeatures"} echo ${debugFile "diffedDefaultPackageFeatures"} mkdir -p $out ${if expectedOutput == null then '' echo === SKIP RUNNING echo "(no executables)" '' else '' echo === RUNNING ${derivation.crateName} | tee $out/run.log echo === VERIFYING expectedOutput grep '${expectedOutput}' $out/run.log || { echo '${expectedOutput}' not found in: cat $out/run.log exit 23 } ''} ${if lib.length expectedTestOutputs == 0 then '' echo === SKIP RUNNING TESTS echo "(no tests)" '' else '' echo === RUNNING TESTS cp ${derivation.test} $out/tests.log echo === VERIFYING expectedTestOutputs ''} ${lib.concatMapStringsSep "\n" ( output: '' grep '${output}' $out/tests.log || { echo '${output}' not found in: cat $out/tests.log exit 23 } '' ) expectedTestOutputs} ''; }; in if skip then pkgs.runCommandNoCCLocal "skip_${name}" { passthru = { forceSkipped = testDerivation; }; } '' echo SKIPPED touch $out '' else testDerivation; buildTestConfigs = [ # # BASIC # # Artificial tests that tend to test only a few features. # { name = "bin"; src = ./sample_projects/bin; expectedOutput = "Hello, world!"; } { name = "lib_and_bin"; src = ./sample_projects/lib_and_bin; expectedOutput = "Hello, lib_and_bin!"; } { name = "bin_with_lib_dep"; src = ./sample_projects; cargoToml = "bin_with_lib_dep/Cargo.toml"; expectedOutput = "Hello, bin_with_lib_dep!"; } { name = "bin_with_default_features"; src = ./sample_projects; cargoToml = "bin_with_default_features/Cargo.toml"; expectedOutput = "Hello, bin_with_default_features!"; } { name = "bin_with_NON_default_features"; src = ./sample_projects; cargoToml = "bin_with_default_features/Cargo.toml"; features = [ "default" "do_not_activate" ]; expectedOutput = "Hello, bin_with_default_features, do_not_activate!"; } { name = "bin_with_NON_default_ROOT_features"; src = ./sample_projects; cargoToml = "bin_with_default_features/Cargo.toml"; expectedOutput = "Hello, bin_with_default_features, do_not_activate!"; customBuild = "sample_projects/bin_with_default_features/override-root-features.nix"; } { name = "bin_required_features"; src = ./sample_projects/bin_required_features; expectedOutput = "Hello from bin_required_features default binary"; features = [ "compilemainbinary" ]; } { name = "bin_with_lib_git_dep"; src = ./sample_projects/bin_with_lib_git_dep; expectedOutput = "Hello world from bin_with_lib_git_dep!"; } { name = "bin_with_git_branch_dep"; src = ./sample_projects/bin_with_git_branch_dep; expectedOutput = "Hello world from bin_with_git_branch_dep!"; } { name = "bin_with_rerenamed_lib_dep"; src = ./sample_projects; cargoToml = "bin_with_rerenamed_lib_dep/Cargo.toml"; expectedOutput = "Hello, bin_with_rerenamed_lib_dep!"; } { name = "bin_with_dep_features"; src = ./sample_projects; cargoToml = "bin_with_dep_features/Cargo.toml"; expectedOutput = "Hello, bin_with_dep_features!"; } { name = "sub_dir_crates"; src = ./sample_projects/sub_dir_crates; expectedOutput = "main with lib1 lib2"; pregeneratedBuild = "sample_projects/sub_dir_crates/Cargo.nix"; } # Test for PR #394: git dependencies with subcrates in nested directories # This exercises the IFD path in tools.nix that handles finding the correct # Cargo.toml when a git repo has multiple crates without a root workspace { name = "sub_dir_crates_ifd"; src = ./sample_projects/sub_dir_crates; expectedOutput = "main with lib1 lib2"; } { name = "cfg_test"; src = ./sample_projects/cfg-test; cargoToml = "Cargo.toml"; expectedOutput = "Hello, cfg-test!"; } { name = "cfg_test-with-tests"; src = ./sample_projects/cfg-test; cargoToml = "Cargo.toml"; expectedOutput = "Hello, cfg-test!"; expectedTestOutputs = [ "test echo_foo_test ... ok" "test lib_test ... ok" "test in_source_dir ... ok" "test exec_cowsay ... ok" ]; customBuild = "sample_projects/cfg-test/test.nix"; } { name = "test_flag_passing"; src = ./sample_projects/test_flag_passing; cargoToml = "Cargo.toml"; expectedTestOutputs = [ "test this_must_run ... ok" "1 filtered out" ]; expectedOutput = "Banana is a veggie and tomato is a fruit"; customBuild = "sample_projects/test_flag_passing/test.nix"; } { name = "renamed_build_deps"; src = ./sample_projects/renamed_build_deps; expectedOutput = "Hello, renamed_build_deps!"; } { name = "renamed_dev_deps"; src = ./sample_projects/renamed_dev_deps; expectedTestOutputs = [ "test test::ran_a_test ... ok" ]; customBuild = "sample_projects/renamed_dev_deps/test.nix"; } { name = "sample_workspace"; src = ./sample_workspace; expectedOutput = "Hello, with_tera!"; derivationAttrPath = [ "workspaceMembers" "with_tera" ]; } { name = "sample_workspace"; src = ./sample_workspace; expectedOutput = lib.optionalString stdenv.hostPlatform.isUnix "Hello, bin_with_cond_lib_dep!"; derivationAttrPath = [ "workspaceMembers" "bin_with_cond_lib_dep" ]; } { name = "bin_with_git_submodule_dep"; src = ./sample_projects/bin_with_git_submodule_dep; pregeneratedBuild = "sample_projects/bin_with_git_submodule_dep/Cargo.nix"; customBuild = "sample_projects/bin_with_git_submodule_dep/default.nix"; expectedOutput = "Hello world from with_git_submodule_dep!"; } { name = "bin_with_git_submodule_dep_customBuildRustCrate"; src = ./sample_projects/bin_with_git_submodule_dep; pregeneratedBuild = "sample_projects/bin_with_git_submodule_dep/Cargo.nix"; customBuild = "sample_projects/bin_with_git_submodule_dep/default-with-customBuildRustCrate.nix"; expectedOutput = "Hello world from with_git_submodule_dep!"; } { name = "bin_with_git_submodule_dep_customBuildRustCrateForPkgs"; src = ./sample_projects/bin_with_git_submodule_dep; pregeneratedBuild = "sample_projects/bin_with_git_submodule_dep/Cargo.nix"; customBuild = "sample_projects/bin_with_git_submodule_dep/default-with-customBuildRustCrateForPkgs.nix"; expectedOutput = "Hello world from with_git_submodule_dep!"; } { name = "conditional_features_bye"; src = ./sample_projects/conditional_features; expectedOutput = "Bye, not foo!"; } { name = "conditional_features_bye_foo"; src = ./sample_projects/conditional_features; features = [ "foo" ]; expectedOutput = "Bye, foo!"; } { name = "conditional_features_hello"; src = ./sample_projects/conditional_features; features = [ "hello" "allow-build" ]; expectedOutput = "Hello, not foo!"; } { name = "conditional_features_hello_foo"; src = ./sample_projects/conditional_features; features = [ "hello" "allow-build" "foo" ]; expectedOutput = "Hello, foo!"; } { name = "cdylib"; src = ./sample_projects/cdylib; customBuild = "sample_projects/cdylib/test.nix"; expectedOutput = "cdylib test"; # Disable this on Mac OS. FIXME: https://github.com/kolloch/crate2nix/issues/116 skip = stdenv.hostPlatform.isDarwin; } { name = "numtest_new_cargo_lock"; src = ./sample_projects/numtest_new_cargo_lock; expectedOutput = "Hello from numtest, world!"; } { name = "integration_test"; src = ./sample_projects/integration_test; cargoToml = "Cargo.toml"; customBuild = "sample_projects/integration_test/test.nix"; expectedOutput = "expected one argument"; expectedTestOutputs = [ "test read_source_file ... ok" "test write_output_file ... ok" ]; } { name = "cross_compile_build_dependencies"; src = ./sample_projects/cross_compile_build_dependencies; customBuild = "sample_projects/cross_compile_build_dependencies/default.nix"; } # # Prefetch tests # { name = "simple_dep_prefetch_test"; src = ./sample_projects/simple_dep; additionalCargoNixArgs = [ "--no-cargo-lock-checksums" ]; expectedOutput = "Hello, simple_dep!"; nixPrefetchUrl = '' case "$@" in "https://static.crates.io/crates/nix-base32/nix-base32-0.1.1.crate --name nix-base32-0.1.1") echo "04jnq6arig0amz0scadavbzn9bg9k4zphmrm1562n6ygfj1dnj45" ;; *) echo -e "\e[31mUnrecognized fetch:\e[0m $(basename $0) $@" >&2 exit 1 ;; esac ''; } { name = "git_prefetch_test"; src = ./sample_projects/bin_with_lib_git_dep; expectedOutput = "Hello world from bin_with_lib_git_dep!"; additionalCargoNixArgs = [ "--dont-read-crate-hashes" ]; nixPrefetchGit = '' case "$@" in "--url https://github.com/kolloch/nix-base32 --fetch-submodules --rev 42f5544e51187f0c7535d453fcffb4b524c99eb2") echo ' { "url": "https://github.com/kolloch/nix-base32", "rev": "42f5544e51187f0c7535d453fcffb4b524c99eb2", "date": "2019-11-29T22:22:24+01:00", "sha256": "011f945b48xkilkqbvbsxazspz5z23ka0s90ms4jiqjbhiwll1nw", "fetchSubmodules": true } ' ;; *) echo -e "\e[31mUnrecognized fetch:\e[0m $(basename $0) $@" >&2 exit 1 ;; esac ''; } # # Compatibility tests with "real" crates # { name = "futures_compat_test"; src = ./sample_projects/futures_compat; cargoToml = "Cargo.toml"; expectedOutput = "Hello, futures_compat!"; } { name = "futures_util_multiple_version"; src = ./sample_projects/futures_compat; cargoToml = "Cargo.toml"; expectedOutput = "Hello, futures_compat!"; } { name = "numtest"; src = ./sample_projects/numtest; expectedOutput = "Hello from numtest, world!"; } { name = "renaming"; src = ./sample_projects/renaming; expectedOutput = "Hello, world!"; } { name = "codegen"; src = ./sample_projects/codegen; expectedOutput = "Hello, World!"; pregeneratedBuild = "sample_projects/codegen/Cargo.nix"; } { name = "dependency_issue_65_all_features"; src = ./sample_projects/dependency_issue_65; # This will not work with only default features. # Therefore, it tests that the default is really --all-features. customBuild = "sample_projects/dependency_issue_65/default.nix"; expectedOutput = "Hello, dependency_issue_65!"; } { name = "dependency_issue_65_sqlite_no_default_feature"; additionalCargoNixArgs = [ "--no-default-features" "--features" "sqlite" ]; src = ./sample_projects/dependency_issue_65; customBuild = "sample_projects/dependency_issue_65/default.nix"; expectedOutput = "Hello, dependency_issue_65"; } { name = "dependency_issue_65_sqlite_default_features"; additionalCargoNixArgs = [ "--default-features" "--features" "sqlite" ]; src = ./sample_projects/dependency_issue_65; customBuild = "sample_projects/dependency_issue_65/default.nix"; expectedOutput = "Hello, dependency_issue_65"; } { name = "workspace_with_nondefault_lib"; src = ./sample_projects/workspace_with_nondefault_lib; expectedOutput = "Hello, workspace_with_nondefault_lib"; derivationAttrPath = [ "workspaceMembers" "main" ]; } { name = "with_problematic_crates"; src = ./sample_projects/with_problematic_crates; expectedOutput = "Hello, with_problematic_crates!"; customBuild = "sample_projects/with_problematic_crates/default.nix"; # Disable this on Mac OS. FIXME: https://github.com/kolloch/crate2nix/issues/116 skip = stdenv.hostPlatform.isDarwin; } { name = "future_util_multi_version"; src = ./sample_projects/future_util_multi_version; expectedOutput = "Hello, world!"; # FIXME: https://github.com/kolloch/crate2nix/issues/83 skip = true; } { name = "empty_cross"; src = ./sample_projects/empty_cross; cargoToml = "Cargo.toml"; customBuild = "sample_projects/empty_cross/default.nix"; # # FIXME: https://github.com/nix-community/crate2nix/issues/319 skip = true; } { name = "aliased_dependencies"; src = ./sample_projects/aliased-dependencies; expectedOutput = "Hello World !\nHello World !"; } ]; buildTestDerivationAttrSet = let buildTestDerivations = builtins.map (c: { name = c.name; value = buildTest c; }) buildTestConfigs; in builtins.listToAttrs buildTestDerivations; in { checks = { help = pkgs.stdenv.mkDerivation { name = "help"; phases = [ "buildPhase" ]; buildPhase = '' mkdir -p $out ${crate2nix}/bin/crate2nix help >$out/crate2nix.log echo grepping grep USAGE $out/crate2nix.log ''; }; fail = pkgs.stdenv.mkDerivation { name = "fail"; phases = [ "buildPhase" ]; buildPhase = '' mkdir -p $out ${crate2nix}/bin/crate2nix 2>$out/crate2nix.log \ && exit 23 || echo expect error echo grepping grep USAGE $out/crate2nix.log ''; }; } // { # # "source add" tests # sourceAddGit = pkgs.stdenv.mkDerivation { name = "source_add_git"; src = pkgs.symlinkJoin { name = "empty"; paths = [ ]; }; buildInputs = [ crate2nix pkgs.jq ( pkgs.writeShellScriptBin "nix-prefetch-git" '' case "$@" in "--url https://github.com/kolloch/nix-base32.git --fetch-submodules --rev 42f5544e51187f0c7535d453fcffb4b524c99eb2") echo ' { "url": "https://github.com/kolloch/nix-base32.git", "rev": "42f5544e51187f0c7535d453fcffb4b524c99eb2", "date": "2019-11-29T22:22:24+01:00", "sha256": "011f945b48xkilkqbvbsxazspz5z23ka0s90ms4jiqjbhiwll1nw", "fetchSubmodules": true } ' ;; *) echo -e "\e[31mUnrecognized fetch:\e[0m $(basename $0) $@" >&2 exit 1 ;; esac '' ) ]; phases = [ "buildPhase" ]; expectedSources = pkgs.writeTextFile { name = "expected-sources.json"; text = '' { "nix-base32": { "type": "Git", "url": "https://github.com/kolloch/nix-base32.git", "rev": "42f5544e51187f0c7535d453fcffb4b524c99eb2", "sha256": "011f945b48xkilkqbvbsxazspz5z23ka0s90ms4jiqjbhiwll1nw" }, "other-name": { "type": "Git", "url": "https://github.com/kolloch/nix-base32.git", "rev": "42f5544e51187f0c7535d453fcffb4b524c99eb2", "sha256": "011f945b48xkilkqbvbsxazspz5z23ka0s90ms4jiqjbhiwll1nw" } } ''; }; buildPhase = '' mkdir $out jq . $expectedSources >$out/expected-sources.json crate2nix source add git https://github.com/kolloch/nix-base32.git \ --rev 42f5544e51187f0c7535d453fcffb4b524c99eb2 crate2nix source add git --name other-name https://github.com/kolloch/nix-base32.git \ --rev 42f5544e51187f0c7535d453fcffb4b524c99eb2 jq .sources $out/sources.json diff -u $out/expected-sources.json $out/sources.json ''; }; sourceAddCratesIo = pkgs.stdenv.mkDerivation { name = "source_add_crates_io"; src = pkgs.symlinkJoin { name = "empty"; paths = [ ]; }; buildInputs = [ crate2nix pkgs.jq ( pkgs.writeShellScriptBin "nix-prefetch-url" '' case "$@" in "https://static.crates.io/crates/ripgrep/ripgrep-12.0.1.crate --name ripgrep-12.0.1") echo "1arw9pk1qiih0szd26wq76bc0wwbcmhyyy3d4dnwcflka8kfkikx" ;; *) echo -e "\e[31mUnrecognized fetch:\e[0m $(basename $0) $@" >&2 exit 1 ;; esac '' ) ]; phases = [ "buildPhase" ]; expectedSources = pkgs.writeTextFile { name = "expected-sources.json"; text = '' { "other-name": { "type": "CratesIo", "name": "ripgrep", "version": "12.0.1", "sha256": "1arw9pk1qiih0szd26wq76bc0wwbcmhyyy3d4dnwcflka8kfkikx" }, "ripgrep": { "type": "CratesIo", "name": "ripgrep", "version": "12.0.1", "sha256": "1arw9pk1qiih0szd26wq76bc0wwbcmhyyy3d4dnwcflka8kfkikx" } } ''; }; buildPhase = '' mkdir $out jq . $expectedSources >$out/expected-sources.json crate2nix source add cratesIo ripgrep 12.0.1 crate2nix source add cratesIo --name other-name ripgrep 12.0.1 jq .sources $out/sources.json diff -u $out/expected-sources.json $out/sources.json ''; }; sourceAddNix = pkgs.stdenv.mkDerivation { name = "source_add_nix"; src = pkgs.symlinkJoin { name = "empty"; paths = [ ]; }; buildInputs = [ crate2nix pkgs.jq ]; phases = [ "buildPhase" ]; expectedSources = pkgs.writeTextFile { name = "expected-sources.json"; text = '' { "import-sources": { "type": "Nix", "import": "sources.nix" }, "import-sources-attr": { "type": "Nix", "import": "sources2.nix", "attr": "attr.path" }, "name-from-attr": { "type": "Nix", "package": "sources.nix", "attr": "attr.path.name-from-attr" }, "name-from-attr2": { "type": "Nix", "import": "sources.nix", "attr": "attr.path.name-from-attr2" }, "package-sources": { "type": "Nix", "package": "package.nix" }, "package-sources-attr": { "type": "Nix", "package": "package2.nix", "attr": "attr.path" } } ''; }; buildPhase = '' mkdir $out jq . $expectedSources >$out/expected-sources.json crate2nix source add nix --name import-sources --import sources.nix crate2nix source add nix --name import-sources-attr --import sources2.nix attr.path crate2nix source add nix --package sources.nix attr.path.name-from-attr crate2nix source add nix --import sources.nix attr.path.name-from-attr2 crate2nix source add nix --name package-sources --package package.nix crate2nix source add nix --name package-sources-attr --package package2.nix attr.path jq .sources $out/sources.json diff -u $out/expected-sources.json $out/sources.json ''; }; buildNixTestWithLatestCrate2nix = pkgs.callPackage ./nix/nix-test-runner/package.nix { crate2nixTools = tools; }; # PR #453: verify JSON-mode buildTests wires dev-dependencies. # Without the fix, rustc fails with "unresolved import cli_test_dir". json_build_tests = let cargoNix = import ./lib/build-from-json.nix { inherit pkgs; src = ./sample_projects/integration_test; resolvedJson = ./sample_projects/integration_test/Cargo.json; }; testDrv = cargoNix.rootCrate.buildTests; in pkgs.stdenv.mkDerivation { name = "json_build_tests"; phases = [ "buildPhase" ]; buildPhase = '' mkdir -p $out echo === VERIFYING test binary exists test -x ${testDrv}/tests/integration_test echo "test binary compiled successfully with dev-dependencies" echo === VERIFYING test binary lists expected tests # --list prints test names without running them ${testDrv}/tests/integration_test --list 2>&1 | tee $out/test-list.log grep 'read_source_file' $out/test-list.log grep 'write_output_file' $out/test-list.log ''; }; } // rec { # # "source generate" tests # withFetchedSources = pkgs.runCommandNoCCLocal "with-fetched-sources" { } '' mkdir $out ln -s ${crate2nixJsonWithRipgrep}/* $out ln -s ${sourcesMemberDirectory} $out/crate2nix-sources ''; generatedWithFetchedSources = tools.generatedCargoNix { name = "generatedWithFetchedSources"; src = withFetchedSources; }; # buildSourcesProject = # (pkgs.callPackage generatedCargoFilesUpdateProject { }).workspaceMembers.ripgrep; # Test support # # It is to have them directly as attributes for testing. registryGit = pkgs.fetchgit { url = "https://github.com/rust-lang/crates.io-index"; rev = "18e3f063f594fc08a078f0de2bb3f94beed16ae2"; sha256 = "0rpv12ifgnni55phlkb5ppmala7y3zrsc9dl8l99pbsjpqx95vmj"; }; registry = pkgs.linkFarm "crates.io-index" [ { name = "index"; path = registryGit; } ]; cargoConfigWithLocalRegistry = pkgs.writeTextFile { name = "cargo_config"; destination = "/.cargo/config"; text = '' [source] [source.crates-io] replace-with = "local-copy" [source.local-copy] local-registry = "${registry}" ''; }; crate2nixJsonWithRipgrep = pkgs.writeTextFile { name = "crate2nix_json"; destination = "/crate2nix.json"; text = '' { "sources": { "ripgrep": { "type": "CratesIo", "name": "ripgrep", "version": "12.0.1", "sha256": "1arw9pk1qiih0szd26wq76bc0wwbcmhyyy3d4dnwcflka8kfkikx" } } } ''; }; sourcesMemberDirectory = (pkgs.callPackage sourcesNix { }).fetchedSources; sourcesNix = pkgs.stdenv.mkDerivation { name = "crate2nix-sources_nix"; src = crate2nixJsonWithRipgrep; buildInputs = [ crate2nix ]; phases = [ "buildPhase" ]; buildPhase = '' ln -s $src/crate2nix.json . crate2nix source generate mkdir $out ln -s $src/crate2nix.json $out cp crate2nix-sources.nix $out/default.nix ''; }; } // buildTestDerivationAttrSet; inherit buildTestConfigs; } ================================================ FILE: tools.nix ================================================ # # Some tools that might be useful in builds. # # Part of the "public" API of crate2nix in the sense that we will try to # avoid breaking the API and/or mention breakages in the CHANGELOG. # { pkgs ? import ./nix/nixpkgs.nix { config = { }; } , lib ? pkgs.lib , stdenv ? pkgs.stdenv , strictDeprecation ? true }: let cargoNix = pkgs.callPackage ./crate2nix/Cargo.nix { inherit strictDeprecation; }; crate2nix = cargoNix.rootCrate.build; in rec { /* Returns a derivation containing the whole top-level function generated by crate2nix (`Cargo.nix`) which is typically called with `pkgs.callPackage`. name: will be part of the derivation name src: the source that is needed to build the crate, usually the crate/workspace root directory cargoToml: Path to the Cargo.toml file relative to src, "Cargo.toml" by default. */ generatedCargoNix = { name , src , cargoToml ? "Cargo.toml" , additionalCargoNixArgs ? [ ] , additionalCrateHashes ? internal.parseOptHashesFile (src + "/crate-hashes.json") , cargo ? pkgs.cargo }: let crateDir = dirOf (src + "/${cargoToml}"); vendor = internal.vendorSupport rec { inherit crateDir; lockFiles = internal.gatherLockFiles crateDir; hashes = internal.gatherHashes (lockFiles) // additionalCrateHashes; }; in stdenv.mkDerivation { name = "${name}-crate2nix"; buildInputs = [ cargo crate2nix pkgs.jq ]; preferLocalBuild = true; inherit src; phases = [ "unpackPhase" "buildPhase" ]; buildPhase = '' set -e mkdir -p "$out/cargo" export CARGO_HOME="$out/cargo" export HOME="$out" cp ${vendor.cargoConfig} $out/cargo/config crate_hashes="$out/crate-hashes.json" echo -n '${builtins.toJSON vendor.extendedHashes}' | jq > "$crate_hashes" # Remove last trailing newline, which crate2nix doesn't (yet) include truncate -s -1 "$crate_hashes" crate2nix_options="" if [ -r ./${cargoToml} ]; then crate2nix_options+=" -f ./${cargoToml}" fi if test -r "./crate2nix.json" ; then cp "./crate2nix.json" "$out/crate2nix.json" crate2nix_options+=" -c $out/crate2nix.json" fi if test -r "${src}/crate2nix-sources" ; then ln -s "${src}/crate2nix-sources" "$out/crate2nix-sources" fi set -x crate2nix generate \ $crate2nix_options \ -o "Cargo-generated.nix" \ -h "$crate_hashes" \ ${lib.escapeShellArgs additionalCargoNixArgs} || { { set +x; } 2>/dev/null echo "crate2nix failed." >&2 echo "== cargo/config (BEGIN)" >&2 sed 's/^/ /' $out/cargo/config >&2 echo "" echo "== cargo/config (END)" >&2 echo "" echo "== crate-hashes.json (BEGIN)" >&2 if [ -r $crate_hashes ]; then sed 's/^/ /' $crate_hashes >&2 echo "" else echo "$crate_hashes missing" fi echo "== crate-hashes.json (END)" >&2 echo "" echo "== ls -la (BEGIN)" >&2 ls -la echo "== ls -la (END)" >&2 exit 3 } { set +x; } 2>/dev/null if test -r "./crate-hashes.json" ; then set -x diff -u "./crate-hashes.json" $crate_hashes { set +x; } 2>/dev/null fi cp -r . $out/crate echo "import ./crate/Cargo-generated.nix" > $out/default.nix ''; }; # Applies the default arguments from pkgs to the generated `Cargo.nix` file. # # name: will be part of the derivation name # src: the source that is needed to build the crate, usually the crate/workspace root directory # cargoToml: Path to the Cargo.toml file relative to src, "Cargo.toml" by default. appliedCargoNix = { cargoToml ? "Cargo.toml", ... } @ args: pkgs.callPackage (generatedCargoNix args) { }; generate = cargoNix.internal.deprecationWarning "crate2nix/tools.nix: generate deprecated since 0.7. Use generatedCargoNix instead." generatedCargoNix; generated = cargoNix.internal.deprecationWarning "crate2nix/tools.nix: generated deprecated since 0.7. Use appliedCargoNix in instead." appliedCargoNix; internal = rec { # Unpack sources and add a .cargo-checksum.json file to make cargo happy. unpacked = { sha256, src }: assert builtins.isString sha256; assert builtins.isAttrs src; pkgs.runCommand (lib.removeSuffix ".tar.gz" src.name) { } '' mkdir -p $out tar -xzf ${src} --strip-components=1 -C $out echo '{"package":"${sha256}","files":{}}' > $out/.cargo-checksum.json ''; sourceType = { source ? null, ... } @ package: assert source == null || builtins.isString source; if source == null then null else if source == "registry+https://github.com/rust-lang/crates.io-index" then "crates-io" else if lib.hasPrefix "git+" source then "git" else builtins.throw "unknown source type: ${source}"; # Extracts URL and rev from a git source URL. # # Crude, should be more robust :( parseGitSource = source: assert builtins.isString source; let withoutGitPlus = lib.removePrefix "git+" source; splitHash = lib.splitString "#" withoutGitPlus; preFragment = builtins.elemAt splitHash 0; fragment = if builtins.length splitHash >= 2 then builtins.elemAt splitHash 1 else null; splitQuestion = lib.splitString "?" preFragment; preQueryParams = builtins.elemAt splitQuestion 0; queryParamsList = lib.optionals (builtins.length splitQuestion >= 2) (lib.splitString "&" (builtins.elemAt splitQuestion 1)); kv = s: let l = lib.splitString "=" s; key = builtins.elemAt l 0; in { # Cargo supports using the now-obsoleted "ref" key in place of # "branch"; see cargo-vendor source name = if key == "ref" then "branch" else key; value = builtins.elemAt l 1; }; queryParams = builtins.listToAttrs (map kv queryParamsList); in assert builtins.length splitHash <= 2; assert builtins.length splitQuestion <= 2; queryParams // { url = preQueryParams; urlFragment = fragment; }; gatherLockFiles = crateDir: let fromCrateDir = if builtins.pathExists (crateDir + "/Cargo.lock") then [ (crateDir + "/Cargo.lock") ] else [ ]; fromSources = if builtins.pathExists (crateDir + "/crate2nix-sources") then let subdirsTypes = builtins.readDir (crateDir + "/crate2nix-sources"); subdirs = builtins.attrNames subdirsTypes; toLockFile = subdir: (crateDir + "/crate2nix-sources/${subdir}/Cargo.lock"); in builtins.map toLockFile subdirs else [ ]; in fromCrateDir ++ fromSources; parseOptHashesFile = hashesFile: lib.optionalAttrs (builtins.pathExists hashesFile) (builtins.fromJSON (builtins.readFile hashesFile)); gatherHashes = lockFiles: let hashesFiles = builtins.map (cargoLock: "${dirOf cargoLock}/crate-hashes.json") lockFiles; parsedFiles = builtins.map parseOptHashesFile hashesFiles; in lib.foldl (a: b: a // b) { } parsedFiles; vendorSupport = { crateDir ? ./. , lockFiles ? [ ] , hashes ? { } }: rec { toPackageId = { name, version, source, ... }: "${name} ${version} (${source})"; # New-format package ID matching cargo 1.78+, used as crate-hashes.json # keys. Strips the commit fragment from the Cargo.lock source URL and # replaces it with "name@version". toHashKey = { name, version, source, ... }: let sourceBase = builtins.head (lib.splitString "#" source); in "${sourceBase}#${name}@${version}"; locked = let parseFile = cargoLock: lib.importTOML cargoLock; allParsedFiles = builtins.map parseFile lockFiles; merge = merged: lock: { package = merged.package ++ lock.package or [ ]; metadata = merged.metadata // lock.metadata or { }; }; in lib.foldl merge { package = [ ]; metadata = { }; } allParsedFiles; mkGitHash = { source, ... }@attrs: let parsed = parseGitSource source; src = builtins.fetchGit ({ submodules = true; inherit (parsed) url; rev = if isNull parsed.urlFragment then parsed.rev else parsed.urlFragment; } // (if (parsed ? branch || parsed ? tag) then { ref = parsed.branch or "refs/tags/${parsed.tag}"; } else { allRefs = true; }) ); hash = pkgs.runCommand "hash-of-${attrs.name}" { nativeBuildInputs = [ pkgs.nix ]; } '' echo -n "$(nix-hash --type sha256 --base32 ${src})" > $out ''; in rec { name = toHashKey attrs; # Fetching git submodules with builtins.fetchGit is only supported in nix > 2.3 value = hashes.${name} or hashes.${toPackageId attrs} or (if lib.versionAtLeast builtins.nixVersion "2.4" then builtins.readFile hash else builtins.throw "Checksum for ${name} not found in `hashes`"); }; extendedHashes = hashes // builtins.listToAttrs (map mkGitHash (packagesByType.git or [ ])); packages = let packagesWithDuplicates = assert builtins.isList locked.package; locked.package; packagesWithoutLocal = builtins.filter (p: p ? source) packagesWithDuplicates; packageById = package: { name = toPackageId package; value = package; }; packagesById = builtins.listToAttrs (builtins.map packageById packagesWithoutLocal); in builtins.attrValues packagesById; packagesWithType = builtins.filter (pkg: (sourceType pkg) != null) packages; packagesByType = lib.groupBy sourceType packagesWithType; # Returns a derivation with all the transitive dependencies in # sub directories suitable for cargo vendoring. vendoredSources = let crateSources = builtins.map ( package: let fetcher = fetchers.${sourceType package}; source = fetcher package; in { # We are using the store path (without the store directory) # as the name of a symlink, and don't care about store # store path we got that string one. It will in fact be # tract in the value's string context anyways. # # This is needed for Nixpkgs 22.11 and beyond where the # names are deduplicated with an attrset, and attrset keys # are required to not have a string context. name = builtins.baseNameOf (builtins.unsafeDiscardStringContext source); path = source; } ) packagesWithType; in pkgs.linkFarm "deps" crateSources; cargoConfig = let gitSourceConfig = { source, ... }@attrs: assert builtins.isString source; let parsed = parseGitSource source; in '' [source."${lib.removePrefix "git+" source}"] git = "${parsed.url}" ${lib.optionalString (parsed ? rev) ''rev = "${parsed.rev}"''} ${lib.optionalString (parsed ? tag) ''tag = "${parsed.tag}"''} ${lib.optionalString (parsed ? branch) ''branch = "${parsed.branch}"''} replace-with = "vendored-sources" ''; gitSources = packagesByType."git" or [ ]; uniqueBy = f: lib.foldl' (acc: e: if lib.elem (f e) (map f acc) then acc else acc ++ [ e ]) [ ]; gitSourcesUnique = uniqueBy (c: c.source) gitSources; gitSourceConfigs = builtins.map gitSourceConfig gitSourcesUnique; gitSourceConfigsString = lib.concatStrings gitSourceConfigs; in pkgs.writeText "vendor-config" '' [source.crates-io] replace-with = "vendored-sources" ${gitSourceConfigsString} [source.vendored-sources] directory = "${vendoredSources}" ''; # Fetchers by source type that can fetch the package source. fetchers = { "crates-io" = { name, version, source, ... } @ package: assert (sourceType package) == "crates-io"; let packageId = toPackageId package; sha256 = package.checksum or locked.metadata."checksum ${packageId}" or (builtins.throw "Checksum for ${packageId} not found in Cargo.lock"); in unpacked { src = pkgs.fetchurl { name = "crates-io-${name}-${version}.tar.gz"; # https://www.pietroalbini.org/blog/downloading-crates-io/ # Not rate-limited, CDN URL. url = "https://static.crates.io/crates/${name}/${name}-${version}.crate"; inherit sha256; }; inherit sha256; }; "git" = { name, version, source, ... } @ package: assert (sourceType package) == "git"; let hashKey = toHashKey package; sha256 = extendedHashes.${hashKey} or extendedHashes.${toPackageId package}; parsed = parseGitSource source; src = pkgs.fetchgit { name = "${name}-${version}"; inherit sha256; inherit (parsed) url; rev = if isNull parsed.urlFragment then parsed.rev else parsed.urlFragment; }; allCargoTomls = lib.filter (lib.hasSuffix "Cargo.toml") (lib.filesystem.listFilesRecursive src); getCrateNameFromPath = path: let cargoTomlCrate = builtins.fromTOML (builtins.readFile path); in cargoTomlCrate.package.name or null; packageCargoToml = builtins.head (builtins.filter (to_filter: (getCrateNameFromPath to_filter) == name ) allCargoTomls); pathToExtract = lib.removeSuffix "Cargo.toml" packageCargoToml; in pkgs.runCommand (lib.removeSuffix ".tar.gz" src.name) { } '' mkdir -p $out cp -apR ${pathToExtract}/* $out echo '{"package":null,"files":{}}' > $out/.cargo-checksum.json ''; }; }; }; }