Repository: oxalica/nocargo Branch: main Commit: 089f487c2c12 Files: 91 Total size: 247.4 KB Directory structure: gitextract_9z4bbede/ ├── .github/ │ └── workflows/ │ ├── binary-cache.yaml │ ├── ci.yaml │ └── update.yaml ├── .gitignore ├── LICENSE-MIT ├── README.md ├── build-rust-crate/ │ ├── builder-bin.sh │ ├── builder-build-script.sh │ ├── builder-common.sh │ ├── builder-lib.sh │ └── default.nix ├── cache/ │ ├── Cargo.toml │ ├── default.nix │ └── src/ │ └── lib.rs ├── crates-io-override/ │ ├── default.nix │ └── proc-macro.nix ├── flake.nix ├── lib/ │ ├── default.nix │ ├── glob.nix │ ├── pkg-info.nix │ ├── resolve.nix │ ├── semver.nix │ ├── support.nix │ └── target-cfg.nix ├── noc/ │ ├── Cargo.toml │ ├── src/ │ │ ├── init.rs │ │ └── main.rs │ └── templates/ │ └── init-flake.nix ├── scripts/ │ └── cratesio-utils.py ├── tests/ │ ├── build-deps/ │ │ ├── Cargo.toml │ │ ├── build.rs │ │ └── src/ │ │ └── main.rs │ ├── build-feature-env-vars/ │ │ ├── Cargo.toml │ │ ├── build.rs │ │ └── src/ │ │ └── main.rs │ ├── cap-lints/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ ├── crate-names/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ ├── custom-lib-name/ │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── lib.rs │ │ └── main.rs │ ├── default.nix │ ├── dependency-v1/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ ├── dependency-v2/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ ├── dependency-v3/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ ├── fake-semver/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── lib.rs │ ├── features/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ ├── libz-dynamic/ │ │ ├── Cargo.toml │ │ ├── build.rs │ │ └── src/ │ │ └── main.rs │ ├── libz-static/ │ │ ├── Cargo.toml │ │ ├── build.rs │ │ └── src/ │ │ ├── lib.rs │ │ └── main.rs │ ├── lto-fat/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ ├── lto-proc-macro/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ ├── lto-thin/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ ├── tokio-app/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ ├── workspace-inline/ │ │ ├── Cargo.toml │ │ ├── bar/ │ │ │ ├── Cargo.toml │ │ │ └── src/ │ │ │ └── lib.rs │ │ ├── baz/ │ │ │ ├── Cargo.toml │ │ │ └── src/ │ │ │ └── lib.rs │ │ └── src/ │ │ └── main.rs │ ├── workspace-proc-macro-lto/ │ │ ├── Cargo.toml │ │ ├── procm/ │ │ │ ├── Cargo.toml │ │ │ └── src/ │ │ │ └── lib.rs │ │ └── src/ │ │ └── lib.rs │ └── workspace-virtual/ │ ├── Cargo.toml │ └── crates/ │ ├── bar/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── lib.rs │ ├── exc/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ └── foo/ │ ├── Cargo.toml │ └── src/ │ └── main.rs └── toml2json/ ├── Cargo.toml ├── README.md ├── default.nix └── src/ └── main.rs ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/binary-cache.yaml ================================================ name: Binary cache on: push: branches: - main workflow_run: workflows: - update types: - completed branches: - main permissions: contents: read jobs: populate: name: Build and push binary cache runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Install Nix uses: cachix/install-nix-action@v24 - name: Setup Cachix uses: cachix/cachix-action@v13 with: name: nocargo authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' pushFilter: '(-source$|\.tar\.gz)' - name: Build cached crates run: nix build .#cache --show-trace --no-update-lock-file ================================================ FILE: .github/workflows/ci.yaml ================================================ name: CI on: pull_request: push: workflow_run: workflows: - update types: - completed branches: - main permissions: contents: read jobs: flake-check: name: Flake check (locked) runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Install Nix uses: cachix/install-nix-action@v24 - run: nix flake check --show-trace --no-update-lock-file ================================================ FILE: .github/workflows/update.yaml ================================================ name: Update registry on: schedule: - cron: '0 3 * * *' # *-*-* 03:00:00 UTC workflow_dispatch: env: BRANCH: main permissions: contents: write jobs: update: name: Update registry runs-on: ubuntu-latest env: CRATES_TOML_DIR: ./crates-toml steps: - name: Checkout uses: actions/checkout@v4 with: rev: ${{ env.BRANCH }} token: ${{ secrets.GITHUB_TOKEN }} - name: Install Nix uses: cachix/install-nix-action@v24 with: nix_path: nixpkgs=channel:nixpkgs-unstable - name: Cache fetched Cargo.toml files uses: actions/cache@v3 with: path: ${{ env.CRATES_TOML_DIR }} key: crates-toml - name: Sync crates.io database run: ./scripts/cratesio-utils.py sync - name: Update proc-macro crates run: ./scripts/cratesio-utils.py update-proc-macro-crates - name: Update lockfile of popular crates run: ./scripts/cratesio-utils.py update-popular-crates # This should be the last. So the registry locked is not earlier than references. - name: Update flake run: nix flake update - name: Flake check run: nix flake check --show-trace --no-update-lock-file - name: Push changes run: | git config user.name github-actions git config user.email github-actions@github.com git add flake.lock crates-io-override/proc-macro.nix cache/Cargo.{lock,toml} git commit -m "registry: update" git push HEAD:${{ env.BRANCH }} ================================================ FILE: .gitignore ================================================ result result-* ================================================ 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 ================================================ # nocargo: Cargo in Nix 🚧 *This project is under development and is not ready for production yet. APIs are subjects to change.* Build Rust crates with *Nix Build System*. - **No IFDs** (import-from-derivation). See [meme](https://gist.github.com/oxalica/d3b1251eb29d10e6f3cb2005167ddcd9). - No `cargo` dependency during building. Only `rustc`. - No need for hash prefetching or code generation[^no-code-gen]. - Crate level caching, globally shared. - [nixpkgs] integration for non-Rust dependencies. [^no-code-gen]: Initial template generation and `Cargo.lock` updatin don't count for "code generation". The former is optional, and the latter is indeed not "code".
Feature checklist - Binary cache - [x] Top 256 popular crate versions with default features - Nix library - [ ] Non-flake support. - [x] `[workspace]` - [x] `members` - [ ] Auto-`members` - [x] `excludes` FIXME: Buggy. - [ ] `resolver` Currently has custom resolution algorithm, more like v2. - [x] `links` - [x] `[profile]` - [x] `[{,dev-,build-}dependencies]` - [x] `[features]` - [x] Overriding API - [x] `[target..dependencies]` - [x] `[patch]` Automatically supported. Since the dependency graph `Cargo.lock` currently relies on `cargo`'s generation. - [ ] Cross-compilation. FIXME: Buggy with proc-macros. - `noc` helper - [x] `noc init`: Initial template `flake.nix` generation - Dependency kinds - [ ] `registry` - [x] `registry-index` - [x] `git` - [x] `path` inside workspace - [ ] `path` outside workspace - Target detection - [ ] Library FIXME: Assume to always exist. - [x] Binary - [ ] Test - [ ] Bench - [ ] Example - [ ] `Cargo.lock` generation and updating
## Start with Nix flake 1. (Optional) Add binary substituters for pre-built popular crates, by either - Install `cachix` and run `cachix use nocargo` ([see more detail about `cachix`](https://app.cachix.org/cache/nocargo)), or - Manually add substituter `https://nocargo.cachix.org` with public key `nocargo.cachix.org-1:W6jkp5htZBA1tUdU8XHLaD7zBrIFnor0MsLhHgrJeHk=` 1. Enter the root directory of your rust workspace or package. Currently, you should have `Cargo.lock` already created by `cargo`. 1. Run `nix run github:oxalica/nocargo init` to generate `flake.nix`. Or write it by hand by following [the next section](#example-flake.nix-structure). 1. Check flake outputs with `nix flake show`. Typically, the layout would be like, ``` └───packages └───x86_64-linux ├───default: package 'rust_mypkg1-0.1.0' # The "default" package. For workspace, it's the top-level one if exists. ├───mypkg1: package 'rust_mypkg1-0.1.0' # Crate `mypkg1` with `release` profile. ├───mypkg1-dev: package 'rust_mypkg1-debug-0.1.0' # Crate `mypkg1` with `dev` profile. ├───mypkg2: package 'rust_mypkg2-0.1.0' # etc. └───mypkg2-dev: package 'rust_mypkg2-debug-0.1.0' ``` 1. Run `nix build .#` to build your package. Built binaries (if any) will be placed in `./result/bin`, and the library will be in `./result/lib`. 1. Have fun! ## Example `flake.nix` structure for reference A template `flake.nix` with common setup are below. It's mostly the same as the generated one, except that the helper `noc` will scan the workspace and discover all external registries and git dependencies for you. ```nix { description = "My Rust packages"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs"; flake-utils.url = "github:numtide/flake-utils"; nocargo = { url = "github:oxalica/nocargo"; inputs.nixpkgs.follows = "nixpkgs"; # See below. # inputs.registry-crates-io.follows = "registry-crates-io"; }; # Optionally, you can explicitly import crates.io-index here. # So you can `nix flake update` at any time to get cutting edge version of crates, # instead of waiting `nocargo` to dump its dependency. # Otherwise, you can simply omit this to use the locked registry from `nocargo`, # which is updated periodically. # registry-crates-io = { url = "github:rust-lang/crates.io-index"; flake = false; }; }; outputs = { nixpkgs, flake-utils, nocargo, ... }@inputs: flake-utils.lib.eachSystem [ "x86_64-linux" ] (system: let # The entry API to make Nix derivations from your Rust workspace or package. # The output of it consists of profile names, like `release` or `dev`, each of which is # a attrset of all member package derivations keyed by their package names. ws = nocargo.lib.${system}.mkRustPackageOrWorkspace { # The root directory, which contains `Cargo.lock` and top-level `Cargo.toml` # (the one containing `[workspace]` for workspace). src = ./.; # If you use registries other than crates.io, they should be imported in flake inputs, # and specified here. Note that registry should be initialized via `mkIndex`, # with an optional override. extraRegistries = { # "https://example-registry.org" = nocargo.lib.${system}.mkIndex inputs.example-registry {}; }; # If you use crates from git URLs, they should be imported in flake inputs, # and specified here. gitSrcs = { # "https://github.com/some/repo" = inputs.example-git-source; }; # If some crates in your dependency closure require packages from nixpkgs. # You can override the argument for `stdenv.mkDerivation` to add them. # # Popular `-sys` crates overrides are maintained in `./crates-io-override/default.nix` # to make them work out-of-box. PRs are welcome. buildCrateOverrides = with nixpkgs.legacyPackages.${system}; { # Use package id format `pkgname version (registry)` to reference a direct or transitive dependency. "zstd-sys 2.0.1+zstd.1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = old: { nativeBuildInputs = [ pkg-config ]; propagatedBuildInputs = [ zstd ]; }; # Use package name to reference local crates. "mypkg1" = old: { nativeBuildInputs = [ git ]; }; }; # We use the rustc from nixpkgs by default. # But you can override it, for example, with a nightly version from https://github.com/oxalica/rust-overlay # rustc = rust-overlay.packages.${system}.rust-nightly_2022-07-01; }; in rec { # For convenience, we hoist derivations of `release` and `dev` profile for easy access, # with `dev` packages postfixed by `-dev`. # You can export different packages of your choice. packages = ws.release // nixpkgs.lib.mapAttrs' (name: value: { name = "${name}-dev"; inherit value; }) ws.dev // { # The "default" features are turned on by default. # You can `override` the library derivation to enable a different set of features. # Explicit overriding will disable "default", unless you manually include it. mypkg1-with-custom-features = (ws.release.mypkg1.override { # Enables two features (and transitive ones), and disables "default". features = [ "feature1" "feature2" ]; }).bin; }; }); } ``` ## FAQ ### Comparison with [cargo2nix] and [naersk]? Main differences are already clarified [on the top](#nocargo%3A-cargo-in-nix). `nocargo` is inspired by `cargo2nix` and `buildRustCrate` in `nixpkgs`. We are more like `cargo2nix` while the generation part is implemented by pure Nix, but less like `naersk` which is a wrapper to call `cargo` to build the package inside derivations. In other words, we and `cargo2nix` use Nix as a *Build System*, while `nearsk` use Nix as a *Package Manager* or *Packager*.
Detail comparison of nocargo, cargo2nix/buildRustCrate, naersk and buildRustPackage | | nocargo | [cargo2nix]/`buildRustCrate` | [naersk] | `buildRustPackage` | |-|-|-|-|-| | Depend on `cargo` | Updating `Cargo.lock` | Updating & generating & building | Updating & vendoring & building | Building | | Derivation granularity | Per crate | Per crate | Per package + one dependency closure | All in one | | Crate level sharing | ✔️ | ✔️ | ✖ | ✖ | | Binary substitution per crate | ✔️ | Not implemented | ✖ | ✖ | | Code generation | ✖ | ✔️ | ✖ | ✖ | | Edit workspace & rebuild | Rebuild leaf crates | Rebuild leaf crates | Rebuild leaf crates | Refetch and rebuild all crates | | Edit dependencies & rebuild | Rebuild changed crates (refetch if needed) | Refetch, regenerate and rebuild changed crates | Refetch and rebuild all crates | Refetch and rebuild all crates | | Offline rebuild as long as | Not adding unfetched crate dependency | Not adding unfetched crate dependency | Not changing any dependencies | ✖ |
### But why pure Nix build system? - Sharing through fine-grained derivations between all projects, not just in one workspace. - Binary substitution per crate. No need for global `target_dir`/`CARGO_TARGET_DIR` or [sccache]. - Easy `nixpkgs` integration for non-Rust package dependencies, cross-compilation (planned) and package overriding. - More customizability: per-crate `rustc` flags tweaking, arbitrary crate patching, force dynamic linking and more. ### Can I really throw away `cargo`? Sorry, currently no. :crying_cat_face: Updating of `Cargo.lock` still relies on `cargo`. This can happen when creating a new project or changing dependencies. We are mainly using `cargo`'s SAT solver to pin down the dependency graph. It's *possible* to implement it ourselves, but not yet, due to the complexity. ## License MIT Licensed. [nixpkgs]: https://github.com/NixOS/nixpkgs [naersk]: https://github.com/nix-community/naersk [cargo2nix]: https://github.com/cargo2nix/cargo2nix [sccache]: https://github.com/mozilla/sccache ================================================ FILE: build-rust-crate/builder-bin.sh ================================================ source $stdenv/setup source $builderCommon declare -A buildFlagsMap declare -A binPathMap dontInstall=1 addBin() { local name="$1" path="$2" binEdition="$3" local -a pathCandidates if [[ -z "$name" ]]; then echo "Name of the binary target is not specified" exit 1 fi if [[ -n "$path" ]]; then pathCandidates=("$path") else pathCandidates=() if [[ -f "src/bin/$name.rs" ]]; then pathCandidates+=("src/bin/$name.rs") fi if [[ -f "src/bin/$name/main.rs" ]]; then pathCandidates+=("src/bin/$name/main.rs") fi if [[ "$name" == "$pkgName" && -f "src/main.rs" ]]; then pathCandidates+=("src/main.rs") fi fi case ${#pathCandidates[@]} in 0) echo "Cannot guess path of binary target" exit 1 ;; 1) path="${pathCandidates[0]}" ;; *) echo "Ambiguous binary target, candidate paths: ${pathCandidates[*]}" exit 1 ;; esac printf "Found binary %q at %q\n" "$name" "$path" binPathMap["$path"]=1 # TODO: Other flags. buildFlagsMap["$name"]="$path --crate-name ${name//-/_} -C metadata=$rustcMeta-$name" if [[ -n "${binEdition:=$globalEdition}" ]]; then buildFlagsMap["$name"]+=" --edition $binEdition" fi } configurePhase() { runHook preConfigure convertCargoToml globalEdition="$(jq --raw-output '.package.edition // ""' "$cargoTomlJson")" pkgName="$(jq --raw-output '.package.name // ""' "$cargoTomlJson")" # For packages with the 2015 edition, the default for auto-discovery is false if at least one target is # manually defined in Cargo.toml. Beginning with the 2018 edition, the default is always true. # See: https://doc.rust-lang.org/cargo/reference/cargo-targets.html#target-auto-discovery autoDiscovery= if [[ "$(jq --raw-output '.package.autobins' "$cargoTomlJson")" != false && ( "${globalEdition:-2015}" != 2015 || ${#buildFlagsMap[@]} = 0 ) ]]; then autoDiscovery=1 fi local binsStr while read -r name; do read -r path read -r binEdition addBin "$name" "$path" "$binEdition" # Don't strip whitespace. done < <(jq --raw-output '.bin // [] | .[] | .name // "", .path // "", .edition // ""' "$cargoTomlJson") if [[ -n "$autoDiscovery" ]]; then if [[ -f src/main.rs && -z ${binPathMap[src/main.rs]} ]]; then addBin "$pkgName" src/main.rs "" fi local f name for f in src/bin/*; do name="${f##*/}" if [[ "$f" = *.rs && -f "$f" ]]; then [[ -n "${binPathMap["$f"]}" ]] || addBin "${name%.rs}" "$f" "" elif [[ -f "$f/main.rs" ]]; then [[ -n "${binPathMap["$f/main.rs"]}" ]] || addBin "$name" "$f/main.rs" "" fi done fi if [[ ${#buildFlagsMap[@]} = 0 ]]; then echo "No binaries to be built" mkdir $out exit 0 fi # Implicitly link library of current crate, if exists. if [[ -e "$libDevDrv"/lib ]]; then addExternFlags buildFlagsArray link "::$libOutDrv:$libDevDrv" fi # Actually unused. declare -a cdylibBuildFlagsArray addExternFlags buildFlagsArray link $dependencies addFeatures buildFlagsArray $features importBuildOut buildFlagsArray cdylibBuildFlagsArray "$buildDrv" setCargoCommonBuildEnv depsClosure="$(mktemp -d)" collectTransDeps "$depsClosure" $dependencies buildFlagsArray+=(-Ldependency="$depsClosure") runHook postConfigure } buildPhase() { runHook preBuild mkdir -p $out/bin local binName for binName in "${!buildFlagsMap[@]}"; do export CARGO_CRATE_NAME="$binName" export CARGO_BIN_NAME="$binName" runRustc "Building binary $binName" \ ${buildFlagsMap["$binName"]} \ --crate-type bin \ --out-dir $out/bin \ $buildFlags \ "${buildFlagsArray[@]}" done runHook postBuild } genericBuild ================================================ FILE: build-rust-crate/builder-build-script.sh ================================================ source $stdenv/setup source $builderCommon shopt -s nullglob preInstallPhases+="runBuildScriptPhase " buildFlagsArray+=( -Cmetadata="$rustcMeta" ) configurePhase() { runHook preConfigure convertCargoToml buildScriptSrc="$(jq --raw-output '.package.build // ""' "$cargoTomlJson")" if [[ -z "$buildScriptSrc" && -e build.rs ]]; then buildScriptSrc=build.rs elif [[ -z "$buildScriptSrc" ]]; then echo "No build script, doing nothing" mkdir -p $out exit 0 fi edition="$(jq --raw-output '.package.edition // ""' "$cargoTomlJson")" if [[ -n "$edition" ]]; then buildFlagsArray+=(--edition="$edition") fi addFeatures buildFlagsArray $features addExternFlags buildFlagsArray link $dependencies setCargoCommonBuildEnv depsClosure="$(mktemp -d)" collectTransDeps "$depsClosure" $dependencies buildFlagsArray+=(-Ldependency="$depsClosure") runHook postConfigure } buildPhase() { runHook preBuild mkdir -p $out/bin runRustc "Building build script" \ "$buildScriptSrc" \ --out-dir="$out/bin" \ --crate-name="build_script_build" \ --crate-type=bin \ --emit=link \ -Cembed-bitcode=no \ $buildScriptBuildFlags \ "${buildFlagsArray[@]}" runHook postBuild } runBuildScriptPhase() { runHook preRunBuildScript export CARGO_MANIFEST_DIR="$(pwd)" if [[ -n "$links" ]]; then export CARGO_MANIFEST_LINKS="$links" fi for feat in $features; do feat_uppercase="${feat^^}" export "CARGO_FEATURE_${feat_uppercase//-/_}"=1 done export OUT_DIR="$out/rust-support/out-dir" export NUM_JOBS=$NIX_BUILD_CORES export RUSTC_BACKTRACE=1 # Make debugging easier. local buildOut for buildOut in $linksDependencies; do if [[ -e "$buildOut/rust-support/links-metadata" ]]; then source "$buildOut/rust-support/links-metadata" fi done # Other flags are set outside. # - CARGO_CFG_ # - HOST # - TARGET # - RUSTC # - CARGO # - RUSTDOC mkdir -p "$out/rust-support" "$OUT_DIR" echo "Running build script" stdoutFile="$out/rust-support/build-stdout" "$out/bin/build_script_build" | tee "$stdoutFile" runHook postRunBuildScript } installPhase() { runHook preInstall # https://doc.rust-lang.org/1.61.0/cargo/reference/build-scripts.html#outputs-of-the-build-script local line rhs while read -r line; do rhs="${line#*=}" case "$line" in cargo:rerun-if-changed=*|cargo:rerun-if-env-changed=*) # Ignored due to the sandbox. ;; cargo:rustc-link-arg=*) echo "-Clink-arg=$rhs" >>"$out/rust-support/rustc-link-args" ;; cargo:rustc-link-arg-bin=*) if [[ "$rhs" != *=* ]]; then echo "Missing binary name: $line" exit 1 fi echo "-Clink-arg=${rhs%%=*}" >>"$out/rust-support/rustc-link-args-bin-${rhs#*=}" ;; cargo:rustc-link-arg-bins=*) echo "-Clink-arg=$rhs" >>"$out/rust-support/rustc-link-args-bins" ;; cargo:rustc-link-arg-tests=*) echo "-Clink-arg=$rhs" >>"$out/rust-support/rustc-link-args-tests" ;; cargo:rustc-link-arg-examples=*) echo "-Clink-arg=$rhs" >>"$out/rust-support/rustc-link-args-examples" ;; cargo:rustc-link-arg-benches=*) echo "-Clink-arg=$rhs" >>"$out/rust-support/rustc-link-args-benches" ;; cargo:rustc-link-lib=*) if [[ -z "$rhs" ]]; then echo "Empty link path: $line" exit 1 fi echo "-l$rhs" >>"$out/rust-support/rustc-flags" ;; cargo:rustc-link-search=*) if [[ -z "$rhs" ]]; then echo "Empty link path: $line" exit 1 fi echo "-L$rhs" >>"$out/rust-support/rustc-flags" ;; cargo:rustc-flags=*) local flags i flag read -r -a flags <<<"$rhs" for (( i = 0; i < ${#flags[@]}; i++ )); do flag="${flags[i]}" if [[ "$flag" = -[lL] ]]; then (( i++ )) flag+="${flags[i]}" elif [[ "$flag" != -[lL]* ]]; then echo "Only -l and -L are allowed from build script: $line" exit 1 fi if [[ ${#flag} == 2 ]]; then echo "Empty link path: $line" exit 1 fi echo "$flag" >>"$out/rust-support/rustc-flags" done ;; cargo:rustc-cfg=*) echo "--cfg=$rhs" >>"$out/rust-support/rustc-flags" ;; cargo:rustc-env=*=*) printf 'export %q=%q\n' "${rhs%%=*}" "${rhs#*=}" >>"$out/rust-support/rustc-env" ;; cargo:rustc-cdylib-link-arg=*) echo "-Clink-arg=$rhs" >>"$out/rust-support/rustc-cdylib-flags" ;; cargo:warning=*) printf "\033[0;1;33mWarning\033[0m: %s\n" "$rhs" ;; cargo:*=*) if [[ -n "${links:-}" ]]; then rhs="${line#*:}" local k="DEP_${links}_${rhs%%=*}" v="${rhs#*=}" k="${k^^}" k="${k//-/_}" printf 'export %q=%q\n' "$k" "$v" >>"$out/rust-support/links-metadata" else printf "\033[0;1;33mWarning\033[0m: no 'links' defined in Cargo.toml, ignoring %s" "$line" fi ;; cargo:*) echo "Unknown or wrong output line: $line" exit 1 ;; *) ;; esac done <"$stdoutFile" local file for file in "$out"/rust-support/{rustc-*,links-metadata*}; do sort "$file" -o "$file" done runHook postInstall } genericBuild ================================================ FILE: build-rust-crate/builder-common.sh ================================================ declare -a buildFlagsArray buildFlagsArray+=( --color=always ) # Collect all transitive dependencies (symlinks). collectTransDeps() { local collectDir="$1" line rename procMacro depOut depDev mkdir -p "$collectDir" shift for line in "$@"; do IFS=: read -r rename procMacro depOut depDev <<<"$line" # May be empty. cp --no-dereference --no-clobber -t $collectDir $depDev/rust-support/deps-closure/* 2>/dev/null || true done } addExternFlags() { local var="$1" kind="$2" line rename procMacro depOut depDev paths shift 2 for line in "$@"; do IFS=: read -r rename procMacro depOut depDev <<<"$line" if [[ -n "$procMacro" ]]; then paths=("$depOut"/lib/*"$sharedLibraryExt") elif [[ "$kind" == meta ]]; then paths=("$depDev"/lib/*.rmeta) else # FIXME: Currently we only link rlib. paths=("$depOut"/lib/*.rlib) fi if (( ${#paths[@]} == 0 )); then echo "No dependent library found for $line" exit 1 elif (( ${#paths[@]} > 1 )); then echo "Multiple candidate found for dependent library $line, found: ${paths[*]}" exit 1 fi if [[ -z "$rename" ]]; then if [[ "${paths[0]##*/}" =~ ^lib(.*)(-.*)(\.rmeta|\.rlib|"$sharedLibraryExt")$ ]]; then rename="${BASH_REMATCH[1]}" else echo "Invalid library name: ${paths[0]}" exit 1 fi fi eval "$var"'+=(--extern="$rename=${paths[0]}")' done } addFeatures() { local var="$1" feat shift for feat in "$@"; do eval "$var"'+=(--cfg="feature=\"$feat\"")' done } importBuildOut() { local var="$1" cvar="$2" drv="$3" flags [[ ! -e "$drv/rust-support/build-stdout" ]] && return echo export OUT_DIR="$drv/rust-support/out-dir" export OUT_DIR="$drv/rust-support/out-dir" if [[ -e "$drv/rust-support/rustc-env" ]]; then cat "$drv/rust-support/rustc-env" source "$drv/rust-support/rustc-env" fi if [[ -e "$drv/rust-support/rustc-flags" ]]; then mapfile -t flags <"$drv/rust-support/rustc-flags" eval "$var"'+=("${flags[@]}")' fi if [[ -e "$drv/rust-support/rustc-cdylib-flags" ]]; then mapfile -t flags <"$drv/rust-support/rustc-cdylib-flags" eval "$cvar"'+=("${flags[@]}")' fi } runRustc() { local msg="$1" shift echo "$msg: RUSTC ${*@Q}" $RUSTC "$@" } convertCargoToml() { local cargoToml="${1:-"$(pwd)/Cargo.toml"}" cargoTomlJson="$(mktemp "$(dirname "$cargoToml")/Cargo.json.XXX")" toml2json <"$cargoToml" >"$cargoTomlJson" } # https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates setCargoCommonBuildEnv() { # CARGO_CRATE_NAME is set outside since targets have individual crate names. # export CARGO= CARGO_MANIFEST_DIR="$(dirname "$cargoTomlJson")" export CARGO_MANIFEST_DIR CARGO_PKG_NAME="$(jq --raw-output '.package.name // ""' "$cargoTomlJson")" CARGO_PKG_VERSION="$(jq --raw-output '.package.version // ""' "$cargoTomlJson")" if [[ -z "CARGO_PKG_NAME" ]]; then echo "Package name must be set" exit 1 fi if [[ -z "CARGO_PKG_VERSION" ]]; then echo "Package version must be set" exit 1 fi CARGO_PKG_AUTHORS="$(jq --raw-output '.package.authors // [] | join(":")' "$cargoTomlJson")" CARGO_PKG_DESCRIPTION="$(jq --raw-output '.package.description // ""' "$cargoTomlJson")" CARGO_PKG_HOMEPAGE="$(jq --raw-output '.package.homepage // ""' "$cargoTomlJson")" CARGO_PKG_LICENSE="$(jq --raw-output '.package.license // ""' "$cargoTomlJson")" CARGO_PKG_LICENSE_FILE="$(jq --raw-output '.package."license-file" // ""' "$cargoTomlJson")" export CARGO_PKG_NAME CARGO_PKG_VERSION CARGO_PKG_AUTHORS CARGO_PKG_DESCRIPTION \ CARGO_PKG_HOMEPAGE CARGO_PKG_LICENSE CARGO_PKG_LICENSE_FILE if [[ "$version" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(-([A-Za-z0-9.-]+))?(\+.*)?$ ]]; then export CARGO_PKG_VERSION_MAJOR="${BASH_REMATCH[1]}" export CARGO_PKG_VERSION_MINOR="${BASH_REMATCH[2]}" export CARGO_PKG_VERSION_PATCH="${BASH_REMATCH[3]}" export CARGO_PKG_VERSION_PRE="${BASH_REMATCH[4]}" else echo "Invalid version: $version" fi } ================================================ FILE: build-rust-crate/builder-lib.sh ================================================ source $stdenv/setup source $builderCommon shopt -s nullglob buildFlagsArray+=( -Cmetadata="$rustcMeta" ) configurePhase() { runHook preConfigure convertCargoToml libSrc="$(jq --raw-output '.lib.path // ""' "$cargoTomlJson")" if [[ -z "$libSrc" && -e src/lib.rs ]]; then libSrc=src/lib.rs fi if [[ ! -e "$libSrc" ]]; then echo "No library to be built" mkdir $out $dev exit 0 fi crateName="$(jq --raw-output '.lib.name // (.package.name // "" | gsub("-"; "_"))' "$cargoTomlJson")" if [[ -z "$crateName" ]]; then echo "Package name must be set" exit 1 fi edition="$(jq --raw-output '.package.edition // .lib.edition // ""' "$cargoTomlJson")" if [[ -n "$edition" ]]; then buildFlagsArray+=(--edition="$edition") fi mapfile -t crateTypes < <(jq --raw-output '.lib."crate-type" // ["lib"] | .[]' "$cargoTomlJson") cargoTomlIsProcMacro="$(jq --raw-output 'if .lib."proc-macro" then "1" else "" end' "$cargoTomlJson")" if [[ "$cargoTomlIsProcMacro" != "$procMacro" ]]; then echo "Cargo.toml says proc-macro = ${cargoTomlIsProcMacro:-0} but it is built with procMacro = ${procMacro:-0}" exit 1 fi if [[ -n "$procMacro" ]]; then # Override crate type. crateTypes=("proc-macro") buildFlagsArray+=(--extern=proc_macro) fi needLinkDeps= buildCdylib= for crateType in "${crateTypes[@]}"; do case "$crateType" in lib|rlib) ;; dylib|staticlib|proc-macro|bin) needLinkDeps=1 ;; cdylib) buildCdylib=1 ;; *) echo "Unsupported crate-type: $crateType" exit 1 ;; esac done if [[ -n "$needLinkDeps" ]]; then addExternFlags buildFlagsArray link $dependencies else addExternFlags buildFlagsArray meta $dependencies fi declare -a cdylibBuildFlagsArray importBuildOut buildFlagsArray cdylibBuildFlagsArray "$buildDrv" # FIXME: cargo include cdylib flags for all crate-types once cdylib is included. buildFlagsArray+=( "${cdylibBuildFlagsArray[@]}" ) addFeatures buildFlagsArray $features setCargoCommonBuildEnv export CARGO_CRATE_NAME="$crateName" collectTransDeps $dev/rust-support/deps-closure $dependencies buildFlagsArray+=(-Ldependency="$dev/rust-support/deps-closure") runHook postConfigure } buildPhase() { runHook preBuild local crateTypesCommaSep printf -v crateTypesCommaSep '%s,' "${crateTypes[@]}" crateTypesCommaSep="${crateTypesCommaSep%,}" mkdir -p $out/lib runRustc "Building lib" \ "$libSrc" \ --out-dir="$out/lib" \ --crate-name="$crateName" \ --crate-type="$crateTypesCommaSep" \ --emit=metadata,link \ -Cextra-filename="-$rustcMeta" \ $buildFlags \ "${buildFlagsArray[@]}" runHook postBuild } installPhase() { runHook preInstall mkdir -p $out $bin $dev/lib $dev/rust-support/deps-closure # May be empty. mv -t $dev/lib $out/lib/*.rmeta 2>/dev/null || true ln -sft $dev/rust-support/deps-closure $out/lib/* $dev/lib/* 2>/dev/null || true runHook postInstall } genericBuild ================================================ FILE: build-rust-crate/default.nix ================================================ { lib, nocargo-lib, stdenv, buildPackages, rust, toml2json, jq }: { pname , version , src , rustc ? buildPackages.rustc , links ? null # [ { rename = "foo" /* or null */; drv = ; } ] , dependencies ? [] # Normal dependencies with non empty `links`, which will propagate `DEP__` environments to build script. , linksDependencies ? dependencies , buildDependencies ? [] , features ? [] , profile ? {} , capLints ? null , buildFlags ? [] , buildScriptBuildFlags ? [] , procMacro ? false , nativeBuildInputs ? [] , propagatedBuildInputs ? [] , ... }@args: let inherit (nocargo-lib.target-cfg) platformToCfgAttrs; mkRustcMeta = dependencies: features: let deps = lib.concatMapStrings (dep: dep.drv.rustcMeta) dependencies; feats = lib.concatStringsSep ";" features; final = "${pname} ${version} ${feats} ${deps}"; in lib.substring 0 16 (builtins.hashString "sha256" final); buildRustcMeta = mkRustcMeta buildDependencies []; rustcMeta = mkRustcMeta dependencies []; # TODO: Pass target binary paths instead of drv here? mkDeps = map ({ rename, drv, ... }: lib.concatStringsSep ":" [ (toString rename) (toString drv.procMacro) drv.out drv.dev ]); toDevDrvs = map ({ drv, ... }: drv.dev); buildDeps = mkDeps buildDependencies; libDeps = mkDeps dependencies; builderCommon = ./builder-common.sh; convertBool = f: t: x: if x == true then t else if x == false then f else x; # https://doc.rust-lang.org/cargo/reference/profiles.html profileToRustcFlags = p: [] ++ lib.optional (p.opt-level or 0 != 0) "-Copt-level=${toString p.opt-level}" ++ lib.optional (p.debug or false != false) "-Cdebuginfo=${if p.debug == true then "2" else toString p.debug}" # TODO: `-Cstrip` is not handled since stdenv will always strip them. ++ lib.optional (p ? debug-assertions) "-Cdebug-assertions=${convertBool "no" "yes" p.debug-assertions}" ++ lib.optional (p ? overflow-checks) "-Coverflow-checks=${convertBool "no" "yes" p.debug-assertions}" ++ lib.optional (!procMacro && p.lto or false != false) "-Clto=${if p.lto == true then "fat" else p.lto}" ++ lib.optional (p.panic or "unwind" != "unwind") "-Cpanic=${p.panic}" # `incremental` is not useful since Nix builds in a sandbox. ++ lib.optional (p ? codegen-units) "-Ccodegen-units=${toString p.codegen-units}" ++ lib.optional (p.rpath or false) "-Crpath" ++ lib.optional (p.lto or false == false) "-Cembed-bitcode=no" ; convertProfile = p: { buildFlags = profileToRustcFlags p ++ lib.optional (capLints != null) "--cap-lints=${capLints}" ++ buildFlags; buildScriptBuildFlags = profileToRustcFlags (p.build-override or {}) ++ buildScriptBuildFlags; # Build script environments. PROFILE = p.name or null; OPT_LEVEL = p.opt-level or 0; DEBUG = p.debug or 0 != 0; }; profile' = convertProfile profile; buildProfile' = convertProfile (profile.build-override or {}); commonArgs = { inherit pname version src; nativeBuildInputs = [ toml2json jq ] ++ nativeBuildInputs; sharedLibraryExt = stdenv.hostPlatform.extensions.sharedLibrary; inherit capLints; RUSTC = "${rustc}/bin/rustc"; } // removeAttrs args [ "pname" "version" "src" "rustc" "links" "dependencies" "linksDependencies" "buildDependencies" "features" "profile" "capLints" "buildFlags" "buildScriptBuildFlags" "procMacro" "nativeBuildInputs" "propagatedBuildInputs" ]; cargoCfgs = lib.mapAttrs' (key: value: { name = "CARGO_CFG_${lib.toUpper key}"; value = if lib.isList value then lib.concatStringsSep "," value else if value == true then "" else value; }) (platformToCfgAttrs stdenv.hostPlatform); buildDrv = stdenv.mkDerivation ({ name = "rust_${pname}-${version}-build"; builder = ./builder-build-script.sh; inherit propagatedBuildInputs builderCommon features links; rustcMeta = buildRustcMeta; dependencies = buildDeps; linksDependencies = map (dep: dep.drv.buildDrv) linksDependencies; HOST = rust.toRustTarget stdenv.buildPlatform; TARGET = rust.toRustTarget stdenv.hostPlatform; # This drv links for `build_script_build`. # So include transitively propagated upstream `-sys` crates' ld dependencies. buildInputs = toDevDrvs dependencies; # Build script may produce object files and static libraries which should not be modified. dontFixup = true; } // commonArgs // cargoCfgs // buildProfile'); libDrv = stdenv.mkDerivation ({ name = "rust_${pname}-${version}"; builder = ./builder-lib.sh; outputs = [ "out" "dev" ]; inherit builderCommon buildDrv features rustcMeta procMacro; # Transitively propagate upstream `-sys` crates' ld dependencies. # Since `rlib` doesn't link. propagatedBuildInputs = toDevDrvs dependencies ++ propagatedBuildInputs; dependencies = libDeps; } // commonArgs // profile'); binDrv = stdenv.mkDerivation ({ name = "rust_${pname}-${version}-bin"; builder = ./builder-bin.sh; inherit propagatedBuildInputs builderCommon buildDrv features rustcMeta; libOutDrv = libDrv.out; libDevDrv = libDrv.dev; # This requires linking. # Include transitively propagated upstream `-sys` crates' ld dependencies. buildInputs = toDevDrvs dependencies; dependencies = libDeps; } // commonArgs // profile'); in libDrv // { build = buildDrv; bin = binDrv; } ================================================ FILE: cache/Cargo.toml ================================================ [package] name = "cache" version = "0.0.0" description = "Virtual package to reference all popular crates to be cached" # N.B. We cannot use `={version}` since cargo doesn't allow two compatible versions of # the same crate appears in the dependency tree, eg. getrandom 0.2.6 and getrandom@0.2.7. # So we use carpet specifiers to allow merging compatible versions to the highest one currently. [dependencies] addr2line = "0.17" adler = "1" ahash = "0.7" aho-corasick = "0.7" ansi_term = "0.12" anyhow = "1" arrayref = "0.3" arrayvec = "0.7" arrayvec-2 = { package = "arrayvec", version = "0.5" } async-stream = "0.3" async-stream-impl = "0.3" async-trait = "0.1" atty = "0.2" autocfg = "1" base64 = "0.13" bincode = "1" bitflags = "1" block-buffer = "0.10" block-buffer-2 = { package = "block-buffer", version = "0.9" } bstr = "0.2" byteorder = "1" bytes = "1" cc = "1" cfg-if = "1" cfg-if-2 = { package = "cfg-if", version = "0.1" } chrono = "0.4" clap = "2" clap_lex = "0.2" constant_time_eq = "0.1" convert_case = "0.4" core-foundation-sys = "0.8" cpufeatures = "0.2" crc32fast = "1" crossbeam-channel = "0.5" crossbeam-deque = "0.8" crossbeam-utils = "0.8" crunchy = "0.2" crypto-common = "0.1" csv = "1" csv-core = "0.1" darling_macro = "0.13" data-encoding = "2" derivative = "2" derive_more = "0.99" digest = "0.10" digest-2 = { package = "digest", version = "0.9" } dirs-next = "2" dirs-sys = "0.3" dirs-sys-next = "0.1" either = "1" encoding_rs = "0.8" env_logger = "0.9" fallible-iterator = "0.2" fastrand = "1" flate2 = "1" fnv = "1" foreign-types = "0.3" foreign-types-shared = "0.1" form_urlencoded = "1" futures = "0.3" futures-channel = "0.3" futures-core = "0.3" futures-executor = "0.3" futures-io = "0.3" futures-macro = "0.3" futures-sink = "0.3" futures-task = "0.3" futures-util = "0.3" generic-array = "0.14" getrandom = "0.2" getrandom-2 = { package = "getrandom", version = "0.1" } gimli = "0.26" glob = "0.3" h2 = "0.3" half = "1" hashbrown = "0.12" hashbrown-2 = { package = "hashbrown", version = "0.11" } heck = "0.4" heck-2 = { package = "heck", version = "0.3" } hermit-abi = "0.1" hex = "0.4" hmac = "0.12" hostname = "0.3" http = "0.2" http-body = "0.4" httparse = "1" httpdate = "1" humantime = "2" hyper = "0.14" hyper-tls = "0.5" ident_case = "1" idna = "0.2" indexmap = "1" instant = "0.1" ipnet = "2" itertools = "0.10" itoa = "1" itoa-2 = { package = "itoa", version = "0.4" } jobserver = "0.1" lazy_static = "1" lazycell = "1" libc = "0.2" libloading = "0.7" linked-hash-map = "0.5" lock_api = "0.4" log = "0.4" maplit = "1" match_cfg = "0.1" matchers = "0.1" matches = "0.1" memchr = "2" memoffset = "0.6" mime = "0.3" minimal-lexical = "0.2" miniz_oxide = "0.5" miniz_oxide-2 = { package = "miniz_oxide", version = "0.4" } mio = "0.8" multimap = "0.8" native-tls = "0.2" nom = "7" num-bigint = "0.4" num-integer = "0.1" num-iter = "0.1" num-traits = "0.2" num_cpus = "1" num_threads = "0.1" object = "0.29" once_cell = "1" oorandom = "11" opaque-debug = "0.3" openssl-macros = "0.1" openssl-probe = "0.1" openssl-sys = "0.9" parking_lot = "0.12" parking_lot-2 = { package = "parking_lot", version = "0.11" } parking_lot_core = "0.9" parking_lot_core-2 = { package = "parking_lot_core", version = "0.8" } peeking_take_while = "0.1" percent-encoding = "2" phf_shared = "0.10" pin-project-lite = "0.2" pin-utils = "0.1" pkg-config = "0.3" ppv-lite86 = "0.2" proc-macro-error = "1" proc-macro-error-attr = "1" proc-macro-hack = "0.5" proc-macro2 = "1" quick-error = "1" quote = "1" rand = "0.8" rand-2 = { package = "rand", version = "0.7" } rand_chacha = "0.3" rand_chacha-2 = { package = "rand_chacha", version = "0.2" } rand_core = "0.6" rand_core-2 = { package = "rand_core", version = "0.5" } rayon = "1" rayon-core = "1" regex = "1" regex-automata = "0.1" regex-syntax = "0.6" remove_dir_all = "0.5" reqwest = "0.11" ring = "0.16" rustc-demangle = "0.1" rustc-hash = "1" rustc_version = "0.4" rustc_version-2 = { package = "rustc_version", version = "0.2" } rustls = "0.20" ryu = "1" same-file = "1" scopeguard = "1" sct = "0.7" semver = "0.9" semver-parser = "0.7" serde = "1" serde_cbor = "0.11" serde_derive = "1" serde_json = "1" serde_urlencoded = "0.7" sha-1 = "0.10" sha2 = "0.10" sha2-2 = { package = "sha2", version = "0.9" } sharded-slab = "0.1" shlex = "1" signal-hook-registry = "1" siphasher = "0.3" slab = "0.4" smallvec = "1" socket2 = "0.4" spin = "0.5" stable_deref_trait = "1" static_assertions = "1" strsim = "0.10" strsim-2 = { package = "strsim", version = "0.8" } subtle = "2" syn = "1" synstructure = "0.12" tempfile = "3" termcolor = "1" terminal_size = "0.1" textwrap = "0.15" textwrap-2 = { package = "textwrap", version = "0.11" } thiserror = "1" thiserror-impl = "1" thread_local = "1" time = "0.1" time-macros = "0.2" tinyvec = "1" tinyvec_macros = "0.1" tokio-macros = "1" tokio-native-tls = "0.3" tokio-rustls = "0.23" tokio-stream = "0.1" tokio-util = "0.7" toml = "0.5" tower-layer = "0.3" tower-service = "0.3" tracing = "0.1" tracing-attributes = "0.1" tracing-core = "0.1" tracing-futures = "0.2" tracing-log = "0.1" try-lock = "0.2" typenum = "1" unicase = "2" unicode-bidi = "0.3" unicode-ident = "1" unicode-normalization = "0.1" unicode-segmentation = "1" unicode-width = "0.1" unicode-xid = "0.2" untrusted = "0.7" url = "2" uuid = "1" uuid-2 = { package = "uuid", version = "0.8" } vcpkg = "0.2" vec_map = "0.8" version_check = "0.9" void = "1" walkdir = "2" want = "0.3" wasi = "0.11" webpki = "0.22" which = "4" winapi = "0.3" winapi-i686-pc-windows-gnu = "0.4" winapi-util = "0.1" winapi-x86_64-pc-windows-gnu = "0.4" windows-sys = "0.36" windows_aarch64_msvc = "0.36" windows_i686_gnu = "0.36" windows_i686_msvc = "0.36" windows_x86_64_gnu = "0.36" windows_x86_64_msvc = "0.36" yaml-rust = "0.4" ================================================ FILE: cache/default.nix ================================================ { writeText, mkRustPackageOrWorkspace }: let ws = mkRustPackageOrWorkspace { src = ./.; }; in writeText "cache-paths" (toString (ws.dev.cache.dependencies ++ ws.release.cache.dependencies)) ================================================ FILE: cache/src/lib.rs ================================================ ================================================ FILE: crates-io-override/default.nix ================================================ { lib, pkgs }: let inherit (lib) optionalAttrs listToAttrs; inherit (builtins) compareVersions; procMacroOverrides = listToAttrs (map (name: { inherit name; value = _: { procMacro = true; }; }) (import ./proc-macro.nix)); in with pkgs; procMacroOverrides // { libz-sys = { features, ... }: optionalAttrs (!(features ? static)) { nativeBuildInputs = [ pkg-config ]; propagatedBuildInputs = [ zlib ]; }; openssl-sys = { features, ... }: optionalAttrs (!(features ? vendored)) { nativeBuildInputs = [ pkg-config ]; propagatedBuildInputs = [ openssl ]; }; proc-macro-hack = { version, ... }: { procMacro = compareVersions version "0.5.0" >= 0; }; } ================================================ FILE: crates-io-override/proc-macro.nix ================================================ [ "a2kit_macro_derive" "a2lmacros" "aa-regex" "abi_stable_derive" "ability" "abistr-macros" "abomonation_derive" "abscissa_derive" "accel-derive" "accessors" "accessors-rs" "accio" "accursed-unutterable-type-id-derive" "acme-derive" "acme-macros" "act-zero-macro" "actionable-macros" "activitypub_federation_derive" "activitystreams-derive" "actix-admin-macros" "actix-auth" "actix-default-responder" "actix-easy-multipart-derive" "actix-grants-proc-macro" "actix-handler-macro" "actix-json-responder" "actix-jwt-auth-middleware-derive" "actix-macros" "actix-multipart-derive-impl" "actix-multipart-extract-derive" "actix-proc-macros" "actix-signal-derive" "actix-telegram-derive" "actix-web-codegen" "actix-web-cute-codegen" "actix-web-error-derive" "actix-web-lab-derive" "actix-web-macros" "actix_derive" "actix_header" "actix_responder_macro" "actix_telepathy_derive" "actix_validated_forms_derive" "actori-macros" "actori-web-codegen" "actori_derive" "actors-macros" "actyx_sdk_macros" "actyxos_sdk_macros" "add_getters_setters" "address-cmp" "address-literal" "adhoc_derive" "adminix_macro" "admiral-derive" "adom" "adorn" "adsabs-macro" "aegir_compile" "aegir_derive" "aeiou-macros" "aeon_derive" "af-core-proc-macros" "af-proc-macros" "af_path" "affix" "afmt" "ag-grid-derive" "agb_image_converter" "agb_macros" "agb_sound_converter" "agnostik-attributes" "agpu-macro" "agui_macros" "ahecha_macro" "aide-macros" "aingle_middleware_bytes_derive" "ak-codegen" "akin" "akita_derive" "aktrs-macros" "alacritty_config_derive" "alass-ffi-macros" "alchemy-macros" "alder-derive" "aleo-std-time" "aleo-std-timed" "alga_derive" "algorithmia-entrypoint" "alipay_derive" "alipay_macros" "aliri_braid_impl" "alkahest-proc" "alloc_counter_macro" "allochronic-util-macros" "alpaca-lexer-derive" "alphabet-macro" "alt_serde_derive" "amadeus-derive" "amadeus-parquet-derive" "ambassador" "amethyst-inspector-derive" "amethyst_derive" "amitu_macros" "amper_mac" "amplify_derive" "anansi-macros" "anchor-attribute-access-control" "anchor-attribute-account" "anchor-attribute-constant" "anchor-attribute-error" "anchor-attribute-event" "anchor-attribute-interface" "anchor-attribute-program" "anchor-attribute-state" "anchor-cereal-derive" "anchor-derive-accounts" "anchor-gen" "anchor-generate-cpi-crate" "anchor-generate-cpi-interface" "anchored-macros" "anim-derive" "annotation-rs-codegen" "ansi-colors-macro" "ansiform" "anthill-di-derive" "anu-macros" "any_ref_macro" "anyint_macros" "anykind" "anystring_macro" "aoc-runner-derive" "aoc-toolbox-derive" "aoc_codegen" "aoc_macros" "aopt-macro" "aorist_concept" "aorist_derive" "aorist_paste" "apache-avro-derive" "apdu-derive" "apecs-derive" "aper_derive" "app_route_derive" "appkit-derive" "aptos-crypto-derive" "aptos-log-derive" "aquamarine" "aquatic_toml_config_derive" "ar_pe_ce_macro" "aragog-macros" "arangoq_derive" "arbitrary-model-tests" "arcdps_codegen" "arcon_macros" "arctk-attr" "arctk-proc" "arel_macro" "arg-derive" "arg_enum_proc_macro" "argh_derive" "argio-macro" "argmin_codegen" "argopt-impl" "argser-macros" "ark-api-macros" "ark-ff-asm" "ark-ff-macros" "ark-serialize-derive" "arm7tdmi_aeabi" "arma-rs-proc" "armature-macro" "armour-derive" "arr_macro_impl" "array-const-fn-init" "array_map_derive" "arraygen" "arraygen-docfix" "arrow2_convert_derive" "artemis-codegen-proc-macro" "as-dyn-trait" "as_tuple_derive" "ascent_macro" "ascii-literal-impl" "askama-enum" "askama_derive" "asn1-rs-derive" "asn1-rs-impl" "asn1_codecs_derive" "asn1_der_derive" "asn1_derive" "asn1rs-macros" "asn_codecs_derive" "assay-proc-macro" "assemble-macros" "assemblylift-core-guest-macros" "assert-parse-register-assert-macro" "assert2-macros" "assert2ify-macros" "assert_fn" "asserter-macros" "assertify_proc_macros" "assets_manager_macros" "associated-derive" "ast2str-derive" "ast_node" "asteracea_proc-macro-definitions" "asterix-derive" "async-attributes" "async-coap-uri-macros" "async-debug-derive" "async-ecs-derive" "async-entry" "async-graphql-derive" "async-graphql-relay-derive" "async-injector-derive" "async-log-attributes" "async-metronome-attributes" "async-proto-derive" "async-recursion" "async-reply-derive" "async-std-test" "async-stream-impl" "async-test-derive" "async-trait" "async-trait-ext" "async-trait-fn" "async-trait-static" "async-trait-with-sync" "async-xml-derive" "async-zmq-derive" "async_auto_traits" "async_event_streams_derive" "async_fn-proc_macros" "async_for" "async_init" "async_object_derive" "async_static" "async_t_internal" "async_trait_proto" "ataraxy-macros" "atat_derive" "atomecs-derive" "atomic_enum" "atomic_hooks_macros" "atomic_macro" "atomig-macro" "atomize-macro" "attempted" "attribute-derive-macro" "attributes" "attribution-macros" "attrsets" "aurum_actors_macros" "autd3-traits" "authorized_derive" "auto-add-lifetimes-to-impl" "auto-args-derive" "auto-diff-macros" "auto-enum" "auto-impl-ops" "auto-trait" "auto-version" "auto_enums_core" "auto_enums_derive" "auto_from" "auto_impl" "auto_ref" "autocorrect-derive" "autocxx-macro" "autodefault" "autodiff_injector_core_derive" "autodo_helper" "autoerror" "autograph_derive" "autoimpl-derive" "autoincrement_derive" "autoload" "autolog" "automate-derive" "automato" "automato_sync" "automato_sync_non_mut" "automod" "autorand-derive" "autowired-derive" "auxtools-impl" "avalanche-macro" "avantis-utils-derive" "aversion-macros" "avocado_derive" "avr-device-macros" "awesome-glib" "awf-codegen" "awint_macros" "awmpde_derive" "awred" "aws-smt-ir-derive" "awto-macros" "axum-debug" "axum-debug-macros" "axum-derive-error" "axum-macros" "axum-route" "axum_client_derive" "ay" "azalea-block-macros" "azalea-buf-macros" "azalea-protocol-macros" "azalea-registry-macros" "azul-peek-poke-derive" "azure-functions-codegen" "azure-functions-shared-codegen" "baburu-macros" "bachue-auto_impl" "bae" "baker" "bakery-derive" "bakkesmod-macros" "banjin" "baroque" "bart_derive" "bash_builtins_macro" "basic-text-literals" "batbox-derive" "batch-codegen" "bdk-macros" "bdk-testutils-macros" "be-generust" "beamcode_derive" "bean-macro" "bearings-proc" "bee-common-derive" "below_derive" "bencher-macro" "benvolio" "bern-kernel-macros" "bern-test-macros" "beserial_derive" "best_macros" "better-bae-macros" "better-macro" "better_typeid_derive" "bevy-contrib-inspector-derive" "bevy-crevice-derive" "bevy-inspector-egui-derive" "bevy_aseprite_derive" "bevy_asset_loader_derive" "bevy_console_derive" "bevy_derive" "bevy_dioxus_macro" "bevy_discovery" "bevy_ecs_ldtk_macros" "bevy_ecs_macros" "bevy_editor_iris_derive" "bevy_encase_derive" "bevy_fallible_derive" "bevy_hecs_macros" "bevy_mod_check_filter_macros" "bevy_mod_scripting_derive" "bevy_mod_sysfail_macros" "bevy_property_derive" "bevy_proto_derive" "bevy_reflect_derive" "bevy_render_macros" "bevy_state_macros" "bevycheck" "bf-impl" "bian-proc" "big_enum_set_derive" "bigbang_derive" "bigml_derive" "bin-layout-derive" "bin_codec_derive" "binary_enclave_macro" "binary_macros_impl" "bincode-grpc-macro" "bincode_derive" "bind_it" "bindata-derive" "bindata-impl" "bingen" "binhoc_macros" "binprot_derive" "binread_derive" "binreader-macros" "binrw_derive" "binser-derive" "binserde_derive" "binver_derive" "binverse_derive" "binwrite_derive" "bio-seq-derive" "bird-machine-macros" "bird-protocol-derive" "biscuit-quote" "bit-by-bit" "bit_collection_derive" "bit_manager_derive" "bitaccess_macros" "bitbag_derive" "bitbar-derive" "bitbash-macros" "bitbuffer_derive" "bitbybit" "bitcoin-cash-code" "bitcoin-cash-script-macro" "bitcoin-script" "bitf" "bitfield-derive" "bitfield-register-macro" "bitfield-struct" "bitmask-enum" "bitmatch" "bitmath_macros" "bitpattern" "bitpatterns-proc-macro" "bitrange_plugin" "bitregions-impl" "bitset-const-macros" "bitsparrow-derive" "bitstream_reader_derive" "bitstruct_derive" "bittwiddler" "bitwrap_derive" "bitwrap_derive_extra" "bk2d-macro" "bl602-macros" "black_marlin-macros" "blackbox_derive" "blanket" "blaze-proc" "block_effects" "block_on_proc" "blocked" "blockz_derive" "bloom42_diesel_as_jsonb" "blpapi-derive" "bm-le-derive" "bma-benchmark-proc" "bmart-derive" "bmw-derive" "bointer-derive" "bolero-generator-derive" "bolt-client-macros" "bolt-proto-derive" "bondrewd-derive" "bonfida-macros" "bonsaidb-macros" "boolenum" "boomerang_derive" "border-derive" "borsh-derive" "bot-rs-core-derive" "botanist_codegen" "boulder_derive" "bounce-macros" "bounded-integer-macro" "bounded-static-derive" "box_shorthand" "boxext_derive" "bpaf_derive" "bpf-rs-macros" "bpxe-internal-macros" "brain_fuck" "bronze_derive" "brush_contracts_derive" "brush_lang_macro" "bruteforce-macros" "bstring_macros_hack" "bstringify" "btmgmt-packet-macros" "buffalo_macro" "buffering_nocopy_macro" "build-features" "build-info-proc" "build-time" "build-trie" "build_cfg_macros" "build_timestamp" "builder-pattern-macro" "buildstructor" "bullet_macros" "bunt-macros" "burn-derive" "butane_codegen" "butcher_proc_macro" "buttplug_derive" "bve-derive" "byewlma-macros" "bystr-impl" "byte-enum-derive" "byte-slab-derive" "byte-strings-proc-macro" "byte-strings-proc_macros" "byte_eater" "byte_struct_derive" "bytecheck_derive" "byteme" "bytemuck_derive" "bytepack_derive" "bytes-cast-derive" "bytes-lit" "bytestream_derive" "bytey_derive" "bytify-impl" "c-like-concat" "c2rs" "c2rust-bitfields-derive" "c2rust-macros" "c3-lang-macro" "c3p0_diesel_macro" "c_api_prefix" "c_import" "c_macros" "cache-macro" "cached_proc_macro" "cached_property" "cagra-parser" "cain" "cairo-svgpath" "calcite_proc_macros" "calculagraph" "call-trace-macro" "caller_modpath_macros" "calmio_filters" "cameleon-impl-macros" "camelpaste" "camo-derive" "can-bit-timings-proc-macro" "canadensis_derive_register_block" "canadensis_macro" "canary-macro" "candid_derive" "canonical_derive" "canrun_codegen" "cantor_macros" "capsule-macros" "captures" "carapax-codegen" "carbide_derive" "cardparse_derive" "cargho_derive" "cargo-kconfig" "cargo-snippet" "cargo-up-derive" "cargo_equip_marker" "cargo_meta_proc" "carrier-pigeon-netmsg-derive" "cascading-wasm-language" "casey" "casper-node-macros" "casper_types_derive" "cassandra_macro_derive" "casserole-derive" "cast_trait_object_macros" "castflip_derive" "castle_macro" "catalytic_macro" "catch_panic_macros" "catchr" "catinator_macros" "cbor_enhanced_derive_protocol" "cbordata-derive" "cbored-derive" "cdbc-macro" "cdds_derive" "cde_codegen" "cdefines" "cdr-encoding-size-derive" "cdrs-tokio-helpers-derive" "cdrs_helpers_derive" "cdrs_helpers_derive_temp" "celery-codegen" "celeste_derive" "cell-map-macro" "cenum" "cenum-derive" "ceres-derive" "cex_derive" "cffi-impl" "cfg-derive" "cglue-macro" "chain-cmp" "chalk-derive" "channel-server-derive" "char-lex-macro" "charify" "chassis" "chazi_macros" "checked_decimal_macro_core" "checked_expr" "checkers-macros" "checkout_server_derive" "chekov_macros" "cherry-derive" "chia_streamable_macro" "chikatetsu-macros" "choices-derive" "chrome-native-macros" "chronobreak_derive" "chrony-candm-derive" "circus_test" "citerne-derive" "ckb-fixed-hash-macros" "ckb-migration-template" "ckb-occupied-capacity-macros" "cl-traits-derive" "clams-derive" "clap-handler-derive" "clap-vergen-macro" "clap_derive" "clap_derive-v3" "clapi_internal" "clapi_macros" "clapme_derive" "clean-macro-docs" "cleu-orm-derive" "cli-editor" "cli-errors-macros" "cli-table-derive" "cli-toolbox" "clickhouse-derive" "clickrs_proc_macro" "clightningrpc-plugin-macros" "climer_derive" "clingo-derive" "clippy-mini-macro-test" "clone-fields-derive" "clone_cell_derive" "clone_dyn_meta" "closure-pass" "clouseau_macros" "cmd-derive" "cmd_lib_cf_macros" "cmd_lib_macros" "cmd_wrk_macros" "cmdargs-macros" "cmdparse-derive" "cmdr_macro" "cntrlr-macros" "code-hasher" "code-sandwich-crates-io-release-test-macros" "code-tour" "code_snippet_generator" "codesnip_attr" "codespan-derive-proc" "coerce-macros" "coffer-macros" "coi-actix-web-derive" "coi-derive" "coi-rocket-derive" "coil_proc_macro" "col_proc_macros_impl" "collections-fromstr" "color-hex" "color-macro" "color-print-proc-macro" "column_derive" "column_store_proc_macros" "com_macros" "combination-err" "comet-rs-impl" "command-macros-plugin" "command_attr" "commander-macros" "commandy_macros" "comn-pms" "compact_macros" "compact_sql" "comparable_derive" "comparable_helper" "compare_by_address_derive" "compile-ints" "compile-time-crc32-impl" "compile-time-create-file" "compile-time-lisp" "compile-time-lua-bind-hash" "compile-time-run" "compile-time-run-macro" "compile_ops" "compiled-uuid" "compiler-tools-derive" "compiletime" "completion-macro" "component_group_derive" "compose-derive" "compound-error" "comprehension" "comprez_macro" "comptime" "concat-arrays" "concat-idents" "concat_strs_impl" "concision-derive" "concision-macros" "concordium-contracts-common-derive" "concordium-std-derive" "concourse-resource-derive" "condition-derive" "conditional_impl" "config-derive" "config_parser_derive" "configr_derive" "configure_derive" "configurs_derive" "confique-macro" "conflagrate-macros" "conform-derive" "conformance" "confql-proc-macro" "confu_derive" "conniecs-derive" "conrod_derive" "consecuit_macros" "const-array-attrs" "const-c-str-impl" "const-default-derive" "const-enum" "const-enum-tools-derive" "const-field-offset-macro" "const-gen-derive" "const-random-macro" "const-regex" "const-source-position" "const-table" "const-tweaker-attribute" "const_cge_macro" "const_fn" "const_format_proc_macros" "const_guards_attribute" "const_internals" "const_num_bigint_derive" "const_panic_proc_macros" "const_strum" "const_trait_impl" "constant-cstr" "constany_blank" "constany_stage_one" "constany_stage_two" "constduck-procmacro" "constexpr-macros" "constlua" "constrainer" "constructor-macro" "consulrs_derive" "context-attribute" "continuation-router-syn" "contraband_codegen" "contracts" "contrail-derive" "controlled-option-macros" "contructor-derive" "converge_derive" "converge_test" "convert-enum" "convert-js-macros" "convert_by_name" "convey_derive" "cooked-waker-derive" "core_extensions_proc_macros" "corewars-parser-macro" "cornetto" "corresponding-macros" "cortex-m-funnel-macros" "cortex-m-rt-macros" "cortex-m-rtfm-macros" "cortex-m-rtic-macros" "cosmian-wit-bindgen-rust-impl" "cosmic-macros" "cosmic-macros-primitive" "cosmwasm-derive" "cosmwasm-schema-derive" "couch_rs_derive" "count-macro" "count-tys" "count_tts" "counting-macros" "courier" "coverage-helper" "covers" "cow_struct" "cpclib-macros" "cpp-inherit" "cpp_macros" "cppvtbl-macros" "cpreprocess" "cps" "cqrs_macro" "crabler_derive" "crate-_-name" "craydate-macro" "crayfish-macros" "crayfish-trace-macros" "crcany-macro" "creator-derive" "crepe" "crevice-derive" "crevice_notan-derive" "criterion-macro" "crndm_derive" "crochet_macro" "crokey-proc_macros" "crossbundle-derive" "cruiser_derive" "cryiorust_derive" "crypto-literal-macro" "cryptraits-macros" "css-loader-macros" "css-modules-macros" "css-rs-macro" "cssparser-macros" "cstr" "cstr-enum-derive" "cstr-macros" "ct-for" "ctc" "ctchi_codegen" "ctflag_derive" "ctor" "cuach-derive" "cubic-protocol-derive" "cucumber-codegen" "cucumber_rust_codegen" "cuda_std_macros" "cuneiform" "cur_macro" "cust_derive" "custom-format-macros" "custom-slice-macros" "custom_debug_derive" "cute_custom_default" "cutlass" "cvars" "cw-storage-macro" "cxx-async-macro" "cxx-qt" "cxxbridge-macro" "cyfs-base-derive" "cynic-proc-macros" "cynthia-macros" "cypher_derive" "d3-derive" "dacquiri_derive" "dade_derive" "dade_macro" "dag-cbor-derive" "daml-derive" "dapr-derive" "darkly-macros" "darksteel-codegen" "darling_macro" "darpi-code-gen" "dashu-macros" "data-encoding-macro-internal" "data-query-proc" "data-url-encode-macro-impl" "data_doc_derive" "datasize_derive" "datastore_derive" "datatest-derive" "db-derive-impl" "db-helpers-derive" "db_ip_macros" "dbfile_derive" "dbg-pls-derive" "dbi-macros" "dbstruct-derive" "dbus-async-derive" "dcc-lsystem-derive" "dd_maths_traits_macros" "ddd_dapr_derive" "dddk_macro" "de-ref" "deasync" "debcontrol_struct_derive" "debil-derive" "debug-derive" "debug-try" "debug2-derive" "debug3-derive" "debug_stub_derive" "debugger_test" "declio_derive" "decorators" "decurse_macro" "dede" "deen-proc" "deepsize_derive" "def-mod" "default-args" "default-boxed-derive" "default-conversion" "default-env" "default_kwargs" "defaults" "defile-proc_macros" "defmt-macros" "defmt-test-macros" "defs" "defunctionalize-proc-macro" "degeneric-macros" "deku_derive" "delegate" "delegate-attr" "deltastruct_proc" "deltoid-derive" "deluge-rpc-macro" "demo-hack-impl" "dendrite_macros" "dengine_derive" "deno_bindgen_macro" "deno_json_op" "deno_ops" "depcon_codegen" "deprive" "deq_macros" "deque-loader-derive" "der-oid-macro" "der_derive" "deranged-macros" "derefable" "derivate" "derivation" "derivative" "derive-adhoc-macros" "derive-aktor" "derive-collect-docs" "derive-com-impl" "derive-com-wrapper" "derive-debug-extras" "derive-deref-rs" "derive-diesel-connection" "derive-diff" "derive-dynamic-node" "derive-enum-error" "derive-enum-from-into" "derive-env-url" "derive-error" "derive-error-chain" "derive-ex" "derive-finite-automaton-derive" "derive-from-ext" "derive-getters" "derive-hex" "derive-into-owned" "derive-knet" "derive-new" "derive-newtype" "derive-object-merge" "derive-ocaml" "derive-parse" "derive-partial-eq-extras" "derive-patch" "derive-redis-json" "derive-redis-swapplex" "derive-serialize-into" "derive-syn-parse" "derive-system" "derive-try-from-primitive" "derive-where" "derive_asref" "derive_bounded" "derive_builder_macro" "derive_builder_macro_fork_arti" "derive_codec_sv2" "derive_constructor" "derive_custom_enum_traits" "derive_deref" "derive_derive" "derive_destructure" "derive_destructure2" "derive_di_macro" "derive_display_from_debug" "derive_dumb" "derive_enum_macros" "derive_from_as" "derive_jface" "derive_less" "derive_merge" "derive_miniconf" "derive_more" "derive_pod" "derive_rich" "derive_setters" "derive_type_level" "derive_weak" "derive_wrapper" "derived" "derivenum" "descent_macro" "descriptor_derive" "deser-derive" "deserialize_xml_derive" "despatma" "desse-derive" "dessert-derive" "destruct-derive" "destruct-drop-derive" "desugar" "desugar-impl" "devault" "devbox-test-args" "device-register-macro" "devise_codegen" "df_rocket_okapi_codegen" "df_st_derive" "dhall_proc_macros" "dialectic-macro" "dialoguer_macro" "dialtone_sqlx_macros" "dict_derive" "diem-crypto-derive" "diem-framework-crypto-derive" "diesel-crud-codegen" "diesel-derive-enum" "diesel-derive-more" "diesel-derive-newtype" "diesel-ease" "diesel-enum" "diesel-factories-code-gen" "diesel-selectable-macro" "diesel-sort-struct-fields" "diesel_as_jsonb" "diesel_codegen" "diesel_derive_model" "diesel_derives" "diesel_derives_extra" "diesel_enum_derive" "diesel_filter_query" "diesel_findable" "diesel_mate_derive" "diff-enum" "diff_derive" "diffus-derive" "dill-impl" "dims_derive" "diny_derive" "dionysos-derives" "dioptre-derive" "dioxus-core-macro" "dioxus-html-macro" "dioxus-native-core-macro" "dioxus-table-macro" "dioxus-use-request-macro" "dipa-derive" "diplomat" "direct-asm" "director_macros" "directree_macros" "dirmod-codegen" "dis_rs_macros" "disabled_contracts" "disarray-derive" "disarray-macros" "disc-derive" "discord_typed_interactions_proc_macro" "discrab_codegen" "discretionary" "discrim-codegen" "discrimenum" "discriminant_hash_derive" "disp" "display-as-proc-macro" "display_adapter_attr" "display_attr" "display_derive" "display_json" "display_json_derive" "displaydoc" "displaydoc-lite-proc_macros" "displaydoc-watt" "displaythis-impl" "dispose-derive" "distill-serde-importable-derive" "dizpacho" "django-query_derive" "djin-protocol-derive" "dlhn_derive" "dlopen2_derive" "dlopen_derive" "dmutils_derive" "dmv_derive" "doc-cfg" "doc-since" "doc_item" "doc_link" "docbot-derive" "dock-derive" "document-features" "docx-codegen" "dodo-derive" "dogehouse-macros" "doku-derive" "domain_derive" "dont-expand" "doodle_derive" "dos-uid-derive" "dose-derive" "dosio-macros" "dotenv_codegen_impl" "dotenv_codegen_implementation" "dotenv_config" "dotenvy_codegen_impl" "dotinstall-macro" "double-dyn" "doubter-macros" "dough" "draw_bridge_derive" "drm-macros" "drone-core-macros" "drone-cortex-m-macros" "drone-cortexm-macros" "drone-macros" "drone-riscv-macros" "drone-stm32-macros" "drone-sx1276-macros" "drop-derive" "drop_struct_macro_derive" "dropshot_endpoint" "druid-derive" "druid-lens-compose" "dry" "dtd-macro" "dtn7-codegen" "dto_derive" "dtypei-derive" "duang" "dukt-macros" "duktape-macros" "dummy" "duplicate" "dusk-proc-macros" "dxr_derive" "dylink_macro" "dyn-any-derive" "dyn-clonable-impl" "dyn-context-macro" "dyn-dyn-macros" "dyn_partial_eq_derive" "dyn_safe-proc_macros" "dyn_struct_derive" "dyn_struct_derive2" "dynamic-object-derive" "dynamize" "dynamo_mapper_macro" "dynasm" "dync-derive" "dynomite-derive" "dynpath" "e_num_derive" "ea" "easy-csv-derive" "easy-ext" "easy-jsonrpc-proc-macro" "easy-jsonrpc-proc-macro-mw" "easy-pin-proc-macro" "easy_pool_proc_macro" "easy_proc_derive" "easy_proc_macro" "ebay_derive" "ebml-iterable-specification-derive" "ebpf-kern-macros" "ebpf-user-macros" "ecal-derive" "econf-derive" "ector-macros" "ed-derive" "ed2-derive" "edgedb-derive" "edgedb-query-derive" "edgedb-sdk-macros" "edited" "edn-derive" "educe" "eff-attr" "efg" "efmt_derive" "eg_derive" "egui_inspect_derive" "eiga_builder_derive" "einops-macros" "either_trait_macro" "eko-gc-derive" "elapsed-printer" "elapsed-time" "elastic_derive" "elastic_types_derive" "elephantry-derive" "elfo-macros" "elise-derive" "elm_rs_derive" "elm_rusty" "elrond-codec-derive" "elrond-wasm-derive" "elucify" "elvis-core-support" "elvis-derive" "emacs-macros" "embed-doc-image" "embed_js_derive" "embedded-error-chain-macros" "embedded-layout-macros" "embedded-profiling-proc-macros" "embeddir" "empty_type_derive" "emu_glsl" "emu_macro" "emv-qrcps-derive" "encase_derive" "encdec-macros" "encoding-asn1-derive" "encrypted_id_derive" "encryption-macros-encryption" "encryption-macros-key-generation" "endian_codec_derive" "endian_trait_derive" "endiannezz_derive" "eng-wasm-derive" "enpow" "ensicoin_serializer_derive" "enso-shapely-macros" "entity-async-graphql-macros" "entity-gym-derive" "entity_macros" "entity_noop_macros" "entrait_macros" "entrance_derive" "enum-as-derive" "enum-as-inner" "enum-assoc" "enum-display-derive" "enum-each-variant-derive" "enum-error-derive" "enum-flags" "enum-group" "enum-iterator-derive" "enum-kinds" "enum-kinds-macros" "enum-lexer-macro" "enum-map-derive" "enum-methods" "enum-ordinalize" "enum-other" "enum-primitive-derive" "enum-primitive-derive-nostd" "enum-repr" "enum-repr-derive" "enum-tags-macros" "enum-tryfrom-derive" "enum-utils" "enum-variants-strings-derive" "enum_access" "enum_coder" "enum_const_derive" "enum_const_value" "enum_cycling_derive" "enum_default" "enum_derive_list" "enum_dispatch" "enum_for_matches" "enum_from_str_derive" "enum_index_derive" "enum_kind" "enum_like_derive" "enum_pipeline_derive" "enum_to_enum_derive" "enum_to_str_derive" "enum_to_u8_slice_derive" "enum_traits_macros" "enum_variant_macros_macros" "enum_variant_type" "enumber" "enumer_derive" "enumerare-macros" "enumerate" "enumflags2_derive" "enumflags_derive" "enumn" "enumoid_derive" "enumscribe_derive" "enumset_derive" "enumx_derive" "env-cast" "env-config" "env_ast" "env_derive" "env_proc" "envconf_derive" "envconfig_derive" "envcrypt-macro" "envdot" "envir_derive" "envload_derive" "envopt-derive" "eosio-macro" "eosio-scale-info-derive" "eosio_bytes_derive" "eosio_macros_internal" "eosio_numstr_macros_impl" "ephem_derive" "epics-sys" "epoxy_macros" "epp-client-macros" "equalia" "eraserhead-derive" "ergo-pin" "ergo_headless_dapp_framework_derive" "ergol_proc_macro" "erl_nif_macro" "err-derive" "err-gen" "err-per-field-derive" "errer_derive" "erroneous-derive" "error-chain-mini-derive" "error-chain-utils" "error-gen" "error-rules" "error-stack-derive" "error-stack-macros" "error-utils-derive" "error_generator" "error_spanned_derive" "errorderive" "erst-derive" "escher-derive" "esize" "esp-hal-procmacros" "esp32-hal-proc-macros" "esp8266-hal-proc-macros" "espr-derive" "essrpc_macros" "etanol_macros" "eternal-macro" "eth2_ssz_derive" "eth_event_macro" "eth_pairings_repr_derive" "ethabi-derive" "ethaddr-macros" "ethanol-derive" "ethcontract-derive" "ethdigest-macros" "ethercat-derive" "ethers-contract-derive" "ethers-derive-eip712" "ethnum-macros" "eu" "euclid_macros" "euphony-core-macros" "euphony-macros" "ev3dev-lang-rust-derive" "ev3robot_macros" "eva-sdk-derive" "evegfx-macros" "eventmill_derive" "eventsourcing-derive" "ever-macro" "every_variant_macro" "everyday_macros" "evitable-derive" "evm-precompiles-derive" "evmap-derive" "evmc-declare" "exclusive" "exec_time" "executor-macros" "exhaust-macros" "exit_status" "exocore-apps-macros" "exonum-derive" "exotic_macro" "expand" "expectation_plugin" "expose" "expression_format_impl" "expry_macros" "ext-php-rs-derive" "ext-trait-proc_macros" "extargsparse_codegen" "extdot-impl" "extend" "extends-rs" "extension-trait" "extprim_literals_macros" "extra_args" "ez-proc-macro" "ezinput_macros" "ezjsonrpc-macros" "ezmenu-derive" "ezmenu-macros" "f128_input" "f64ad_core_derive" "fabric-support-procedural" "fabric-support-procedural-tools-derive" "fabric_contract_macros" "factori-impl" "factory_steel_derive" "fail_on_ci" "failure_derive" "failure_derive_without_backtrace" "fake_serialize" "fallacy-clone-derive" "fallback-derive" "fancy" "fantasy-cpu-emulator-macros" "far_macros" "fast-map-derive" "fast-rustc-ap-rustc_macros" "fast_new_type" "fast_tuple" "fastcrypto-derive" "fasters_derive" "fastly-macros" "fastobo-derive-internal" "fastrlp-derive" "fatality-proc-macro" "fates_macro" "faux_macros" "fawkes-crypto_derive" "fax_derive" "fbinit_macros" "fbthrift_codegen_includer_proc_macro" "fce-timestamp-macro" "fefix_derive" "fehler-macros" "feign-macros" "feignhttp-codegen" "fem-macros" "feo-oop-engine-proc-macros" "feos-derive" "ferric-macros" "ferris-extensions" "ferris-gc-proc-macro" "festive-macros" "ff-derive-num" "ff-uint_derive" "ff_derive" "ff_derive-zeroize" "ff_derive_ce" "fff_derive" "ffi-convert-derive" "ffi-export-proc-macro" "ffi-gen-macro" "ffishim_derive" "ffizz-macros" "fi-night" "field-derive" "field_accessor" "field_count_derive" "field_names" "field_types" "fieldfilter-derive" "fieldmask_derive" "fields-converter-derive" "fieldwise" "fievar" "file-metadata-mditem-macros" "finchers-derive" "finite_repr_derive" "finiteelement_macros" "finny_derive" "finte-derive" "fire-rs-core" "fitsio-derive" "fix-hidden-lifetime-bug-proc_macros" "fix-rs-macros" "fixed-codec-derive" "fixed-macro-impl" "fixed-map-derive" "fixed-point-macros" "fixed_len_str" "fixed_typemap_macros" "fixed_width_derive" "fj-proc" "flag-mast-derive" "flaggy-codegen" "flaky_test" "flarie_macros" "flat-bytes-derive" "flatk-derive" "flatty-macros" "flax-derive" "flexiber_derive" "flexible-locks_derive" "flexpiler_derive" "flickr_derive" "float_eq_derive" "floccus-proc" "flow_impl_derive" "flow_macro" "flowmacro" "flowutils" "fltk-derive" "fltk-form-derive" "fluence-fork-libp2p-core-derive" "fluence-fork-libp2p-swarm-derive" "fluence-sdk-macro" "fluence-sdk-test-macro" "fluent-impl" "fluent-template-macros" "fluid_attributes" "fluidity-derive" "fluidity-macros" "flutter_rust_bridge_macros" "fluvio-protocol-derive" "fluvio-smartmodule-derive" "fluvio-smartstream-derive" "fluvio-test-derive" "fmt-derive-proc" "fn-error-context" "fn-fixture" "fn_abi" "fn_has_this" "fn_mut" "fn_once" "fn_type_alias" "fncmd-impl" "fnsql-macro" "fnum-derive" "fold_impls" "fondant_derive" "for_any" "for_ch" "foreign-types-macros" "foreignc_derive" "forever-rs" "formal_spec" "format-bytes-macros" "format-macro" "format_env" "former_derive" "former_meta" "formy_derive" "fort" "fortify_derive" "forward_goto" "foundationdb-macros" "four-char-code-macros-impl" "fp-bindgen-macros" "fpdec-macros" "fraco_point_derive" "fractk_macro" "frame-support-procedural" "frame-support-procedural-tools-derive" "frc42_macros" "freeze-macros" "frender-macros" "frenezulo-macros-beta" "frodobuf-derive" "from-enum-derive" "from-mapper-derive" "from-regex-macros" "from-repr-enum-derive" "from-to-repr" "from_bytes_derive" "from_file_derive" "from_hashmap" "from_int_derive" "from_remote_derive" "from_tuple" "from_value_derive" "from_variant" "from_variants_impl" "fruit-salad_proc-macro-definitions" "frunk-enum-derive" "frunk_derives" "frunk_proc_macros_impl" "fs_pro_macros" "fstrings-proc-macro" "fstrings-rust-proc-macro" "fuel-pest_derive" "fuel_codegen" "fuel_line_derive" "fuels-abigen-macro" "fui_macros" "full_moon_derive" "func_core" "func_trace" "funcmap_derive" "function-frame" "function_docs" "function_from_equation" "function_group" "function_name-proc-macro" "functionate" "funtime" "futility-try-catch" "future-union-impl" "futures-async-stream-macro" "futures-await-async-macro" "futures-await-test-macro" "futures-await-test-macro-preview" "futures-cputask-derive" "futures-derive" "futures-enum" "futures-join-macro-preview" "futures-macro" "futures-macro-async" "futures-macro-async-preview" "futures-macros-lite" "futures-net-macro" "futures-select-macro-preview" "futures-signals-structs-derive" "futurize-derive" "fuzzcheck_mutators_derive" "fuzzy-pickles-derive" "fxsm-derive" "fyrox-core-derive" "g1-macros" "g2gen" "gaclen_shader" "gallium_ecs_derive" "galvanic-mock" "game-metrics-macro" "game_kernel_ecs_derive" "gantz-derive" "gauge-mac-derive" "gazebo_derive" "gba-proc-macro" "gc-arena-derive" "gc_derive" "gcad" "gcad_proc_macros" "gcmodule_derive" "gdnative-derive" "gdnative-impl-proc-macros" "gdnative-macros" "gdrust_macros" "gekko-generator" "gem-macros" "gemachain-frozen-abi-macro" "gemachain-sdk-macro" "gen-nested-iter-yield" "gen_attributes_interface_generator" "genawaiter-proc-macro" "genco-derive" "genco-macros" "generate-derive" "generate-random-macro" "generate_sql" "generates" "generic-bytes-derive" "generic-derive" "generic-new" "generic-predicates-macro" "generic-simd-macros" "generic-tests" "generic_parameterize" "generoust" "genesis-impl" "geng-derive" "geng-ecs-derive" "geng-ui-derive" "genserver_codegen" "gensym" "gentian" "gents_derives" "geobacter-runtime-amd-macros" "geoip2-codegen" "germ-macros-impl" "get-random-const" "get-size-derive" "get_len_base_10_as_usize_macros" "get_params_derive" "getset" "getset-scoped" "getter-derive-rs" "getters-by-type" "gettext-macros" "gf256-macros" "gfaas-macro" "gff-derive" "gflags-derive" "gflags-impl" "gfx_macros" "ggplot-derive" "ghost" "ghosts-proc_macros" "giftwrap" "git-testament-derive" "git-version-macro" "git_rev" "gitarena-macros" "gitver" "glade_derive" "gladis4_proc_macro" "gladis_proc_macro" "glib-macros" "glicol_macros" "glitchup_derive" "glium_derive" "gll-macros" "gll-pg-macros" "glsl-lang-quote" "glsl-layout-derive" "glsl-quasiquote" "glsl-to-spirv-macros-impl" "glsp-proc-macros" "glsp-proc-macros2" "gltf-derive" "gluon-salsa-macros" "gluon_codegen" "gmod-macros" "go-away-derive" "goglob-proc-macro" "golang-type-decl-macro" "golang-type-macro" "golang-type-name-macro" "goldberg" "golem-rpc-macros" "goods-proc" "google-cloud-derive" "gorm_macros" "gostd_derive" "gotham_derive" "gotham_formdata_derive" "gotham_restful_derive" "gpkg-derive" "gpt3_macro" "graal-bindgen-macros" "gradients-derive" "grafana-plugin-sdk-macros" "gramatika-macro" "grammar-tech-macro" "graphannis-malloc_size_of_derive" "graphblas_sparse_linear_algebra_proc_macros" "graphite_binary_macros" "graphite_command_macros" "graphql_query_derive" "graphrpc-derive" "grass-macro" "grb-macro" "greenie-proc" "gremlin-derive" "group_derive" "grpc-build-derive" "gs11n_derive" "gsettings-macro" "gtk-blueprint" "gtk-properties-macro" "gtk-rust-app-derive" "gtk3-macros" "gtk4-macros" "gtk_liststore_item_derive" "gtmpl_derive" "guard_let" "gui-derive" "guid-macro-impl" "gull_derive" "gumdrop_derive" "gusket-codegen" "gut-derive" "gut-plugin" "guzzle-derive" "gvariant-macro" "gvdb-macros" "gxi-derive" "gxi-macros" "gxi-transpiler" "gxi_macro" "hackfn" "hacspec-attributes" "halcyon_macro_impl" "hanger" "hannibal-derive" "happi-derive" "hard-xml-derive" "harled_macro" "harmony_derive" "haru-decorator" "has_command" "hash32-derive" "hashfn" "hashmap_derive" "hazmat-macros" "hazy_derive" "hb_macros" "hcaptcha_derive" "hdf5-derive" "hdk_derive" "hdk_proc_macros" "he_di_derive" "headers-derive" "heapsize_derive" "heck-but-macros" "hecs-component-provider-macros" "hecs-macros" "hedera_rust_client_derive" "heim-derive" "helper" "helper_fn" "herald_derive" "heron_macros" "hesione_macros" "hesoyam_macro" "hex-literal-impl" "hex-magic" "hexbytes" "hexf" "hexf-impl" "hippo-macro" "hippotat-macros" "hirola-macros" "hirpdag_derive" "hitbox-derive" "hlbc-derive" "hoax" "hobo_css_macros" "hobo_derive" "hodoku" "hoist_temporaries" "holium-macro" "holochain_json_derive" "holochain_serialized_bytes_derive" "holochain_tracing_macros" "hot-lib-reloader-macro" "hotg-rune-proc-block-macros" "hotpatch_macros" "houseflow-macros" "hrpc-proc-macro" "hstrace_derive" "html-extractor-macros" "html-macro" "html-to-string-macro" "http-api-problem-derive" "httpmock-macros" "huber-procmacro" "hubpack_derive" "humphrey_json_derive" "hyperbole_macros" "hyperderive" "hypermod" "hypersearch_codegen" "i-macros" "i-slint-core-macros" "i18n-embed-fl" "i18n-embed-impl" "i18n-macro" "i18n_codegen" "i2c-reg-derive" "iai_macro" "ibuilder_derive" "ic-cdk-macros" "ic-event-hub-macros" "ic-kit-macros" "ice-derive" "iced_focus_derive" "icu_provider_macros" "id-derive" "id_collections_derive" "identifier_derive" "identify-tts" "identity-diff-derive" "iderive" "idl-macro" "idmap-derive" "if_debug" "if_empty_derive" "iffy" "ifmt-impl" "iftree" "ignite-rs_derive" "igri_derive" "ike-derive" "illicit-macro" "imgui-ext-derive" "imgui-inspect-derive" "imm_gc_derive" "imperative-rs-derive" "impl-enum" "impl-service" "impl-template" "impl-tools" "impl-trait-for-tuples" "impl_inheritance_macros" "impl_table" "impl_trait" "implicit-await-macro" "impls_index_meta" "import_fn" "import_generated_code" "in_space_routes" "incdir" "include-base64" "include-bytes-plus" "include-cargo-toml" "include-crypt-bytes-macro" "include-crypt-codegen" "include-dir-macro" "include-flate-codegen" "include-glsl-impl" "include-lua-macro" "include-oracle-sql-args" "include-repo" "include-repo-impl" "include-sql" "include-transformed" "include-url" "include_cstr" "include_dir_impl" "include_dir_macros" "include_display_mode_tex" "include_js_codegen" "include_optional" "include_path" "include_rgba" "include_wgsl" "indigo-proc-macros" "indigo-structopt-derive" "indirect-once-derive" "indiscriminant_impl" "indiscriminant_impl_bits" "indiscriminant_impl_byte_str" "indiscriminant_impl_str" "indoc" "indoc-impl" "indra-proc-macro" "inert_derive" "inertia-macros" "inet2_derive" "infer_schema_macros" "infinitree-macros" "influxdb-derives" "influxdb2-derive" "influxdb2-structmap-derive" "influxdb_derive" "inherent" "inherent-pub" "inheritance-proc-macro" "init_codegen" "inject-macro" "ink_lang_macro" "ink_macro" "ink_storage_derive" "inkpad-derive" "inkwell_internals" "inline-c-macro" "inline-mod" "inline-proc" "inline-rust" "inline-spirv" "inline-vbs-macros" "inpm-impl" "inpt-macros" "instrumented-codegen" "int-enum-impl" "integra8_decorations_impl" "integra8_impl" "integration-test" "inter" "inter-struct-codegen" "interact_derive" "intercom-attributes" "interfacer-http-attribute" "intermittent" "interoptopus_proc" "interpol" "interpol-impl" "interpolate_name" "intertrait-macros" "into-attr-derive" "into_query_derive" "into_response_derive" "into_variant_macro" "intricate-macros" "introspection-derive" "intuitive_macros" "intuple_derive" "inventory-impl" "invoke_impl" "io-enum" "io_self_derive" "iocmap" "ion-c-sys-macros" "iota-sc-hname-generator" "ip-macro" "ipcs-codegen" "iredismodule-macros" "iref-enum" "iroha-codegen" "iroha-derive" "irrefutable" "irzha" "is-macro" "is-same-derive" "is_not" "is_variant" "iso-macro" "issue-macros" "itconfig-macro" "iter-enum" "iterate-proc-macro" "iterator_item_macros" "its_ok" "j-api-derive" "j4rs_derive" "jaded-derive" "jam_derive" "janetrs_macros" "jcers_proc" "jeep-train-macro" "jenner-macro" "jens_derive" "jet-proto-proc-macros" "jlrs-derive" "jni-glue-macros" "jni_fn" "jnix-macros" "jo" "jockey_derive" "join" "join_export" "join_str" "jomini_derive" "josephine_derive" "josh_hates_closures" "jrpc-macro" "jrsonnet-gc-derive" "jrsonnet-gcmodule-derive" "js-intern-proc-macro" "js-macros" "js-sandbox-macros" "json_api_derive" "json_derive" "json_in_type_derive" "json_schema_test_suite_proc_macro" "json_typegen" "jsondata-derive" "jsonresponder" "jsonrpc-derive" "jsonrpc-sdk-macros" "jsonrpc-utils-macros" "jsonrpc-v2-macros" "jsonrpc_client_macro" "jsonrpsee-proc-macros" "jss_derive" "jump-kun-macros" "juniper-compose-macros" "juniper-eager-loading-code-gen" "juniper-from-schema-code-gen" "juniper_codegen" "jvm-macro" "k8s-openapi-derive" "kaitai-macros" "kal-derive" "kamikaze_di_derive" "kanata-keyberon-macros" "kanin_derive" "kas-macros" "katalyst_macros" "katexit" "kayrx-macro" "kconfig_impl" "keeshond_derive" "kefta_macro" "ketos_derive" "kexplain" "keyby" "keycode_macro" "keyframe_derive" "keypath-proc-macros" "kf-protocol-derive" "kg-diag-derive" "kg-display-derive" "klickhouse_derive" "kll-macros" "knative-derive" "knife-macro" "kobold_macros" "kommand" "kommandozeilen_argumente_derive" "kompact-actor-derive" "kompact-component-derive" "konst_proc_macros" "korat_derive" "kotlin-bridge-macro" "kproc_macros" "krator-derive" "kserd_derive" "kube-derive" "kuon_request_derive" "kurisu-derive" "kv-derive-macro" "l10n_impl" "label-macros" "label_attribute" "labelled-enum-derive" "laby_macros" "lain_derive" "lalrproc" "lambda_runtime_errors_derive" "lamedh_attributes" "lamellar-impl" "lamellar-prof" "lando-attr" "lang-util-derive" "langbox_procmacro" "lark-debug-derive" "lark-test-generate" "later-derive" "lattice_qcd_rs-procedural_macro" "layeredconf-derive" "lazing" "lazy-re" "lazy-regex-proc_macros" "lazy-settings-macros" "lazy_fn" "lazylink-macro" "lbs_derive" "lc3-macros" "leafwing_input_manager_macros" "ledb-derive" "leetcode_test" "legion_codegen" "lemmy_apub_lib_derive" "lemon-tree-derive" "lending-iterator-proc_macros" "lens-rs_derive" "lerp_derive" "leveldb-orm-derive" "lexpr-macros" "lfa_derive" "lfsr-macro-generate" "lfsr-macro-lookup" "lib3d6" "lib3h_persistence_derive" "libafl_derive" "libcnb-proc-macros" "libfj_parsable_macro_derive" "libftd2xx-cc1101-derive" "libipld-cbor-derive" "libktx-rs-macros" "libmw-macro" "libp2p-core-derive" "libp2p-swarm-derive" "libpacket-derive" "libside-procmacro" "libtor-derive" "libvmm_macros" "libwifi_macros" "lies-impl" "lifetime_proc_macros" "ligen-c-macro" "ligen-cmake-macro" "ligen-macro" "lighter-derive" "lightning-wire-msgs-derive" "lightning_encoding_derive" "limelight-derive" "limine-proc" "lingon-macro" "linkme-impl" "linux-macros" "linux-rtic-macros" "linux_details_macros" "liquid-derive" "list_files_macro" "lit-html-macro" "litcrypt" "literal_cstr" "liutongshuo_decoding_macros_impl" "livemod-derive" "llml_derive_crate" "llml_simd_proc" "llvm-plugin-inkwell-internals" "llvm-plugin-macros" "lnpbp_derive" "load-dotenv" "local-impl" "localghost-macros" "locenv-macros" "lock_order" "lockjaw_processor" "locktree-derive" "locspan-derive" "locustdb-derive" "locutus-macros" "log-attributes" "log-derive" "log-termination" "logfn" "logging_timer_proc_macros" "loggy-macros" "loginmanager-codegen" "logisheets_derives" "logisheets_workbook_derives" "logos-derive" "lokaproc" "lombok" "looking-glass-derive" "loosen" "loupe-derive" "lovm2_internals" "lovm2_module" "lpc55-rtic-macros" "lpu-macros" "lrpc-macros" "lru-cache-macros" "lsp_msg_derive" "lspower-macros" "ltv_derive_impl" "lua-macro-impl" "luao3-macros" "lucene_query_builder_rs_derive" "lucet-runtime-macros" "luminance-derive" "lunatic-macros" "lunatic-test" "lunatic_message_derive" "lurk-ipld-cbor-derive" "luther-derive" "lv2-core-derive" "lv2-urid-derive" "lvbitfile2rust-macros" "machine" "machinery-macros" "macrame" "macro-class-render" "macro-galois-field" "macro-input-macros" "macro-ob" "macro-ruby" "macro-vis" "macro_find_and_replace" "macro_helper" "macro_io" "macro_macro" "macro_pub" "macro_rules_attribute-proc_macro" "macro_state_macros" "macro_tt_utils" "macroclassrender" "macrofied-toolbox" "macromath" "macropol" "macroquad_macro" "macroscope-macro" "macrotk-derive" "macrowind" "madsim-macros" "mady_macro" "magic_static_macro" "magnet_derive" "magnus-macros" "make_singleton" "make_singleton_derive" "makeit-derive" "makepad-shader-ast-impl" "makepad-tinyserde-derive" "malloc_size_of_derive" "malvolio_codegen" "mammoth-macro" "manifest-dir-macros" "mantle-macros" "many-macros" "maomi-macro" "maomi-skin" "maple-core-macro" "marigold-macros" "marine-macro" "marine-test-macro" "marine-timestamp-macro" "marker-blanket" "market_derive" "markup-proc-macro" "marshall_derive" "masala" "mashup-impl" "mat-macros" "match-lookup" "match-template" "match_deref" "math_dsl_macro" "mathml-macros" "matrix-sdk-test-macros" "matterdb-derive" "maud_macros" "mauzi_macros" "maybe-async" "maybe-async-cfg" "mazzaroth-rs-derive" "mbot_proc_macro_helpers" "mcl_derive" "mclient_macro" "mcre" "md-icons-helper" "mdbx-proc" "mdbx-speedy" "mdsycx-macro" "medea-macro" "mediaflow_derive" "meet-macro" "megadex_derive" "megenginelite-derive" "meilimelo-macros" "melange_macros" "membrane_macro" "memflow-derive" "memoise" "memoize-inner" "memor" "memtable-macros" "memuse_derive" "mendes-derive" "mendes-macros" "merfolk_frontend_derive_macros" "merge_derive" "merkle_light_derive" "meshx-derive" "messagebus_derive" "messagepack-rs-macros" "metafor" "metaldb-derive" "metamorphose" "metered-macro" "methods-enum" "metrics-catalogue-macros" "metrics-fn-codegen" "metrics-macros" "metriki-macros" "mg-settings-macros" "mic_impl" "michie-macro" "micro-timer-macros" "microamp-macros" "microrm-macros" "microserde-derive" "microtemplate_derive" "microtype-macro" "miette-derive" "migrations_macros" "miku-macros" "millennium-macros" "milter-callback" "mime-macro-4" "mimeograph_router_codegen" "mimicry-derive" "mincache-impl" "minceraft-derive" "minecraft-protocol-derive" "mini-internal" "mini-internal-miku" "mini_paste-proc_macro" "mini_template_macro" "miniarg_derive" "minicbor-derive" "minicdn_macros" "minihttp-codegen" "miniserde-derive-enum" "miniserde-enum" "minitrace-macro" "minnow-derive" "minutus-macros" "mips-rt-macros" "mixin" "mjb_gc_derive" "mkit-derive" "mlua_derive" "mm0_deepsize_derive" "mmtk-macros" "mobile-entry-point" "mock-it_codegen" "mock_derive" "mockall_derive" "mockall_double" "mockalloc-macros" "mockers_derive" "mockiato-codegen" "mocktopus_macros" "mod_interface_meta" "modelfox_macro" "models-proc-macro" "modifier_macro" "modtype_derive" "modular-bitfield-impl" "modular-bitfield-msb-impl" "modus_ponens_derive" "mogwai-html-macro" "mol-derive" "molt-argparse-procmacro" "mongod-derive" "mongodb-ext-derive" "moniker-derive" "mono-macro" "monoio-macros" "monomo_macros" "monostate-impl" "moonboot-macros" "moonramp-lunar-macro" "moore-derive" "moose-macros" "moq_derive" "moq_lambda" "moretypes" "mork-message-derive" "motoko_proc_macro" "motore-macros" "mouscache_derive" "movie_derive" "mox" "mox-impl" "mp4ameta_proc" "mpesa_derive" "mpi-derive" "mpl-macro" "mpst-seq-proc" "mquote-impl" "mrsbfh-macros" "mry_macros" "mscorlib_safe_derive" "msfs_derive" "msgpack-schema-impl" "msgpacker-derive" "msiz_rustc-ap-rustc_macros" "msp430-rt-macros" "muds-derive" "mula_proc_macro" "multi-default-trait-impl" "multi_index_map" "multiconst_proc_macros" "multihash-derive" "multiversion-macros" "mun_codegen_macros" "munge_macro" "murray" "mushin_derive" "musli-macros" "must-implement-trait" "muta-apm-derive" "muta-codec-derive" "mutable_derive" "mutants" "mutview" "mux-stream-macros" "mwapi_responses_derive" "mws-derive" "mwt" "mxml" "my-desire-macros" "mybatis-macro" "mycroft-macros-impl" "mysql_enum_derive" "mysql_helper" "mysqldump-quick-xml-derive" "nacos-api_macro" "nacos-rs-sdk-macro" "naia-derive" "naia-serde-derive" "naked-function-macro" "nalgebra-macros" "name-it-macros" "name-variant" "named" "named-tup-derive" "named_constants" "named_return" "named_tuple" "named_type_derive" "nameless-clap_derive" "nametag" "namewise" "namewise-derive" "nanorpc-derive" "nanoserde-derive" "naphtha-proc-macro" "napi-derive" "napi-rs-derive" "naro-derive" "narrow-derive" "narui_macros" "nate-derive" "native-windows-derive" "nativeshell_derive" "nb-blocking-util" "ndjsonlogger" "ndk-macro" "ndless-macros" "near-bindgen-macros" "near-contract-tools-macros" "near-env" "near-paperclip-macros" "near-performance-metrics-macros" "near-plugins-derive" "near-prop-macros" "near-rpc-error-macro" "near-sdk-macros" "near-units-macro" "nebula-derive" "negate" "negative-impl" "neli-proc-macros" "neo-mime-macro" "neo4rs-macros" "neon-frame-macro" "neon-macros" "nereon_derive" "nestor_codegen" "net-literals" "net-literals-impl" "netlify_lambda_attributes" "netxbuilder" "new-home-application-macro" "new_derivable" "newport_codegen" "newt_component_derive" "newt_proc_macros" "newtype-enum-macro" "next-gen-proc_macros" "next-gen_proc-macro" "ni-fpga-macros" "nicolas_macros" "nifpga-type-macro" "nimble-derive" "nitroglycerin_derive" "nitrokey-test" "nj-derive" "nject-macro" "no-mangle-if-debug" "no-panic" "no_error_macro" "noble-contracts-proc-macro" "noble-staking-reward-curve" "node_api_macro" "nodex-macros" "nom-both-macros" "nom-derive-impl" "nom-packrat-macros" "nom-peg" "nom-recursive-macros" "nom-rule" "nom-tracable-macros" "nommy_derive" "nongoose-derive" "nonparallel" "nonzero" "noop-attr" "noop_proc_macro" "nop-json-derive" "norpc-macros" "nostalgia-derive" "notan_macro" "notation_macro" "note_frequencies" "nougat-proc_macros" "nova-macro" "npy-derive" "npyz-derive" "nt-list_macros" "nt-packet-derive" "ntest_test_cases" "ntest_timeout" "ntex-macros" "ntex-rt-macros" "null_fn" "num-derive" "num-variants" "num_enum_derive" "numeric-lut" "numeric_literals" "numext-constructor" "numext-fixed-hash-hack" "numext-fixed-uint-hack" "nut_self" "nyar-macro" "nys" "oapi_derive" "oars_proc_macro" "oasis-amqp-macros" "oasis-borsh-derive" "oasis-cbor-derive" "oasis-game-core-derive" "oasis-macros" "oauth1-request-derive" "obake_macros" "obfstr-impl" "obi-derive" "objc-foundation-derive" "objc2-proc-macros" "objekt-clonable-impl" "objrs_frameworks_foundation_macros" "objrs_macros" "observe-macro" "observer_attribute" "ocaml-derive" "ocaml-gen-derive" "ockam_macro" "ockam_macros" "ockam_message_derive" "ockam_node_attribute" "ockam_node_test_attribute" "ockam_vault_test_attribute" "octane_macros" "odata_client_derive" "odbc-futures-derive" "odra-proc-macros" "oe" "off-side" "ogma-macros" "okapi-operation-macro" "olympia_derive" "one_at_a_time_please_derive" "one_user" "onehot-derive" "ontio-codegen" "ontio-derive-codec" "opaque_typedef_macros" "open-enum-derive" "open-metrics-client-derive-text-encode" "openapi_type_derive" "openbrush_contracts_derive" "openbrush_lang_macro" "opensmtpd_derive" "openssl-macros" "opentelemetry-auto-span" "openwhisk_macro" "opg_derive" "ophelia-derive" "opimps" "ops-derive" "opt_args" "optargs-macro" "optbuilder" "optee-utee-macros" "optfield" "optick-attr" "option-constructor-derive" "optional-fields-serde-macro" "optional_struct" "oracle_procmacro" "orbtk-proc-macros" "orchestra-proc-macro" "ord_by_key" "ordes-macros" "ordinalizer" "orga-macros" "organix-derive" "orientdb-macro" "origen-core-support" "ormlite-macro" "ormx-macros" "osaka-macros" "osauth-derive" "osc_address_derive" "osmosis-std-derive" "oso-derive" "osquery-rust-codegen" "otopr-derive" "otspec_macros" "otto_vec_derive" "ouroboros_macro" "overflow" "overloadable" "overloadf_derive" "overrideGetterSetter" "overrider" "ovr-vsdb-derive" "owasm-abi-derive" "owned-singleton-macros" "owo-code" "oxi-module" "oxi-test" "oxidate-macros" "oxide-macros" "oxidizer-entity-macro" "oxlex-derive" "oxygen_quark_derive" "oxygengine-ignite-derive" "oy-derive" "pacaptr-macros" "packable-derive" "package_info_derive" "packattack-derive" "packed_bools" "packed_struct_codegen" "packer_derive" "packetrs-macro" "packing_codegen" "packs-proc" "packtool-macro" "pakr-assert-size" "palette_derive" "pallet-contracts-proc-macro" "pallet-macros" "pallet-staking-reward-curve" "pam-macros" "panda-re-macros" "pandora-api-derive" "panoramix-derive" "paperclip-macros" "papito_codegen" "parameterized-macro" "parce_macros" "parity-codec-derive" "parity-scale-codec-derive" "parity-util-mem-derive" "parker_codegen" "parkour_derive" "parquet_derive" "parsable-macro" "parse-display-derive" "parse-variants-derive" "parsel_derive" "parser-c-macro" "parst_derive" "partfun_derive" "partial-application-rs" "partial-borrow-macros" "partial_derive" "partial_ref_derive" "partialdebug-derive" "parze-declare" "passive_derive" "paste" "paste-impl" "pasture-derive" "patchable-macros" "paw-attributes" "paw-structopt" "pax-macro" "pbdb-macros" "pcb-rs-macros" "pcd-rs-derive" "pchain-sdk-macros" "pdf_derive" "peace_data_derive" "peace_full_spec_id_macro" "pear_codegen" "peek-poke-derive" "peg-macros" "pegcel-macros" "peginator_macro" "pegtastic-macros" "pelite-macros" "pen-ffi-macro" "penguin-config-derive" "penrose_proc" "peppi-derive" "per_test_directory_macros" "percy-css-macro" "percy-router-macro" "perseus-macro" "persia-rpc-macro" "persia-speedy-derive" "persian-rug_derive" "persistentcache_procmacro" "pest-ast" "pest-deconstruct-derive" "pest-typed-tree" "pest_consume_macros" "pest_derive" "pest_derive_tmp" "pformat_macro" "pg-extern-attr" "pg_mapper" "pgc-derive" "pgmacro" "pgx-macros" "pgx-named-columns" "phantom-fields" "phenotype-macro" "phf_macros" "photonix_derive" "phper-macros" "physx-macros" "pi_async_macro" "pi_ecs_derive_old" "pi_ecs_macros" "pi_enum_default_macro" "picorv32-rt-macros" "pijul-macros" "pin-project-internal" "pinar-derive" "pinetime-macros" "pink-extension-macro" "pink-sidevm-macro" "pinned-init-macro" "pinwheel_elements_macro" "pinwheel_macro" "pio-proc" "pipe-op" "pipederive" "piston2d_abstraction_proc_macros" "pixset_derive" "pkbuffer_derive" "pkg-macros" "pkg-version-impl" "pkg_impl" "pl-hlist-derive" "pl-lens-derive" "pl-lens-macros" "placeholder_closure" "placement-new-derive" "plaster-router-macro" "plate-macros" "plctag-derive" "pleingres-macros" "plex" "pliantdb-macros" "plotly_derive" "plugin-runtime-codegen" "plugin-system" "pluralize_derive" "plutonium" "plutus_data_derive" "pmacro_ruly" "pmhelp-internal-macros" "pn-dcp-macro" "pnet_macros" "pocket_prover-derive" "poe-superfilter-support" "poem-derive" "poem-extensions-macro" "poem-grants-proc-macro" "poem-openapi-api-derive" "poem-openapi-derive" "poem-openapi-macro" "poem_openapi_validator_derive" "poggers-derive" "pogo_attr" "point-derive" "poise_macros" "pokeapi-macro" "politeness-macro-impl" "poll_token_derive" "polling-async-trait" "polyerror" "polygraph-macro" "polyhorn-macros" "polyhorn-ui-macros" "polymesh-api-codegen-macro" "polymesh-primitives-derive" "pomelo-impl" "pomsky-macro" "portus_export" "positional_derive" "postcard-derive" "postfix-macros-impl" "postgres-derive" "postgres-derives" "postgres-extension-macro" "postgres-mapper-derive" "postgres-syntax" "postgres_query_derive" "postgres_query_macro" "potatonet-codegen" "power-instruction-analyzer-proc-macro" "power-of-two-impl" "powerset-enum-attr" "ppc750cl-macros" "pr47-codegen" "prae_macro" "praiya-macro" "pravega-client-macros" "pre-proc-macro" "predicate-macros" "prefixopt_derive" "preftool-clap-derive" "preftool-derive" "pretend-codegen" "primitive-enum-derive" "print_each_line" "prismatic" "privsep-derive" "pro_lang_macro" "pro_storage_derive" "proc-bitfield-macros" "proc-caesar" "proc-concat-bytes-impl" "proc-lock-macro" "proc-macro-error-attr" "proc-macro-hack-impl" "proc-macro-id" "proc-macro-kwargs-derive" "proc-macro-regex" "proc-macro-rules-macros" "proc-macro2-test" "proc-quote-impl" "proc-spirv" "proc-test-catalog" "proc_macro_global_state" "proc_macro_test" "proc_qq_codegen" "proc_static_assertions" "proc_unroll" "proc_use_inline" "proc_vector2d" "procedurals" "proclock-macro" "procmac" "proconio-derive" "procopt" "profiling-procmacros" "progenitor-macro" "projection-macros" "prom-attire" "prom-attire-bootstrap" "prom-timer-macro" "prometheus-client-derive-text-encode" "prometheus-metric-storage-derive" "prometheus-static-metric" "proofsize_derive" "propane-macros" "proper" "property" "propfuzz-macro" "proptest-attr-macro" "proptest-derive" "prost-reflect-derive" "protected-id-derive" "proto-vulcan-macros" "protobuf-convert" "protocol-derive" "prov-cosmwasm-derive" "prove_derive" "proxy-enum" "prune_derive" "prusto-macros" "pseudonym" "ptah_derive" "ptr_eq-macros" "ptr_meta_derive" "pub-sub-client-derive" "puball" "pubcfg" "public" "pulz-ecs-macros" "purescript_waterslide_derive" "purpurea" "pwasm-abi-derive" "pwn-helper-macros" "pydeco" "pyo3-asyncio-macros" "pyo3-macros" "pyo3-prost" "pyo3cls" "python_comm_macros" "pywrapper-macro" "q1tsim-derive" "qadapt-macro" "qbittorrent-web-api-gen" "qjs-derive" "qmetaobject_impl" "qname-macro" "qoqo-macros" "qqx-macro" "qsk-macros" "qt_macros" "qtpl-macros" "qty-macros" "qu-derive" "qualia_derive" "quantifiable-derive" "quartz_commands_macros" "quartz_nbt_macros" "query-params-derive" "query_params" "quick_from" "quickcheck_async" "quickcheck_macros" "quickjs_regex_derive" "quilkin-macros" "quit_macros" "quix-derive" "quork-proc" "quote-use" "quote_precise" "r-gen-macro" "r-lombok-macros" "r3bl_rs_utils_macro" "rabbithole-derive" "racetrack-proc-macro" "rad_ext_template" "radiation-macros" "raffia_macro" "rafka_codegen" "rainbow-macros" "ral-macro" "ramhorns-derive" "rand_derive" "rand_derive2" "randerive" "random_struct_layout" "random_variant_macro" "rapira-derive" "rapt_derive" "rart-macros" "rash_derive" "rasn-derive" "ratcc" "rate-macros" "raui-derive" "raw_serde_derive" "rawcode_derive" "rayon-attr" "rayon-macro" "rayon-macro-hack" "rbatis-macro-driver" "rbatis_sql_macro" "rbtag_derive" "rbxm-proc" "rclrust-msg-gen" "rcstruct" "rd-derive" "rdi_macros" "rdxl" "rdxl_static_macros" "re-parse-macros" "reacty_yew" "readonly" "reaktor" "real-async-trait" "real-proc" "real_c_string" "realia" "realm_macros" "reaper-macros" "reborrow-derive" "rebound-proc" "rebpf-macro" "rec" "rec_derive" "recap-derive" "reciter" "recorder" "records" "red_asn1_derive" "redacted_debug" "redbpf-macros" "redis-derive" "redis-lua-macro" "redis_serde_json" "redismodule_cmd_procmacros" "ref-cast-impl" "ref-mut-n" "ref_clone_derive" "refinery-macros" "reflect-internal" "reflection_derive" "reflective" "refloctopus-derive" "reformation_derive" "refptr-macros" "refview_derive" "regex_static_macro" "reign_derive" "reinda-macros" "relational_types_procmacro" "relearn_derive" "relexer-derive" "relm-attributes" "relm-derive" "relm-derive-state" "relm4-macros" "remain" "remoc_macro" "remote-trait-object-macro" "remove_macro_call" "renaissance" "rename" "rename-item" "render_macros" "renderdoc-derive" "rental-impl" "rep_derive" "repeated" "replman_derive" "repr-with-fallback" "repr_offset_derive" "reql-macros" "requestty-macro" "requiem-macros" "requiem-web-codegen" "require_unsafe_in_body" "rerust" "reset-router-macros" "reshape_helper" "resource_list_proc_macro" "rest-client_codegen" "restcrab_macros" "restep" "restest_macros" "restruct_derive" "result-like-derive" "resx_derives" "retrieve" "retrofit_codegen" "retroqwest-derive" "retryable-proc_macros" "reusable_derive" "reverse_differentiable" "review-macro" "rg3d-core-derive" "rglua-macros" "rhachis-run-macro" "rhai_codegen" "rhizome_proc-macro-definitions" "rhs_first_assign" "rhythmc_macros" "rifgen_attr" "riker-macros" "rill-derive" "riscv-minimal-rt-macros" "riscv-rt-macros" "ritz_impl" "rkyv_derive" "rkyv_derive_test" "rkyv_dyn_derive" "rkyv_typename_derive" "rlink-derive" "rlp-derive" "rlua-builders-derive" "rlua-table-derive" "rnet-macros" "robespierre-fw-macros" "robin-derives" "robma_builder" "robusta-codegen" "rocket-config-codegen" "rocket-grants-proc-macro" "rocket-jwt" "rocket-jwt-authorization" "rocket-model-codegen" "rocket_codegen" "rocket_contrib_codegen" "rocket_db_pools_codegen" "rocket_extra_codegen" "rocket_modules" "rocket_okapi_codegen" "rocket_okapi_codegen_fork" "rocket_sync_db_pools_codegen" "rocketjson_macro" "rokol_derive" "rollo-macros" "roopert_macro_root" "rooty_derive" "roqoqo-derive" "rorm-macro" "roslibrust_codegen_macro" "rosrust_codegen" "rotate-enum" "rotenv_codegen" "rotonda-macros" "router-rs-macro" "router-xiaobei-macro" "rovv_derive" "roxido_macro" "rp1-macros" "rp2040-hal-macros" "rpa_derives" "rpa_enum" "rpc-toolkit-macro" "rpcx_derive" "rplugin_macros" "rq_derive" "rquickjs-macro" "rrw_macro" "rs-blocks-derive" "rs-data-formats_derive" "rs-graph-derive" "rs-odbc_derive" "rs-sandbox-derive" "rs-sandbox-macros" "rs_unit" "rsb_derive" "rschema-derive" "rsconnect_macros" "rsexp-derive" "rsfbclient-derive" "rsip-derives" "rslint_macros" "rspc-macros" "rspg-macros" "rsrl_derive" "rstest_macros" "rstest_reuse" "rsys_macro" "rt-local-macros" "rtic-trace-macros" "rtlola-macros" "rtti-derive" "rubber_duck_macro" "rudeboy-derive" "ruint-macro" "rulex-macro" "ruma-api-macros" "ruma-events-macros" "ruma-identifiers-macros" "ruma-macros" "ruma-serde-macros" "rumpsteak-macros" "rundo_attrs" "rundo_attrsca" "rune-macros" "runestick-macros" "runng_derive" "runtime-attributes" "runtime-fmt-derive" "ruspiro-interrupt-macros" "rusqlite-model-derive" "rust-ad-core-macros" "rust-ad-macros" "rust-cef-derive" "rust-embed-impl" "rust-fixed-point-decimal-macros" "rust-fsm-dsl" "rust-gl-proc" "rust-hdl-macros" "rust-i18n-macro" "rust-jni-generator" "rust-libretro-proc" "rust-sitter-macro" "rust-spice-derive" "rust-xfinal-macro" "rust_decimal_macro_impls" "rust_decimal_macros" "rust_events_derive" "rust_gui_macros" "rust_hawktracer_proc_macro" "rust_transit_derive" "rustabi-derive" "rustacuda_derive" "rustbus_derive" "rustc-ap-rustc_macros" "rustc_codegen_nvvm_macros" "rusteval-derive" "rustfmt-config_proc_macro" "rustfsm_procmacro" "rustico" "rustier-macros" "rustiful-derive" "rustify_derive" "rustmex-entrypoint" "rustorm-derive" "rustorm_codegen" "rustpub-macro" "rustpython-derive" "rustructure-macros" "ruststep-derive" "rustversion" "rusty-gql-macro" "rusty-html-macros" "rusty-junctions-client-api-proc-macro" "rusty-junctions-library-generation-proc-macro" "rusty_german_entity_macro" "rusty_jsc_macros" "rusty_v8_helper_derive" "rustyle" "rustyline-derive" "rutel_derive" "rutenspitz_macro" "ruukh-codegen" "rvs_derive" "rvv-asm" "rwasm_macro" "rweb-macros" "rweb-testing-macros" "rxml_proc" "rye-macros" "rysk-tools-macro" "rzhavchina" "s_test_fixture" "sabi_derive" "sad_machine" "safe" "safe-builder-derive" "safe-bytes-derive" "safe-regex-macro" "safe-uninit-derive" "safe_attr" "safe_ecs_derive" "safecoin-frozen-abi-macro" "safecoin-sdk-macro" "safer_ffi-proc_macro" "safety-guard" "safina-async-test-core" "sai_component_derive" "sailfish-macros" "saito-macros" "salak_derive" "salsa-macros" "salvia_macro" "salvo_macro" "salvo_macros" "samotop-async-trait" "samp-codegen" "sana_derive" "sanitizeable_derive" "sanitizer_macros" "saphir_macro" "sapio_macros" "saturating_arithmetic" "sauron-component-macro" "sauron-node-macro" "savage_macros" "save_state_derive" "savefile-derive" "savory-derive" "savory-elements-derive" "savory-router" "saw_mcr" "sawp-ffi-derive" "sawp-flags-derive" "sbml-macros" "sc-chain-spec-derive" "sc-tracing-proc-macro" "sc2-proc-macro" "scale-info-derive" "scale_documentation_parser" "scale_impl_generator" "scanfmt_macros" "schema-derive" "schema_oxidation" "schemafy" "schemars_derive" "scientific-macro" "scones_macros" "scoped-gc-derive" "scoped_css" "scoundrel-macro" "scpi_derive" "scraper-macros" "scrappy-derive" "scrappy-macros" "scrappy-web-codegen" "scrappy_do_codegen" "scripthookv-rs-macros" "scroll_derive" "scsys-derive" "scsys-macros" "scylla-macros" "scylla-macros-flex" "scylla_orm_macro" "scylladb-macros" "scylladb-parse-macros" "sdb_macro" "sea-orm-field-updater" "sea-orm-macros" "sea-orm-rocket-codegen" "sea-query-attr" "sea-query-derive" "sea-query-driver" "sea-schema-derive" "sea-strum_macros" "sealed" "sealed_test_derive" "seamless_macros" "seaography-derive" "seawater-macro" "secop-derive" "section_parser_derive" "seed_style_macros" "segsource-derive" "select-rustc" "selectme-macros" "self-rust-tokenize-derive" "selfstack" "semantics-derive" "sensible" "sentinel-macros" "separable-derive" "seq-macro" "sequential-macro" "serbia" "serde-bindgen-core-derive" "serde-deserialize-over-derive" "serde-diff-derive" "serde-enum" "serde-enum-derive" "serde-enum-str" "serde-ext-macros" "serde-indexed" "serde-intermediate-derive" "serde-json-helpers" "serde-lite-derive" "serde-query-derive" "serde-reflect-intermediate-derive" "serde-semver-derive" "serde-tc-macro" "serde-versions-derive" "serde_alias" "serde_amqp_derive" "serde_apply_macros" "serde_closure_derive" "serde_compact" "serde_default" "serde_derive" "serde_derive_state" "serde_dhall_typegen" "serde_flat_regex" "serde_int_map_derive" "serde_mtproto_derive" "serde_piecewise_default_derive" "serde_prefix" "serde_python_derive" "serde_repr" "serde_roundtrip_derive" "serde_schema_derive" "serde_someip_derive" "serde_tuple_macros" "serde_unit_struct_derive" "serde_valid_derive" "serde_version_derive" "serde_with_macros" "serdebug_derive" "serdine_derive" "serenity_group_name" "serial_test_derive" "serialize-to-javascript-impl" "seripack_macros" "serum-borsh-derive" "servant-macro" "settings-schema-derive" "sewup-derive" "sexpy_derive" "sfsm-proc" "sgr" "sgr_macros" "sgx-type-debug" "sgx_align_struct_attribute" "sgx_rand_derive" "sgx_serialize_derive" "sgxs-tools" "sh-builtin-bash-proc" "sha2-derive-proc-macro" "shader_macro" "shaderc-macro" "shades-edsl" "shaku_derive" "shank_macro" "shard_ecs_derive" "shardize" "shared_memory_derive" "shellfn-attribute" "shine-ecs-macro" "shine-gltf-macro" "shine-graph-macro" "shine_component_derive" "shinyframework_codegen" "shippai_derive" "shipyard_proc" "shogun-rust-procedural" "shoogah_macros" "shorthand" "shoulda_macro" "shred-derive" "shredder_derive" "shrinkwraprs" "shumai-config-impl" "shuttle-codegen" "sia-macro" "sidetree" "sidevm-macro" "sierra-proc" "sif_macro" "signature_derive" "sim_derive" "simavr-section-macro" "simd-json-derive-int" "simd_helpers" "simi-macros" "simple-bind" "simple-hash-macro" "simple-tlv_derive" "simple_calculator_deliver" "simple_func_timer" "simple_parse_derive" "simple_tables-derive" "simple_test_case" "simple_xml_serialize_macro" "simpleord" "simplicity_derive" "simulink-binder" "simx" "sing_macros" "singleton-derive" "singletonum-derive" "sir-macro" "sixtyfps-corelib-macros" "sixtyfps-macros" "size-of-derive" "sjis-literals" "skidscan-macros" "sky-derive" "skyline_macro" "slashies-macros" "slashook-macros" "slashy_macros" "slicert" "slices" "slices-hack" "slint-macros" "slip-imp" "slist-derive" "slog-extlog-derive" "slog-mock-proc-macros" "sm-ext-derive" "sm_macro" "small_ctor" "smart-contract-macros" "smart-default" "smart-hash-derive" "smd_macro" "smelter" "smlang-macros" "smoke-macros" "smol-attributes" "smol-potat-derive" "smol-potat-macro" "smoldb_derive" "snafu-cli-debug" "snafu-derive" "snake_cased_derive" "snapshot-proc-macro" "snarkvm-circuit-environment-witness" "snarkvm-derives" "snarkvm-utilities-derives" "snec_macros" "snowcat_macros" "snowchains_proc_macros" "soa_derive_internal" "soak-derive" "solana-frozen-abi-macro" "solana-sdk-macro" "solana-sdk-macro-frozen-abi" "solana_libra_proto_conv_derive" "solarsail-macros" "solders-macros" "solid-derive" "solidity-bindgen-macros" "soloud-derive" "solstice-derive" "some-error" "someip_derive" "sonic_serde_macros" "sonic_spin" "sonicbot-macros" "sonicbot-matrix-macros" "soroban-env-macros" "soroban-native-sdk-macros" "soroban-sdk-macros" "sort_by_derive" "sorted_locks_derive" "sourcegen" "sp-api-proc-macro" "sp-core-hashing-proc-macro" "sp-debug-derive" "sp-multihash-derive" "sp-npos-elections-compact" "sp-phragmen-compact" "sp-runtime-interface-proc-macro" "sp-version-proc-macro" "spa-server-derive" "spaad_internal" "spacetimedb-bindgen" "spandoc-attribute" "sparta-proc-macros" "spec" "specit" "specs-derive" "specs-visitor-derive" "specta-macros" "spectacle-derive" "spectacle-impl-tuples" "spectrum-macros" "speculate" "speedy-derive" "spez-macros" "sphinx-use-state" "spice21procs" "spirv-std-macros" "spirv-struct-layout-derive" "splitter-derive" "sppparse_derive" "sprattus-derive" "sql_db_mapper_derive" "sqlb-macros" "sqlstate-macros" "sqlx-crud-macros" "sqlx-database-tester-macros" "sqlx-derive-with" "sqlx-macros" "sqlx-model-macros" "sqlx-models-derive" "sqlx-models-proc-macro" "sqlx-plus-macros" "sqlx-type-macro" "sqlxinsert" "sqlxmq_macros" "square-ox-derive" "squark-macros" "squtils" "srcpos_get_derive" "srpc-macro" "ssbh_write_derive" "sscanf_macro" "ssvm-evmc-declare" "ssz-derive" "ssz_rs_derive" "sszb_derive" "stability" "stable-step-derive" "staged-builder-internals" "standard-dist" "starchart-derive" "stargate-grpc-derive" "starlane-macros" "starlark_derive" "starlark_module" "starship_module_config_derive" "stateful_macro_rules" "statemachine-macro" "stateroom-wasm-macro" "static-address" "static-address-macro" "static-iref" "static-map-macro" "static-noise" "static-pubkey" "static-reflect-derive" "static-router-macros" "static_init_macro" "static_map_macros" "static_res" "static_table_derive" "static_test" "staticfilemap" "std140-macros" "stdf-record-derive" "stdin_parser_derive" "stdweb-derive" "stdweb-internal-macros" "stdweb-internal-test-macro" "steam-language-gen-derive" "stidgen" "stivale-proc" "stix_derive" "stlog-macros" "storm_macro" "stowaway-derive" "str-match" "str_to_enum_derive" "stratisd_proc_macros" "stratus-macros" "stream-future-impl" "streamduck-core-derive" "strict_encoding_derive" "string_enum" "string_literal" "stringify-attr" "stringly_typed_derive" "strong-xml-derive" "stronghold-derive" "struct-convert" "struct-field-names-as-array" "struct-merge-codegen" "struct-variant" "struct2swagger_derive" "struct2vec_derive" "struct_deser-derive" "struct_field" "struct_field_names" "struct_fragment" "struct_gen_derive" "struct_layout" "structbuilder_derive" "structconf_derive" "structdoc-derive" "structdump-derive" "structenv_derive" "structform-derive" "structmap-derive" "structmapper-codegen" "structmeta-derive" "structopt-derive" "structopt-toml-derive" "structopt-yaml-derive" "structout" "structscan_derive" "structstruck" "structsy-derive" "structural-assert" "structural_derive" "structure-macro-impl" "structview_derive" "structx_derive" "structype_derive" "strukt" "strum_macros" "strung_derive" "struple-impl" "stry-attrouter" "stub_trait" "stubr-attributes" "stuck-macros" "stylist-macros" "sub-model" "submillisecond_macros" "suborbital-macro" "subplotlib-derive" "subrpcer-impl" "substance-macro" "substrait-validator-derive" "substrate-subxt-proc-macro" "substrate-test-utils-derive" "substreams-ethereum-derive" "substreams-macro" "subtle-derive" "subtle-ng-derive" "subxt-macro" "sudograph-generate" "sul" "sully_peg" "sum_error" "summer-boot-macro" "sunbeam-macro" "sundial-derives" "sunfish_macro" "sunscreen_compiler_macros" "sunset-sshwire-derive" "super-sabicom-macro" "superbitty-macros" "supermod" "superstruct" "suricata-derive" "surjective-enum" "surrealdb-derive" "suspend_fn_proc_macro" "susy-codec-derive" "susy-jsonrpc-derive" "susyabi-derive" "susydev-jsonrpc-derive" "susyp2p-core-derive" "sv-parser-macros" "sval_derive" "svec_macro" "svgbobdoc" "swarm-bot-packets-macro" "swarm-derive" "sway-ir-macros" "swc_config_macro" "swc_css_codegen_macros" "swc_ecma_codegen_macros" "swc_ecma_parser_macros" "swc_ecma_quote_macros" "swc_ecma_transforms_macros" "swc_ecma_visit_macros" "swc_eq_ignore_macros" "swc_estree_macros" "swc_html_codegen_macros" "swc_plugin_macro" "swc_trace_macro" "swc_visit_macros" "swc_xml_codegen_macros" "swift-bridge-macro" "swipl-macros" "sycamore-macro" "sycamore-router-macro" "syllogism-macro" "sylt-macro" "sylvia-derive" "symm_impl" "symoxide_macros" "syncwrap" "syndicate-macros" "synonym" "synthez-codegen" "t4rust-derive" "tabled_derive" "tablefy_derive" "tackt-macros" "tagged_bytes" "taikai" "tailcall-impl" "talos_procs" "tamasfe-schemars_derive" "tamata-macros" "tangram_macro" "taos-macros" "tapa-trait-serde-derive" "tapioca-codegen" "tappet-derive" "tarantool-proc" "target-cpu-macro" "target-lexicon-macros" "tari_comms_rpc_macros" "tari_infra_derive" "tarpc-plugins" "tarrasque-macro" "tauri-macros" "tc-chain-spec-derive" "tc-tracing-proc-macro" "tch-tensor-like-derive" "tco" "tealr_derive" "teensy-lc-macros" "teil_derive" "telebot-derive" "telegraf_derive" "telegram_derive" "telexide_fork_proc_macros" "telexide_proc_macros" "teloc_macros" "teloxide-macros" "templar_macros" "templing" "temply-derive" "tencent_scf_derive" "tensorflow-internal-macros" "tensorflow_proto_derive" "tent_codegen" "tent_proc_macros" "term-data-table-derive" "termcandy-macros" "termcolor_output_impl" "termination_attrib" "termpixels_derive" "test-case-derive" "test-case-macros" "test-collector-derive" "test-context-macros" "test-env-helpers" "test-env-log" "test-fuzz-macro" "test-generator" "test-ignore-if" "test-impl" "test-log" "test-span-macro" "test-strategy" "test-with" "test_deps_if" "test_double" "testaun" "testcat" "testdata-macros" "testgen" "testing_macros" "tet-libp2p-core-derive" "tetcore-subxt-proc-macro" "tetcore-test-utils-derive" "tetsy-codec-derive" "tetsy-jsonrpc-derive" "tetsy-libp2p-core-derive" "tetsy-rlp-derive" "tetsy-scale-codec-derive" "tetsy-scale-info-derive" "tetsy-util-mem-derive" "tex_derive" "textwrap-macros-impl" "thalo-macros" "thcon_macro" "thespis_derive" "thin_trait_object" "third-pact" "thirtyfour-macros" "thirtyfour-querier-derive" "thisctx_impl" "thiserror-impl" "thiserror-impl-no-std" "thiserror_core2-impl" "thread_spawn" "three_em_macro" "throttle_my_fn" "thruster-proc" "thruster-socketio-proc" "thruster-x" "thunder" "thunderbird-macros" "tia" "tiberius-derive" "tide-jsx-impl" "tide-serve-dir-macro" "tidy-builder" "tilde" "time-graph-macros" "time-macros" "time-macros-impl" "time_main" "timed_function" "timed_proc_macros" "timesource-derive" "tiny-multihash-derive" "tiny-multihash-proc-macro" "tiny-rpc-macros" "tiny_orm_macro_derive" "tinyhttp-codegen" "tinyparse_macro" "tl-proto-proc" "tlayuda" "tls_codec_derive" "tlua-derive" "tm-derive" "to-syn-error-derive" "to-syn-value_derive" "to_bytes_derive" "to_hash_map" "todo-or-die" "tojson_macros" "tokay-macros" "token-parser-derive" "tokio-actor" "tokio-async-attributes" "tokio-async-await-test" "tokio-macros" "tokio-pg-mapper-derive" "tokio-zmq-derive" "tokio_env_macro" "toml-cfg" "toml-query_derive" "tonic-error-impl" "tonic-include-proto" "tonic-rpc-macro" "tonic_catch_proc" "tonic_include_protos" "took-macro" "topo-macro" "toql_derive" "toql_enum_derive" "toql_fields_macro" "toql_paths_macro" "toql_query_macro" "toql_role_expr_macro" "toql_sql_expr_macro" "torn-api-macros" "tower-lsp-macros" "tower-web-macros" "toy-arms_derive" "toy-rpc-macros" "tp-api-proc-macro" "tp-npos-elections-compact" "tp-runtime-interface-proc-macro" "tql_macros" "trace" "trace-tools-attributes" "trace2_macro" "trace_caller_macro" "tracelogging_macros" "tracers-macros-hack" "tracing-attributes" "tracing-attributes-http" "tracing-attributes-hyper" "tracing-forest-macros" "tracing-rc-derive" "tracing-test-macro" "track-macro" "trackable_derive" "tracked-impl" "tracker-macros" "tractor-macros" "trade_aggregation_derive" "trait-async" "trait-enumizer-derive" "trait-match-proc-macro" "trait-set" "trait-union-proc" "trait_adapters_macros" "trait_cast_impl_rs" "trait_derive" "trait_tests" "traitlit" "traitor-derive" "trans-derive" "trans-schema-derive" "transit_model_procmacro" "translation_provider" "translator" "transmute-tools" "trapper_macro" "trdelnik-test" "treasure-map-derive" "tree-buf-macros" "tree_hash_derive" "treeflection_derive" "trigraph" "trillium-include-dir-impl" "trillium-static-compiled-macros" "trimmer_derive" "trpc-rs-macros" "trustfall_filetests_macros" "try-catch" "try-let" "try_clone_derive" "try_match_inner" "tryfromfail" "ts-bindgen-build-support" "ts-rs-macros" "ts2rs" "ts3_derive" "tsify-macros" "tskit-derive" "tstr_proc_macros" "tsukuyomi-macros" "tsync" "tt-equal" "tt-identifier" "tttr-toolbox-proc-macros" "tui-markup-ansi-macro" "tuirealm_derive" "tuix_derive" "tulip-derivative" "tumbleweed_derive" "tuna-macros" "tupiter-proc-macro" "tuple-iter" "tupleops-macros" "turbocharger-impl" "turbolift_macros" "turbomod" "turbonone" "turbosloth-macros" "turbosql-impl" "tvm-macros" "tw-storage-macros" "twilight-interactions-derive" "two-rusty-forks-macro" "tyenum_attribute" "tylift" "typ" "type-census-derive" "type-change" "type-cli-derive" "type-info-derive" "type-layout-derive" "type-name-derive" "type-rules-derive" "type-uuid-derive" "type_at_derive" "type_description_derive" "type_hash_macros" "type_utils" "typed-builder" "typed-html-macros" "typed-qb-procmacro" "typed-sql-derive" "typed-store-derive" "typed-urls-derive" "typed_index_derive" "typemap-meta-derive" "typename_derive" "typenaming-derive" "typenum-promote" "typesafe-derive-builder" "typescript-definitions-derive" "typescript-definitions-derive-ufo-patch" "typescript-type-def-derive" "typescript-wasm-bindgen-macros" "typescriptify-derive" "typesense_derive" "typesets-macro" "typeshare_marker" "typestate-proc-macro" "typetag-impl" "typetrait" "typic-derive" "typify-macro" "typsy-macros" "u256-literal" "u64_array_bigints_macros" "uapi-proc" "uavcan-derive" "ublox_derive" "uclicious_derive" "uefi-macros" "ufmt-macros" "ugli-derive" "uhppote-derive" "ui4-macros" "uindex_derive" "uncon_derive" "unconst_trait_impl" "unhtml_derive" "unhygienic-impl" "unhygienic2" "uni_components_macro" "uni_localservice_macro" "unicode_names2_macros" "uniffi_macros" "unimock_macros" "union_export" "union_type" "unique-type-id-derive" "unit-derive" "unit-proc" "unit_system_derive" "unite" "unitval-derive" "uniui_gui_macro" "unix-ts-macros" "unpat" "unprolix" "unquote" "unrest_codegen" "unroll" "unsafe_fn" "unstringify" "unwind_aborts" "unwrap_all" "unwrap_helpers_proc_macros" "unzip-n" "uom-macros" "update-sync_derive" "uptown_funk_macro" "uri_path_router" "urid-derive" "usbd-hid-macros" "usdt-attr-macro" "usdt-macro" "user_doc-doc_proc_macro" "usual-proc" "ut" "utf16_literal" "utf32-lit" "utility-types" "utils-plugs-proc" "utoipa-gen" "utools" "uucore_procs" "uuid-macro-internal" "ux-macro" "v11_macros" "v9-attr" "v_escape_derive" "vale-derive" "validated_struct_macros" "validator_derive" "validators-derive" "valor_plugin" "valora_derive" "valuable-derive" "value_from_type_macros" "valuetypes" "vapabi-derive" "vararg" "variant_access" "variant_access_derive" "variant_count" "variant_counter_derived" "variant_name_derive" "varianteq" "variantly" "variants-struct" "variation" "varies" "varisat-internal-macros" "varlen_macro" "vary" "vec-reg-macro" "vecmerge-impl" "vecn" "velcro_macros" "veneer-macros" "veriform_derive" "verify-macros" "verify_macro" "verilated-module" "version-consts-git-impl" "versionize_derive" "versuch" "vertigo-html-macro" "vertigo-macro" "vgtk-macros" "viable-impl" "view" "view_macro" "vimwiki_macros" "visa-rs-proc" "visibility" "visible" "visit" "visit_diff_derive" "visita_macros" "viz-macros" "vizz_derive" "vk-shader-macros" "vk-shader-macros-impl" "vmnet-derive" "vmprotect-macros" "volkswagen" "volo-macros" "vptr-macros" "vride-macros" "vru-noise-macros" "vsdb_derive" "vtable-macro" "vtables_derive" "vte_generate_state_changes" "vujio" "vulkano-shaders" "wa-serde-derive" "wa_proto_macro" "waiter_codegen" "walle-macro" "warp_dsl_impl" "wasi-common-cbindgen" "wasm-bindgen-derive" "wasm-bindgen-macro" "wasm-bindgen-test-macro" "wasm-bus-macros" "wasm-interfacegen-macro" "wasm-rpc-macros" "wasm-run-proc-macro" "wasm-typescript-definition" "wasm-wrapper-gen-impl" "wasm4-impl" "wasm_plugin_guest_derive" "wasmbin-derive" "wasmbox-macro" "wasmbus-macros" "wasmcloud-actor-core-derive" "wasmedge-bindgen-macro" "wasmer-bus-macros" "wasmer-derive" "wasmer-derive-asml-fork" "wasmer-derive-near" "wasmer-inline-c-macro" "wasmer-wit-bindgen-rust-impl" "wasmer_enumset_derive" "wasmesh-macros" "wasmir" "wasmtime-component-macro" "wasmtime-rust-macro" "wasmtime-wiggle-macro" "wasmtime_plugin_guest_derive" "wasmy-macros" "wasmyon-macro-support" "waterbear-instruction-derive" "wayk_proto_derive" "wayland-scanner" "waytogo-macro" "wchar-impl" "wd-40" "wd_macro" "we-derive" "wearte_derive" "web-component-derive" "web-glitz-macros" "web-sys-query-derive" "webassembly-test" "webforms_derive" "webframework-derive" "webgl-rc-macro" "weblab-macros" "weblog-proc-macro" "webview2-com-callback-macros" "webview2-com-macros" "weechat-macro" "weft_derive" "wezterm-dynamic-derive" "wgpu-traits" "wgpu_macros" "whasm-grammar-derive" "whaterror_macros" "wide-literals" "wide-str-impl" "wig" "wiggle-macro" "wikiproc" "willow-codegen" "win_etw_macros" "windows-define" "windows-dll-codegen" "windows-implement" "windows-interface" "windows_gen_macros" "windows_winmd_macros" "windy-macros" "winit-main-proc" "winmd-macros" "winrt_gen_macros" "winrt_macros" "winspawn-macro" "winstr-macros" "winwrap-derive" "witchcraft-server-macros" "witgen_macro" "with_builtin_macros-proc_macros" "with_locals-proc_macros" "with_tempdir" "wither_derive" "withers_derive" "wll-macros" "wlroots-dehandle" "woab-macros" "wolfram-library-link-macros" "wonderbox-codegen" "wood_derive" "woptions_meta" "worker-macros" "workflow_macro" "wotw_seedgen_derive" "wrapcenum-derive" "wrapped-vec" "wrapping_arithmetic" "wrapping_proc_macro" "wraptest" "ws2812-spi-write-constants" "wstr_impl" "wundergraph_derive" "x86test-macro" "xactor-derive" "xaynet-macros" "xdr-rs-serialize-derive" "xecs_derive" "xflags-macros" "xladd-derive" "xls_table_derive" "xml-attributes-derive" "xml-data-derive" "xml-schema-derive" "xmlparser-derive-core" "xmpp-derive" "xops_macros" "xorstring-procmacro" "xous-macros" "xql-derive" "xshell-macros" "xtask-wasm-run-example" "xtensa-lx-rt-proc-macros" "xtensa-lx106-rt-proc-macros" "xtensa-lx6-rt-proc-macros" "xtor_derive" "xtra_proc" "xui-macros" "xxlib_builder" "xylem-codegen" "ya-runtime-sdk-derive" "yaaf-macros" "yade" "yaga-derive" "yare-macro" "yarte_derive" "yarte_lexer_gencode" "yasec_derive" "yaserde_derive" "yatima-rustyline-derive" "yatt_orm_derive" "yerpc_derive" "yew-component" "yew-fs-router-macro" "yew-interop-macro" "yew-macro" "yew-route-breadcrumbs-derive" "yew-router-macro" "yew-router-min-macro" "yew-router-nested-macro" "yew-style-in-rs-macro" "yew_form_derive" "yewdux-macros" "yewtil-macro" "yield-closures-impl" "yolo_keyword" "yooper_derive" "you-can-build-macros" "youxi-codegen" "yoyo-macros" "yui_derive" "z3d" "zapper_derive" "zbus_macros" "zc-derive" "zephyrus-macros" "zero_v_gen" "zeroable_derive" "zeroconf-macros" "zerocopy-derive" "zerogc-derive" "zeroize_derive" "zeronet_sign_derive" "zestors-codegen" "zfs-rs-macros" "zhi_enum_derive" "zipkin-macros" "zisvalidator_derive" "zkp-macros-impl" "zoet-macro" "zone_cfg_derive" "zookeeper_derive" "zstr" "zvariant_derive" ] ================================================ FILE: flake.nix ================================================ { inputs = { flake-utils.url = "github:numtide/flake-utils"; nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; registry-crates-io = { url = "github:rust-lang/crates.io-index"; flake = false; }; }; outputs = { self, flake-utils, nixpkgs, registry-crates-io }@inputs: let supportedSystems = [ "x86_64-linux" "aarch64-linux" ]; inherit (builtins) toJSON typeOf; inherit (nixpkgs.lib) isDerivation isFunction isAttrs mapAttrsToList listToAttrs flatten; nocargo-lib = import ./lib { inherit (nixpkgs) lib; }; in flake-utils.lib.eachSystem supportedSystems (system: let pkgs = nixpkgs.legacyPackages.${system}; defaultRegistries = { "https://github.com/rust-lang/crates.io-index" = nocargo-lib.pkg-info.mkIndex pkgs.fetchurl registry-crates-io (import ./crates-io-override { inherit (nixpkgs) lib; inherit pkgs; }); }; in rec { apps.default = { type = "app"; program = "${packages.noc}/bin/noc"; }; # Is there a better place? `naersk` places builders under `lib.${system}`. lib = rec { mkIndex = nocargo-lib.pkg-info.mkIndex pkgs.fetchurl; buildRustCrate = pkgs.callPackage ./build-rust-crate { inherit (packages) toml2json; inherit nocargo-lib; }; mkRustPackageOrWorkspace = pkgs.callPackage nocargo-lib.support.mkRustPackageOrWorkspace { inherit defaultRegistries buildRustCrate; }; }; packages = rec { default = noc; toml2json = pkgs.callPackage ./toml2json { }; noc = (lib.mkRustPackageOrWorkspace { src = ./noc; }).release.nocargo.bin; cache = pkgs.callPackage ./cache { inherit (lib) mkRustPackageOrWorkspace; }; }; checks = let okDrv = derivation { name = "success"; inherit system; builder = "/bin/sh"; args = [ "-c" ": >$out" ]; }; checkArgs = { inherit pkgs defaultRegistries; assertEq = got: expect: { __assertion = true; fn = name: if toJSON got == toJSON expect then okDrv else pkgs.runCommand name { nativeBuildInputs = [ pkgs.jq ]; got = toJSON got; expect = toJSON expect; } '' if [[ ''${#got} < 32 && ''${#expect} < 32 ]]; then echo "got: $got" echo "expect: $expect" else echo "got:" jq . <<<"$got" echo echo "expect:" jq . <<<"$expect" echo echo "diff:" diff -y <(jq . <<<"$got") <(jq . <<<"$expect") exit 1 fi ''; }; }; tests = with nocargo-lib; { _0000-semver-compare = semver.semver-compare-tests; _0001-semver-req = semver.semver-req-tests; _0002-cfg-parser = target-cfg.cfg-parser-tests; _0003-cfg-eval = target-cfg.cfg-eval-tests; _0004-platform-cfg = target-cfg.platform-cfg-tests; _0005-glob = glob.glob-tests; _0006-sanitize-relative-path = support.sanitize-relative-path-tests; _0100-pkg-info-from-toml = pkg-info.pkg-info-from-toml-tests; _0101-preprocess-feature = resolve.preprocess-feature-tests; _0102-update-feature = resolve.update-feature-tests; _0103-resolve-feature = resolve.resolve-feature-tests; _0200-resolve-deps = resolve.resolve-deps-tests; _0201-build-from-src-dry = support.build-from-src-dry-tests; } // import ./tests { inherit pkgs self inputs defaultRegistries; }; flattenTests = prefix: v: if isDerivation v then { name = prefix; value = v; } else if v ? __assertion then { name = prefix; value = v.fn prefix; } else if isFunction v then flattenTests prefix (v checkArgs) else if isAttrs v then mapAttrsToList (name: flattenTests "${prefix}-${name}") v else throw "Unexpect test type: ${typeOf v}"; tests' = listToAttrs (flatten (mapAttrsToList flattenTests tests)); in tests'; }); } ================================================ FILE: lib/default.nix ================================================ { lib }: let callLib = file: import file { inherit lib self; }; self = { glob = callLib ./glob.nix; semver = callLib ./semver.nix; target-cfg = callLib ./target-cfg.nix; pkg-info = callLib ./pkg-info.nix; resolve = callLib ./resolve.nix; support = callLib ./support.nix; }; in self ================================================ FILE: lib/glob.nix ================================================ { lib, ... }: let inherit (builtins) match readDir split foldl'; inherit (lib) replaceStrings isString concatStrings stringLength hasPrefix substring concatStringsSep head tail init filter isAttrs attrNames mapAttrs getAttrFromPath; in rec { # We don't allow root-based glob and separator `/` or `\` inside brackets. globBracketPat = ''\[!?[^\/][^]\/]*]''; globAtomPat = ''[^[\/]|${globBracketPat}''; globPat = ''((${globAtomPat})+[\/])*(${globAtomPat})+''; # Parse a glob pattern into a list of segments. # # String -> List ({ lit: String } | { re: String } | { deep: true }) # # `lit` for simple string literal. # `re` for segment matching. # `deep` for `**` # # https://docs.rs/glob/0.3.0/glob/struct.Pattern.html # https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_04 parseGlob = glob: let translateAtom = m: let m' = head m; in if isString m then replaceStrings [ "\\" "{" "(" ")" "^" "$" "|" "." "?" "*" "+" ] [ "/" "\\{" "\\(" "\\)" "\\^" "\\$" "\\|" "\\." "." ".*" "\\+" ] m else if hasPrefix "[!" m' then "[^" + substring 2 (stringLength m') m' else m'; translateSegment = seg: if seg == "**" then { deep = true; } else if match "[^[?*]+" seg != null then { lit = seg; } else { re = concatStrings (map translateAtom (split "(${globBracketPat})" seg)); }; segments = filter (s: isString s && s != "") (split ''/|\\'' glob); in if match globPat glob == null then throw '' Invalid glob pattern: ${glob} Note that we don't support root-based pattern and separator `/` or `\` inside brackets. '' else map translateSegment segments; # Find all paths in a tree matching a given glob pattern. # A path is represented as a list of segments. # # String -> Set -> List (List String) globMatchTree = glob: tree: let flatMap = f: foldl' (ret: x: ret ++ f x) []; go = path: pats: let pat = head pats; pats' = tail pats; curTree = getAttrFromPath path tree; keys = if isAttrs curTree then attrNames curTree else []; in if pats == [] then [ path ] else if pat ? lit then if pat.lit == "." then go path pats' else if pat.lit == ".." then if path == [] then [] else go (init path) pats' else if curTree ? ${pat.lit} then go (path ++ [ pat.lit ]) pats' else [] else if pat ? deep then # `**` go path pats' ++ flatMap (k: go (path ++ [ k ]) pats) keys # Pass `pats` to recurse into deep directories. else if pat ? re then flatMap (k: go (path ++ [ k ]) pats') (filter (k: match pat.re k != null) keys) else throw "Unreachable"; in go [] (parseGlob glob); # Find all paths inside a filesystem directory matching a given glob pattern. # A path is represented as a relative string with `/` as segment separator, eg. "" (root), "foo", "foo/bar" # # String -> Path -> List String globMatchDir = glob: dir: let pathToTree = dir: mapAttrs (k: ty: if ty == "directory" then pathToTree (dir + "/${k}") else ty) (readDir dir); ret = globMatchTree glob (pathToTree dir); in map (concatStringsSep "/") ret; glob-tests = { assertEq, ... }: { "0parse" = let assertInvalid = glob: assertEq (builtins.tryEval (parseGlob glob)) { success = false; value = false; }; assertParsed = glob: expect: assertEq (parseGlob glob) expect; in { invalid1 = assertInvalid ""; invalid2 = assertInvalid "/"; invalid3 = assertInvalid "/foo"; invalid4 = assertInvalid "foo//bar"; lit1 = assertParsed "foo" [ { lit = "foo"; } ]; lit2 = assertParsed "!.(^$){}" [ { lit = "!.(^$){}"; } ]; lit3 = assertParsed "." [ { lit = "."; } ]; lit4 = assertParsed ".." [ { lit = ".."; } ]; re1 = assertParsed "*" [ { re = ''.*''; } ]; re2 = assertParsed ".*" [ { re = ''\..*''; } ]; re3 = assertParsed "*.*" [ { re = ''.*\..*''; } ]; re4 = assertParsed "[[][]][![][!]][a-z0-]" [ { re = ''[[][]][^[][^]][a-z0-]''; } ]; re5 = assertParsed "?.*[[][?.*]?.*" [ { re = ''.\..*[[][?.*].\..*''; } ]; re6 = assertParsed ".[.]" [ { re = ''\.[.]''; } ]; deep1 = assertParsed "**" [ { deep = true; } ]; compound1 = assertParsed "./foo/**/*.nix" [ { lit = "."; } { lit = "foo"; } { deep = true; } { re = ''.*\.nix''; } ]; compound2 = assertParsed ".*/../log/[!abc]*-[0-9T:-]+0000.log" [ { re = ''\..*''; } { lit = ".."; } { lit = "log"; } { re = ''[^abc].*-[0-9T:-]\+0000\.log''; } ]; }; "1match-tree" = let tree = { a = null; b = null; b1 = null; b2 = null; bcd = null; bed = null; c = { d.e = { af = null; f = null; }; g.h = null; wtf = null; }; f = null; z = { a = null; b = { c = null; d.e = null; }; }; }; assertMatch = glob: expect: let ret = globMatchTree glob tree; ret' = map (concatStringsSep "/") ret; in assertEq ret' expect; in { exact1 = assertMatch "a" [ "a" ]; exact2 = assertMatch "c/g/h" [ "c/g/h" ]; exact3 = assertMatch "c/g" [ "c/g" ]; dot1 = assertMatch "./a" [ "a" ]; dot2 = assertMatch "./a/../c/g/./h" [ "c/g/h" ]; re1 = assertMatch "b*" [ "b" "b1" "b2" "bcd" "bed" ]; re2 = assertMatch "b?" [ "b1" "b2" ]; re3 = assertMatch "c/*" [ "c/d" "c/g" "c/wtf" ]; re4 = assertMatch "b?d" [ "bcd" "bed" ]; deep1 = assertMatch "**/b?d" [ "bcd" "bed" ]; deep2 = assertMatch "c/**/*f" [ "c/wtf" "c/d/e/af" "c/d/e/f" ]; deep3 = assertMatch "**/f/.." [ "" "c/d/e" ]; deep4 = assertMatch "[wz]/**" [ "z" "z/a" "z/b" "z/b/c" "z/b/d" "z/b/d/e" ]; }; "2match-dir" = { compound1 = assertEq (globMatchDir "*-*.nix" ./.) [ "pkg-info.nix" "target-cfg.nix" ]; compound2 = assertEq (globMatchDir "./features/../**/tokio-[!wtf][opq]?" ../tests) [ "tokio-app" ]; }; }; } ================================================ FILE: lib/pkg-info.nix ================================================ { lib, ... }: let inherit (builtins) readFile readDir fromJSON fromTOML toString attrNames match; inherit (lib) stringLength splitString replaceStrings substring isString toLower filter listToAttrs mapAttrs mapAttrsToList optionalAttrs warnIf; in rec { toPkgId = { name, version, source ? null, ... }: if source != null then "${name} ${version} (${source})" else # Local crates must be collide names. Simply use the name to make overriding easier. name; mkIndex = fetchurl: path: overrides: let # TODO: We currently only support legacy format used by crates.io-index. # https://github.com/rust-lang/cargo/blob/2f3df16921deb34a92700f4d5a7ecfb424739558/src/cargo/sources/registry/mod.rs#L230-L244 downloadEndpoint = (fromJSON (readFile (path + "/config.json"))).dl; mkDownloadUrl = assert match ".*\\{.*" downloadEndpoint == null; { name, version, ... }: "${downloadEndpoint}/${name}/${version}/download"; mkSrc = { name, version, sha256 }@args: fetchurl { # Use the same name as nixpkgs to benifit from cache. # https://github.com/NixOS/nixpkgs/pull/122158/files#diff-eb8b8729bfd36f8878c2d8a99f67a2bebb912e9f78c5d2a72457b1f572e26986R67 name = "crate-${name}-${version}.tar.gz"; url = mkDownloadUrl args; inherit sha256; }; go = path: mapAttrs (k: v: if v == "directory" then go (path + "/${k}") else mkPkgInfoSet mkSrc k (readFile (path + "/${k}")) (overrides.${k} or null) ) (removeAttrs (readDir path) [ "config.json" ]); in go path // { __registry_index = true; }; # Get pkg info of the given package, with overrides applied if exists. getPkgInfoFromIndex = index: { name, version, checksum ? null, ... }: let name' = toLower name; len = stringLength name'; crate = if len == 1 then index."1".${name'} or null else if len == 2 then index."2".${name'} or null else if len == 3 then index."3".${substring 0 1 name'}.${name'} or null else index.${substring 0 2 name'}.${substring 2 2 name'}.${name'} or null; info = crate.${version} or null; in if !(index ? __registry_index) then throw "Invalid registry. Do you forget `mkIndex` on registry paths?" else if crate == null then throw "Package ${name} is not found in index" else if info == null then throw "Package ${name} doesn't have version ${version} in index. Available versions: ${toString (attrNames crate)}" else if info.sha256 != null && checksum != null && info.sha256 != checksum then throw "Package ${name} ${version} hash mismatched, expect ${info.sha256}, got ${checksum}" else info; # Make a set of pkg infos keyed by version. mkPkgInfoSet = mkSrc: name: content: override: let lines = filter (line: line != "") (splitString "\n" content); parseLine = line: let parsed = fromJSON line; in { name = parsed.vers; value = mkPkgInfoFromRegistry mkSrc parsed // optionalAttrs (override != null) { # Proc macro crates behave differently in dependency resolution. procMacro = (override { inherit (parsed) version; features = { }; }).procMacro or false; __override = override; }; }; in listToAttrs (map parseLine lines); # Package info: # { # name = "libz-sys"; # The name in registry. # version = "0.1.0"; # Semver. # src = ; # Source path. # sha256 = "123456...."; # Hash of the `src` tarball. (null or string) # yanked = false; # Whether it's yanked. # links = "z"; # The native library to link. (null or string) # procMacro = false; # Whether this is a proc-macro library. See comments below. # features = { # Features provided. # default = [ "std" ]; # std = []; # }; # dependencies = [ # { # name = "libc"; # Reference name. # package = "libc"; # Dependency's name in registry. (default to be `name`) # req = "^0.1.0"; # Semver requirement. # features = [ "foo" ]; # Enabled features. # optional = false; # Whether this dependency is optional. # default_features = true; # Whether to enable default features. # target = "cfg(...)"; # Only required on some targets. (null or string, default to be null) # kind = "normal"; # Dependencies (one of "normal", "dev", "build", default to be "normal") # # `registry` and `public` are not supported. # } # ]; # } mkPkgInfoFromRegistry = mkSrc: # https://github.com/rust-lang/cargo/blob/2f3df16921deb34a92700f4d5a7ecfb424739558/src/cargo/sources/registry/mod.rs#L259 { name, vers, deps, features, cksum, yanked ? false, links ? null, v ? 1, ... }: if v != 1 then throw "${name} ${vers}: Registry layout version ${toString v} is too new to understand" else { inherit name features yanked links; version = vers; sha256 = cksum; dependencies = map sanitizeDep deps; # N.B. Proc macro indicator is not in the registry: https://github.com/rust-lang/cargo/issues/9605 # This would be overrided in `mkPkgInfoSet`. procMacro = false; src = mkSrc { inherit name; version = vers; sha256 = cksum; }; }; # Sanitize a dependency reference. # Handling `package` and fill missing fields. sanitizeDep = { name , package ? name , version ? null # Cargo.toml use `version` , req ? version , features ? [] , optional ? false , default_features ? true , target ? null , kind , ... }@args: args // { inherit name package req features optional default_features target kind; # Note that `package` == `name` is not the same as omitting `package`. # See: https://github.com/rust-lang/cargo/issues/6827 # Here we let `package` fallback to name, but set a special `rename` to the renamed `name` # if `package` != `name`. `rename` will affect the `--extern` flags. # # For remind: # - `name` is used for coresponding feature name for optional dependencies. # - `package` is used for the original package name of dependency crate. # - If `package` isn't set, the code name (for `use` or `extern crate`) of the dependency is its lib name. # `--extern` also use its own lib name. # - If `package` is set, the code name and `--extern` both use the renamed `name`. } // optionalAttrs (args.package or null != null) { rename = replaceStrings ["-"] ["_"] name; }; # Build a simplified crate into from a parsed Cargo.toml. mkPkgInfoFromCargoToml = { lockVersion ? 3, package, features ? {}, target ? {}, ... }@args: src: let transDeps = target: kind: mapAttrsToList (name: v: { inherit name target kind; package = v.package or name; # For path or git dependencies, `version` can be omitted. req = if isString v then v else v.version or null; features = v.features or []; optional = v.optional or false; # It's `default-features` in Cargo.toml, but `default_features` in index and in pkg info. default_features = warnIf (v ? default_features) "Ignoring `default_features`. Do you mean `default-features`?" (v.default-features or true); # This is used for dependency resoving inside Cargo.lock. source = if v ? registry then throw "Dependency with `registry` is not supported. Use `registry-index` with explicit URL instead." else if v ? registry-index then "registry+${v.registry-index}" else if v ? git then # For v1 and v2, git-branch URLs are encoded as "git+url" with no query parameters. if v ? branch && lockVersion >= 3 then "git+${v.git}?branch=${v.branch}" else if v ? tag then "git+${v.git}?tag=${v.tag}" else if v ? rev then "git+${v.git}?rev=${v.rev}" else "git+${v.git}" else if v ? path then # Local crates are mark with `null` source. null else # Default to use crates.io registry. # N.B. This is necessary and must not be `null`, or it will be indinstinguishable # with local crates or crates from other registries. "registry+https://github.com/rust-lang/crates.io-index"; # See `sanitizeDep` } // optionalAttrs (v.package or null != null) { rename = replaceStrings ["-"] ["_"] name; }); collectTargetDeps = target: { dependencies ? {}, dev-dependencies ? {}, build-dependencies ? {}, ... }: transDeps target "normal" dependencies ++ transDeps target "dev" dev-dependencies ++ transDeps target "build" build-dependencies; in { inherit (package) name version; inherit src features; links = package.links or null; procMacro = args.lib.proc-macro or false; dependencies = collectTargetDeps null args ++ mapAttrsToList collectTargetDeps target; }; pkg-info-from-toml-tests = { assertEq, ... }: { simple = let cargoToml = fromTOML (readFile ../tests/tokio-app/Cargo.toml); info = mkPkgInfoFromCargoToml cargoToml ""; expected = { name = "tokio-app"; version = "0.0.0"; features = { }; src = ""; links = null; procMacro = false; dependencies = [ { name = "tokio"; package = "tokio"; default_features = false; features = [ "rt-multi-thread" "macros" "time" ]; kind = "normal"; optional = false; req = "1"; target = null; source = "registry+https://github.com/rust-lang/crates.io-index"; } ]; }; in assertEq info expected; build-deps = let cargoToml = fromTOML (readFile ../tests/build-deps/Cargo.toml); info = mkPkgInfoFromCargoToml cargoToml ""; expected = { name = "build-deps"; version = "0.0.0"; features = { }; src = ""; links = null; procMacro = false; dependencies = [ { name = "semver"; package = "semver"; default_features = true; features = [ ]; kind = "build"; optional = false; req = "1"; target = null; source = "registry+https://github.com/rust-lang/crates.io-index"; } ]; }; in assertEq info expected; }; } ================================================ FILE: lib/resolve.nix ================================================ { lib, self }: let inherit (builtins) readFile match fromTOML toJSON; inherit (lib) foldl' concatStringsSep listToAttrs filter elemAt length optional sort elem flatten hasPrefix substring attrValues mapAttrs attrNames filterAttrs composeManyExtensions assertMsg; inherit (self.semver) parseSemverReq; inherit (self.pkg-info) mkPkgInfoFromCargoToml toPkgId sanitizeDep; in rec { # Resolve the dependencies graph based on the lock file. # Output: # { # "libz-sys 0.1.0 (https://...)" = { # # name, sha256, ... All fields from pkg info. # dependencies = [ # { # # name, kind, ... All fields from dependency in the pkg info. # resolved = "libc 0.1.0 (https://...)"; # }; # }; # }; # } # # Currently (rust 1.63.0), there are 3 versions of the lock file. # We supports V1, V2 and V3. # See: # https://github.com/rust-lang/cargo/blob/rust-1.63.0/src/cargo/core/resolver/resolve.rs#L56 # https://github.com/rust-lang/cargo/blob/rust-1.63.0/src/cargo/core/resolver/encode.rs resolveDepsFromLock = getPkgInfo: lock: let # For git sources, they are referenced without the locked hash part after `#`. # Define: "git+https://github.com/dtolnay/semver?tag=1.0.4#ea9ea80c023ba3913b9ab0af1d983f137b4110a5" # Reference: "semver 1.0.4 (git+https://github.com/dtolnay/semver?tag=1.0.4)" removeUrlHash = s: let m = match "([^#]*)#.*" s; in if m == null then s else elemAt m 0; pkgs = map (pkg: if pkg ? source then pkg // { source = removeUrlHash pkg.source; } else pkg) lock.package; pkgsByName = foldl' (set: { name, ... }@pkg: set // { ${name} = (set.${name} or []) ++ [ pkg ]; } ) {} pkgs; resolved = listToAttrs (map resolvePkg pkgs); resolvePkg = { name, version, source ? "", dependencies ? [], ... }@args: let info = getPkgInfo args; candidates = map findPkgId dependencies; id = toPkgId args; resolvedDependencies = map (dep: dep // { resolved = selectDep candidates dep; }) info.dependencies; # Find the exact package id of a dependency key, which may omit version or source. findPkgId = key: let m = match "([^ ]+)( ([^ ]+))?( \\(([^\\)]*)\\))?" key; lockName = elemAt m 0; lockVersion = elemAt m 2; lockSource = elemAt m 4; candidates = filter (pkg: (lockVersion != null -> pkg.version == lockVersion) && (lockSource != null -> pkg.source or null == lockSource)) (pkgsByName.${lockName} or []); candidateCnt = length candidates; in if candidateCnt == 0 then throw "When resolving ${id}, locked dependency `${key}` not found" else if candidateCnt > 1 then throw '' When resolving ${id}, locked dependency `${key}` is ambiguous. Found: ${toJSON candidates} '' else elemAt candidates 0; selectDep = candidates: { name, package, req, source ? null, ... }: let # Local path or git dependencies don't have version req. checkReq = if req != null then parseSemverReq req else (ver: true); checkSource = if source != null then s: s == source else s: true; selected = filter ({ name, version, source ? null, ... }: name == package && checkReq version && checkSource source) candidates; selectedCnt = length selected; in if selectedCnt == 0 then # Cargo will omit disabled optional dependencies in lock file. # throw "When resolving ${pkgName} ${crateVersion}, dependency ${package} ${req} isn't satisfied in lock file" null else if selectedCnt > 1 then throw '' When resolving ${id}, dependency ${package} ${if req == null then "*" else req} has multiple candidates in lock file. Found: ${toJSON selected} '' else toPkgId (elemAt selected 0); in { name = toPkgId args; value = info // { dependencies = resolvedDependencies; }; }; in assert assertMsg (lock.version or 3 == 3) "Unsupported version of Cargo.lock: ${toString lock.version}"; resolved; # Calculate the closure of each feature, with `dep:pkg` and `pkg?/feat` syntax desugared. # [String] -> { [String] } -> { [String | { dep: String, feat?: String }] } preprocessFeatures = optionalDeps: defs: let allRefs = flatten (attrValues defs); defs' = listToAttrs (map (dep: { name = dep; value = [ "dep:${dep}" ]; }) (filter (dep: !elem "dep:${dep}" allRefs) optionalDeps)) // defs; go = prev: feat: let m = match "([a-zA-Z0-9]+)(\\?)?/([a-zA-Z0-9]+)" feat; depName = elemAt m 0; isWeak = elemAt m 1 != null; depFeat = elemAt m 2; in if elem feat prev then prev else if defs' ? ${feat} then foldl' go ([ feat ] ++ prev) defs'.${feat} else if hasPrefix "dep:" feat then [ { dep = substring 4 (-1) feat; } ] ++ prev else if m == null then [ feat ] ++ prev else if isWeak then [ { dep = depName; feat = depFeat; } ] ++ prev else [ { dep = depName; } { dep = depName; feat = depFeat; } ] ++ prev; fixed = mapAttrs (feat: _: go [ ] feat) defs'; in fixed; # Enable `features` in `prev` and do recursive update according to `defs`. # Optional dependencies must be included in `defs`. enableFeatures = pkgId: defs: prev: features: foldl' (prev: feat: let m = match "(.*)/.*" feat; mDep = elemAt m 0; nexts = if m == null then # Must be defined. defs.${feat} or (throw '' Feature '${feat}' is invalid for ${pkgId}. Possible features: ${concatStringsSep "," (attrNames defs)} '') else # Dependent features implies optional dependency to be enabled. # But non-optional dependency doesn't have coresponding feature flag. optional (defs ? ${mDep}) mDep; in if prev.${feat} or false then prev else enableFeatures pkgId defs (prev // { ${feat} = true; }) nexts ) prev features; # Resolve all features. # Note that dependent features like `foo/bar` are only available during resolution, # and will be removed in result set. # # Returns: # { # "libc 0.1.0 (https://...)" = [ "default" "foo" "bar" ]; # } resolveFeatures = { # Follows the layout of the output of `resolveDepsFromLock`. pkgSet # Dependency edges (`{ name, kind, resolved, ... }`) will be checked by this filter. # Only edges returning `true` are considered and propagated. , depFilter ? dep: true # Eg. "libc 0.1.0 (https://...)" , rootId # Eg. [ "foo" "bar/baz" ] , rootFeatures }: let featureDefs = mapAttrs (id: { features, dependencies, ... }: features // listToAttrs (map (dep: { name = dep.name; value = []; }) # We should collect all optional dependencies for feature def, even though they are not selected. # This happens on `rand@0.8.3`, whose `default` feature enables `rand_hc`, which is only available # for `cfg(target_os = "emscripten")`. This feature should be still enable, though optional dependency # is not. (filter (dep: dep.optional) dependencies)) ) pkgSet; # initialFeatures = mapAttrs (id: defs: mapAttrs (k: v: false) defs) featureDefs; initialFeatures = mapAttrs (id: info: {}) pkgSet; # Overlay of spreading 's nested features into dependencies and enable optional dependencies. updateDepsOverlay = id: final: prev: let info = pkgSet.${id}; finalFeatures = final.${id} or {}; updateDep = { name, optional, resolved, default_features, features, ... }: final: prev: let depFeatures = lib.optional (default_features && featureDefs.${resolved} ? default) "default" ++ features ++ filter (feat: feat != null) (map (feat: let m = match "(.*)/(.*)" feat; in if m != null && elemAt m 0 == name then elemAt m 1 else null ) (attrNames finalFeatures)); in { ${resolved} = # This condition must be evaluated under `${resolved} =`, # or we'll enter an infinite recursion. if optional -> finalFeatures.${name} or false then enableFeatures resolved featureDefs.${resolved} prev.${resolved} depFeatures else prev.${resolved}; }; in composeManyExtensions (map updateDep (filter depFilter info.dependencies)) final prev; rootOverlay = final: prev: { ${rootId} = enableFeatures rootId featureDefs.${rootId} initialFeatures.${rootId} rootFeatures; }; final = composeManyExtensions (map updateDepsOverlay (attrNames pkgSet) ++ [ rootOverlay ]) final initialFeatures; final' = mapAttrs (id: feats: filter (feat: match ".*/.*" feat == null) (attrNames feats)) final; in final'; preprocess-feature-tests = { assertEq, ... }: let test = optionalDeps: featureDefs: expect: assertEq (preprocessFeatures optionalDeps featureDefs) expect; in { recursive = test [ ] { a = [ "b" ]; b = [ "a" "c" ]; c = [ ]; } { a = [ "c" "b" "a" ]; b = [ "c" "a" "b" ]; c = [ "c" ]; }; auto-dep = test [ "a" ] { b = [ "a" ]; } { a = [ { dep = "a"; } "a" ]; b = [ { dep = "a"; } "a" "b" ]; }; manual-dep = test [ "a" ] { b = [ "dep:a" ]; } { b = [ { dep = "a"; } "b" ]; }; strong-dep = test [ "a" ] { b = [ "a/c" ]; } { a = [ { dep = "a"; } "a" ]; b = [ { dep = "a"; } { dep = "a"; feat = "c"; } "b" ]; }; weak-dep = test [ "a" ] { b = [ "a?/c" ]; } { a = [ { dep = "a"; } "a" ]; b = [ { dep = "a"; feat = "c"; } "b" ]; }; }; update-feature-tests = { assertEq, ... }: let testUpdate = defs: features: expect: let init = mapAttrs (k: v: false) defs; out = enableFeatures "pkgId" defs init features; enabled = attrNames (filterAttrs (k: v: v) out); in assertEq enabled expect; in { simple1 = testUpdate { a = []; } [] []; simple2 = testUpdate { a = []; } [ "a" ] [ "a" ]; simple3 = testUpdate { a = []; } [ "a" "a" ] [ "a" ]; simple4 = testUpdate { a = []; b = []; } [ "a" ] [ "a" ]; simple5 = testUpdate { a = []; b = []; } [ "a" "b" ] [ "a" "b" ]; simple6 = testUpdate { a = []; b = []; } [ "a" "b" "a" ] [ "a" "b" ]; } // (let defs = { a = []; b = [ "a" ]; }; in { link1 = testUpdate defs [ "a" ] [ "a" ]; link2 = testUpdate defs [ "b" "a" ] [ "a" "b" ]; link3 = testUpdate defs [ "b" ] [ "a" "b" ]; link4 = testUpdate defs [ "b" "a" ] [ "a" "b" ]; link5 = testUpdate defs [ "b" "b" ] [ "a" "b" ]; }) // (let defs = { a = []; b = [ "a" ]; c = [ "a" ]; }; in { common1 = testUpdate defs [ "a" ] [ "a" ]; common2 = testUpdate defs [ "b" ] [ "a" "b" ]; common3 = testUpdate defs [ "a" "b" ] [ "a" "b" ]; common4 = testUpdate defs [ "b" "a" ] [ "a" "b" ]; common5 = testUpdate defs [ "b" "c" ] [ "a" "b" "c" ]; common6 = testUpdate defs [ "a" "b" "c" ] [ "a" "b" "c" ]; common7 = testUpdate defs [ "b" "c" "b" ] [ "a" "b" "c" ]; }) // (let defs = { a = [ "b" "c" ]; b = [ "d" "e" ]; c = [ "f" "g"]; d = []; e = []; f = []; g = []; }; in { tree1 = testUpdate defs [ "a" ] [ "a" "b" "c" "d" "e" "f" "g" ]; tree2 = testUpdate defs [ "b" ] [ "b" "d" "e" ]; tree3 = testUpdate defs [ "d" ] [ "d" ]; tree4 = testUpdate defs [ "d" "b" "g" ] [ "b" "d" "e" "g" ]; tree5 = testUpdate defs [ "c" "e" "f" ] [ "c" "e" "f" "g" ]; }) // (let defs = { a = [ "b" ]; b = [ "c" ]; c = [ "b" ]; }; in { cycle1 = testUpdate defs [ "b" ] [ "b" "c" ]; cycle2 = testUpdate defs [ "c" ] [ "b" "c" ]; cycle3 = testUpdate defs [ "a" ] [ "a" "b" "c" ]; }); resolve-feature-tests = { assertEq, ... }: let test = pkgSet: rootId: rootFeatures: expect: let resolved = resolveFeatures { inherit pkgSet rootId rootFeatures; }; expect' = mapAttrs (id: feats: sort (a: b: a < b) feats) expect; in assertEq resolved expect'; pkgSet1 = { a = { features = { foo = [ "bar" ]; bar = []; baz = [ "b" ]; }; dependencies = [ { name = "b"; resolved = "b-id"; optional = true; default_features = true; features = [ "a" ]; } { name = "unused"; resolved = null; optional = true; default_features = true; features = []; } ]; }; b-id = { features = { default = []; foo = []; bar = [ "foo" ]; a = []; }; dependencies = []; }; }; pkgSet2 = { my-id = { features = { default = [ "tokio/macros" ]; }; dependencies = [ { name = "tokio"; resolved = "tokio-id"; optional = false; default_features = false; features = [ "fs" ]; } { name = "dep"; resolved = "dep-id"; optional = false; default_features = true; features = []; } ]; }; dep-id = { features = { default = [ "tokio/sync" ]; }; dependencies = [ { name = "tokio"; resolved = "tokio-id"; optional = false; default_features = false; features = [ "sync" ]; } ]; }; tokio-id = { features = { default = []; fs = []; sync = []; macros = []; io = []; }; dependencies = []; }; }; in { simple = test pkgSet1 "a" [ "foo" ] { a = [ "foo" "bar" ]; b-id = [ ]; }; depend = test pkgSet1 "a" [ "foo" "baz" ] { a = [ "foo" "bar" "baz" "b" ]; b-id = [ "default" "a" ]; }; override = test pkgSet1 "a" [ "b/bar" ] { a = [ "b" ]; b-id = [ "default" "a" "bar" "foo" ]; }; merge = test pkgSet2 "my-id" [ "default" ] { my-id = [ "default" ]; dep-id = [ "default" ]; tokio-id = [ "fs" "sync" "macros" ]; }; }; resolve-deps-tests = { assertEq, defaultRegistries, ... }: { simple = let index = { libc."0.1.12" = { name = "libc"; version = "0.1.12"; dependencies = []; }; libc."0.2.95" = { name = "libc"; version = "0.2.95"; dependencies = []; }; testt."0.1.0" = { name = "testt"; version = "0.1.0"; dependencies = map sanitizeDep [ { name = "libc"; req = "^0.1.0"; kind = "normal"; } { name = "liba"; package = "libc"; req = "^0.2.0"; kind = "normal"; } ]; }; }; lock = { package = [ { name = "libc"; version = "0.1.12"; source = "registry+https://github.com/rust-lang/crates.io-index"; } { name = "libc"; version = "0.2.95"; source = "registry+https://github.com/rust-lang/crates.io-index"; } { name = "testt"; version = "0.1.0"; dependencies = [ "libc 0.1.12" "libc 0.2.95 (registry+https://github.com/rust-lang/crates.io-index)" ]; } ]; }; expected = { "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = { name = "libc"; version = "0.1.12"; dependencies = [ ]; }; "libc 0.2.95 (registry+https://github.com/rust-lang/crates.io-index)" = { name = "libc"; version = "0.2.95"; dependencies = [ ]; }; "testt" = { name = "testt"; version = "0.1.0"; dependencies = [ { name = "libc"; package = "libc"; req = "^0.1.0"; resolved = "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)"; kind = "normal"; optional = false; features = []; default_features = true; target = null; } { name = "liba"; rename = "liba"; package = "libc"; req = "^0.2.0"; resolved = "libc 0.2.95 (registry+https://github.com/rust-lang/crates.io-index)"; kind = "normal"; optional = false; features = []; default_features = true; target = null; } ]; }; }; getPkgInfo = { name, version, ... }: index.${name}.${version}; resolved = resolveDepsFromLock getPkgInfo lock; in assertEq resolved expected; workspace-virtual = let lock = fromTOML (readFile ../tests/workspace-virtual/Cargo.lock); cargoTomlFoo = fromTOML (readFile ../tests/workspace-virtual/crates/foo/Cargo.toml); cargoTomlBar = fromTOML (readFile ../tests/workspace-virtual/crates/bar/Cargo.toml); infoFoo = mkPkgInfoFromCargoToml cargoTomlFoo ""; infoBar = mkPkgInfoFromCargoToml cargoTomlBar ""; getCrateInfo = args: if args ? source then throw "No crates.io dependency" else if args.name == "foo" then infoFoo else if args.name == "bar" then infoBar else throw "Unknow crate: ${toJSON args}"; resolved = resolveDepsFromLock getCrateInfo lock; in assertEq resolved { bar = { dependencies = []; features = {}; links = null; name = "bar"; procMacro = false; src = ""; version = "0.1.0"; }; foo = { dependencies = [ { default_features = true; features = []; kind = "normal"; name = "bar"; optional = false; package = "bar"; req = null; resolved = "bar"; source = null; target = null; } ]; features = {}; links = null; name = "foo"; procMacro = false; src = ""; version = "0.1.0"; }; }; }; } ================================================ FILE: lib/semver.nix ================================================ { lib, ... }: let inherit (builtins) match elemAt fromJSON; inherit (lib) compare compareLists splitString all any; in rec { parseSemver = ver: let m = match "([0-9]+)\\.([0-9]+)\\.([0-9]+)(-([A-Za-z0-9.-]+))?(\\+[A-Za-z0-9.-]+)?" ver; in if m == null then throw "Invalid semver: `${ver}`" else { maj = fromJSON (elemAt m 0); min = fromJSON (elemAt m 1); pat = fromJSON (elemAt m 2); pre = elemAt m 4; }; compareSemver = a: b: let m1 = parseSemver a; m2 = parseSemver b; in if m1.maj != m2.maj then if m1.maj < m2.maj then -1 else 1 else if m1.min != m2.min then if m1.min < m2.min then -1 else 1 else if m1.pat != m2.pat then if m1.pat < m2.pat then -1 else 1 else comparePre m1.pre m2.pre; comparePre = a: b: if a == null then if b == null then 0 else 1 else if b == null then -1 else comparePreList (splitString "." a) (splitString "." b); isNumber = s: match "[0-9]+" s != null; comparePreList = compareLists (a: b: let num1 = if isNumber a then fromJSON a else null; num2 = if isNumber b then fromJSON b else null; in if num1 != null then if num2 != null then compare num1 num2 else -1 else if num2 != null then 1 else compare a b ); parseSemverReq = req: let reqs = splitString "," req; comparators = map parseComparators reqs; in ver: all (f: f ver) comparators && (isPreVersion ver -> any (containsExactPreVersion ver) reqs); isPreVersion = ver: match "[0-9.]+-.*" ver != null; containsExactPreVersion = ver: req: let m = parseSemver ver; in match " *(=|<|<=|>|>=|~|\\^)? *${toString m.maj}\\.${toString m.min}\\.${toString m.pat}-.*" req != null; opEq = { compMaj, compMin, compPat, compPre }: { maj, min, pat, pre }: maj == compMaj && (compMin == null || min == compMin) && (compPat == null || pat == compPat) && (compPre == null || comparePre pre compPre == 0); opLtGt = op: { compMaj, compMin, compPat, compPre }: { maj, min, pat, pre }: if maj != compMaj then op maj compMaj else if compMin == null then false else if min != compMin then op min compMin else if compPat == null then false else if pat != compPat then op pat compPat else op (comparePre pre compPre) 0; opTilde = { compMaj, compMin, compPat, compPre }: { maj, min, pat, pre }: maj == compMaj && (compMin == null || min == compMin) && (compPat == null || pat > compPat || (pat == compPat && comparePre pre compPre >= 0)); opCaret = { compMaj, compMin, compPat, compPre }: { maj, min, pat, pre }: if maj != compMaj then false else if compMin == null then true else if compPat == null then if maj > 0 then min >= compMin else min == compMin else if maj > 0 then if min != compMin then min > compMin else if pat != compPat then pat > compPat else comparePre pre compPre >= 0 else if min > 0 then if min != compMin then false else if pat != compPat then pat > compPat else comparePre pre compPre >= 0 else if min != compMin || pat != compPat then false else comparePre pre compPre >= 0; parseComparators = req: let toInt = s: if s == null then null else fromJSON s; star = match " *(([0-9]+)\\.(([0-9]+)\\.)?)?\\*(\\.\\*)? *" req; star0 = elemAt star 1; star1 = elemAt star 3; comp = match " *(=|>|>=|<|<=|~|\\^)? *(([0-9]+)(\\.([0-9]+)(\\.([0-9]+)(-([A-Za-z0-9.-]+))?(\\+[A-Za-z0-9.-]*)?)?)?) *" req; compOp = elemAt comp 0; compVer = elemAt comp 1; compArgs = { compMaj = toInt (elemAt comp 2); compMin = toInt (elemAt comp 4); compPat = toInt (elemAt comp 6); compPre = elemAt comp 8; }; less = a: b: a < b; greater = a: b: a > b; op = { "=" = opEq compArgs; "<" = opLtGt less compArgs; "<=" = args: opLtGt less compArgs args || opEq compArgs args; ">" = opLtGt greater compArgs; ">=" = args: opLtGt greater compArgs args || opEq compArgs args; "~" = opTilde compArgs; "^" = opCaret compArgs; }.${if compOp != null then compOp else "^"}; in if star != null then if star0 == null then (ver: true) else if star1 == null then (ver: match "${toString star0}\\..*" ver != null) else (ver: match "${toString star0}\\.${toString star1}\\..*" ver != null) else if comp != null then (ver: op (parseSemver ver)) else throw "Invalid version comparator: `${req}`"; semver-compare-tests = { assertEq, ... }: { compare-simple1 = assertEq (compareSemver "1.2.3" "1.2.2") 1; compare-simple2 = assertEq (compareSemver "1.2.3" "1.2.3") 0; compare-simple3 = assertEq (compareSemver "1.2.3" "1.2.4") (-1); compare-simple4 = assertEq (compareSemver "1.2.3" "1.1.3") 1; compare-simple5 = assertEq (compareSemver "1.2.3" "1.3.3") (-1); compare-simple6 = assertEq (compareSemver "1.2.3" "0.2.3") 1; compare-simple7 = assertEq (compareSemver "1.2.3" "2.2.3") (-1); }; # From https://github.com/dtolnay/semver/blob/a03d376560e0c4d16518bc271867b1981c85acf0/tests/test_version_req.rs semver-req-tests = { assertEq, ... }: let testMatchReq = req: { yes ? [], no ? [] }: let inherit (lib) const; checker = parseSemverReq req; got = map checker (yes ++ no); expect = map (const true) yes ++ map (const false) no; in assertEq got expect; in { eq1 = testMatchReq "=1.0.0" { yes = [ "1.0.0" ]; no = [ "1.0.1" "0.9.9" "0.10.0" "0.1.0" "1.0.0-pre" ]; }; default = testMatchReq "^1.0.0" { yes = [ "1.0.0" "1.1.0" "1.0.1" ]; no = [ "0.9.9" "0.10.0" "0.1.0" "1.0.0-pre" "1.0.1-pre" ]; }; exact1 = testMatchReq "=1.0.0" { yes = [ "1.0.0" ]; no = [ "1.0.1" "0.9.9" "0.10.0" "0.1.0" "1.0.0-pre" ]; }; exact2 = testMatchReq "=0.9.0" { yes = [ "0.9.0" ]; no = [ "0.9.1" "1.9.0" "0.0.9" "0.9.0-pre" ]; }; exact3 = testMatchReq "=0.0.2" { yes = [ "0.0.2" ]; no = [ "0.0.1" "0.0.3" "0.0.2-pre" ]; }; exact4 = testMatchReq "=0.1.0-beta2.a" { yes = [ "0.1.0-beta2.a" ]; no = [ "0.9.1" "0.1.0" "0.1.1-beta2.a" "0.1.0-beta2" ]; }; exact5 = testMatchReq "=0.1.0" { yes = [ "0.1.0" "0.1.0+meta" "0.1.0+any" ]; }; gt1 = testMatchReq ">= 1.0.0" { yes = [ "1.0.0" "2.0.0" ]; no = [ "0.1.0" "0.0.1" "1.0.0-pre" "2.0.0-pre" ]; }; gt2 = testMatchReq ">=2.1.0-alpha2" { yes = [ "2.1.0-alpha2" "2.1.0-alpha3" "2.1.0" "3.0.0" ]; no = [ "2.0.0" "2.1.0-alpha1" "2.0.0-alpha2" "3.0.0-alpha2" ]; }; lt1 = testMatchReq "<1.0.0" { yes = [ "0.1.0" "0.0.1" ]; no = [ "1.0.0" "1.0.0-beta" "1.0.1" "0.9.9-alpha" ]; }; le1 = testMatchReq "<= 2.1.0-alpha2" { yes = [ "2.1.0-alpha2" "2.1.0-alpha1" "2.0.0" "1.0.0" ]; no = [ "2.1.0" "2.2.0-alpha1" "2.0.0-alpha2" "1.0.0-alpha2" ]; }; multi1 = testMatchReq ">1.0.0-alpha, <1.0.0" { yes = [ "1.0.0-beta" ]; }; multi2 = testMatchReq ">1.0.0-alpha, <1.0" { no = [ "1.0.0-beta" ]; }; multi3 = testMatchReq ">1.0.0-alpha, <1" { no = [ "1.0.0-beta" ]; }; multi4 = testMatchReq "> 0.0.9, <= 2.5.3" { yes = [ "0.0.10" "1.0.0" "2.5.3" ]; no = [ "0.0.8" "2.5.4" ]; }; multi5 = testMatchReq "0.3.0, 0.4.0" { no = [ "0.0.8" "0.3.0" "0.4.0" ]; }; multi6 = testMatchReq "<= 0.2.0, >= 0.5.0" { no = [ "0.0.8" "0.3.0" "0.5.1" ]; }; multi7 = testMatchReq "^0.1.0, ^0.1.4, ^0.1.6" { yes = [ "0.1.6" "0.1.9" ]; no = [ "0.1.0" "0.1.4" "0.2.0" ]; }; multi8 = testMatchReq ">=0.5.1-alpha3, <0.6" { yes = [ "0.5.1-alpha3" "0.5.1-alpha4" "0.5.1-beta" "0.5.1" "0.5.5" ]; no = [ "0.5.1-alpha1" "0.5.2-alpha3" "0.5.5-pre" "0.5.0-pre" "0.6.0" "0.6.0-pre" ]; }; tilde1 = testMatchReq "~1" { yes = [ "1.0.0" "1.0.1" "1.1.1" ]; no = [ "0.9.1" "2.9.0" "0.0.9" ]; }; tilde2 = testMatchReq "~1.2" { yes = [ "1.2.0" "1.2.1" ]; no = [ "1.1.1" "1.3.0" "0.0.9" ]; }; tilde3 = testMatchReq "~1.2.2" { yes = [ "1.2.2" "1.2.4" ]; no = [ "1.2.1" "1.9.0" "1.0.9" "2.0.1" "0.1.3" ]; }; tilde4 = testMatchReq "~1.2.3-beta.2" { yes = [ "1.2.3" "1.2.4" "1.2.3-beta.2" "1.2.3-beta.4" ]; no = [ "1.3.3" "1.1.4" "1.2.3-beta.1" "1.2.4-beta.2" ]; }; caret1 = testMatchReq "^1" { yes = [ "1.1.2" "1.1.0" "1.2.1" "1.0.1" ]; no = [ "0.9.1" "2.9.0" "0.1.4" "1.0.0-beta1" "0.1.0-alpha" "1.0.1-pre" ]; }; caret2 = testMatchReq "^1.1" { yes = [ "1.1.2" "1.1.0" "1.2.1" ]; no = [ "0.9.1" "2.9.0" "1.0.1" "0.1.4" ]; }; caret3 = testMatchReq "^1.1.2" { yes = [ "1.1.2" "1.1.4" "1.2.1" ]; no = [ "0.9.1" "2.9.0" "1.1.1" "0.0.1" "1.1.2-alpha1" "1.1.3-alpha1" "2.9.0-alpha1" ]; }; caret4 = testMatchReq "^0.1.2" { yes = [ "0.1.2" "0.1.4" ]; no = [ "0.9.1" "2.9.0" "1.1.1" "0.0.1" "0.1.2-beta" "0.1.3-alpha" "0.2.0-pre" ]; }; caret5 = testMatchReq "^0.5.1-alpha3" { yes = [ "0.5.1-alpha3" "0.5.1-alpha4" "0.5.1-beta" "0.5.1" "0.5.5" ]; no = [ "0.5.1-alpha1" "0.5.2-alpha3" "0.5.5-pre" "0.5.0-pre" "0.6.0" ]; }; caret6 = testMatchReq "^0.0.2" { yes = [ "0.0.2" ]; no = [ "0.9.1" "2.9.0" "1.1.1" "0.0.1" "0.1.4" ]; }; caret7 = testMatchReq "^0.0" { yes = [ "0.0.2" "0.0.0" ]; no = [ "0.9.1" "2.9.0" "1.1.1" "0.1.4" ]; }; caret8 = testMatchReq "^0" { yes = [ "0.9.1" "0.0.2" "0.0.0" ]; no = [ "2.9.0" "1.1.1" ]; }; caret9 = testMatchReq "^1.4.2-beta.5" { yes = [ "1.4.2" "1.4.3" "1.4.2-beta.5" "1.4.2-beta.6" "1.4.2-c" ]; no = [ "0.9.9" "2.0.0" "1.4.2-alpha" "1.4.2-beta.4" "1.4.3-beta.5" ]; }; star1 = testMatchReq "*" { yes = [ "0.9.1" "2.9.0" "0.0.9" "1.0.1" "1.1.1" ]; }; star2 = testMatchReq "1.*" { yes = [ "1.2.0" "1.2.1" "1.1.1" "1.3.0" ]; no = [ "0.0.9" ]; }; star3 = testMatchReq "1.2.*" { yes = [ "1.2.0" "1.2.2" "1.2.4" ]; no = [ "1.9.0" "1.0.9" "2.0.1" "0.1.3" ]; }; pre = testMatchReq "=2.1.1-really.0" { yes = [ "2.1.1-really.0" ]; }; }; } ================================================ FILE: lib/support.nix ================================================ { lib, self }: let inherit (builtins) fromTOML toJSON match tryEval split; inherit (lib) readFile mapAttrs mapAttrs' makeOverridable warnIf isString hasPrefix filter flatten elem elemAt listToAttrs subtractLists concatStringsSep attrNames attrValues recursiveUpdate optionalAttrs; inherit (self.pkg-info) mkPkgInfoFromCargoToml getPkgInfoFromIndex toPkgId; inherit (self.resolve) resolveDepsFromLock resolveFeatures; inherit (self.target-cfg) platformToCfgs evalTargetCfgStr; inherit (self.glob) globMatchDir; in rec { # https://doc.rust-lang.org/cargo/reference/profiles.html#default-profiles defaultProfiles = rec { dev = { name = "dev"; build-override = defaultBuildProfile; opt-level = 0; debug = true; debug-assertions = true; overflow-checks = true; lto = false; panic = "unwind"; codegen-units = 256; rpath = false; }; release = { name = "release"; build-override = defaultBuildProfile; opt-level = 3; debug = false; debug-assertions = false; overflow-checks = false; lto = false; panic = "unwind"; codegen-units = 16; rpath = false; }; test = dev // { name = "test"; }; bench = release // { name = "bench"; }; }; defaultBuildProfile = { opt-level = 0; codegen-units = 256; }; profilesFromManifest = manifest: let knownFields = [ "name" "inherits" # "package" # Unsupported yet. "build-override" "opt-level" "debug" # split-debug-info # Unsupported. "strip" "debug-assertions" "overflow-checks" "lto" "panic" # incremental # Unsupported. "codegen-units" "rpath" ]; profiles = mapAttrs (name: p: let unknown = removeAttrs p knownFields; in warnIf (unknown != {}) "Unsupported fields of profile ${name}: ${toString (attrNames unknown)}" (optionalAttrs (p ? inherits) profiles.${p.inherits} // p) ) (recursiveUpdate defaultProfiles (manifest.profile or {})); in profiles; mkRustPackageOrWorkspace = { defaultRegistries, pkgsBuildHost, buildRustCrate, stdenv }@default: { src # : Path , gitSrcs ? {} # : Attrset Path , buildCrateOverrides ? {} # : Attrset (Attrset _) , extraRegistries ? {} # : Attrset Registry , registries ? defaultRegistries // extraRegistries , rustc ? pkgsBuildHost.rustc , stdenv ? default.stdenv }: let manifest = fromTOML (readFile (src + "/Cargo.toml")); profiles = profilesFromManifest manifest; selected = flatten (map (glob: globMatchDir glob src) manifest.workspace.members); excluded = map sanitizeRelativePath (manifest.workspace.exclude or []); members = subtractLists excluded selected; lock = fromTOML (readFile (src + "/Cargo.lock")); # We don't distinguish between v1 and v2. But v3 is different from both. lockVersionSet = { lockVersion = lock.version or 2; }; localSrcInfos = listToAttrs (map (relativePath: let memberRoot = src + ("/" + relativePath); memberManifest = fromTOML (readFile (memberRoot + "/Cargo.toml")) // lockVersionSet; in { name = toPkgId memberManifest.package; value = mkPkgInfoFromCargoToml memberManifest memberRoot; } ) (if manifest ? workspace then members else [ "" ])); in mkRustPackageSet { gitSrcInfos = mapAttrs (url: src: mkPkgInfoFromCargoToml (fromTOML (readFile (src + "/Cargo.toml")) // lockVersionSet) src ) gitSrcs; inherit lock profiles localSrcInfos buildRustCrate buildCrateOverrides registries rustc stdenv; }; # -> { = { = ; }; } mkRustPackageSet = { lock # : , localSrcInfos # : Attrset PkgInfo , gitSrcInfos # : Attrset PkgInfo , profiles # : Attrset Profile , buildCrateOverrides # : Attrset (Attrset _) , buildRustCrate # : Attrset -> Derivation , registries # : Attrset Registry # FIXME: Cross compilation. , rustc , stdenv }: let getPkgInfo = { source ? null, name, version, ... }@args: let m = match "(registry|git)\\+([^#]*).*" source; kind = elemAt m 0; url = elemAt m 1; in # Local crates have no `source`. if source == null then localSrcInfos.${toPkgId args} or (throw "Local crate is outside the workspace: ${toPkgId args}") // { isLocalPkg = true; } else if m == null then throw "Invalid source: ${source}" else if kind == "registry" then getPkgInfoFromIndex (registries.${url} or (throw "Registry `${url}` not found. Please define it in `extraRegistries`.")) args // { inherit source; } # `source` is for crate id, which is used for overrides. else if kind == "git" then gitSrcInfos.${url} or (throw "Git source `${url}` not found. Please define it in `gitSrcs`.") else throw "Invalid source: ${source}"; hostCfgs = platformToCfgs stdenv.hostPlatform; pkgSetRaw = resolveDepsFromLock getPkgInfo lock; pkgSet = mapAttrs (id: info: info // { dependencies = map (dep: dep // { targetEnabled = dep.target != null -> evalTargetCfgStr hostCfgs dep.target; }) info.dependencies; }) pkgSetRaw; selectDeps = pkgs: deps: features: selectKind: onlyLinks: map (dep: { rename = dep.rename or null; drv = pkgs.${dep.resolved}; }) (filter ({ kind, name, optional, targetEnabled, resolved, ... }@dep: targetEnabled && kind == selectKind && (optional -> elem name features) && (if resolved == null then throw "Unresolved dependency: ${toJSON dep}" else true) && (onlyLinks -> pkgSet.${resolved}.links != null)) deps); buildRustCrate' = info: args: let # TODO: Proc macro crates should behave differently in dependency resolution. # But this override is applied just before the `buildRustCrate` call. args' = args // (info.__override or lib.id) args; args'' = args' // (buildCrateOverrides.${toPkgId info} or lib.id) args'; in buildRustCrate args''; mkPkg = profile: rootId: makeOverridable ( { features }: let rootFeatures = if features != null then features else if pkgSet.${rootId}.features ? default then [ "default" ] else []; resolvedBuildFeatures = resolveFeatures { inherit pkgSet rootId rootFeatures; depFilter = dep: dep.targetEnabled && dep.kind == "normal" || dep.kind == "build"; }; resolvedNormalFeatures = resolveFeatures { inherit pkgSet rootId rootFeatures; depFilter = dep: dep.targetEnabled && dep.kind == "normal"; }; pkgsBuild = mapAttrs (id: features: let info = pkgSet.${id}; in if features != null then buildRustCrate' info { inherit (info) version src procMacro; inherit features profile rustc; pname = info.name; capLints = if localSrcInfos ? id then null else "allow"; buildDependencies = selectDeps pkgsBuild info.dependencies features "build" false; # Build dependency's normal dependency is still build dependency. dependencies = selectDeps pkgsBuild info.dependencies features "normal" false; linksDependencies = selectDeps pkgsBuild info.dependencies features "normal" true; } else null ) resolvedBuildFeatures; pkgs = mapAttrs (id: features: let info = pkgSet.${id}; in if features != null then buildRustCrate' info { inherit (info) version src links procMacro; inherit features profile rustc; pname = info.name; capLints = if localSrcInfos ? id then null else "allow"; buildDependencies = selectDeps pkgsBuild info.dependencies features "build" false; dependencies = selectDeps pkgs info.dependencies features "normal" false; linksDependencies = selectDeps pkgs info.dependencies features "normal" true; } else null ) resolvedNormalFeatures; in pkgs.${rootId} ) { features = null; }; in mapAttrs (_: profile: mapAttrs' (pkgId: pkgInfo: { name = pkgInfo.name; value = mkPkg profile pkgId; }) localSrcInfos ) profiles; sanitizeRelativePath = path: if hasPrefix "/" path then throw "Absolute path is not allowed: ${path}" else concatStringsSep "/" (filter (s: isString s && s != "" && s != "." && (if match ''.*[[?*].*|\.\.'' s != null then throw '' Globing and `..` are not allowed: ${path} '' else true)) (split ''[\/]'' path)); build-from-src-dry-tests = { assertEq, pkgs, defaultRegistries, ... }: let inherit (builtins) head listToAttrs; mkPackage = pkgs.callPackage mkRustPackageOrWorkspace { inherit defaultRegistries; buildRustCrate = args: args; }; build = src: args: head (attrValues (mkPackage ({ inherit src; } // args)).dev); in { features = let ret = build ../tests/features {}; in assertEq ret.features [ "a" "default" "semver" ]; # FIXME dependency-features = let ret = build ../tests/features { }; semver = (head ret.dependencies).drv; serde = (head semver.dependencies).drv; in assertEq [ semver.features serde.features ] [ [ "default" "serde" "std" ] [ /* Don't trigger default features */ ] ]; dependency-overrided = let ret = build ../tests/features { buildCrateOverrides."" = old: { a = "b"; }; buildCrateOverrides."serde 1.0.139 (registry+https://github.com/rust-lang/crates.io-index)" = old: { buildInputs = [ "some-inputs" ]; }; }; semver = (head ret.dependencies).drv; serde = (head semver.dependencies).drv; in assertEq serde.buildInputs [ "some-inputs" ]; dependency-kinds = let mkSrc = from: { __toString = _: ../tests/fake-semver; inherit from; }; gitSrcs = { "https://github.com/dtolnay/semver?tag=1.0.0" = mkSrc "tag"; "http://github.com/dtolnay/semver" = mkSrc "branch"; # v1, v2 "http://github.com/dtolnay/semver?branch=master" = mkSrc "branch"; # v3 "ssh://git@github.com/dtolnay/semver?rev=a2ce5777dcd455246e4650e36dde8e2e96fcb3fd" = mkSrc "rev"; "ssh://git@github.com/dtolnay/semver" = mkSrc "nothing"; }; extraRegistries = { "https://www.github.com/rust-lang/crates.io-index" = head (attrValues defaultRegistries); }; ret = build ../tests/dependency-v3 { inherit gitSrcs extraRegistries; }; ret' = listToAttrs (map (dep: { name = if dep.rename != null then dep.rename else dep.drv.pname; value = dep.drv.src.from or dep.drv.src.name; }) ret.dependencies); in assertEq ret' { cratesio = "crate-semver-1.0.12.tar.gz"; git_branch = "branch"; git_head = "nothing"; git_rev = "rev"; git_tag = "tag"; registry_index = "crate-semver-1.0.12.tar.gz"; }; libz-propagated = let ret = build ../tests/libz-dynamic {}; libz = (head ret.dependencies).drv; in assertEq (head libz.propagatedBuildInputs).pname "zlib"; libz-link = let ret = build ../tests/libz-dynamic {}; libz = (head ret.dependencies).drv; libz' = (head ret.linksDependencies).drv; in assertEq [ libz.links libz'.links ] [ "z" "z" ]; }; sanitize-relative-path-tests = { assertEq, ... }: let assertOk = raw: expect: assertEq (tryEval (sanitizeRelativePath raw)) { success = true; value = expect; }; assertInvalid = raw: assertEq (tryEval (sanitizeRelativePath raw)) { success = false; value = false; }; in { empty = assertOk "" ""; simple1 = assertOk "foo" "foo"; simple2 = assertOk "foo/bar" "foo/bar"; dot1 = assertOk "." ""; dot2 = assertOk "./././" ""; dot3 = assertOk "./foo/./bar/" "foo/bar"; dotdot1 = assertInvalid ".."; dotdot2 = assertInvalid "./foo/.."; dotdot3 = assertInvalid "../bar"; root1 = assertInvalid "/"; root2 = assertInvalid "/foo"; }; } ================================================ FILE: lib/target-cfg.nix ================================================ { lib, ... }: let inherit (builtins) match tryEval; inherit (lib) concatStrings length elem elemAt any all sort flatten isList optionalAttrs mapAttrsToList; in rec { # https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch platformToTargetArch = platform: if platform.isAarch32 then "arm" else platform.parsed.cpu.name; # https://doc.rust-lang.org/reference/conditional-compilation.html#target_os platformToTargetOs = platform: if platform.isDarwin then "macos" else platform.parsed.kernel.name; # https://github.com/rust-lang/rust/blob/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/compiler/rustc_session/src/config.rs#L835 # https://doc.rust-lang.org/reference/conditional-compilation.html platformToCfgAttrs = platform: { # Arch info. # https://github.com/NixOS/nixpkgs/blob/c63d4270feed5eb6c578fe2d9398d3f6f2f96811/pkgs/build-support/rust/build-rust-crate/configure-crate.nix#L126 target_arch = platformToTargetArch platform; target_endian = if platform.isLittleEndian then "little" else if platform.isBigEndian then "big" else throw "Unknow target_endian for ${platform.config}"; target_env = if platform.isNone then "" else if platform.libc == "glibc" then "gnu" else if platform.isMusl then "musl" else if platform.isDarwin then "" # Empty else lib.trace platform (throw "Unknow target_env for ${platform.config}"); target_family = if platform.isUnix then "unix" else if platform.isWindows then "windows" else null; target_os = platformToTargetOs platform; target_pointer_width = toString platform.parsed.cpu.bits; target_vendor = platform.parsed.vendor.name; } // optionalAttrs platform.isx86 { # These features are assume to be available. target_feature = [ "fxsr" "sse" "sse2" ]; } // optionalAttrs platform.isUnix { unix = true; } // optionalAttrs platform.isWindows { windows = true; }; platformToCfgs = platform: flatten ( mapAttrsToList (key: value: if value == true then { inherit key; } else if isList value then map (value: { inherit key value; }) value else { inherit key value; } ) (platformToCfgAttrs platform)); # cfgs: [ # { key = "atom1"; } # { key = "atom2"; } # { key = "feature"; value = "foo"; } # { key = "feature"; value = "bar"; } # ] evalTargetCfgStr = cfgs: s: evalCfgExpr cfgs (parseTargetCfgExpr s); # Cargo's parse is stricter than rustc's. # - Must starts with `cfg(` and ends with `)`. No spaces are allowed before and after. # - Identifiers must follows /[A-Za-z_][A-Za-z_0-9]*/. # - Raw identifiers, raw strings, escapes in strings are not allowed. # # The target can also be a simple target name like `aarch64-unknown-linux-gnu`, which will be parsed # as if it's `cfg(target = "...")`. # # https://github.com/rust-lang/cargo/blob/dcc95871605785c2c1f2279a25c6d3740301c468/crates/cargo-platform/src/cfg.rs parseTargetCfgExpr = cfg: let fail = reason: throw "${reason}, when parsing `${cfg}"; go = { fn, values, afterComma, prev }@stack: s: let m = match ''((all|any|not) *\( *|(\)) *|(,) *|([A-Za-z_][A-Za-z_0-9]*) *(= *"([^"]*)" *)?)(.*)'' s; mFn = elemAt m 1; mClose = elemAt m 2; mComma = elemAt m 3; mIdent = elemAt m 4; mString = elemAt m 6; mRest = elemAt m 7; in if s == "" then stack else if m == null then fail "No parse `${s}`" # else if builtins.trace ([ stack m ]) (mFn != null) then else if mFn != null then if !afterComma then fail "Missing comma before `${mFn}` at `${s}" else go { fn = mFn; values = []; afterComma = true; prev = stack; } mRest else if mClose != null then if prev == null then fail "Unexpected `)` at `${s}`" else if fn == "not" && length values == 0 then fail "`not` must have exact one argument, got 0" else if prev.fn == "not" && length prev.values != 0 then fail "`not` must have exact one argument, got at least 2" else go (prev // { values = prev.values ++ [ { inherit (stack) fn values; } ]; afterComma = false; }) mRest else if mComma != null then if afterComma then fail "Unexpected `,` at `${s}`" else go (stack // { afterComma = true; }) mRest else if !afterComma then fail "Missing comma before identifier `${mIdent}` at `${s}" else if fn == "not" && length values != 0 then fail "`not` must have exact one argument, got at least 2" else let kv = if mString != null then { key = mIdent; value = mString; } else { key = mIdent; }; in go (stack // { afterComma = false; values = values ++ [ kv ]; }) mRest; mSimpleTarget = match "[A-Za-z_0-9_.-]+" cfg; mCfg = match ''cfg\( *(.*)\)'' cfg; mCfgInner = elemAt mCfg 0; ret = go { fn = "cfg"; values = []; afterComma = true; prev = null; } mCfgInner; in if mSimpleTarget != null then { key = "target"; value = cfg; } else if mCfg == null then fail "Cfg expr must be a simple target string, or start with `cfg(` and end with `)`" else if ret.prev != null then fail "Missing `)`" else if length ret.values != 1 then fail "`cfg` must have exact one argument, got ${toString (length ret.values)}" else elemAt ret.values 0; evalCfgExpr = cfgs: tree: if !(tree ? fn) then elem tree cfgs else if tree.fn == "all" then all (evalCfgExpr cfgs) tree.values else if tree.fn == "any" then any (evalCfgExpr cfgs) tree.values else !evalCfgExpr cfgs (elemAt tree.values 0); cfg-parser-tests = { assertEq, ... }: let shouldParse = cfg: expect: assertEq (tryEval (parseTargetCfgExpr cfg)) { success = true; value = expect; }; shouldNotParse = cfg: assertEq (tryEval (parseTargetCfgExpr cfg)) { success = false; value = false; }; in { simple-target1 = shouldParse "thumbv8m.base-none-eabi" { key = "target"; value = "thumbv8m.base-none-eabi"; }; simple-target2 = shouldParse "aarch64-unknown-linux-gnu" { key = "target"; value = "aarch64-unknown-linux-gnu"; }; simple1 = shouldParse "cfg(atom)" { key = "atom"; }; simple2 = shouldParse ''cfg(k = "v")'' { key = "k"; value = "v"; }; complex = shouldParse ''cfg( all ( not ( a , ) , b , all ( ) , any ( c , d = "e" ) , ) )'' { fn = "all"; values = [ { fn = "not"; values = [ { key = "a"; } ]; } { key = "b"; } { fn = "all"; values = []; } { fn = "any"; values = [ { key = "c"; } { key = "d"; value = "e"; } ]; } ]; }; invalid-cfg1 = shouldNotParse "cfg (a)"; invalid-cfg2 = shouldNotParse "cfg()"; invalid-cfg3 = shouldNotParse "cfg(a,b)"; invalid-not1 = shouldNotParse "cfg(not(a,b))"; invalid-not2 = shouldNotParse "cfg(not())"; invalid-comma1 = shouldNotParse "cfg(all(,))"; invalid-comma2 = shouldNotParse "cfg(all(a,,b))"; invalid-comma3 = shouldNotParse "cfg(all(a,b,,))"; invalid-comma4 = shouldNotParse "cfg(all(a b))"; invalid-comma5 = shouldNotParse "cfg(all(any() any()))"; invalid-paren1 = shouldNotParse "cfg(all(a)))"; invalid-paren2 = shouldNotParse "cfg(all(a)"; }; cfg-eval-tests = { assertEq, ... }: let cfgs = [ { key = "foo"; } { key = "bar"; } { key = "feature"; value = "foo"; } { key = "feature"; value = "bar"; } ]; test = cfg: expect: assertEq (evalTargetCfgStr cfgs cfg) expect; in { simple1 = test ''cfg(foo)'' true; simple2 = test ''cfg(baz)'' false; simple3 = test ''cfg(feature = "foo")'' true; simple4 = test ''cfg(foo = "")'' false; simple5 = test ''cfg(wtf = "foo")'' false; all1 = test ''cfg(all())'' true; all2 = test ''cfg(all(foo))'' true; all3 = test ''cfg(all(baz))'' false; all4 = test ''cfg(all(foo,bar))'' true; all5 = test ''cfg(all(foo,bar,baz))'' false; all6 = test ''cfg(all(foo,baz,bar))'' false; all7 = test ''cfg(all(baz,foo))'' false; all8 = test ''cfg(all(baz,feature="foo"))'' false; all9 = test ''cfg(all(baz,feature="wtf"))'' false; all10 = test ''cfg(all(foo,feature="foo"))'' true; any1 = test ''cfg(any())'' false; any2 = test ''cfg(any(foo))'' true; any3 = test ''cfg(any(baz))'' false; any4 = test ''cfg(any(foo,bar))'' true; any5 = test ''cfg(any(foo,bar,baz))'' true; any6 = test ''cfg(any(foo,baz,bar))'' true; any7 = test ''cfg(any(baz,foo))'' true; any8 = test ''cfg(any(baz,feature="foo"))'' true; any9 = test ''cfg(any(baz,feature="wtf"))'' false; any10 = test ''cfg(any(foo,feature="wtf"))'' true; not1 = test ''cfg(not(foo))'' false; not2 = test ''cfg(not(wtf))'' true; }; platform-cfg-tests = { assertEq, ... }: let inherit (lib.systems) elaborate; test = config: expect: let cfgs = platformToCfgs (elaborate config); strs = map ({ key, value ? null }: if value != null then "${key}=\"${value}\"\n" else "${key}\n" ) cfgs; got = concatStrings (sort (a: b: a < b) strs); in assertEq got expect; in { attrs-x86_64-linux = assertEq (platformToCfgAttrs (elaborate "x86_64-unknown-linux-gnu")) { target_arch = "x86_64"; target_endian = "little"; target_env = "gnu"; target_family = "unix"; target_feature = ["fxsr" "sse" "sse2"]; target_os = "linux"; target_pointer_width = "64"; target_vendor = "unknown"; unix = true; }; cfg-x86_64-linux = test "x86_64-unknown-linux-gnu" '' target_arch="x86_64" target_endian="little" target_env="gnu" target_family="unix" target_feature="fxsr" target_feature="sse" target_feature="sse2" target_os="linux" target_pointer_width="64" target_vendor="unknown" unix ''; cfg-aarch64-linux = test "aarch64-unknown-linux-gnu" '' target_arch="aarch64" target_endian="little" target_env="gnu" target_family="unix" target_os="linux" target_pointer_width="64" target_vendor="unknown" unix ''; }; } ================================================ FILE: noc/Cargo.toml ================================================ [package] name = "nocargo" version = "0.0.0" edition = "2021" license = "MIT" description = "Helper program for github.com/oxalica/nocargo" repository = "https://github.com/oxalica/nocargo" [[bin]] name = "noc" path = "src/main.rs" [dependencies] anyhow = "1.0.40" askama = { version = "0.11.1", default-features = false } cargo_toml = "0.11.5" clap = { version = "3.2.8", features = ["derive"] } glob = "0.3.0" once_cell = "1.12.0" regex = "1.5.4" toml = "0.5.9" [dev-dependencies] expect-test = "1.3.0" ================================================ FILE: noc/src/init.rs ================================================ use std::collections::{BTreeMap, HashSet}; use std::fs::{read_dir, read_to_string, File}; use std::io::Write; use std::path::{Path, PathBuf}; use anyhow::{bail, ensure, Context, Result}; use askama::Template; use cargo_toml::{Dependency, Manifest, Product}; use glob::glob; use once_cell::sync::Lazy; use regex::Regex; /// Create or print template `flake.nix` for your rust crate. #[derive(clap::Args)] pub struct Args { /// Print the content of initial `flake.nix` to stdout rather than to `flake.nix` in /// the current directory. #[clap(long, short)] print: bool, /// Force overwrite `flake.nix` even if it exists. #[clap(long, short, conflicts_with = "print")] force: bool, /// The Rust project root directory, where the root `Cargo.toml` lies in, /// either a project or a workspace. /// Default to be the current directory. #[clap(long)] root: Option, } impl super::App for Args { fn run(self) -> Result<()> { let root = self .root .as_deref() .unwrap_or_else(|| Path::new(".")) .canonicalize() .context("Failed locate the current directory")?; // Always at CWD. let out_path = Path::new("flake.nix"); // Fail fast. ensure!( self.print || self.force || !out_path.exists(), "flake.nix already exists. Use `--force` to overwrite or `--print` to print to stdout only", ); let manifest = Manifest::from_path(root.join("Cargo.toml")).context("Failed to load Cargo.toml")?; // Check ancestor manifest files for (maybe) workspace definition. if manifest.workspace.is_none() && self.root.is_none() { if let Some(parent_manifest) = root .ancestors() .skip(1) .map(|p| p.join("Cargo.toml")) .find(|p| p.exists()) { bail!( "Are we in a workspace? Found ancestor manifest at {}\n\ Please run `init` in the *workspace root* directory. \n\ If you are sure the current directory is the root, use `--root=.`", parent_manifest.display(), ); } } let lock_path = root.join("Cargo.lock"); ensure!( lock_path.exists(), "Cargo.lock does not exist at {}\n\ We doesn't support lock generation currently. Please run `cargo update` first. ", lock_path.display(), ); let lock_version = (|| -> Result<_> { let content = read_to_string(&lock_path)?; let lock = toml::from_str::(&content)?; Ok(lock .get("version") .and_then(|v| v.as_integer()) .unwrap_or(2)) })() .context("Parse Cargo.lock")?; match lock_version { // v1 or v2. 2 => { eprintln!( "warning: Cargo.lock is generated by cargo < 1.53.0.\n\ The old lock format is supported but encodes git URLs in a different way,\n\ which results in different `gitSrcs` arguments when calling `mkRustPackageOrWorkspace`.\n\ We recommand to regenerate the lock via `cargo update` with cargo >= 1.53.0 and retry.", ) } 3 => {} _ => eprintln!("warning: Unsupported version of Cargo.lock, building may fail"), } let out = generate_flake(&root, &manifest, lock_version)?; if self.print { println!("{}", out); } else { (|| { let mut f = File::options() .write(true) .create(true) .truncate(self.force) .open(out_path)?; f.write_all(out.as_bytes())?; f.flush() })() .with_context(|| format!("Failed write to {:?}", out_path))?; } Ok(()) } } fn generate_flake(root: &Path, manifest: &Manifest, lock_version: i64) -> Result { ensure!(manifest.patch.is_empty(), "[patch] is not supported yet"); let is_workspace = manifest.workspace.is_some(); let mut templ = FlakeTemplate { is_workspace, main_pkg: None, registries: Default::default(), git_srcs: Default::default(), }; if let Some(pkg) = &manifest.package { templ.main_pkg = Some(( pkg.name.clone(), Products::from_path_manifest(root, manifest)?, )); } match &manifest.workspace { Some(ws) => { ensure!( !ws.members.is_empty(), "[workspace] without explicit `members` declarations are not supported yet", ); let member_roots = get_workspace_members(root, &ws.members)?; let absolute_member_roots = member_roots .iter() .map(|p| p.canonicalize()) .collect::, _>>()?; ensure!( member_roots.len() == absolute_member_roots.len(), "Duplicated workspace members" ); let member_manifests = member_roots .iter() .map(|root| { let manifest_path = root.join("Cargo.toml"); let manifest = Manifest::from_path(&manifest_path).with_context(|| { format!( "Failed to load member Cargo.toml at {}", manifest_path.display() ) })?; Ok(manifest) }) .collect::>>()?; let root_pkg = manifest .package .is_some() .then(|| (Path::new("."), manifest)); for (member_root, member_manifest) in member_roots .iter() .map(|p| &**p) .zip(&member_manifests) .chain(root_pkg) { for (dep_name, dep) in get_all_dependencies(member_manifest) { templ .check_dependency(dep, lock_version, |local_path| { let local_dep_root = member_root.join(local_path).canonicalize()?; ensure!( absolute_member_roots.contains(&local_dep_root), "Local dependency not in workspace: {}", local_path.display(), ); Ok(()) }) .with_context(|| { format!( "In dependency {:?} of workspace member {:?}", dep_name, member_root.display(), ) })?; } } } None => { for (dep_name, dep) in get_all_dependencies(manifest) { templ .check_dependency(dep, lock_version, |local_path| { bail!( "Local dependency is not supported for non-workspace: {}", local_path.display(), ) }) .with_context(|| format!("In dependency {:?}", dep_name))?; } } } // The trailing newline is suppressed by default. Add it back. Ok(templ.render().unwrap() + "\n") } #[derive(Template)] #[template(path = "../templates/init-flake.nix", escape = "none")] struct FlakeTemplate { is_workspace: bool, main_pkg: Option<(String, Products)>, // source_id -> flake_ref registries: BTreeMap, // source_id -> flake_ref git_srcs: BTreeMap, } mod filters { pub fn nix_escape(s: &str) -> askama::Result { Ok(s.replace('\\', "\\\\").replace('"', "\\\"")) } pub fn ident_or_str(s: &str) -> askama::Result { const KEYWORDS: &[&str] = &[ "if", "then", "else", "assert", "with", "let", "in", "rec", "inherit", "or", ]; let is_ident_start = |c: char| c.is_ascii_alphabetic() || c == '_'; let is_ident_char = |c: char| c.is_ascii_alphanumeric() || c == '_' || c == '-' || c == '\''; if s.starts_with(is_ident_start) && s.chars().all(is_ident_char) && !KEYWORDS.contains(&s) { Ok(s.into()) } else { Ok(s.replace('\\', "\\\\").replace('"', "\\\"")) } } } impl FlakeTemplate { fn check_dependency( &mut self, dep: &Dependency, lock_version: i64, mut on_local_dep: impl FnMut(&Path) -> Result<()>, ) -> Result<()> { match DepSource::try_from(dep)? { // Automatically handled by nocargo. DepSource::CratesIo => {} DepSource::RegistryName { name } => { bail!("External registry with name {:?} is not supported", name) } DepSource::Path { path } => { on_local_dep(path)?; } DepSource::RegistryUrl { url } => { let flake_ref = git_url_to_flake_ref(url, None, None)?; self.registries.insert(url.into(), flake_ref); } DepSource::Git { url, ref_ } => { let source_url = match ref_ { GitRef::Tag(tag) => format!("{}?tag={}", url, tag), GitRef::Branch(branch) if lock_version >= 3 => { format!("{}?branch={}", url, branch) } GitRef::Rev(rev) => format!("{}?rev={}", url, rev), GitRef::NotSpecified | GitRef::Branch(_) => url.to_owned(), }; let (ref_name, rev) = match ref_ { GitRef::Tag(ref_name) | GitRef::Branch(ref_name) => (Some(ref_name), None), GitRef::NotSpecified => (None, None), GitRef::Rev(rev) => (None, Some(rev)), }; let flake_ref = git_url_to_flake_ref(url, ref_name, rev)?; self.git_srcs.insert(source_url, flake_ref); } } Ok(()) } } fn get_workspace_members(root: &Path, members: &[impl AsRef]) -> Result> { let mut ret = Vec::new(); for member in members { let pat = root.join(member.as_ref()); let pat = pat .to_str() .with_context(|| format!("Non UTF-8 path is not supported: {}", pat.display()))?; for path in glob(pat)? { ret.push(path?); } } Ok(ret) } fn get_all_dependencies(manifest: &Manifest) -> impl Iterator { manifest .dependencies .iter() .chain(&manifest.dev_dependencies) .chain(&manifest.build_dependencies) .chain(manifest.target.values().flat_map(|tgt| { tgt.dependencies .iter() .chain(&tgt.dev_dependencies) .chain(&tgt.build_dependencies) })) .map(|(name, dep)| (&**name, dep)) } #[allow(dead_code)] #[derive(Debug, Clone)] struct Products { library: bool, binary: bool, bench: bool, test: bool, example: bool, } impl Products { // https://github.com/rust-lang/cargo/blob/rust-1.63.0/src/cargo/util/toml/targets.rs#L3-L8 fn from_path_manifest(path: &Path, manifest: &Manifest) -> Result { let pkg = manifest.package.as_ref().context("Missing [product]")?; let has_product = |decls: &[Product], allow_discover: bool, extra_path: Option<&str>, convention_dir: &str| -> Result { if !decls.is_empty() { return Ok(true); } if allow_discover { return Ok(false); } if matches!(extra_path, Some(p) if Path::new(p).is_file()) { return Ok(true); } for ent in read_dir(path.join(convention_dir))? { let ent = ent?; let file_type = ent.file_type()?; // `/*.rs` if file_type.is_file() && Path::new(&ent.file_name()) .extension() .map_or(false, |ext| ext == "rs") { return Ok(true); } // `/*/main.rs` if file_type.is_dir() && ent.path().join("main.rs").is_file() { return Ok(true); } } Ok(false) }; Ok(Self { library: manifest.lib.is_some() || path.join("src/lib.rs").exists(), binary: has_product(&manifest.bin, pkg.autobins, Some("src/main.rs"), "src/bin")?, bench: has_product(&manifest.bin, pkg.autobenches, None, "benches")?, test: has_product(&manifest.test, pkg.autotests, None, "tests")?, example: has_product(&manifest.example, pkg.autoexamples, None, "examples")?, }) } } // https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html #[derive(Debug, Clone, Copy)] enum DepSource<'a> { CratesIo, RegistryName { name: &'a str }, RegistryUrl { url: &'a str }, Path { path: &'a Path }, Git { url: &'a str, ref_: GitRef<'a> }, } #[derive(Debug, Clone, Copy)] enum GitRef<'a> { NotSpecified, Tag(&'a str), Branch(&'a str), Rev(&'a str), } impl<'a> TryFrom<&'a Dependency> for DepSource<'a> { type Error = anyhow::Error; fn try_from(dep: &'a Dependency) -> Result { match dep { Dependency::Simple(_) => Ok(Self::CratesIo), Dependency::Detailed(detail) => { match (&detail.registry, &detail.registry_index, &detail.path, &detail.git) { (None, None, None, None) => Ok(Self::CratesIo), (Some(name), None, None, None) => Ok(Self::RegistryName { name }), (None, Some(url), None, None) => Ok(Self::RegistryUrl { url }), (None, None, Some(path), None) => Ok(Self::Path { path: Path::new(path), }), (None, None, None, Some(url)) => { let ref_ = match (&detail.branch, &detail.tag, &detail.rev) { (None, None, None) => GitRef::NotSpecified, (Some(b), None, None) => GitRef::Branch(b), (None, Some(t), None) => GitRef::Tag(t), (None, None, Some(r)) => GitRef::Rev(r), _ => bail!("For git dependency, at most one of `branch`, `rev` and `tag` is allowed"), }; Ok(Self::Git { url, ref_ }) } _ => bail!( "Only one of `registry`, `registry-index`, `path`, `git` can be specified: {:?}", dep, ), } } } } } // https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake.html?#flake-inputs pub fn git_url_to_flake_ref( url_orig: &str, ref_name: Option<&str>, rev: Option<&str>, ) -> Result { let url = url_orig.strip_prefix("git+").unwrap_or(url_orig); ensure!( url.starts_with("http://") || url.starts_with("https://") || url.starts_with("ssh://") || url.starts_with("git://"), "Only http/https/ssh/git schemas are supported for git url, got: {}", url_orig, ); static RE_GITHUB_URL: Lazy = Lazy::new(|| Regex::new(r"^https?://github.com/([^/?#]+)/([^/?#]+?)(.git)?/?$").unwrap()); if let Some(cap) = RE_GITHUB_URL.captures(url) { let owner = cap.get(1).unwrap().as_str(); let repo = cap.get(2).unwrap().as_str(); return Ok(match rev.or(ref_name) { Some(rev) => format!("github:{}/{}/{}", owner, repo, rev), None => format!("github:{}/{}", owner, repo), }); } ensure!( !url.contains(|c| c == '?' || c == '#'), "Url containing `?` or `#` is not supported yet: {}", url_orig, ); let prefix = if url.starts_with("git://") { "" } else { "git+" }; let ret = match (ref_name, rev) { (_, Some(rev)) => format!("{}{}?rev={}", prefix, url, rev), (Some(ref_name), None) => format!("{}{}?ref={}", prefix, url, ref_name), (None, None) => format!("{}{}", prefix, url), }; Ok(ret) } #[cfg(test)] mod tests { use super::git_url_to_flake_ref as f; #[test] fn test_flake_url_schemas() { assert_eq!( f("https://example.com", Some("dev"), Some("123")).unwrap(), "git+https://example.com?rev=123" ); assert_eq!( f("https://example.com", None, Some("123")).unwrap(), "git+https://example.com?rev=123" ); assert_eq!( f("https://example.com", Some("dev"), None).unwrap(), "git+https://example.com?ref=dev" ); assert_eq!( f("https://example.com", None, None).unwrap(), "git+https://example.com" ); assert_eq!( f("http://example.com", None, None).unwrap(), "git+http://example.com" ); assert_eq!( f("git+https://example.com", None, None).unwrap(), "git+https://example.com" ); assert_eq!( f("git://example.com", None, None).unwrap(), "git://example.com" ); assert_eq!( f("git+git://example.com", None, None).unwrap(), "git://example.com" ); assert_eq!( f("git+ssh://git@github.com/foo/bar", None, None).unwrap(), "git+ssh://git@github.com/foo/bar" ); f("ws://example.com", None, None).unwrap_err(); } #[test] fn test_flake_url_github() { assert_eq!( f("https://github.com/foo/bar", Some("dev"), Some("123")).unwrap(), "github:foo/bar/123" ); assert_eq!( f("https://github.com/foo/bar", None, Some("123")).unwrap(), "github:foo/bar/123" ); assert_eq!( f("https://github.com/foo/bar", Some("dev"), None).unwrap(), "github:foo/bar/dev" ); assert_eq!( f("https://github.com/foo/bar", None, None).unwrap(), "github:foo/bar" ); assert_eq!( f("https://github.com/foo/bar.git", None, None).unwrap(), "github:foo/bar" ); assert_eq!( f("https://github.com/foo/bar/", None, None).unwrap(), "github:foo/bar" ); assert_eq!( f("http://github.com/foo/bar.git", None, None).unwrap(), "github:foo/bar" ); assert_eq!( f("git+https://github.com/foo/bar.git", None, None).unwrap(), "github:foo/bar" ); } } ================================================ FILE: noc/src/main.rs ================================================ use anyhow::Result; use clap::Parser; mod init; trait App { fn run(self) -> Result<()>; } #[derive(Parser)] #[clap(version, about, long_about = None)] enum Args { Init(init::Args), } impl App for Args { fn run(self) -> Result<()> { match self { Self::Init(args) => args.run(), } } } fn main() -> Result<()> { Args::from_args().run() } ================================================ FILE: noc/templates/init-flake.nix ================================================ # See more usages of nocargo at https://github.com/oxalica/nocargo#readme { {%- if let Some((pkg_name, _)) = main_pkg %} description = "Rust package {{ pkg_name|nix_escape }}"; {%- else %} description = "My Rust packages"; {%- endif %} inputs = { nixpkgs.url = "github:NixOS/nixpkgs"; flake-utils.url = "github:numtide/flake-utils"; nocargo = { url = "github:oxalica/nocargo"; inputs.nixpkgs.follows = "nixpkgs"; # inputs.registry-crates-io.follows = "registry-crates-io"; }; # Optionally, you can override crates.io index to get cutting-edge packages. # registry-crates-io = { url = "github:rust-lang/crates.io-index"; flake = false; }; {%- for (_, flake_ref) in registries %} registry-{{ loop.index }} = { url = "{{ flake_ref|nix_escape }}"; flake = false; }; {%- endfor %} {%- for (_, flake_ref) in git_srcs %} git-{{ loop.index }} = { url = "{{ flake_ref|nix_escape }}"; flake = false; }; {%- endfor %} }; outputs = { nixpkgs, flake-utils, nocargo, ... }@inputs: flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" ] (system: let ws = nocargo.lib.${system}.mkRustPackageOrWorkspace { src = ./.; {%- if !registries.is_empty() %} # Referenced external registries other than crates.io. extraRegistries = { {%- for (source_id, _) in registries %} "{{ source_id|nix_escape }}" = nocargo.lib.${system}.mkIndex inputs.registry-{{ loop.index }} {}; {%- endfor %} }; {%- endif %} {%- if !git_srcs.is_empty() %} # Referenced external rust packages from git. gitSrcs = { {%- for (source_id, _) in git_srcs %} "{{ source_id|nix_escape }}" = inputs.git-{{ loop.index }}; {%- endfor %} }; {%- endif %} }; in rec { {%- if is_workspace %} packages = {% if let Some((pkg_name, prod)) = main_pkg %}{ default = packages.{{ pkg_name|ident_or_str }}{% if prod.binary %}.bin{% endif %}; } // {% endif %}ws.release // nixpkgs.lib.mapAttrs' (name: value: { name = "${name}-dev"; inherit value; }) ws.dev; {%- else if let Some((pkg_name, prod)) = main_pkg %} packages = { default = packages.{{ pkg_name|ident_or_str }}; {{ pkg_name|ident_or_str }} = ws.release.{{ pkg_name|ident_or_str }}{% if prod.binary %}.bin{% endif %}; {{ pkg_name|ident_or_str }}-dev = ws.dev.{{ pkg_name|ident_or_str }}{% if prod.binary %}.bin{% endif %}; }; {%- endif %} }); } ================================================ FILE: scripts/cratesio-utils.py ================================================ #!/usr/bin/env nix-shell #!nix-shell -i python3 -p cargo "python3.withPackages (ps: with ps; [ aiohttp toml ])" from io import BytesIO from pathlib import Path from typing import Callable, Optional from typing_extensions import Self from urllib import request import aiohttp import argparse import asyncio import csv import os import re import sqlite3 import subprocess import sys import tarfile import toml # Check dependencies. subprocess.check_call(['cargo', '--version'], stdout=subprocess.DEVNULL) # CRATE_TARBALL_URL = 'https://crates.io/api/v1/crates/{name}/{version}/download' # -> 302 CRATE_TARBALL_URL = 'https://static.crates.io/crates/{name}/{name}-{version}.crate' # -> 200 application/gzip POPULAR_CRATES_MANIFEST_PATH = Path(__file__).parent.parent / 'cache' / 'Cargo.toml' PROC_MACRO_LIST_PATH = Path(__file__).parent.parent / 'crates-io-override' / 'proc-macro.nix' CACHE_DIR = Path(os.environ.get('XDG_CACHE_HOME') or (Path.home() / '.cache')) / 'cratesio' CRATES_TOML_DIR = Path(os.environ.get('CRATES_TOML_DIR') or (CACHE_DIR / 'toml')) def noisily(*args) -> None: print(*args, file=sys.stderr) class CratesioDB(sqlite3.Connection): DB_URL = 'https://static.crates.io/db-dump.tar.gz' DB_DIR = CACHE_DIR DB_PATH = DB_DIR / 'db.sqlite' MTIME_PATH = DB_DIR / 'mtime.txt' INIT_SQL = r''' PRAGMA journal_mode = off; PRAGMA cache_size = -{cache_kb}; CREATE TABLE IF NOT EXISTS crates ( id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, downloads INTEGER NOT NULL ) STRICT; CREATE TABLE IF NOT EXISTS versions ( id INTEGER NOT NULL PRIMARY KEY, crate_id INTEGER NOT NULL, num TEXT NOT NULL, updated_at TEXT NOT NULL, created_at TEXT NOT NULL, downloads INTEGER NOT NULL, features TEXT NOT NULL, yanked INTEGER NOT NULL, license TEXT ) STRICT; CREATE TABLE IF NOT EXISTS version_downloads ( version_id INTEGER NOT NULL, downloads INTEGER NOT NULL, date TEXT NOT NULL, PRIMARY KEY (version_id, date) ) STRICT, WITHOUT ROWID; ''' INIT_INDEX_SQL = r''' CREATE INDEX IF NOT EXISTS versions_ix_crate_to_version ON versions (crate_id, id); ANALYZE; ''' INSERTERS: dict[str, tuple[str, Callable[[dict[str, str]], tuple]]] = { 'crates.csv': ( 'INSERT INTO crates VALUES (?,?,?,?,?)', lambda row: ( int(row['id']), row['name'], row['created_at'], row['updated_at'], int(row['downloads']), ), ), 'versions.csv': ( 'INSERT INTO versions VALUES (?,?,?,?,?,?,?,?,?)', lambda row: ( int(row['id']), int(row['crate_id']), row['num'], row['updated_at'], row['created_at'], int(row['downloads']), row['features'], row['yanked'] == 't', row['license'], ), ), 'version_downloads.csv': ( 'INSERT INTO version_downloads VALUES (?,?,?)', lambda row: ( int(row['version_id']), int(row['downloads']), row['date'], ) ), } def __init__(self, *, check: bool=True, cache_mb: int=1024) -> None: assert not check or self.MTIME_PATH.exists(), 'Database not initialized, please run `sync` subcommand' super().__init__(str(self.DB_PATH)) self.executescript(self.INIT_SQL.format(cache_kb=cache_mb * 1024)) @classmethod def sync(cls, *, cache_mb: int=1024) -> None: cls.DB_DIR.mkdir(exist_ok=True) last_mtime = cls.MTIME_PATH.read_text() if cls.MTIME_PATH.exists() else None noisily('Synchronizing database') with request.urlopen(cls.DB_URL) as resp: assert resp.status == 200, f'HTTP failure {resp.status}' mtime: str = resp.headers['last-modified'] if mtime == last_mtime: noisily(f'Database is up-to-date at {mtime}') return noisily(f'Fetching and importing database dump at {mtime}, previous at {last_mtime}') cls.DB_PATH.unlink(missing_ok=True) db = CratesioDB(check=False, cache_mb=cache_mb) csv.field_size_limit(2 ** 30) # There are large fields. with tarfile.open(fileobj=resp, mode='r|gz') as tar: for member in tar: name = member.path.split('/')[-1] if name in cls.INSERTERS: sql, preprocessor = cls.INSERTERS[name] fileobj = tar.extractfile(member) assert fileobj is not None rdr = csv.DictReader(map(bytes.decode, fileobj)) db.executemany(sql, map(preprocessor, rdr)) noisily(f'Creating indices') db.executescript(cls.INIT_INDEX_SQL) db.commit() db.close() cls.MTIME_PATH.write_text(mtime) noisily('Database initialized') def update_popular_crates(db: CratesioDB, *, time: str, limit: int) -> None: class VersMajor(tuple): RE_SEMVER = re.compile(r'^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$') def __new__(cls: type[Self], s: str) -> Self: m = cls.RE_SEMVER.match(s) assert m is not None, f'Invalid semver: {s}' maj, min, pat = map(int, (m[1], m[2], m[3])) vers = (maj,) if maj else (maj, min) if min else (maj, min, pat) return super().__new__(cls, vers) def __str__(self) -> str: return '.'.join(map(str, self)) # Reversed. def __lt__(self, rhs: Self) -> bool: return super().__gt__(rhs) noisily('Querying database') cursor = db.cursor() cursor.execute(''' SELECT crates.name, versions.num FROM version_downloads JOIN versions ON versions.id == version_id JOIN crates ON crates.id == versions.crate_id WHERE version_downloads.date > DATE('now', ?) GROUP BY version_id ORDER BY SUM(version_downloads.downloads) DESC ''', (time,)) crates_set: set[tuple[str, VersMajor]] = set() for row in cursor: crates_set.add((str(row[0]), VersMajor(row[1]))) if len(crates_set) == limit: break crates = sorted(crates_set) noisily('======== Start of top crates') for name, vers in crates: noisily(name, vers) noisily('======== End of top crates') out_path = POPULAR_CRATES_MANIFEST_PATH tmp_path = out_path.with_suffix('.tmp') with out_path.open('r') as fin, tmp_path.open('w') as fout: for line in fin: fout.write(line) if line.startswith('[dependencies]'): break last_name: Optional[str] = None crate_idx = 1 for name, vers in crates: if last_name != name: crate_idx = 1 fout.write(f'{name} = "{vers}"\n') else: crate_idx += 1 fout.write(f'{name}-{crate_idx} = {{ package = "{name}", version = "{vers}" }}\n') last_name = name tmp_path.replace(out_path) noisily('Updating lock file') subprocess.check_call(['cargo', 'update', f'--manifest-path={out_path}']) noisily('Verifying crates metadata') subprocess.check_call( ['cargo', 'metadata', f'--manifest-path={out_path}', '--format-version=1'], stdout=subprocess.DEVNULL, ) def list_popular_crates(db: CratesioDB, *, time: str, pat: Optional[str]) -> None: noisily('Querying database') cursor = db.cursor() cursor.execute(''' SELECT crates.name, SUM(version_downloads.downloads) AS crate_downloads FROM crates JOIN versions ON versions.crate_id == crates.id JOIN version_downloads ON version_downloads.version_id == versions.id WHERE crates.name LIKE ? AND version_downloads.date > DATE('now', ?) GROUP BY versions.crate_id ORDER BY crate_downloads DESC ''', (pat if pat is not None else '%', time)) try: for row in cursor: print(row[0], row[1]) except BrokenPipeError as _: # Suppress backtrace. exit(1) def update_proc_macro_crates(db: CratesioDB, *, concurrency: int) -> None: CRATES_TOML_DIR.mkdir(exist_ok=True) noisily('Querying database') cursor = db.cursor() cursor.execute(''' SELECT crates.name, versions.num AS max_vers, MAX(versions.created_at) FROM versions JOIN crates ON crates.id == crate_id WHERE NOT yanked GROUP BY crate_id ORDER BY crates.name ASC ''') RE_ITEMS = re.compile(r'"([^"]*)"', re.S) proc_macro_crates: set[str] = set(m[1] for m in RE_ITEMS.finditer(PROC_MACRO_LIST_PATH.read_text())) async def load_or_fetch(sema: asyncio.Semaphore, name: str, version: str) -> None: try: out_path = CRATES_TOML_DIR / f'{name}.toml' if not out_path.exists(): noisily(f'GET {name} {version}') url = CRATE_TARBALL_URL.format(name=name, version=version) async with aiohttp.request(method='GET', url=url) as resp: resp.raise_for_status() data = await resp.read() with tarfile.open(mode='r|gz', fileobj=BytesIO(data)) as tar: for member in tar: segments = member.path.split('/') if len(segments) == 2 and segments[-1] == 'Cargo.toml': f = tar.extractfile(member) assert f is not None tmp_path = out_path.with_suffix('.tmp') tmp_path.write_bytes(f.read()) tmp_path.replace(out_path) break else: assert False, 'No Cargo.toml found' except (aiohttp.ClientError, tarfile.TarError, UnicodeDecodeError, AssertionError) as exc: print(f'For {name} {version}: {exc}', file=sys.stderr) return finally: sema.release() try: # Must exist if we are here. with open(out_path, 'r') as fin: manifest = toml.load(fin) lib = manifest.get('lib', {}) if isinstance(lib, dict) and lib.get('proc-macro', False) is True: proc_macro_crates.add(name) except (UnicodeDecodeError, toml.TomlDecodeError) as exc: print(f'For cached {name}: {exc}', file=sys.stderr) return async def proc() -> None: sema = asyncio.Semaphore(concurrency) for row in cursor: name: str = row[0] vers: str = row[1] await sema.acquire() asyncio.create_task(load_or_fetch(sema, name, vers)) # Wait until all done. for _ in range(concurrency): await sema.acquire() noisily('Retrieving metadata') asyncio.run(proc()) noisily(f'Writing to {PROC_MACRO_LIST_PATH}') with PROC_MACRO_LIST_PATH.open('w') as fout: fout.write('[\n') for name in sorted(proc_macro_crates): assert '"' not in name fout.write(f'"{name}"\n') fout.write(']\n') def main() -> None: parser = argparse.ArgumentParser(description='Metadata updater and utilities using crates.io database dump') parser.add_argument('--cache-mb', type=int, required=False, default=1024, help='Sqlite cache size in MiB') subparser = parser.add_subparsers(required=True) p = subparser.add_parser('sync', help='Synchronize or initialize the database') p.set_defaults(sync=True) p = subparser.add_parser('update-popular-crates', help='Update popular crates cache') p.add_argument('--limit', type=int, required=False, default=256, help='Number of top crates to cache') p.add_argument('--time', type=str, default='-90 days', help='Time period to count recent downloads') p.set_defaults(fn=update_popular_crates) p = subparser.add_parser('list-popular-crates', help='Print popular crates with download counts in a given period') p.add_argument('--time', type=str, default='-90 days', help='Time period to count recent downloads') p.add_argument('--pat', type=str, default=None, help='Crate name pattern to filter for SQL "LIKE"') p.set_defaults(fn=list_popular_crates) p = subparser.add_parser('update-proc-macro-crates', help='Update the list of proc-macro crates') p.add_argument('--concurrency', type=int, default=16, help='Connection concurrency') p.set_defaults(fn=update_proc_macro_crates) args = parser.parse_args() if 'sync' in args: CratesioDB.sync(cache_mb=args.cache_mb) else: subargs = vars(args) with CratesioDB(cache_mb=subargs.pop('cache_mb')) as db: subargs.pop('fn')(db, **subargs) main() ================================================ FILE: tests/build-deps/Cargo.toml ================================================ [package] name = "build-deps" version = "0.0.0" edition = "2015" [build-dependencies] semver = "1" ================================================ FILE: tests/build-deps/build.rs ================================================ fn main() { let s = semver::Version::new(1, 2, 3).to_string(); println!("cargo:rustc-cfg=result={:?}", s); } ================================================ FILE: tests/build-deps/src/main.rs ================================================ #[cfg(result = "1.2.3")] fn main() { println!("Hello, world!"); } ================================================ FILE: tests/build-feature-env-vars/Cargo.toml ================================================ [package] name = "build-feature-env-vars" version = "0.0.0" edition = "2015" [features] default = ["foo-bar"] foo-bar = [] quux = [] ================================================ FILE: tests/build-feature-env-vars/build.rs ================================================ use std::env::var; fn main() { if var("CARGO_FEATURE_QUUX").is_err() { if let Ok(s) = var("CARGO_FEATURE_FOO_BAR") { println!("cargo:rustc-cfg=result={:?}", s); } } } ================================================ FILE: tests/build-feature-env-vars/src/main.rs ================================================ #[cfg(result = "1")] fn main() { println!("Hello, world!"); } ================================================ FILE: tests/cap-lints/Cargo.toml ================================================ [package] name = "cap-lints" version = "0.0.0" edition = "2021" [dependencies] # error: use of deprecated associated function `try_lock::TryLock::::try_lock_order`: This method is actually unsafe because it unsafely allows the use of weaker memory ordering. Please use try_lock_explicit instead # --> src/lib.rs:209:63 # | # 209 | if let Some(mut locked) = self.inner.task.try_lock_order(SeqCst, SeqCst) { # | ^^^^^^^^^^^^^^ # | # note: the lint level is defined here # --> src/lib.rs:2:9 # | # 2 | #![deny(warnings)] # | ^^^^^^^^ # = note: `#[deny(deprecated)]` implied by `#[deny(warnings)]` # # https://github.com/seanmonstar/want/blob/v0.3.0/src/lib.rs#L352 want = "=0.3.0" ================================================ FILE: tests/cap-lints/src/main.rs ================================================ fn main() { let _ = want::new(); println!("Hello, world!"); } ================================================ FILE: tests/crate-names/Cargo.toml ================================================ [package] name = "crate-names" version = "0.0.0" edition = "2018" [dependencies] b = "=0.2.0" cc = "=1.0.73" fnv = "=1.0.7" RustyXML = "=0.3.0" # Should be lowered. ================================================ FILE: tests/crate-names/src/main.rs ================================================ use std::hash::Hasher; fn main() { assert_eq!(b::B, "🅱️"); let _ = cc::Build::new(); assert_eq!(fnv::FnvHasher::with_key(42).finish(), 42); assert_eq!(xml::escape("<"), "<"); println!("Hello, world!"); } ================================================ FILE: tests/custom-lib-name/Cargo.toml ================================================ [package] name = "custom-lib-name" version = "0.0.0" edition = "2018" [lib] name = "custom" [dependencies] color-rs = "=0.6.1" renamed = { package = "color-rs", version = "=0.5.0" } ================================================ FILE: tests/custom-lib-name/src/lib.rs ================================================ pub fn custom() -> i32 { 42 } ================================================ FILE: tests/custom-lib-name/src/main.rs ================================================ fn main() { assert_eq!(color::consts::PURPLE, color::Rgb::new(0x80, 0x00, 0x80)); assert_eq!(renamed::consts::PURPLE, renamed::Rgb::new(0x80, 0x00, 0x80)); assert_eq!(custom::custom(), 42); println!("Hello, world!"); } ================================================ FILE: tests/default.nix ================================================ { pkgs, self, inputs, defaultRegistries }: let inherit (pkgs.lib) mapAttrs attrNames attrValues assertMsg head mapAttrsToList; inherit (self.lib.${pkgs.system}) mkRustPackageOrWorkspace; inherit (self.packages.${pkgs.system}) noc; git-semver-1-0-0 = builtins.fetchTarball { url = "https://github.com/dtolnay/semver/archive/1.0.0/master.tar.gz"; sha256 = "0s7gwj5l0h98spgm7vyxak9z3hgrachwxbnf1fpry5diz939x8n4"; }; git-semver-1-0-12 = builtins.fetchTarball { url = "https://github.com/dtolnay/semver/archive/1.0.4/master.tar.gz"; sha256 = "1l2nkfmjgz2zkqw03hmy66q0v1rxvs7fc4kh63ph4lf1924wrmix"; }; gitSrcs = { "https://github.com/dtolnay/semver?tag=1.0.0" = git-semver-1-0-0; "http://github.com/dtolnay/semver" = git-semver-1-0-12; # v1, v2 "http://github.com/dtolnay/semver?branch=master" = git-semver-1-0-12; # v3 "ssh://git@github.com/dtolnay/semver?rev=a2ce5777dcd455246e4650e36dde8e2e96fcb3fd" = git-semver-1-0-0; "ssh://git@github.com/dtolnay/semver" = git-semver-1-0-12; }; extraRegistries = { "https://www.github.com/rust-lang/crates.io-index" = head (attrValues defaultRegistries); }; shouldBeHelloWorld = drv: pkgs.runCommand "${drv.name}" {} '' binaries=(${drv.bin}/bin/*) [[ ''${#binaries[@]} == 1 ]] got="$(''${binaries[0]})" expect="Hello, world!" echo "Got : $got" echo "Expect: $expect" [[ "$got" == "$expect" ]] touch $out ''; mkHelloWorldTest = src: let ws = mkRustPackageOrWorkspace { inherit src gitSrcs extraRegistries; }; profiles = mapAttrs (_: pkgs: shouldBeHelloWorld (head (attrValues pkgs))) ws; in { inherit (profiles) dev release; }; mkWorkspaceTest = src: expectMembers: let ws = mkRustPackageOrWorkspace { inherit src; }; gotMembers = attrNames ws.dev; in assert assertMsg (gotMembers == expectMembers) '' Member assertion failed. expect: ${toString expectMembers} got: ${toString gotMembers} ''; ws; # Recursive Nix setup. # https://github.com/NixOS/nixpkgs/blob/e966ab3965a656efdd40b6ae0d8cec6183972edc/pkgs/top-level/make-tarball.nix#L45-L48 mkGenInit = name: path: pkgs.runCommand "gen-${name}" { nativeBuildInputs = [ noc pkgs.nix ]; checkFlags = mapAttrsToList (from: to: "--override-input ${from} ${to}") { inherit (inputs) nixpkgs flake-utils; nocargo = self; "nocargo/registry-crates-io" = inputs.registry-crates-io; registry-1 = inputs.registry-crates-io; git-1 = git-semver-1-0-0; git-2 = git-semver-1-0-0; git-3 = git-semver-1-0-0; git-4 = git-semver-1-0-0; }; } '' cp -r ${path} src chmod -R u+w src cd src echo "generating flake.nix" noc init cat flake.nix install -D flake.nix $out/flake.nix echo "checking with 'nix flake check'" export NIX_STATE_DIR=$TMPDIR/nix/var export NIX_PATH= export HOME=$TMPDIR nix-store --init nixFlags=( --offline --option build-users-group "" --option experimental-features "ca-derivations nix-command flakes" --store $TMPDIR/nix/store ) nix flake check \ --no-build \ --show-trace \ $checkFlags \ "''${nixFlags[@]}" ''; in { _1000-hello-worlds = mapAttrs (name: path: mkHelloWorldTest path) { build-deps = ./build-deps; build-feature-env-vars = ./build-feature-env-vars; cap-lints = ./cap-lints; crate-names = ./crate-names; custom-lib-name = ./custom-lib-name; dependency-v1 = ./dependency-v1; dependency-v2 = ./dependency-v2; dependency-v3 = ./dependency-v3; features = ./features; libz-dynamic = ./libz-dynamic; libz-static = ./libz-static; lto-fat = ./lto-fat; lto-proc-macro = ./lto-proc-macro; lto-thin = ./lto-thin; tokio-app = ./tokio-app; } // { workspace-inline = mkWorkspaceTest ./workspace-inline [ "bar" "baz" "foo" ]; workspace-proc-macro-lto = mkWorkspaceTest ./workspace-proc-macro-lto [ "acro" "procm" ]; workspace-virtual = mkWorkspaceTest ./workspace-virtual [ "bar" "foo" ]; }; _1100-gen-init = mapAttrs mkGenInit { dependency-v1 = ./dependency-v1; dependency-v2 = ./dependency-v2; dependency-v3 = ./dependency-v3; features = ./features; workspace-virtual = ./workspace-virtual; workspace-inline = ./workspace-inline; }; } ================================================ FILE: tests/dependency-v1/Cargo.toml ================================================ [package] name = "dependencies" version = "0.0.0" edition = "2018" [dependencies] cratesio = { package = "semver", version = "1" } registry-index = { package = "semver", version = "1", registry-index = "https://www.github.com/rust-lang/crates.io-index" } git-tag = { package = "semver", git = "https://github.com/dtolnay/semver", tag = "1.0.0" } git-branch = { package = "semver", git = "http://github.com/dtolnay/semver", branch = "master" } git-rev = { package = "semver", git = "ssh://git@github.com/dtolnay/semver", rev = "a2ce5777dcd455246e4650e36dde8e2e96fcb3fd" } git-head = { package = "semver", git = "ssh://git@github.com/dtolnay/semver" } ================================================ FILE: tests/dependency-v1/README.md ================================================ This `Cargo.lock` is generated by rust 1.37.0. ================================================ FILE: tests/dependency-v1/src/main.rs ================================================ fn main() { cratesio::Version::parse("1.2.3").unwrap(); registry_index::Version::parse("1.2.3").unwrap(); git_tag::Version::parse("1.2.3").unwrap(); git_branch::Version::parse("1.2.3").unwrap(); git_rev::Version::parse("1.2.3").unwrap(); git_head::Version::parse("1.2.3").unwrap(); println!("Hello, world!"); } ================================================ FILE: tests/dependency-v2/Cargo.toml ================================================ [package] name = "dependencies" version = "0.0.0" edition = "2018" [dependencies] cratesio = { package = "semver", version = "1" } registry-index = { package = "semver", version = "1", registry-index = "https://www.github.com/rust-lang/crates.io-index" } git-tag = { package = "semver", git = "https://github.com/dtolnay/semver", tag = "1.0.0" } git-branch = { package = "semver", git = "http://github.com/dtolnay/semver", branch = "master" } git-rev = { package = "semver", git = "ssh://git@github.com/dtolnay/semver", rev = "a2ce5777dcd455246e4650e36dde8e2e96fcb3fd" } git-head = { package = "semver", git = "ssh://git@github.com/dtolnay/semver" } ================================================ FILE: tests/dependency-v2/README.md ================================================ This `Cargo.lock` is generated by rust 1.41.0. ================================================ FILE: tests/dependency-v2/src/main.rs ================================================ fn main() { cratesio::Version::parse("1.2.3").unwrap(); registry_index::Version::parse("1.2.3").unwrap(); git_tag::Version::parse("1.2.3").unwrap(); git_branch::Version::parse("1.2.3").unwrap(); git_rev::Version::parse("1.2.3").unwrap(); git_head::Version::parse("1.2.3").unwrap(); println!("Hello, world!"); } ================================================ FILE: tests/dependency-v3/Cargo.toml ================================================ [package] name = "dependencies" version = "0.0.0" edition = "2018" [dependencies] cratesio = { package = "semver", version = "1" } registry-index = { package = "semver", version = "1", registry-index = "https://www.github.com/rust-lang/crates.io-index" } git-tag = { package = "semver", git = "https://github.com/dtolnay/semver", tag = "1.0.0" } git-branch = { package = "semver", git = "http://github.com/dtolnay/semver", branch = "master" } git-rev = { package = "semver", git = "ssh://git@github.com/dtolnay/semver", rev = "a2ce5777dcd455246e4650e36dde8e2e96fcb3fd" } git-head = { package = "semver", git = "ssh://git@github.com/dtolnay/semver" } ================================================ FILE: tests/dependency-v3/src/main.rs ================================================ fn main() { cratesio::Version::parse("1.2.3").unwrap(); registry_index::Version::parse("1.2.3").unwrap(); git_tag::Version::parse("1.2.3").unwrap(); git_branch::Version::parse("1.2.3").unwrap(); git_rev::Version::parse("1.2.3").unwrap(); git_head::Version::parse("1.2.3").unwrap(); println!("Hello, world!"); } ================================================ FILE: tests/fake-semver/Cargo.toml ================================================ # This crate is for tests in `support.nix` and will not be really built. [package] name = "semver" version = "1.0.0" edition = "2018" [dependencies] ================================================ FILE: tests/fake-semver/src/lib.rs ================================================ ================================================ FILE: tests/features/Cargo.toml ================================================ [package] name = "simple-features" version = "0.0.0" edition = "2018" [dependencies] semver = { version = "1.0.12", optional = true } [features] default = [ "a" ] a = ["default", "semver/serde"] b = [] ================================================ FILE: tests/features/src/main.rs ================================================ #[cfg(all(feature = "a", not(feature = "b")))] fn main() { println!("Hello, world!"); } ================================================ FILE: tests/libz-dynamic/Cargo.toml ================================================ [package] name = "libz-dynamic" version = "0.0.0" edition = "2018" [dependencies] libz-sys = { version = "=1.1.6", default-features = false } ================================================ FILE: tests/libz-dynamic/build.rs ================================================ fn main() { std::env::var("DEP_Z_INCLUDE").expect_err("Dynamic linking should not set DEP_Z_INCLUDE"); println!("cargo:rustc-env=OKAY="); } ================================================ FILE: tests/libz-dynamic/src/main.rs ================================================ fn main() { let crc_init = unsafe { libz_sys::crc32(0, "foo".as_ptr() as _, 3) }; assert_eq!(crc_init, 2356372769); assert_eq!(env!("OKAY"), ""); println!("Hello, world!"); } ================================================ FILE: tests/libz-static/Cargo.toml ================================================ [package] name = "libz-static" version = "0.0.0" edition = "2018" [dependencies] libz-sys = { version = "=1.1.3", default-features = false, features = ["static"] } ================================================ FILE: tests/libz-static/build.rs ================================================ fn main() { let z_include = std::env::var("DEP_Z_INCLUDE").unwrap(); let header = std::fs::read_to_string(z_include + "/zlib.h").unwrap(); let mut lines = header.lines(); assert_eq!( lines.next().unwrap(), "/* zlib.h -- interface of the 'zlib' general purpose compression library" ); assert_eq!( lines.next().unwrap(), " version 1.2.11, January 15th, 2017", "bundled libz MUST be 1.2.11", ); println!("cargo:rustc-env=OKAY="); } ================================================ FILE: tests/libz-static/src/lib.rs ================================================ // Only available in the build script. const _: [(); 1] = [(); option_env!("DEP_Z_INCLUDE").is_none() as usize]; ================================================ FILE: tests/libz-static/src/main.rs ================================================ fn main() { // Only available in the build script. assert!(option_env!("DEP_Z_INCLUDE").is_none()); let crc_init = unsafe { libz_sys::crc32(0, "foo".as_ptr() as _, 3) }; assert_eq!(crc_init, 2356372769); assert_eq!(env!("OKAY"), ""); println!("Hello, world!"); } ================================================ FILE: tests/lto-fat/Cargo.toml ================================================ [package] name = "lto-fat" version = "0.0.0" edition = "2021" [dependencies] semver = "1" [profile.release] lto = "fat" ================================================ FILE: tests/lto-fat/src/main.rs ================================================ fn main() { assert_eq!( semver::Version::parse("1.2.3").unwrap().to_string(), "1.2.3" ); println!("Hello, world!"); } ================================================ FILE: tests/lto-proc-macro/Cargo.toml ================================================ [package] name = "lto-proc-macro" version = "0.0.0" edition = "2021" [dependencies] thiserror = "1" [profile.release] lto = "thin" ================================================ FILE: tests/lto-proc-macro/src/main.rs ================================================ #[derive(Debug, thiserror::Error)] enum Error { #[error("Hello, {0}!")] Hello(&'static str), } fn main() { println!("{}", Error::Hello("world")); } ================================================ FILE: tests/lto-thin/Cargo.toml ================================================ [package] name = "lto-thin" version = "0.0.0" edition = "2021" [dependencies] semver = "1" [profile.release] lto = "thin" ================================================ FILE: tests/lto-thin/src/main.rs ================================================ fn main() { assert_eq!( semver::Version::parse("1.2.3").unwrap().to_string(), "1.2.3" ); println!("Hello, world!"); } ================================================ FILE: tests/tokio-app/Cargo.toml ================================================ [package] name = "tokio-app" version = "0.0.0" edition = "2018" [dependencies] tokio = { version = "1", features = [ "rt-multi-thread", "macros", "time" ], default-features = false } ================================================ FILE: tests/tokio-app/src/main.rs ================================================ #[tokio::main] async fn main() { tokio::time::sleep(std::time::Duration::from_millis(100)).await; println!("Hello, world!"); } ================================================ FILE: tests/workspace-inline/Cargo.toml ================================================ [package] name = "foo" version = "0.1.0" edition = "2018" [dependencies] bar = { path = "bar" } [workspace] members = [".", "b*"] ================================================ FILE: tests/workspace-inline/bar/Cargo.toml ================================================ [package] name = "bar" version = "0.1.0" edition = "2018" [dependencies] ================================================ FILE: tests/workspace-inline/bar/src/lib.rs ================================================ pub fn hello() -> &'static str { "Hello" } ================================================ FILE: tests/workspace-inline/baz/Cargo.toml ================================================ [package] name = "baz" version = "0.1.0" edition = "2018" [dependencies] bar = { path = "../bar" } ================================================ FILE: tests/workspace-inline/baz/src/lib.rs ================================================ pub fn show(s: String) { assert!(s.starts_with(bar::hello())); println!("{}", s); } ================================================ FILE: tests/workspace-inline/src/main.rs ================================================ fn main() { println!("{}, world!", bar::hello()); } ================================================ FILE: tests/workspace-proc-macro-lto/Cargo.toml ================================================ [package] name = "acro" version = "0.1.0" edition = "2018" [dependencies] procm = { path = "./procm" } [workspace] # FIXME: "." is required. members = ["procm", "."] [profile.release] lto = "thin" ================================================ FILE: tests/workspace-proc-macro-lto/procm/Cargo.toml ================================================ [package] name = "procm" version = "0.1.0" edition = "2021" [lib] proc-macro = true ================================================ FILE: tests/workspace-proc-macro-lto/procm/src/lib.rs ================================================ use proc_macro::TokenStream; #[proc_macro] pub fn acro(input: TokenStream) -> TokenStream { format!(r#"fn main() {{ println!({}) }}"#, input).parse().unwrap() } ================================================ FILE: tests/workspace-proc-macro-lto/src/lib.rs ================================================ procm::acro!("Hello, world!"); ================================================ FILE: tests/workspace-virtual/Cargo.toml ================================================ [workspace] members = [ "./crates/*" ] exclude = [ "./crates/./exc" ] ================================================ FILE: tests/workspace-virtual/crates/bar/Cargo.toml ================================================ [package] name = "bar" version = "0.1.0" edition = "2018" [dependencies] ================================================ FILE: tests/workspace-virtual/crates/bar/src/lib.rs ================================================ pub fn world() -> &'static str { "world" } ================================================ FILE: tests/workspace-virtual/crates/exc/Cargo.toml ================================================ [package] name = "exc" version = "0.1.0" edition = "2018" [dependencies] ================================================ FILE: tests/workspace-virtual/crates/exc/src/main.rs ================================================ fn main() { println!("Hello, world!"); } ================================================ FILE: tests/workspace-virtual/crates/foo/Cargo.toml ================================================ [package] name = "foo" version = "0.1.0" edition = "2018" [dependencies] bar = { path = "../bar" } ================================================ FILE: tests/workspace-virtual/crates/foo/src/main.rs ================================================ fn main() { println!("Hello, {}!", bar::world()); } ================================================ FILE: toml2json/Cargo.toml ================================================ [package] name = "toml2json" version = "1.0.0" rust-version = "1.36" [dependencies] serde_json = "1.0.0" toml = "0.5" ================================================ FILE: toml2json/README.md ================================================ ## toml2json: minimal impl for toml -> json This utility program is a dependency of every rust crate derivation. Thus it's is designed to be simple and have minimal dependencies, instead of using `remarshal` which pulls in tons of python packages. ================================================ FILE: toml2json/default.nix ================================================ { stdenv, fetchurl, rustc }: let fetch = name: version: sha256: fetchurl { name = "crate-${name}-${version}.tar.gz"; url = "https://crates.io/api/v1/crates/${name}/${version}/download"; inherit sha256; }; manifest = builtins.fromTOML (builtins.readFile ./Cargo.toml); lock = builtins.fromTOML (builtins.readFile ./Cargo.lock); in stdenv.mkDerivation { pname = manifest.package.name; version = manifest.package.version; srcs = map ({ name, version, checksum ? null, ... }: if checksum != null then fetch name version checksum else null) lock.package; sourceRoot = "."; nativeBuildInputs = [ rustc ]; buildPhase = '' buildFlagsArray+=( --color=always --out-dir . -L . -C codegen-units=1 -C opt-level=3 --cap-lints allow ) run() { echo "rustc $* ''${buildFlagsArray[*]}" rustc "$@" "''${buildFlagsArray[@]}" } run itoa-*/src/lib.rs --crate-name itoa --crate-type lib \ --cfg 'feature="default"' --cfg 'feature="std"' run ryu-*/src/lib.rs --crate-name ryu --crate-type lib run serde-*/src/lib.rs --crate-name serde --crate-type lib \ --cfg 'feature="default"' --cfg 'feature="std"' run serde_json-*/src/lib.rs --crate-name serde_json --crate-type lib \ --edition=2018 \ --cfg 'feature="default"' --cfg 'feature="std"' \ --extern itoa=libitoa.rlib \ --extern ryu=libryu.rlib \ --extern serde=libserde.rlib run toml-*/src/lib.rs --crate-name toml --crate-type lib \ --edition=2018 \ --extern serde=libserde.rlib run ${./src/main.rs} --crate-name toml2json --crate-type bin \ --extern serde_json=./libserde_json.rlib \ --extern toml=libtoml.rlib ''; testToml = '' [hello] world = "good" [target."cfg(target = \"good\")"] foo = "bar" ''; testJson = ''{"hello":{"world":"good"},"target":{"cfg(target = \"good\")":{"foo":"bar"}}}''; doCheck = true; checkPhase = '' ./toml2json <<<"$testToml" >out.json echo "Got : $(cat out.json)" echo "Expect: $testJson" [[ "$(cat out.json)" == "$testJson" ]] ''; installPhase = '' mkdir -p $out/bin cp -t $out/bin ./toml2json ''; } ================================================ FILE: toml2json/src/main.rs ================================================ use std::io::{stdin, stdout, Read, Write}; fn main() -> Result<(), Box> { let mut input = Vec::new(); stdin().lock().read_to_end(&mut input)?; let data: serde_json::Value = toml::from_slice(&input)?; let mut output = serde_json::to_vec(&data)?; output.push(b'\n'); stdout().lock().write_all(&output)?; Ok(()) }