├── .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.lock
├── Cargo.toml
├── default.nix
└── src
│ └── lib.rs
├── crates-io-override
├── default.nix
└── proc-macro.nix
├── flake.lock
├── flake.nix
├── lib
├── default.nix
├── glob.nix
├── pkg-info.nix
├── resolve.nix
├── semver.nix
├── support.nix
└── target-cfg.nix
├── noc
├── Cargo.lock
├── Cargo.toml
├── src
│ ├── init.rs
│ └── main.rs
└── templates
│ └── init-flake.nix
├── scripts
└── cratesio-utils.py
├── tests
├── build-deps
│ ├── Cargo.lock
│ ├── Cargo.toml
│ ├── build.rs
│ └── src
│ │ └── main.rs
├── build-feature-env-vars
│ ├── Cargo.lock
│ ├── Cargo.toml
│ ├── build.rs
│ └── src
│ │ └── main.rs
├── cap-lints
│ ├── Cargo.lock
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── crate-names
│ ├── Cargo.lock
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── custom-lib-name
│ ├── Cargo.lock
│ ├── Cargo.toml
│ └── src
│ │ ├── lib.rs
│ │ └── main.rs
├── default.nix
├── dependency-v1
│ ├── Cargo.lock
│ ├── Cargo.toml
│ ├── README.md
│ └── src
│ │ └── main.rs
├── dependency-v2
│ ├── Cargo.lock
│ ├── Cargo.toml
│ ├── README.md
│ └── src
│ │ └── main.rs
├── dependency-v3
│ ├── Cargo.lock
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── fake-semver
│ ├── Cargo.toml
│ └── src
│ │ └── lib.rs
├── features
│ ├── Cargo.lock
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── libz-dynamic
│ ├── Cargo.lock
│ ├── Cargo.toml
│ ├── build.rs
│ └── src
│ │ └── main.rs
├── libz-static
│ ├── Cargo.lock
│ ├── Cargo.toml
│ ├── build.rs
│ └── src
│ │ ├── lib.rs
│ │ └── main.rs
├── lto-fat
│ ├── Cargo.lock
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── lto-proc-macro
│ ├── Cargo.lock
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── lto-thin
│ ├── Cargo.lock
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── tokio-app
│ ├── Cargo.lock
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── workspace-inline
│ ├── Cargo.lock
│ ├── Cargo.toml
│ ├── bar
│ │ ├── Cargo.toml
│ │ └── src
│ │ │ └── lib.rs
│ ├── baz
│ │ ├── Cargo.toml
│ │ └── src
│ │ │ └── lib.rs
│ └── src
│ │ └── main.rs
├── workspace-proc-macro-lto
│ ├── Cargo.lock
│ ├── Cargo.toml
│ ├── procm
│ │ ├── Cargo.toml
│ │ └── src
│ │ │ └── lib.rs
│ └── src
│ │ └── lib.rs
└── workspace-virtual
│ ├── Cargo.lock
│ ├── Cargo.toml
│ └── crates
│ ├── bar
│ ├── Cargo.toml
│ └── src
│ │ └── lib.rs
│ ├── exc
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
│ └── foo
│ ├── Cargo.toml
│ └── src
│ └── main.rs
└── toml2json
├── Cargo.lock
├── Cargo.toml
├── README.md
├── default.nix
└── src
└── main.rs
/.github/workflows/binary-cache.yaml:
--------------------------------------------------------------------------------
1 | name: Binary cache
2 | on:
3 | push:
4 | branches:
5 | - main
6 |
7 | workflow_run:
8 | workflows:
9 | - update
10 | types:
11 | - completed
12 | branches:
13 | - main
14 |
15 | permissions:
16 | contents: read
17 |
18 | jobs:
19 | populate:
20 | name: Build and push binary cache
21 | runs-on: ubuntu-latest
22 | steps:
23 | - name: Checkout
24 | uses: actions/checkout@v4
25 | - name: Install Nix
26 | uses: cachix/install-nix-action@v24
27 | - name: Setup Cachix
28 | uses: cachix/cachix-action@v13
29 | with:
30 | name: nocargo
31 | authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
32 | pushFilter: '(-source$|\.tar\.gz)'
33 |
34 | - name: Build cached crates
35 | run: nix build .#cache --show-trace --no-update-lock-file
36 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on:
3 | pull_request:
4 | push:
5 |
6 | workflow_run:
7 | workflows:
8 | - update
9 | types:
10 | - completed
11 | branches:
12 | - main
13 |
14 | permissions:
15 | contents: read
16 |
17 | jobs:
18 | flake-check:
19 | name: Flake check (locked)
20 | runs-on: ubuntu-latest
21 | steps:
22 | - name: Checkout
23 | uses: actions/checkout@v4
24 | - name: Install Nix
25 | uses: cachix/install-nix-action@v24
26 | - run: nix flake check --show-trace --no-update-lock-file
27 |
--------------------------------------------------------------------------------
/.github/workflows/update.yaml:
--------------------------------------------------------------------------------
1 | name: Update registry
2 | on:
3 | schedule:
4 | - cron: '0 3 * * *' # *-*-* 03:00:00 UTC
5 |
6 | workflow_dispatch:
7 |
8 | env:
9 | BRANCH: main
10 |
11 | permissions:
12 | contents: write
13 |
14 | jobs:
15 | update:
16 | name: Update registry
17 | runs-on: ubuntu-latest
18 | env:
19 | CRATES_TOML_DIR: ./crates-toml
20 | steps:
21 | - name: Checkout
22 | uses: actions/checkout@v4
23 | with:
24 | rev: ${{ env.BRANCH }}
25 | token: ${{ secrets.GITHUB_TOKEN }}
26 | - name: Install Nix
27 | uses: cachix/install-nix-action@v24
28 | with:
29 | nix_path: nixpkgs=channel:nixpkgs-unstable
30 |
31 | - name: Cache fetched Cargo.toml files
32 | uses: actions/cache@v3
33 | with:
34 | path: ${{ env.CRATES_TOML_DIR }}
35 | key: crates-toml
36 |
37 | - name: Sync crates.io database
38 | run: ./scripts/cratesio-utils.py sync
39 | - name: Update proc-macro crates
40 | run: ./scripts/cratesio-utils.py update-proc-macro-crates
41 | - name: Update lockfile of popular crates
42 | run: ./scripts/cratesio-utils.py update-popular-crates
43 |
44 | # This should be the last. So the registry locked is not earlier than references.
45 | - name: Update flake
46 | run: nix flake update
47 |
48 | - name: Flake check
49 | run: nix flake check --show-trace --no-update-lock-file
50 |
51 | - name: Push changes
52 | run: |
53 | git config user.name github-actions
54 | git config user.email github-actions@github.com
55 | git add flake.lock crates-io-override/proc-macro.nix cache/Cargo.{lock,toml}
56 | git commit -m "registry: update"
57 | git push HEAD:${{ env.BRANCH }}
58 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | result
2 | result-*
3 |
--------------------------------------------------------------------------------
/LICENSE-MIT:
--------------------------------------------------------------------------------
1 | Permission is hereby granted, free of charge, to any person obtaining a copy of
2 | this software and associated documentation files (the "Software"), to deal in
3 | the Software without restriction, including without limitation the rights to
4 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
5 | of the Software, and to permit persons to whom the Software is furnished to do
6 | so, subject to the following conditions:
7 |
8 | The above copyright notice and this permission notice shall be included in all
9 | copies or substantial portions of the Software.
10 |
11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17 | SOFTWARE.
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # nocargo: Cargo in Nix
2 |
3 | 🚧 *This project is under development and is not ready for production yet. APIs are subjects to change.*
4 |
5 | Build Rust crates with *Nix Build System*.
6 | - **No IFDs** (import-from-derivation). See [meme](https://gist.github.com/oxalica/d3b1251eb29d10e6f3cb2005167ddcd9).
7 | - No `cargo` dependency during building. Only `rustc`.
8 | - No need for hash prefetching or code generation[^no-code-gen].
9 | - Crate level caching, globally shared.
10 | - [nixpkgs] integration for non-Rust dependencies.
11 |
12 | [^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".
13 |
14 |
15 | Feature checklist
16 |
17 | - Binary cache
18 | - [x] Top 256 popular crate versions with default features
19 | - Nix library
20 | - [ ] Non-flake support.
21 | - [x] `[workspace]`
22 | - [x] `members`
23 | - [ ] Auto-`members`
24 | - [x] `excludes`
25 | FIXME: Buggy.
26 | - [ ] `resolver`
27 | Currently has custom resolution algorithm, more like v2.
28 | - [x] `links`
29 | - [x] `[profile]`
30 | - [x] `[{,dev-,build-}dependencies]`
31 | - [x] `[features]`
32 | - [x] Overriding API
33 | - [x] `[target..dependencies]`
34 | - [x] `[patch]`
35 | Automatically supported. Since the dependency graph `Cargo.lock` currently relies on `cargo`'s generation.
36 | - [ ] Cross-compilation.
37 | FIXME: Buggy with proc-macros.
38 | - `noc` helper
39 | - [x] `noc init`: Initial template `flake.nix` generation
40 | - Dependency kinds
41 | - [ ] `registry`
42 | - [x] `registry-index`
43 | - [x] `git`
44 | - [x] `path` inside workspace
45 | - [ ] `path` outside workspace
46 | - Target detection
47 | - [ ] Library
48 | FIXME: Assume to always exist.
49 | - [x] Binary
50 | - [ ] Test
51 | - [ ] Bench
52 | - [ ] Example
53 | - [ ] `Cargo.lock` generation and updating
54 |
55 |
56 |
57 | ## Start with Nix flake
58 |
59 | 1. (Optional) Add binary substituters for pre-built popular crates, by either
60 | - Install `cachix` and run `cachix use nocargo` ([see more detail about `cachix`](https://app.cachix.org/cache/nocargo)), or
61 | - Manually add substituter `https://nocargo.cachix.org` with public key `nocargo.cachix.org-1:W6jkp5htZBA1tUdU8XHLaD7zBrIFnor0MsLhHgrJeHk=`
62 | 1. Enter the root directory of your rust workspace or package. Currently, you should have `Cargo.lock` already created by `cargo`.
63 | 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).
64 | 1. Check flake outputs with `nix flake show`. Typically, the layout would be like,
65 | ```
66 | └───packages
67 | └───x86_64-linux
68 | ├───default: package 'rust_mypkg1-0.1.0' # The "default" package. For workspace, it's the top-level one if exists.
69 | ├───mypkg1: package 'rust_mypkg1-0.1.0' # Crate `mypkg1` with `release` profile.
70 | ├───mypkg1-dev: package 'rust_mypkg1-debug-0.1.0' # Crate `mypkg1` with `dev` profile.
71 | ├───mypkg2: package 'rust_mypkg2-0.1.0' # etc.
72 | └───mypkg2-dev: package 'rust_mypkg2-debug-0.1.0'
73 | ```
74 | 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`.
75 | 1. Have fun!
76 |
77 | ## Example `flake.nix` structure for reference
78 |
79 | 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.
80 |
81 | ```nix
82 | {
83 | description = "My Rust packages";
84 |
85 | inputs = {
86 | nixpkgs.url = "github:NixOS/nixpkgs";
87 | flake-utils.url = "github:numtide/flake-utils";
88 | nocargo = {
89 | url = "github:oxalica/nocargo";
90 | inputs.nixpkgs.follows = "nixpkgs";
91 |
92 | # See below.
93 | # inputs.registry-crates-io.follows = "registry-crates-io";
94 | };
95 |
96 | # Optionally, you can explicitly import crates.io-index here.
97 | # So you can `nix flake update` at any time to get cutting edge version of crates,
98 | # instead of waiting `nocargo` to dump its dependency.
99 | # Otherwise, you can simply omit this to use the locked registry from `nocargo`,
100 | # which is updated periodically.
101 | # registry-crates-io = { url = "github:rust-lang/crates.io-index"; flake = false; };
102 | };
103 |
104 | outputs = { nixpkgs, flake-utils, nocargo, ... }@inputs:
105 | flake-utils.lib.eachSystem [ "x86_64-linux" ] (system:
106 | let
107 | # The entry API to make Nix derivations from your Rust workspace or package.
108 | # The output of it consists of profile names, like `release` or `dev`, each of which is
109 | # a attrset of all member package derivations keyed by their package names.
110 | ws = nocargo.lib.${system}.mkRustPackageOrWorkspace {
111 | # The root directory, which contains `Cargo.lock` and top-level `Cargo.toml`
112 | # (the one containing `[workspace]` for workspace).
113 | src = ./.;
114 |
115 | # If you use registries other than crates.io, they should be imported in flake inputs,
116 | # and specified here. Note that registry should be initialized via `mkIndex`,
117 | # with an optional override.
118 | extraRegistries = {
119 | # "https://example-registry.org" = nocargo.lib.${system}.mkIndex inputs.example-registry {};
120 | };
121 |
122 | # If you use crates from git URLs, they should be imported in flake inputs,
123 | # and specified here.
124 | gitSrcs = {
125 | # "https://github.com/some/repo" = inputs.example-git-source;
126 | };
127 |
128 | # If some crates in your dependency closure require packages from nixpkgs.
129 | # You can override the argument for `stdenv.mkDerivation` to add them.
130 | #
131 | # Popular `-sys` crates overrides are maintained in `./crates-io-override/default.nix`
132 | # to make them work out-of-box. PRs are welcome.
133 | buildCrateOverrides = with nixpkgs.legacyPackages.${system}; {
134 | # Use package id format `pkgname version (registry)` to reference a direct or transitive dependency.
135 | "zstd-sys 2.0.1+zstd.1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = old: {
136 | nativeBuildInputs = [ pkg-config ];
137 | propagatedBuildInputs = [ zstd ];
138 | };
139 |
140 | # Use package name to reference local crates.
141 | "mypkg1" = old: {
142 | nativeBuildInputs = [ git ];
143 | };
144 | };
145 |
146 | # We use the rustc from nixpkgs by default.
147 | # But you can override it, for example, with a nightly version from https://github.com/oxalica/rust-overlay
148 | # rustc = rust-overlay.packages.${system}.rust-nightly_2022-07-01;
149 | };
150 | in rec {
151 | # For convenience, we hoist derivations of `release` and `dev` profile for easy access,
152 | # with `dev` packages postfixed by `-dev`.
153 | # You can export different packages of your choice.
154 | packages = ws.release
155 | // nixpkgs.lib.mapAttrs' (name: value: { name = "${name}-dev"; inherit value; }) ws.dev
156 | // {
157 | # The "default" features are turned on by default.
158 | # You can `override` the library derivation to enable a different set of features.
159 | # Explicit overriding will disable "default", unless you manually include it.
160 | mypkg1-with-custom-features = (ws.release.mypkg1.override {
161 | # Enables two features (and transitive ones), and disables "default".
162 | features = [ "feature1" "feature2" ];
163 | }).bin;
164 | };
165 | });
166 | }
167 | ```
168 |
169 | ## FAQ
170 |
171 | ### Comparison with [cargo2nix] and [naersk]?
172 |
173 | Main differences are already clarified [on the top](#nocargo%3A-cargo-in-nix).
174 |
175 | `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.
176 |
177 | In other words, we and `cargo2nix` use Nix as a *Build System*, while `nearsk` use Nix as a *Package Manager* or *Packager*.
178 |
179 |
180 |
181 | Detail comparison of nocargo, cargo2nix/buildRustCrate, naersk and buildRustPackage
182 |
183 |
184 |
185 | | | nocargo | [cargo2nix]/`buildRustCrate` | [naersk] | `buildRustPackage` |
186 | |-|-|-|-|-|
187 | | Depend on `cargo` | Updating `Cargo.lock` | Updating & generating & building | Updating & vendoring & building | Building |
188 | | Derivation granularity | Per crate | Per crate | Per package + one dependency closure | All in one |
189 | | Crate level sharing | ✔️ | ✔️ | ✖ | ✖ |
190 | | Binary substitution per crate | ✔️ | Not implemented | ✖ | ✖ |
191 | | Code generation | ✖ | ✔️ | ✖ | ✖ |
192 | | Edit workspace & rebuild | Rebuild leaf crates | Rebuild leaf crates | Rebuild leaf crates | Refetch and rebuild all crates |
193 | | 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 |
194 | | Offline rebuild as long as | Not adding unfetched crate dependency | Not adding unfetched crate dependency | Not changing any dependencies | ✖ |
195 |
196 |
197 |
198 | ### But why pure Nix build system?
199 |
200 | - Sharing through fine-grained derivations between all projects, not just in one workspace.
201 | - Binary substitution per crate.
202 | No need for global `target_dir`/`CARGO_TARGET_DIR` or [sccache].
203 | - Easy `nixpkgs` integration for non-Rust package dependencies, cross-compilation (planned) and package overriding.
204 | - More customizability: per-crate `rustc` flags tweaking, arbitrary crate patching, force dynamic linking and more.
205 |
206 | ### Can I really throw away `cargo`?
207 |
208 | Sorry, currently no. :crying_cat_face: Updating of `Cargo.lock` still relies on `cargo`.
209 | This can happen when creating a new project or changing dependencies.
210 | We are mainly using `cargo`'s SAT solver to pin down the dependency graph.
211 |
212 | It's *possible* to implement it ourselves, but not yet, due to the complexity.
213 |
214 | ## License
215 |
216 | MIT Licensed.
217 |
218 | [nixpkgs]: https://github.com/NixOS/nixpkgs
219 | [naersk]: https://github.com/nix-community/naersk
220 | [cargo2nix]: https://github.com/cargo2nix/cargo2nix
221 | [sccache]: https://github.com/mozilla/sccache
222 |
--------------------------------------------------------------------------------
/build-rust-crate/builder-bin.sh:
--------------------------------------------------------------------------------
1 | source $stdenv/setup
2 | source $builderCommon
3 |
4 | declare -A buildFlagsMap
5 | declare -A binPathMap
6 |
7 | dontInstall=1
8 |
9 | addBin() {
10 | local name="$1" path="$2" binEdition="$3"
11 | local -a pathCandidates
12 |
13 | if [[ -z "$name" ]]; then
14 | echo "Name of the binary target is not specified"
15 | exit 1
16 | fi
17 |
18 | if [[ -n "$path" ]]; then
19 | pathCandidates=("$path")
20 | else
21 | pathCandidates=()
22 | if [[ -f "src/bin/$name.rs" ]]; then
23 | pathCandidates+=("src/bin/$name.rs")
24 | fi
25 | if [[ -f "src/bin/$name/main.rs" ]]; then
26 | pathCandidates+=("src/bin/$name/main.rs")
27 | fi
28 | if [[ "$name" == "$pkgName" && -f "src/main.rs" ]]; then
29 | pathCandidates+=("src/main.rs")
30 | fi
31 | fi
32 |
33 | case ${#pathCandidates[@]} in
34 | 0)
35 | echo "Cannot guess path of binary target"
36 | exit 1
37 | ;;
38 | 1)
39 | path="${pathCandidates[0]}"
40 | ;;
41 | *)
42 | echo "Ambiguous binary target, candidate paths: ${pathCandidates[*]}"
43 | exit 1
44 | ;;
45 | esac
46 |
47 | printf "Found binary %q at %q\n" "$name" "$path"
48 | binPathMap["$path"]=1
49 |
50 | # TODO: Other flags.
51 | buildFlagsMap["$name"]="$path --crate-name ${name//-/_} -C metadata=$rustcMeta-$name"
52 | if [[ -n "${binEdition:=$globalEdition}" ]]; then
53 | buildFlagsMap["$name"]+=" --edition $binEdition"
54 | fi
55 | }
56 |
57 | configurePhase() {
58 | runHook preConfigure
59 |
60 | convertCargoToml
61 |
62 | globalEdition="$(jq --raw-output '.package.edition // ""' "$cargoTomlJson")"
63 | pkgName="$(jq --raw-output '.package.name // ""' "$cargoTomlJson")"
64 |
65 | # For packages with the 2015 edition, the default for auto-discovery is false if at least one target is
66 | # manually defined in Cargo.toml. Beginning with the 2018 edition, the default is always true.
67 | # See: https://doc.rust-lang.org/cargo/reference/cargo-targets.html#target-auto-discovery
68 | autoDiscovery=
69 | if [[
70 | "$(jq --raw-output '.package.autobins' "$cargoTomlJson")" != false &&
71 | ( "${globalEdition:-2015}" != 2015 || ${#buildFlagsMap[@]} = 0 )
72 | ]]; then
73 | autoDiscovery=1
74 | fi
75 |
76 | local binsStr
77 | while read -r name; do
78 | read -r path
79 | read -r binEdition
80 | addBin "$name" "$path" "$binEdition"
81 | # Don't strip whitespace.
82 | done < <(jq --raw-output '.bin // [] | .[] | .name // "", .path // "", .edition // ""' "$cargoTomlJson")
83 |
84 | if [[ -n "$autoDiscovery" ]]; then
85 | if [[ -f src/main.rs && -z ${binPathMap[src/main.rs]} ]]; then
86 | addBin "$pkgName" src/main.rs ""
87 | fi
88 | local f name
89 | for f in src/bin/*; do
90 | name="${f##*/}"
91 | if [[ "$f" = *.rs && -f "$f" ]]; then
92 | [[ -n "${binPathMap["$f"]}" ]] || addBin "${name%.rs}" "$f" ""
93 | elif [[ -f "$f/main.rs" ]]; then
94 | [[ -n "${binPathMap["$f/main.rs"]}" ]] || addBin "$name" "$f/main.rs" ""
95 | fi
96 | done
97 | fi
98 |
99 | if [[ ${#buildFlagsMap[@]} = 0 ]]; then
100 | echo "No binaries to be built"
101 | mkdir $out
102 | exit 0
103 | fi
104 |
105 | # Implicitly link library of current crate, if exists.
106 | if [[ -e "$libDevDrv"/lib ]]; then
107 | addExternFlags buildFlagsArray link "::$libOutDrv:$libDevDrv"
108 | fi
109 |
110 | # Actually unused.
111 | declare -a cdylibBuildFlagsArray
112 |
113 | addExternFlags buildFlagsArray link $dependencies
114 | addFeatures buildFlagsArray $features
115 | importBuildOut buildFlagsArray cdylibBuildFlagsArray "$buildDrv"
116 | setCargoCommonBuildEnv
117 |
118 | depsClosure="$(mktemp -d)"
119 | collectTransDeps "$depsClosure" $dependencies
120 | buildFlagsArray+=(-Ldependency="$depsClosure")
121 |
122 | runHook postConfigure
123 | }
124 |
125 | buildPhase() {
126 | runHook preBuild
127 |
128 | mkdir -p $out/bin
129 |
130 | local binName
131 | for binName in "${!buildFlagsMap[@]}"; do
132 | export CARGO_CRATE_NAME="$binName"
133 | export CARGO_BIN_NAME="$binName"
134 | runRustc "Building binary $binName" \
135 | ${buildFlagsMap["$binName"]} \
136 | --crate-type bin \
137 | --out-dir $out/bin \
138 | $buildFlags \
139 | "${buildFlagsArray[@]}"
140 | done
141 |
142 | runHook postBuild
143 | }
144 |
145 | genericBuild
146 |
--------------------------------------------------------------------------------
/build-rust-crate/builder-build-script.sh:
--------------------------------------------------------------------------------
1 | source $stdenv/setup
2 | source $builderCommon
3 | shopt -s nullglob
4 |
5 | preInstallPhases+="runBuildScriptPhase "
6 |
7 | buildFlagsArray+=( -Cmetadata="$rustcMeta" )
8 |
9 | configurePhase() {
10 | runHook preConfigure
11 |
12 | convertCargoToml
13 |
14 | buildScriptSrc="$(jq --raw-output '.package.build // ""' "$cargoTomlJson")"
15 | if [[ -z "$buildScriptSrc" && -e build.rs ]]; then
16 | buildScriptSrc=build.rs
17 | elif [[ -z "$buildScriptSrc" ]]; then
18 | echo "No build script, doing nothing"
19 | mkdir -p $out
20 | exit 0
21 | fi
22 |
23 | edition="$(jq --raw-output '.package.edition // ""' "$cargoTomlJson")"
24 | if [[ -n "$edition" ]]; then
25 | buildFlagsArray+=(--edition="$edition")
26 | fi
27 |
28 | addFeatures buildFlagsArray $features
29 | addExternFlags buildFlagsArray link $dependencies
30 | setCargoCommonBuildEnv
31 |
32 | depsClosure="$(mktemp -d)"
33 | collectTransDeps "$depsClosure" $dependencies
34 | buildFlagsArray+=(-Ldependency="$depsClosure")
35 |
36 | runHook postConfigure
37 | }
38 |
39 | buildPhase() {
40 | runHook preBuild
41 |
42 | mkdir -p $out/bin
43 |
44 | runRustc "Building build script" \
45 | "$buildScriptSrc" \
46 | --out-dir="$out/bin" \
47 | --crate-name="build_script_build" \
48 | --crate-type=bin \
49 | --emit=link \
50 | -Cembed-bitcode=no \
51 | $buildScriptBuildFlags \
52 | "${buildFlagsArray[@]}"
53 |
54 | runHook postBuild
55 | }
56 |
57 | runBuildScriptPhase() {
58 | runHook preRunBuildScript
59 |
60 | export CARGO_MANIFEST_DIR="$(pwd)"
61 | if [[ -n "$links" ]]; then
62 | export CARGO_MANIFEST_LINKS="$links"
63 | fi
64 |
65 | for feat in $features; do
66 | feat_uppercase="${feat^^}"
67 | export "CARGO_FEATURE_${feat_uppercase//-/_}"=1
68 | done
69 |
70 | export OUT_DIR="$out/rust-support/out-dir"
71 | export NUM_JOBS=$NIX_BUILD_CORES
72 | export RUSTC_BACKTRACE=1 # Make debugging easier.
73 |
74 | local buildOut
75 | for buildOut in $linksDependencies; do
76 | if [[ -e "$buildOut/rust-support/links-metadata" ]]; then
77 | source "$buildOut/rust-support/links-metadata"
78 | fi
79 | done
80 |
81 | # Other flags are set outside.
82 | # - CARGO_CFG_
83 | # - HOST
84 | # - TARGET
85 | # - RUSTC
86 | # - CARGO
87 | # - RUSTDOC
88 |
89 | mkdir -p "$out/rust-support" "$OUT_DIR"
90 |
91 | echo "Running build script"
92 | stdoutFile="$out/rust-support/build-stdout"
93 | "$out/bin/build_script_build" | tee "$stdoutFile"
94 |
95 | runHook postRunBuildScript
96 | }
97 |
98 | installPhase() {
99 | runHook preInstall
100 |
101 | # https://doc.rust-lang.org/1.61.0/cargo/reference/build-scripts.html#outputs-of-the-build-script
102 | local line rhs
103 | while read -r line; do
104 | rhs="${line#*=}"
105 | case "$line" in
106 | cargo:rerun-if-changed=*|cargo:rerun-if-env-changed=*)
107 | # Ignored due to the sandbox.
108 | ;;
109 | cargo:rustc-link-arg=*)
110 | echo "-Clink-arg=$rhs" >>"$out/rust-support/rustc-link-args"
111 | ;;
112 | cargo:rustc-link-arg-bin=*)
113 | if [[ "$rhs" != *=* ]]; then
114 | echo "Missing binary name: $line"
115 | exit 1
116 | fi
117 | echo "-Clink-arg=${rhs%%=*}" >>"$out/rust-support/rustc-link-args-bin-${rhs#*=}"
118 | ;;
119 | cargo:rustc-link-arg-bins=*)
120 | echo "-Clink-arg=$rhs" >>"$out/rust-support/rustc-link-args-bins"
121 | ;;
122 | cargo:rustc-link-arg-tests=*)
123 | echo "-Clink-arg=$rhs" >>"$out/rust-support/rustc-link-args-tests"
124 | ;;
125 | cargo:rustc-link-arg-examples=*)
126 | echo "-Clink-arg=$rhs" >>"$out/rust-support/rustc-link-args-examples"
127 | ;;
128 | cargo:rustc-link-arg-benches=*)
129 | echo "-Clink-arg=$rhs" >>"$out/rust-support/rustc-link-args-benches"
130 | ;;
131 | cargo:rustc-link-lib=*)
132 | if [[ -z "$rhs" ]]; then
133 | echo "Empty link path: $line"
134 | exit 1
135 | fi
136 | echo "-l$rhs" >>"$out/rust-support/rustc-flags"
137 | ;;
138 | cargo:rustc-link-search=*)
139 | if [[ -z "$rhs" ]]; then
140 | echo "Empty link path: $line"
141 | exit 1
142 | fi
143 | echo "-L$rhs" >>"$out/rust-support/rustc-flags"
144 | ;;
145 | cargo:rustc-flags=*)
146 | local flags i flag
147 | read -r -a flags <<<"$rhs"
148 | for (( i = 0; i < ${#flags[@]}; i++ )); do
149 | flag="${flags[i]}"
150 | if [[ "$flag" = -[lL] ]]; then
151 | (( i++ ))
152 | flag+="${flags[i]}"
153 | elif [[ "$flag" != -[lL]* ]]; then
154 | echo "Only -l and -L are allowed from build script: $line"
155 | exit 1
156 | fi
157 | if [[ ${#flag} == 2 ]]; then
158 | echo "Empty link path: $line"
159 | exit 1
160 | fi
161 | echo "$flag" >>"$out/rust-support/rustc-flags"
162 | done
163 | ;;
164 | cargo:rustc-cfg=*)
165 | echo "--cfg=$rhs" >>"$out/rust-support/rustc-flags"
166 | ;;
167 | cargo:rustc-env=*=*)
168 | printf 'export %q=%q\n' "${rhs%%=*}" "${rhs#*=}" >>"$out/rust-support/rustc-env"
169 | ;;
170 | cargo:rustc-cdylib-link-arg=*)
171 | echo "-Clink-arg=$rhs" >>"$out/rust-support/rustc-cdylib-flags"
172 | ;;
173 | cargo:warning=*)
174 | printf "\033[0;1;33mWarning\033[0m: %s\n" "$rhs"
175 | ;;
176 | cargo:*=*)
177 | if [[ -n "${links:-}" ]]; then
178 | rhs="${line#*:}"
179 | local k="DEP_${links}_${rhs%%=*}" v="${rhs#*=}"
180 | k="${k^^}"
181 | k="${k//-/_}"
182 | printf 'export %q=%q\n' "$k" "$v" >>"$out/rust-support/links-metadata"
183 | else
184 | printf "\033[0;1;33mWarning\033[0m: no 'links' defined in Cargo.toml, ignoring %s" "$line"
185 | fi
186 | ;;
187 | cargo:*)
188 | echo "Unknown or wrong output line: $line"
189 | exit 1
190 | ;;
191 | *)
192 | ;;
193 | esac
194 | done <"$stdoutFile"
195 |
196 | local file
197 | for file in "$out"/rust-support/{rustc-*,links-metadata*}; do
198 | sort "$file" -o "$file"
199 | done
200 |
201 | runHook postInstall
202 | }
203 |
204 | genericBuild
205 |
--------------------------------------------------------------------------------
/build-rust-crate/builder-common.sh:
--------------------------------------------------------------------------------
1 | declare -a buildFlagsArray
2 | buildFlagsArray+=( --color=always )
3 |
4 | # Collect all transitive dependencies (symlinks).
5 | collectTransDeps() {
6 | local collectDir="$1" line rename procMacro depOut depDev
7 | mkdir -p "$collectDir"
8 | shift
9 | for line in "$@"; do
10 | IFS=: read -r rename procMacro depOut depDev <<<"$line"
11 | # May be empty.
12 | cp --no-dereference --no-clobber -t $collectDir $depDev/rust-support/deps-closure/* 2>/dev/null || true
13 | done
14 | }
15 |
16 | addExternFlags() {
17 | local var="$1" kind="$2" line rename procMacro depOut depDev paths
18 | shift 2
19 | for line in "$@"; do
20 | IFS=: read -r rename procMacro depOut depDev <<<"$line"
21 |
22 | if [[ -n "$procMacro" ]]; then
23 | paths=("$depOut"/lib/*"$sharedLibraryExt")
24 | elif [[ "$kind" == meta ]]; then
25 | paths=("$depDev"/lib/*.rmeta)
26 | else
27 | # FIXME: Currently we only link rlib.
28 | paths=("$depOut"/lib/*.rlib)
29 | fi
30 |
31 | if (( ${#paths[@]} == 0 )); then
32 | echo "No dependent library found for $line"
33 | exit 1
34 | elif (( ${#paths[@]} > 1 )); then
35 | echo "Multiple candidate found for dependent library $line, found: ${paths[*]}"
36 | exit 1
37 | fi
38 | if [[ -z "$rename" ]]; then
39 | if [[ "${paths[0]##*/}" =~ ^lib(.*)(-.*)(\.rmeta|\.rlib|"$sharedLibraryExt")$ ]]; then
40 | rename="${BASH_REMATCH[1]}"
41 | else
42 | echo "Invalid library name: ${paths[0]}"
43 | exit 1
44 | fi
45 | fi
46 | eval "$var"'+=(--extern="$rename=${paths[0]}")'
47 | done
48 | }
49 |
50 | addFeatures() {
51 | local var="$1" feat
52 | shift
53 | for feat in "$@"; do
54 | eval "$var"'+=(--cfg="feature=\"$feat\"")'
55 | done
56 | }
57 |
58 | importBuildOut() {
59 | local var="$1" cvar="$2" drv="$3" flags
60 | [[ ! -e "$drv/rust-support/build-stdout" ]] && return
61 |
62 | echo export OUT_DIR="$drv/rust-support/out-dir"
63 | export OUT_DIR="$drv/rust-support/out-dir"
64 |
65 | if [[ -e "$drv/rust-support/rustc-env" ]]; then
66 | cat "$drv/rust-support/rustc-env"
67 | source "$drv/rust-support/rustc-env"
68 | fi
69 |
70 | if [[ -e "$drv/rust-support/rustc-flags" ]]; then
71 | mapfile -t flags <"$drv/rust-support/rustc-flags"
72 | eval "$var"'+=("${flags[@]}")'
73 | fi
74 |
75 | if [[ -e "$drv/rust-support/rustc-cdylib-flags" ]]; then
76 | mapfile -t flags <"$drv/rust-support/rustc-cdylib-flags"
77 | eval "$cvar"'+=("${flags[@]}")'
78 | fi
79 | }
80 |
81 | runRustc() {
82 | local msg="$1"
83 | shift
84 | echo "$msg: RUSTC ${*@Q}"
85 | $RUSTC "$@"
86 | }
87 |
88 | convertCargoToml() {
89 | local cargoToml="${1:-"$(pwd)/Cargo.toml"}"
90 | cargoTomlJson="$(mktemp "$(dirname "$cargoToml")/Cargo.json.XXX")"
91 | toml2json <"$cargoToml" >"$cargoTomlJson"
92 | }
93 |
94 | # https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates
95 | setCargoCommonBuildEnv() {
96 | # CARGO_CRATE_NAME is set outside since targets have individual crate names.
97 |
98 | # export CARGO=
99 | CARGO_MANIFEST_DIR="$(dirname "$cargoTomlJson")"
100 | export CARGO_MANIFEST_DIR
101 |
102 | CARGO_PKG_NAME="$(jq --raw-output '.package.name // ""' "$cargoTomlJson")"
103 | CARGO_PKG_VERSION="$(jq --raw-output '.package.version // ""' "$cargoTomlJson")"
104 | if [[ -z "CARGO_PKG_NAME" ]]; then
105 | echo "Package name must be set"
106 | exit 1
107 | fi
108 | if [[ -z "CARGO_PKG_VERSION" ]]; then
109 | echo "Package version must be set"
110 | exit 1
111 | fi
112 |
113 | CARGO_PKG_AUTHORS="$(jq --raw-output '.package.authors // [] | join(":")' "$cargoTomlJson")"
114 | CARGO_PKG_DESCRIPTION="$(jq --raw-output '.package.description // ""' "$cargoTomlJson")"
115 | CARGO_PKG_HOMEPAGE="$(jq --raw-output '.package.homepage // ""' "$cargoTomlJson")"
116 | CARGO_PKG_LICENSE="$(jq --raw-output '.package.license // ""' "$cargoTomlJson")"
117 | CARGO_PKG_LICENSE_FILE="$(jq --raw-output '.package."license-file" // ""' "$cargoTomlJson")"
118 | export CARGO_PKG_NAME CARGO_PKG_VERSION CARGO_PKG_AUTHORS CARGO_PKG_DESCRIPTION \
119 | CARGO_PKG_HOMEPAGE CARGO_PKG_LICENSE CARGO_PKG_LICENSE_FILE
120 |
121 | if [[ "$version" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(-([A-Za-z0-9.-]+))?(\+.*)?$ ]]; then
122 | export CARGO_PKG_VERSION_MAJOR="${BASH_REMATCH[1]}"
123 | export CARGO_PKG_VERSION_MINOR="${BASH_REMATCH[2]}"
124 | export CARGO_PKG_VERSION_PATCH="${BASH_REMATCH[3]}"
125 | export CARGO_PKG_VERSION_PRE="${BASH_REMATCH[4]}"
126 | else
127 | echo "Invalid version: $version"
128 | fi
129 | }
130 |
--------------------------------------------------------------------------------
/build-rust-crate/builder-lib.sh:
--------------------------------------------------------------------------------
1 | source $stdenv/setup
2 | source $builderCommon
3 | shopt -s nullglob
4 |
5 | buildFlagsArray+=( -Cmetadata="$rustcMeta" )
6 |
7 | configurePhase() {
8 | runHook preConfigure
9 |
10 | convertCargoToml
11 |
12 | libSrc="$(jq --raw-output '.lib.path // ""' "$cargoTomlJson")"
13 | if [[ -z "$libSrc" && -e src/lib.rs ]]; then
14 | libSrc=src/lib.rs
15 | fi
16 | if [[ ! -e "$libSrc" ]]; then
17 | echo "No library to be built"
18 | mkdir $out $dev
19 | exit 0
20 | fi
21 |
22 | crateName="$(jq --raw-output '.lib.name // (.package.name // "" | gsub("-"; "_"))' "$cargoTomlJson")"
23 | if [[ -z "$crateName" ]]; then
24 | echo "Package name must be set"
25 | exit 1
26 | fi
27 |
28 | edition="$(jq --raw-output '.package.edition // .lib.edition // ""' "$cargoTomlJson")"
29 | if [[ -n "$edition" ]]; then
30 | buildFlagsArray+=(--edition="$edition")
31 | fi
32 |
33 | mapfile -t crateTypes < <(jq --raw-output '.lib."crate-type" // ["lib"] | .[]' "$cargoTomlJson")
34 | cargoTomlIsProcMacro="$(jq --raw-output 'if .lib."proc-macro" then "1" else "" end' "$cargoTomlJson")"
35 | if [[ "$cargoTomlIsProcMacro" != "$procMacro" ]]; then
36 | echo "Cargo.toml says proc-macro = ${cargoTomlIsProcMacro:-0} but it is built with procMacro = ${procMacro:-0}"
37 | exit 1
38 | fi
39 | if [[ -n "$procMacro" ]]; then
40 | # Override crate type.
41 | crateTypes=("proc-macro")
42 | buildFlagsArray+=(--extern=proc_macro)
43 | fi
44 |
45 | needLinkDeps=
46 | buildCdylib=
47 | for crateType in "${crateTypes[@]}"; do
48 | case "$crateType" in
49 | lib|rlib)
50 | ;;
51 | dylib|staticlib|proc-macro|bin)
52 | needLinkDeps=1
53 | ;;
54 | cdylib)
55 | buildCdylib=1
56 | ;;
57 | *)
58 | echo "Unsupported crate-type: $crateType"
59 | exit 1
60 | ;;
61 | esac
62 | done
63 | if [[ -n "$needLinkDeps" ]]; then
64 | addExternFlags buildFlagsArray link $dependencies
65 | else
66 | addExternFlags buildFlagsArray meta $dependencies
67 | fi
68 |
69 | declare -a cdylibBuildFlagsArray
70 | importBuildOut buildFlagsArray cdylibBuildFlagsArray "$buildDrv"
71 | # FIXME: cargo include cdylib flags for all crate-types once cdylib is included.
72 | buildFlagsArray+=( "${cdylibBuildFlagsArray[@]}" )
73 |
74 | addFeatures buildFlagsArray $features
75 | setCargoCommonBuildEnv
76 | export CARGO_CRATE_NAME="$crateName"
77 |
78 | collectTransDeps $dev/rust-support/deps-closure $dependencies
79 | buildFlagsArray+=(-Ldependency="$dev/rust-support/deps-closure")
80 |
81 | runHook postConfigure
82 | }
83 |
84 | buildPhase() {
85 | runHook preBuild
86 |
87 | local crateTypesCommaSep
88 | printf -v crateTypesCommaSep '%s,' "${crateTypes[@]}"
89 | crateTypesCommaSep="${crateTypesCommaSep%,}"
90 |
91 | mkdir -p $out/lib
92 | runRustc "Building lib" \
93 | "$libSrc" \
94 | --out-dir="$out/lib" \
95 | --crate-name="$crateName" \
96 | --crate-type="$crateTypesCommaSep" \
97 | --emit=metadata,link \
98 | -Cextra-filename="-$rustcMeta" \
99 | $buildFlags \
100 | "${buildFlagsArray[@]}"
101 |
102 | runHook postBuild
103 | }
104 |
105 | installPhase() {
106 | runHook preInstall
107 |
108 | mkdir -p $out $bin $dev/lib $dev/rust-support/deps-closure
109 |
110 | # May be empty.
111 | mv -t $dev/lib $out/lib/*.rmeta 2>/dev/null || true
112 | ln -sft $dev/rust-support/deps-closure $out/lib/* $dev/lib/* 2>/dev/null || true
113 |
114 | runHook postInstall
115 | }
116 |
117 | genericBuild
118 |
--------------------------------------------------------------------------------
/build-rust-crate/default.nix:
--------------------------------------------------------------------------------
1 | { lib, nocargo-lib, stdenv, buildPackages, rust, toml2json, jq }:
2 | { pname
3 | , version
4 | , src
5 | , rustc ? buildPackages.rustc
6 | , links ? null
7 | # [ { rename = "foo" /* or null */; drv = ; } ]
8 | , dependencies ? []
9 | # Normal dependencies with non empty `links`, which will propagate `DEP__` environments to build script.
10 | , linksDependencies ? dependencies
11 | , buildDependencies ? []
12 | , features ? []
13 | , profile ? {}
14 | , capLints ? null
15 | , buildFlags ? []
16 | , buildScriptBuildFlags ? []
17 | , procMacro ? false
18 |
19 | , nativeBuildInputs ? []
20 | , propagatedBuildInputs ? []
21 | , ...
22 | }@args:
23 | let
24 | inherit (nocargo-lib.target-cfg) platformToCfgAttrs;
25 |
26 | mkRustcMeta = dependencies: features: let
27 | deps = lib.concatMapStrings (dep: dep.drv.rustcMeta) dependencies;
28 | feats = lib.concatStringsSep ";" features;
29 | final = "${pname} ${version} ${feats} ${deps}";
30 | in
31 | lib.substring 0 16 (builtins.hashString "sha256" final);
32 |
33 | buildRustcMeta = mkRustcMeta buildDependencies [];
34 | rustcMeta = mkRustcMeta dependencies [];
35 |
36 | # TODO: Pass target binary paths instead of drv here?
37 | mkDeps = map ({ rename, drv, ... }: lib.concatStringsSep ":" [
38 | (toString rename)
39 | (toString drv.procMacro)
40 | drv.out
41 | drv.dev
42 | ]);
43 | toDevDrvs = map ({ drv, ... }: drv.dev);
44 |
45 | buildDeps = mkDeps buildDependencies;
46 | libDeps = mkDeps dependencies;
47 |
48 | builderCommon = ./builder-common.sh;
49 |
50 | convertBool = f: t: x:
51 | if x == true then t
52 | else if x == false then f
53 | else x;
54 |
55 | # https://doc.rust-lang.org/cargo/reference/profiles.html
56 | profileToRustcFlags = p:
57 | []
58 | ++ lib.optional (p.opt-level or 0 != 0) "-Copt-level=${toString p.opt-level}"
59 | ++ lib.optional (p.debug or false != false) "-Cdebuginfo=${if p.debug == true then "2" else toString p.debug}"
60 | # TODO: `-Cstrip` is not handled since stdenv will always strip them.
61 | ++ lib.optional (p ? debug-assertions) "-Cdebug-assertions=${convertBool "no" "yes" p.debug-assertions}"
62 | ++ lib.optional (p ? overflow-checks) "-Coverflow-checks=${convertBool "no" "yes" p.debug-assertions}"
63 | ++ lib.optional (!procMacro && p.lto or false != false) "-Clto=${if p.lto == true then "fat" else p.lto}"
64 | ++ lib.optional (p.panic or "unwind" != "unwind") "-Cpanic=${p.panic}"
65 | # `incremental` is not useful since Nix builds in a sandbox.
66 | ++ lib.optional (p ? codegen-units) "-Ccodegen-units=${toString p.codegen-units}"
67 | ++ lib.optional (p.rpath or false) "-Crpath"
68 |
69 | ++ lib.optional (p.lto or false == false) "-Cembed-bitcode=no"
70 | ;
71 |
72 | convertProfile = p: {
73 | buildFlags =
74 | profileToRustcFlags p
75 | ++ lib.optional (capLints != null) "--cap-lints=${capLints}"
76 | ++ buildFlags;
77 |
78 | buildScriptBuildFlags =
79 | profileToRustcFlags (p.build-override or {})
80 | ++ buildScriptBuildFlags;
81 |
82 | # Build script environments.
83 | PROFILE = p.name or null;
84 | OPT_LEVEL = p.opt-level or 0;
85 | DEBUG = p.debug or 0 != 0;
86 | };
87 |
88 | profile' = convertProfile profile;
89 | buildProfile' = convertProfile (profile.build-override or {});
90 |
91 | commonArgs = {
92 | inherit pname version src;
93 |
94 | nativeBuildInputs = [ toml2json jq ] ++ nativeBuildInputs;
95 |
96 | sharedLibraryExt = stdenv.hostPlatform.extensions.sharedLibrary;
97 |
98 | inherit capLints;
99 |
100 | RUSTC = "${rustc}/bin/rustc";
101 | } // removeAttrs args [
102 | "pname"
103 | "version"
104 | "src"
105 | "rustc"
106 | "links"
107 | "dependencies"
108 | "linksDependencies"
109 | "buildDependencies"
110 | "features"
111 | "profile"
112 | "capLints"
113 | "buildFlags"
114 | "buildScriptBuildFlags"
115 | "procMacro"
116 | "nativeBuildInputs"
117 | "propagatedBuildInputs"
118 | ];
119 |
120 | cargoCfgs = lib.mapAttrs' (key: value: {
121 | name = "CARGO_CFG_${lib.toUpper key}";
122 | value = if lib.isList value then lib.concatStringsSep "," value
123 | else if value == true then ""
124 | else value;
125 | }) (platformToCfgAttrs stdenv.hostPlatform);
126 |
127 | buildDrv = stdenv.mkDerivation ({
128 | name = "rust_${pname}-${version}-build";
129 | builder = ./builder-build-script.sh;
130 | inherit propagatedBuildInputs builderCommon features links;
131 | rustcMeta = buildRustcMeta;
132 | dependencies = buildDeps;
133 |
134 | linksDependencies = map (dep: dep.drv.buildDrv) linksDependencies;
135 |
136 | HOST = rust.toRustTarget stdenv.buildPlatform;
137 | TARGET = rust.toRustTarget stdenv.hostPlatform;
138 |
139 | # This drv links for `build_script_build`.
140 | # So include transitively propagated upstream `-sys` crates' ld dependencies.
141 | buildInputs = toDevDrvs dependencies;
142 |
143 | # Build script may produce object files and static libraries which should not be modified.
144 | dontFixup = true;
145 |
146 | } // commonArgs // cargoCfgs // buildProfile');
147 |
148 | libDrv = stdenv.mkDerivation ({
149 | name = "rust_${pname}-${version}";
150 |
151 | builder = ./builder-lib.sh;
152 | outputs = [ "out" "dev" ];
153 | inherit builderCommon buildDrv features rustcMeta procMacro;
154 |
155 | # Transitively propagate upstream `-sys` crates' ld dependencies.
156 | # Since `rlib` doesn't link.
157 | propagatedBuildInputs = toDevDrvs dependencies ++ propagatedBuildInputs;
158 |
159 | dependencies = libDeps;
160 |
161 | } // commonArgs // profile');
162 |
163 | binDrv = stdenv.mkDerivation ({
164 | name = "rust_${pname}-${version}-bin";
165 | builder = ./builder-bin.sh;
166 | inherit propagatedBuildInputs builderCommon buildDrv features rustcMeta;
167 |
168 | libOutDrv = libDrv.out;
169 | libDevDrv = libDrv.dev;
170 |
171 | # This requires linking.
172 | # Include transitively propagated upstream `-sys` crates' ld dependencies.
173 | buildInputs = toDevDrvs dependencies;
174 |
175 | dependencies = libDeps;
176 | } // commonArgs // profile');
177 |
178 | in
179 | libDrv // {
180 | build = buildDrv;
181 | bin = binDrv;
182 | }
183 |
--------------------------------------------------------------------------------
/cache/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "cache"
3 | version = "0.0.0"
4 | description = "Virtual package to reference all popular crates to be cached"
5 |
6 | # N.B. We cannot use `={version}` since cargo doesn't allow two compatible versions of
7 | # the same crate appears in the dependency tree, eg. getrandom 0.2.6 and getrandom@0.2.7.
8 | # So we use carpet specifiers to allow merging compatible versions to the highest one currently.
9 | [dependencies]
10 | addr2line = "0.17"
11 | adler = "1"
12 | ahash = "0.7"
13 | aho-corasick = "0.7"
14 | ansi_term = "0.12"
15 | anyhow = "1"
16 | arrayref = "0.3"
17 | arrayvec = "0.7"
18 | arrayvec-2 = { package = "arrayvec", version = "0.5" }
19 | async-stream = "0.3"
20 | async-stream-impl = "0.3"
21 | async-trait = "0.1"
22 | atty = "0.2"
23 | autocfg = "1"
24 | base64 = "0.13"
25 | bincode = "1"
26 | bitflags = "1"
27 | block-buffer = "0.10"
28 | block-buffer-2 = { package = "block-buffer", version = "0.9" }
29 | bstr = "0.2"
30 | byteorder = "1"
31 | bytes = "1"
32 | cc = "1"
33 | cfg-if = "1"
34 | cfg-if-2 = { package = "cfg-if", version = "0.1" }
35 | chrono = "0.4"
36 | clap = "2"
37 | clap_lex = "0.2"
38 | constant_time_eq = "0.1"
39 | convert_case = "0.4"
40 | core-foundation-sys = "0.8"
41 | cpufeatures = "0.2"
42 | crc32fast = "1"
43 | crossbeam-channel = "0.5"
44 | crossbeam-deque = "0.8"
45 | crossbeam-utils = "0.8"
46 | crunchy = "0.2"
47 | crypto-common = "0.1"
48 | csv = "1"
49 | csv-core = "0.1"
50 | darling_macro = "0.13"
51 | data-encoding = "2"
52 | derivative = "2"
53 | derive_more = "0.99"
54 | digest = "0.10"
55 | digest-2 = { package = "digest", version = "0.9" }
56 | dirs-next = "2"
57 | dirs-sys = "0.3"
58 | dirs-sys-next = "0.1"
59 | either = "1"
60 | encoding_rs = "0.8"
61 | env_logger = "0.9"
62 | fallible-iterator = "0.2"
63 | fastrand = "1"
64 | flate2 = "1"
65 | fnv = "1"
66 | foreign-types = "0.3"
67 | foreign-types-shared = "0.1"
68 | form_urlencoded = "1"
69 | futures = "0.3"
70 | futures-channel = "0.3"
71 | futures-core = "0.3"
72 | futures-executor = "0.3"
73 | futures-io = "0.3"
74 | futures-macro = "0.3"
75 | futures-sink = "0.3"
76 | futures-task = "0.3"
77 | futures-util = "0.3"
78 | generic-array = "0.14"
79 | getrandom = "0.2"
80 | getrandom-2 = { package = "getrandom", version = "0.1" }
81 | gimli = "0.26"
82 | glob = "0.3"
83 | h2 = "0.3"
84 | half = "1"
85 | hashbrown = "0.12"
86 | hashbrown-2 = { package = "hashbrown", version = "0.11" }
87 | heck = "0.4"
88 | heck-2 = { package = "heck", version = "0.3" }
89 | hermit-abi = "0.1"
90 | hex = "0.4"
91 | hmac = "0.12"
92 | hostname = "0.3"
93 | http = "0.2"
94 | http-body = "0.4"
95 | httparse = "1"
96 | httpdate = "1"
97 | humantime = "2"
98 | hyper = "0.14"
99 | hyper-tls = "0.5"
100 | ident_case = "1"
101 | idna = "0.2"
102 | indexmap = "1"
103 | instant = "0.1"
104 | ipnet = "2"
105 | itertools = "0.10"
106 | itoa = "1"
107 | itoa-2 = { package = "itoa", version = "0.4" }
108 | jobserver = "0.1"
109 | lazy_static = "1"
110 | lazycell = "1"
111 | libc = "0.2"
112 | libloading = "0.7"
113 | linked-hash-map = "0.5"
114 | lock_api = "0.4"
115 | log = "0.4"
116 | maplit = "1"
117 | match_cfg = "0.1"
118 | matchers = "0.1"
119 | matches = "0.1"
120 | memchr = "2"
121 | memoffset = "0.6"
122 | mime = "0.3"
123 | minimal-lexical = "0.2"
124 | miniz_oxide = "0.5"
125 | miniz_oxide-2 = { package = "miniz_oxide", version = "0.4" }
126 | mio = "0.8"
127 | multimap = "0.8"
128 | native-tls = "0.2"
129 | nom = "7"
130 | num-bigint = "0.4"
131 | num-integer = "0.1"
132 | num-iter = "0.1"
133 | num-traits = "0.2"
134 | num_cpus = "1"
135 | num_threads = "0.1"
136 | object = "0.29"
137 | once_cell = "1"
138 | oorandom = "11"
139 | opaque-debug = "0.3"
140 | openssl-macros = "0.1"
141 | openssl-probe = "0.1"
142 | openssl-sys = "0.9"
143 | parking_lot = "0.12"
144 | parking_lot-2 = { package = "parking_lot", version = "0.11" }
145 | parking_lot_core = "0.9"
146 | parking_lot_core-2 = { package = "parking_lot_core", version = "0.8" }
147 | peeking_take_while = "0.1"
148 | percent-encoding = "2"
149 | phf_shared = "0.10"
150 | pin-project-lite = "0.2"
151 | pin-utils = "0.1"
152 | pkg-config = "0.3"
153 | ppv-lite86 = "0.2"
154 | proc-macro-error = "1"
155 | proc-macro-error-attr = "1"
156 | proc-macro-hack = "0.5"
157 | proc-macro2 = "1"
158 | quick-error = "1"
159 | quote = "1"
160 | rand = "0.8"
161 | rand-2 = { package = "rand", version = "0.7" }
162 | rand_chacha = "0.3"
163 | rand_chacha-2 = { package = "rand_chacha", version = "0.2" }
164 | rand_core = "0.6"
165 | rand_core-2 = { package = "rand_core", version = "0.5" }
166 | rayon = "1"
167 | rayon-core = "1"
168 | regex = "1"
169 | regex-automata = "0.1"
170 | regex-syntax = "0.6"
171 | remove_dir_all = "0.5"
172 | reqwest = "0.11"
173 | ring = "0.16"
174 | rustc-demangle = "0.1"
175 | rustc-hash = "1"
176 | rustc_version = "0.4"
177 | rustc_version-2 = { package = "rustc_version", version = "0.2" }
178 | rustls = "0.20"
179 | ryu = "1"
180 | same-file = "1"
181 | scopeguard = "1"
182 | sct = "0.7"
183 | semver = "0.9"
184 | semver-parser = "0.7"
185 | serde = "1"
186 | serde_cbor = "0.11"
187 | serde_derive = "1"
188 | serde_json = "1"
189 | serde_urlencoded = "0.7"
190 | sha-1 = "0.10"
191 | sha2 = "0.10"
192 | sha2-2 = { package = "sha2", version = "0.9" }
193 | sharded-slab = "0.1"
194 | shlex = "1"
195 | signal-hook-registry = "1"
196 | siphasher = "0.3"
197 | slab = "0.4"
198 | smallvec = "1"
199 | socket2 = "0.4"
200 | spin = "0.5"
201 | stable_deref_trait = "1"
202 | static_assertions = "1"
203 | strsim = "0.10"
204 | strsim-2 = { package = "strsim", version = "0.8" }
205 | subtle = "2"
206 | syn = "1"
207 | synstructure = "0.12"
208 | tempfile = "3"
209 | termcolor = "1"
210 | terminal_size = "0.1"
211 | textwrap = "0.15"
212 | textwrap-2 = { package = "textwrap", version = "0.11" }
213 | thiserror = "1"
214 | thiserror-impl = "1"
215 | thread_local = "1"
216 | time = "0.1"
217 | time-macros = "0.2"
218 | tinyvec = "1"
219 | tinyvec_macros = "0.1"
220 | tokio-macros = "1"
221 | tokio-native-tls = "0.3"
222 | tokio-rustls = "0.23"
223 | tokio-stream = "0.1"
224 | tokio-util = "0.7"
225 | toml = "0.5"
226 | tower-layer = "0.3"
227 | tower-service = "0.3"
228 | tracing = "0.1"
229 | tracing-attributes = "0.1"
230 | tracing-core = "0.1"
231 | tracing-futures = "0.2"
232 | tracing-log = "0.1"
233 | try-lock = "0.2"
234 | typenum = "1"
235 | unicase = "2"
236 | unicode-bidi = "0.3"
237 | unicode-ident = "1"
238 | unicode-normalization = "0.1"
239 | unicode-segmentation = "1"
240 | unicode-width = "0.1"
241 | unicode-xid = "0.2"
242 | untrusted = "0.7"
243 | url = "2"
244 | uuid = "1"
245 | uuid-2 = { package = "uuid", version = "0.8" }
246 | vcpkg = "0.2"
247 | vec_map = "0.8"
248 | version_check = "0.9"
249 | void = "1"
250 | walkdir = "2"
251 | want = "0.3"
252 | wasi = "0.11"
253 | webpki = "0.22"
254 | which = "4"
255 | winapi = "0.3"
256 | winapi-i686-pc-windows-gnu = "0.4"
257 | winapi-util = "0.1"
258 | winapi-x86_64-pc-windows-gnu = "0.4"
259 | windows-sys = "0.36"
260 | windows_aarch64_msvc = "0.36"
261 | windows_i686_gnu = "0.36"
262 | windows_i686_msvc = "0.36"
263 | windows_x86_64_gnu = "0.36"
264 | windows_x86_64_msvc = "0.36"
265 | yaml-rust = "0.4"
266 |
--------------------------------------------------------------------------------
/cache/default.nix:
--------------------------------------------------------------------------------
1 | { writeText, mkRustPackageOrWorkspace }:
2 | let
3 | ws = mkRustPackageOrWorkspace {
4 | src = ./.;
5 | };
6 | in
7 | writeText "cache-paths"
8 | (toString (ws.dev.cache.dependencies ++ ws.release.cache.dependencies))
9 |
--------------------------------------------------------------------------------
/cache/src/lib.rs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oxalica/nocargo/089f487c2c123454c8b9a381a99999b63c53d1d6/cache/src/lib.rs
--------------------------------------------------------------------------------
/crates-io-override/default.nix:
--------------------------------------------------------------------------------
1 | { lib, pkgs }:
2 | let
3 | inherit (lib) optionalAttrs listToAttrs;
4 | inherit (builtins) compareVersions;
5 | procMacroOverrides =
6 | listToAttrs
7 | (map (name: {
8 | inherit name;
9 | value = _: { procMacro = true; };
10 | }) (import ./proc-macro.nix));
11 | in
12 | with pkgs;
13 | procMacroOverrides //
14 | {
15 | libz-sys = { features, ... }: optionalAttrs (!(features ? static)) {
16 | nativeBuildInputs = [ pkg-config ];
17 | propagatedBuildInputs = [ zlib ];
18 | };
19 |
20 | openssl-sys = { features, ... }: optionalAttrs (!(features ? vendored)) {
21 | nativeBuildInputs = [ pkg-config ];
22 | propagatedBuildInputs = [ openssl ];
23 | };
24 |
25 | proc-macro-hack = { version, ... }: {
26 | procMacro = compareVersions version "0.5.0" >= 0;
27 | };
28 | }
29 |
--------------------------------------------------------------------------------
/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "flake-utils": {
4 | "inputs": {
5 | "systems": "systems"
6 | },
7 | "locked": {
8 | "lastModified": 1701680307,
9 | "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
10 | "owner": "numtide",
11 | "repo": "flake-utils",
12 | "rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
13 | "type": "github"
14 | },
15 | "original": {
16 | "owner": "numtide",
17 | "repo": "flake-utils",
18 | "type": "github"
19 | }
20 | },
21 | "nixpkgs": {
22 | "locked": {
23 | "lastModified": 1704161960,
24 | "narHash": "sha256-QGua89Pmq+FBAro8NriTuoO/wNaUtugt29/qqA8zeeM=",
25 | "owner": "NixOS",
26 | "repo": "nixpkgs",
27 | "rev": "63143ac2c9186be6d9da6035fa22620018c85932",
28 | "type": "github"
29 | },
30 | "original": {
31 | "owner": "NixOS",
32 | "ref": "nixpkgs-unstable",
33 | "repo": "nixpkgs",
34 | "type": "github"
35 | }
36 | },
37 | "registry-crates-io": {
38 | "flake": false,
39 | "locked": {
40 | "lastModified": 1704732624,
41 | "narHash": "sha256-FWX5JX33ievm6JuIEp1lI5EPndYt8s6anU+fQBYuFKc=",
42 | "owner": "rust-lang",
43 | "repo": "crates.io-index",
44 | "rev": "4445fa0011e6ff9989985651bec57eb4cbefcadd",
45 | "type": "github"
46 | },
47 | "original": {
48 | "owner": "rust-lang",
49 | "repo": "crates.io-index",
50 | "type": "github"
51 | }
52 | },
53 | "root": {
54 | "inputs": {
55 | "flake-utils": "flake-utils",
56 | "nixpkgs": "nixpkgs",
57 | "registry-crates-io": "registry-crates-io"
58 | }
59 | },
60 | "systems": {
61 | "locked": {
62 | "lastModified": 1681028828,
63 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
64 | "owner": "nix-systems",
65 | "repo": "default",
66 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
67 | "type": "github"
68 | },
69 | "original": {
70 | "owner": "nix-systems",
71 | "repo": "default",
72 | "type": "github"
73 | }
74 | }
75 | },
76 | "root": "root",
77 | "version": 7
78 | }
79 |
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | inputs = {
3 | flake-utils.url = "github:numtide/flake-utils";
4 | nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
5 | registry-crates-io = {
6 | url = "github:rust-lang/crates.io-index";
7 | flake = false;
8 | };
9 | };
10 |
11 | outputs = { self, flake-utils, nixpkgs, registry-crates-io }@inputs:
12 | let
13 | supportedSystems = [ "x86_64-linux" "aarch64-linux" ];
14 |
15 | inherit (builtins) toJSON typeOf;
16 | inherit (nixpkgs.lib) isDerivation isFunction isAttrs mapAttrsToList listToAttrs flatten;
17 |
18 | nocargo-lib = import ./lib { inherit (nixpkgs) lib; };
19 |
20 | in flake-utils.lib.eachSystem supportedSystems (system:
21 | let
22 | pkgs = nixpkgs.legacyPackages.${system};
23 | defaultRegistries = {
24 | "https://github.com/rust-lang/crates.io-index" =
25 | nocargo-lib.pkg-info.mkIndex pkgs.fetchurl registry-crates-io
26 | (import ./crates-io-override {
27 | inherit (nixpkgs) lib;
28 | inherit pkgs;
29 | });
30 | };
31 | in rec {
32 | apps.default = {
33 | type = "app";
34 | program = "${packages.noc}/bin/noc";
35 | };
36 |
37 | # Is there a better place? `naersk` places builders under `lib.${system}`.
38 | lib = rec {
39 | mkIndex = nocargo-lib.pkg-info.mkIndex pkgs.fetchurl;
40 | buildRustCrate = pkgs.callPackage ./build-rust-crate {
41 | inherit (packages) toml2json;
42 | inherit nocargo-lib;
43 | };
44 | mkRustPackageOrWorkspace = pkgs.callPackage nocargo-lib.support.mkRustPackageOrWorkspace {
45 | inherit defaultRegistries buildRustCrate;
46 | };
47 | };
48 |
49 | packages = rec {
50 | default = noc;
51 | toml2json = pkgs.callPackage ./toml2json { };
52 | noc = (lib.mkRustPackageOrWorkspace {
53 | src = ./noc;
54 | }).release.nocargo.bin;
55 |
56 | cache = pkgs.callPackage ./cache {
57 | inherit (lib) mkRustPackageOrWorkspace;
58 | };
59 | };
60 |
61 | checks = let
62 | okDrv = derivation {
63 | name = "success";
64 | inherit system;
65 | builder = "/bin/sh";
66 | args = [ "-c" ": >$out" ];
67 | };
68 |
69 | checkArgs = {
70 | inherit pkgs defaultRegistries;
71 |
72 | assertEq = got: expect: {
73 | __assertion = true;
74 | fn = name:
75 | if toJSON got == toJSON expect then
76 | okDrv
77 | else
78 | pkgs.runCommand name {
79 | nativeBuildInputs = [ pkgs.jq ];
80 | got = toJSON got;
81 | expect = toJSON expect;
82 | } ''
83 | if [[ ''${#got} < 32 && ''${#expect} < 32 ]]; then
84 | echo "got: $got"
85 | echo "expect: $expect"
86 | else
87 | echo "got:"
88 | jq . <<<"$got"
89 | echo
90 | echo "expect:"
91 | jq . <<<"$expect"
92 | echo
93 | echo "diff:"
94 | diff -y <(jq . <<<"$got") <(jq . <<<"$expect")
95 | exit 1
96 | fi
97 | '';
98 | };
99 | };
100 |
101 | tests = with nocargo-lib; {
102 | _0000-semver-compare = semver.semver-compare-tests;
103 | _0001-semver-req = semver.semver-req-tests;
104 | _0002-cfg-parser = target-cfg.cfg-parser-tests;
105 | _0003-cfg-eval = target-cfg.cfg-eval-tests;
106 | _0004-platform-cfg = target-cfg.platform-cfg-tests;
107 | _0005-glob = glob.glob-tests;
108 | _0006-sanitize-relative-path = support.sanitize-relative-path-tests;
109 |
110 | _0100-pkg-info-from-toml = pkg-info.pkg-info-from-toml-tests;
111 | _0101-preprocess-feature = resolve.preprocess-feature-tests;
112 | _0102-update-feature = resolve.update-feature-tests;
113 | _0103-resolve-feature = resolve.resolve-feature-tests;
114 |
115 | _0200-resolve-deps = resolve.resolve-deps-tests;
116 | _0201-build-from-src-dry = support.build-from-src-dry-tests;
117 | } // import ./tests {
118 | inherit pkgs self inputs defaultRegistries;
119 | };
120 |
121 | flattenTests = prefix: v:
122 | if isDerivation v then {
123 | name = prefix;
124 | value = v;
125 | } else if v ? __assertion then {
126 | name = prefix;
127 | value = v.fn prefix;
128 | } else if isFunction v then
129 | flattenTests prefix (v checkArgs)
130 | else if isAttrs v then
131 | mapAttrsToList (name: flattenTests "${prefix}-${name}") v
132 | else
133 | throw "Unexpect test type: ${typeOf v}";
134 |
135 | tests' = listToAttrs (flatten (mapAttrsToList flattenTests tests));
136 |
137 | in tests';
138 | });
139 | }
140 |
141 |
--------------------------------------------------------------------------------
/lib/default.nix:
--------------------------------------------------------------------------------
1 | { lib }:
2 | let
3 | callLib = file: import file { inherit lib self; };
4 | self = {
5 | glob = callLib ./glob.nix;
6 | semver = callLib ./semver.nix;
7 | target-cfg = callLib ./target-cfg.nix;
8 |
9 | pkg-info = callLib ./pkg-info.nix;
10 | resolve = callLib ./resolve.nix;
11 | support = callLib ./support.nix;
12 | };
13 | in self
14 |
--------------------------------------------------------------------------------
/lib/glob.nix:
--------------------------------------------------------------------------------
1 | { lib, ... }:
2 | let
3 | inherit (builtins) match readDir split foldl';
4 | inherit (lib)
5 | replaceStrings isString concatStrings stringLength hasPrefix substring concatStringsSep
6 | head tail init filter
7 | isAttrs attrNames mapAttrs getAttrFromPath;
8 | in
9 | rec {
10 | # We don't allow root-based glob and separator `/` or `\` inside brackets.
11 | globBracketPat = ''\[!?[^\/][^]\/]*]'';
12 | globAtomPat = ''[^[\/]|${globBracketPat}'';
13 | globPat = ''((${globAtomPat})+[\/])*(${globAtomPat})+'';
14 |
15 | # Parse a glob pattern into a list of segments.
16 | #
17 | # String -> List ({ lit: String } | { re: String } | { deep: true })
18 | #
19 | # `lit` for simple string literal.
20 | # `re` for segment matching.
21 | # `deep` for `**`
22 | #
23 | # https://docs.rs/glob/0.3.0/glob/struct.Pattern.html
24 | # https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_04
25 | parseGlob = glob:
26 | let
27 | translateAtom = m:
28 | let m' = head m; in
29 | if isString m then
30 | replaceStrings
31 | [ "\\" "{" "(" ")" "^" "$" "|" "." "?" "*" "+" ]
32 | [ "/" "\\{" "\\(" "\\)" "\\^" "\\$" "\\|" "\\." "." ".*" "\\+" ]
33 | m
34 | else if hasPrefix "[!" m' then
35 | "[^" + substring 2 (stringLength m') m'
36 | else
37 | m';
38 |
39 | translateSegment = seg:
40 | if seg == "**" then
41 | { deep = true; }
42 | else if match "[^[?*]+" seg != null then
43 | { lit = seg; }
44 | else
45 | { re = concatStrings (map translateAtom (split "(${globBracketPat})" seg)); };
46 |
47 | segments = filter (s: isString s && s != "") (split ''/|\\'' glob);
48 |
49 | in
50 | if match globPat glob == null then
51 | throw ''
52 | Invalid glob pattern: ${glob}
53 | Note that we don't support root-based pattern and separator `/` or `\` inside brackets.
54 | ''
55 | else
56 | map translateSegment segments;
57 |
58 | # Find all paths in a tree matching a given glob pattern.
59 | # A path is represented as a list of segments.
60 | #
61 | # String -> Set -> List (List String)
62 | globMatchTree = glob: tree:
63 | let
64 | flatMap = f: foldl' (ret: x: ret ++ f x) [];
65 |
66 | go = path: pats:
67 | let
68 | pat = head pats;
69 | pats' = tail pats;
70 | curTree = getAttrFromPath path tree;
71 | keys = if isAttrs curTree then attrNames curTree else [];
72 | in
73 | if pats == [] then
74 | [ path ]
75 | else if pat ? lit then
76 | if pat.lit == "." then
77 | go path pats'
78 | else if pat.lit == ".." then
79 | if path == [] then
80 | []
81 | else
82 | go (init path) pats'
83 | else if curTree ? ${pat.lit} then
84 | go (path ++ [ pat.lit ]) pats'
85 | else
86 | []
87 | else if pat ? deep then # `**`
88 | go path pats' ++
89 | flatMap (k: go (path ++ [ k ]) pats) keys # Pass `pats` to recurse into deep directories.
90 | else if pat ? re then
91 | flatMap (k: go (path ++ [ k ]) pats')
92 | (filter (k: match pat.re k != null) keys)
93 | else
94 | throw "Unreachable";
95 | in
96 | go [] (parseGlob glob);
97 |
98 | # Find all paths inside a filesystem directory matching a given glob pattern.
99 | # A path is represented as a relative string with `/` as segment separator, eg. "" (root), "foo", "foo/bar"
100 | #
101 | # String -> Path -> List String
102 | globMatchDir = glob: dir:
103 | let
104 | pathToTree = dir:
105 | mapAttrs
106 | (k: ty: if ty == "directory" then pathToTree (dir + "/${k}") else ty)
107 | (readDir dir);
108 | ret = globMatchTree glob (pathToTree dir);
109 | in
110 | map (concatStringsSep "/") ret;
111 |
112 | glob-tests = { assertEq, ... }: {
113 | "0parse" = let
114 | assertInvalid = glob: assertEq (builtins.tryEval (parseGlob glob)) { success = false; value = false; };
115 | assertParsed = glob: expect: assertEq (parseGlob glob) expect;
116 | in {
117 | invalid1 = assertInvalid "";
118 | invalid2 = assertInvalid "/";
119 | invalid3 = assertInvalid "/foo";
120 | invalid4 = assertInvalid "foo//bar";
121 |
122 | lit1 = assertParsed "foo" [ { lit = "foo"; } ];
123 | lit2 = assertParsed "!.(^$){}" [ { lit = "!.(^$){}"; } ];
124 | lit3 = assertParsed "." [ { lit = "."; } ];
125 | lit4 = assertParsed ".." [ { lit = ".."; } ];
126 |
127 | re1 = assertParsed "*" [ { re = ''.*''; } ];
128 | re2 = assertParsed ".*" [ { re = ''\..*''; } ];
129 | re3 = assertParsed "*.*" [ { re = ''.*\..*''; } ];
130 | re4 = assertParsed "[[][]][![][!]][a-z0-]" [ { re = ''[[][]][^[][^]][a-z0-]''; } ];
131 | re5 = assertParsed "?.*[[][?.*]?.*" [ { re = ''.\..*[[][?.*].\..*''; } ];
132 | re6 = assertParsed ".[.]" [ { re = ''\.[.]''; } ];
133 |
134 | deep1 = assertParsed "**" [ { deep = true; } ];
135 |
136 | compound1 = assertParsed
137 | "./foo/**/*.nix"
138 | [ { lit = "."; } { lit = "foo"; } { deep = true; } { re = ''.*\.nix''; } ];
139 | compound2 = assertParsed
140 | ".*/../log/[!abc]*-[0-9T:-]+0000.log"
141 | [ { re = ''\..*''; } { lit = ".."; } { lit = "log"; } { re = ''[^abc].*-[0-9T:-]\+0000\.log''; } ];
142 | };
143 |
144 | "1match-tree" = let
145 | tree = {
146 | a = null;
147 | b = null;
148 | b1 = null;
149 | b2 = null;
150 | bcd = null;
151 | bed = null;
152 | c = {
153 | d.e = {
154 | af = null;
155 | f = null;
156 | };
157 | g.h = null;
158 | wtf = null;
159 | };
160 | f = null;
161 | z = {
162 | a = null;
163 | b = {
164 | c = null;
165 | d.e = null;
166 | };
167 | };
168 | };
169 |
170 | assertMatch = glob: expect:
171 | let
172 | ret = globMatchTree glob tree;
173 | ret' = map (concatStringsSep "/") ret;
174 | in
175 | assertEq ret' expect;
176 |
177 | in {
178 | exact1 = assertMatch "a" [ "a" ];
179 | exact2 = assertMatch "c/g/h" [ "c/g/h" ];
180 | exact3 = assertMatch "c/g" [ "c/g" ];
181 |
182 | dot1 = assertMatch "./a" [ "a" ];
183 | dot2 = assertMatch "./a/../c/g/./h" [ "c/g/h" ];
184 |
185 | re1 = assertMatch "b*" [ "b" "b1" "b2" "bcd" "bed" ];
186 | re2 = assertMatch "b?" [ "b1" "b2" ];
187 | re3 = assertMatch "c/*" [ "c/d" "c/g" "c/wtf" ];
188 | re4 = assertMatch "b?d" [ "bcd" "bed" ];
189 |
190 | deep1 = assertMatch "**/b?d" [ "bcd" "bed" ];
191 | deep2 = assertMatch "c/**/*f" [ "c/wtf" "c/d/e/af" "c/d/e/f" ];
192 | deep3 = assertMatch "**/f/.." [ "" "c/d/e" ];
193 | deep4 = assertMatch "[wz]/**" [ "z" "z/a" "z/b" "z/b/c" "z/b/d" "z/b/d/e" ];
194 | };
195 |
196 | "2match-dir" = {
197 | compound1 = assertEq (globMatchDir "*-*.nix" ./.) [ "pkg-info.nix" "target-cfg.nix" ];
198 | compound2 = assertEq (globMatchDir "./features/../**/tokio-[!wtf][opq]?" ../tests) [ "tokio-app" ];
199 | };
200 | };
201 | }
202 |
--------------------------------------------------------------------------------
/lib/pkg-info.nix:
--------------------------------------------------------------------------------
1 | { lib, ... }:
2 | let
3 | inherit (builtins) readFile readDir fromJSON fromTOML toString attrNames match;
4 | inherit (lib)
5 | stringLength splitString replaceStrings substring isString toLower
6 | filter listToAttrs mapAttrs mapAttrsToList optionalAttrs warnIf;
7 | in
8 | rec {
9 | toPkgId = { name, version, source ? null, ... }:
10 | if source != null then
11 | "${name} ${version} (${source})"
12 | else
13 | # Local crates must be collide names. Simply use the name to make overriding easier.
14 | name;
15 |
16 | mkIndex = fetchurl: path: overrides: let
17 | # TODO: We currently only support legacy format used by crates.io-index.
18 | # https://github.com/rust-lang/cargo/blob/2f3df16921deb34a92700f4d5a7ecfb424739558/src/cargo/sources/registry/mod.rs#L230-L244
19 | downloadEndpoint = (fromJSON (readFile (path + "/config.json"))).dl;
20 | mkDownloadUrl =
21 | assert match ".*\\{.*" downloadEndpoint == null;
22 | { name, version, ... }: "${downloadEndpoint}/${name}/${version}/download";
23 |
24 | mkSrc = { name, version, sha256 }@args: fetchurl {
25 | # Use the same name as nixpkgs to benifit from cache.
26 | # https://github.com/NixOS/nixpkgs/pull/122158/files#diff-eb8b8729bfd36f8878c2d8a99f67a2bebb912e9f78c5d2a72457b1f572e26986R67
27 | name = "crate-${name}-${version}.tar.gz";
28 | url = mkDownloadUrl args;
29 | inherit sha256;
30 | };
31 |
32 | go = path:
33 | mapAttrs (k: v:
34 | if v == "directory"
35 | then go (path + "/${k}")
36 | else mkPkgInfoSet mkSrc k (readFile (path + "/${k}")) (overrides.${k} or null)
37 | ) (removeAttrs (readDir path) [ "config.json" ]);
38 | in
39 | go path // { __registry_index = true; };
40 |
41 | # Get pkg info of the given package, with overrides applied if exists.
42 | getPkgInfoFromIndex = index: { name, version, checksum ? null, ... }: let
43 | name' = toLower name;
44 | len = stringLength name';
45 | crate =
46 | if len == 1 then
47 | index."1".${name'} or null
48 | else if len == 2 then
49 | index."2".${name'} or null
50 | else if len == 3 then
51 | index."3".${substring 0 1 name'}.${name'} or null
52 | else
53 | index.${substring 0 2 name'}.${substring 2 2 name'}.${name'} or null;
54 | info = crate.${version} or null;
55 | in
56 | if !(index ? __registry_index) then
57 | throw "Invalid registry. Do you forget `mkIndex` on registry paths?"
58 | else if crate == null then
59 | throw "Package ${name} is not found in index"
60 | else if info == null then
61 | throw "Package ${name} doesn't have version ${version} in index. Available versions: ${toString (attrNames crate)}"
62 | else if info.sha256 != null && checksum != null && info.sha256 != checksum then
63 | throw "Package ${name} ${version} hash mismatched, expect ${info.sha256}, got ${checksum}"
64 | else
65 | info;
66 |
67 | # Make a set of pkg infos keyed by version.
68 | mkPkgInfoSet = mkSrc: name: content: override: let
69 | lines = filter (line: line != "") (splitString "\n" content);
70 | parseLine = line: let parsed = fromJSON line; in {
71 | name = parsed.vers;
72 | value = mkPkgInfoFromRegistry mkSrc parsed
73 | // optionalAttrs (override != null) {
74 | # Proc macro crates behave differently in dependency resolution.
75 | procMacro = (override { inherit (parsed) version; features = { }; }).procMacro or false;
76 | __override = override;
77 | };
78 | };
79 | in
80 | listToAttrs (map parseLine lines);
81 |
82 | # Package info:
83 | # {
84 | # name = "libz-sys"; # The name in registry.
85 | # version = "0.1.0"; # Semver.
86 | # src = ; # Source path.
87 | # sha256 = "123456...."; # Hash of the `src` tarball. (null or string)
88 | # yanked = false; # Whether it's yanked.
89 | # links = "z"; # The native library to link. (null or string)
90 | # procMacro = false; # Whether this is a proc-macro library. See comments below.
91 | # features = { # Features provided.
92 | # default = [ "std" ];
93 | # std = [];
94 | # };
95 | # dependencies = [
96 | # {
97 | # name = "libc"; # Reference name.
98 | # package = "libc"; # Dependency's name in registry. (default to be `name`)
99 | # req = "^0.1.0"; # Semver requirement.
100 | # features = [ "foo" ]; # Enabled features.
101 | # optional = false; # Whether this dependency is optional.
102 | # default_features = true; # Whether to enable default features.
103 | # target = "cfg(...)"; # Only required on some targets. (null or string, default to be null)
104 | # kind = "normal"; # Dependencies (one of "normal", "dev", "build", default to be "normal")
105 | # # `registry` and `public` are not supported.
106 | # }
107 | # ];
108 | # }
109 | mkPkgInfoFromRegistry =
110 | mkSrc:
111 | # https://github.com/rust-lang/cargo/blob/2f3df16921deb34a92700f4d5a7ecfb424739558/src/cargo/sources/registry/mod.rs#L259
112 | { name, vers, deps, features, cksum, yanked ? false, links ? null, v ? 1, ... }:
113 | if v != 1 then
114 | throw "${name} ${vers}: Registry layout version ${toString v} is too new to understand"
115 | else
116 | {
117 | inherit name features yanked links;
118 | version = vers;
119 | sha256 = cksum;
120 | dependencies = map sanitizeDep deps;
121 | # N.B. Proc macro indicator is not in the registry: https://github.com/rust-lang/cargo/issues/9605
122 | # This would be overrided in `mkPkgInfoSet`.
123 | procMacro = false;
124 | src = mkSrc {
125 | inherit name;
126 | version = vers;
127 | sha256 = cksum;
128 | };
129 | };
130 |
131 | # Sanitize a dependency reference.
132 | # Handling `package` and fill missing fields.
133 | sanitizeDep =
134 | { name
135 | , package ? name
136 | , version ? null # Cargo.toml use `version`
137 | , req ? version
138 | , features ? []
139 | , optional ? false
140 | , default_features ? true
141 | , target ? null
142 | , kind
143 | , ...
144 | }@args: args // {
145 | inherit name package req features optional default_features target kind;
146 |
147 | # Note that `package` == `name` is not the same as omitting `package`.
148 | # See: https://github.com/rust-lang/cargo/issues/6827
149 | # Here we let `package` fallback to name, but set a special `rename` to the renamed `name`
150 | # if `package` != `name`. `rename` will affect the `--extern` flags.
151 | #
152 | # For remind:
153 | # - `name` is used for coresponding feature name for optional dependencies.
154 | # - `package` is used for the original package name of dependency crate.
155 | # - If `package` isn't set, the code name (for `use` or `extern crate`) of the dependency is its lib name.
156 | # `--extern` also use its own lib name.
157 | # - If `package` is set, the code name and `--extern` both use the renamed `name`.
158 | } // optionalAttrs (args.package or null != null) {
159 | rename = replaceStrings ["-"] ["_"] name;
160 | };
161 |
162 | # Build a simplified crate into from a parsed Cargo.toml.
163 | mkPkgInfoFromCargoToml = { lockVersion ? 3, package, features ? {}, target ? {}, ... }@args: src: let
164 | transDeps = target: kind:
165 | mapAttrsToList (name: v:
166 | {
167 | inherit name target kind;
168 | package = v.package or name;
169 | # For path or git dependencies, `version` can be omitted.
170 | req = if isString v then v else v.version or null;
171 | features = v.features or [];
172 | optional = v.optional or false;
173 | # It's `default-features` in Cargo.toml, but `default_features` in index and in pkg info.
174 | default_features =
175 | warnIf (v ? default_features) "Ignoring `default_features`. Do you mean `default-features`?"
176 | (v.default-features or true);
177 |
178 | # This is used for dependency resoving inside Cargo.lock.
179 | source =
180 | if v ? registry then
181 | throw "Dependency with `registry` is not supported. Use `registry-index` with explicit URL instead."
182 | else if v ? registry-index then
183 | "registry+${v.registry-index}"
184 | else if v ? git then
185 | # For v1 and v2, git-branch URLs are encoded as "git+url" with no query parameters.
186 | if v ? branch && lockVersion >= 3 then
187 | "git+${v.git}?branch=${v.branch}"
188 | else if v ? tag then
189 | "git+${v.git}?tag=${v.tag}"
190 | else if v ? rev then
191 | "git+${v.git}?rev=${v.rev}"
192 | else
193 | "git+${v.git}"
194 | else if v ? path then
195 | # Local crates are mark with `null` source.
196 | null
197 | else
198 | # Default to use crates.io registry.
199 | # N.B. This is necessary and must not be `null`, or it will be indinstinguishable
200 | # with local crates or crates from other registries.
201 | "registry+https://github.com/rust-lang/crates.io-index";
202 |
203 | # See `sanitizeDep`
204 | } // optionalAttrs (v.package or null != null) {
205 | rename = replaceStrings ["-"] ["_"] name;
206 | });
207 |
208 | collectTargetDeps = target: { dependencies ? {}, dev-dependencies ? {}, build-dependencies ? {}, ... }:
209 | transDeps target "normal" dependencies ++
210 | transDeps target "dev" dev-dependencies ++
211 | transDeps target "build" build-dependencies;
212 |
213 | in
214 | {
215 | inherit (package) name version;
216 | inherit src features;
217 | links = package.links or null;
218 | procMacro = args.lib.proc-macro or false;
219 | dependencies =
220 | collectTargetDeps null args ++
221 | mapAttrsToList collectTargetDeps target;
222 | };
223 |
224 | pkg-info-from-toml-tests = { assertEq, ... }: {
225 | simple = let
226 | cargoToml = fromTOML (readFile ../tests/tokio-app/Cargo.toml);
227 | info = mkPkgInfoFromCargoToml cargoToml "";
228 |
229 | expected = {
230 | name = "tokio-app";
231 | version = "0.0.0";
232 | features = { };
233 | src = "";
234 | links = null;
235 | procMacro = false;
236 | dependencies = [
237 | {
238 | name = "tokio";
239 | package = "tokio";
240 | default_features = false;
241 | features = [ "rt-multi-thread" "macros" "time" ];
242 | kind = "normal";
243 | optional = false;
244 | req = "1";
245 | target = null;
246 | source = "registry+https://github.com/rust-lang/crates.io-index";
247 | }
248 | ];
249 | };
250 | in
251 | assertEq info expected;
252 |
253 | build-deps =
254 | let
255 | cargoToml = fromTOML (readFile ../tests/build-deps/Cargo.toml);
256 | info = mkPkgInfoFromCargoToml cargoToml "";
257 | expected = {
258 | name = "build-deps";
259 | version = "0.0.0";
260 | features = { };
261 | src = "";
262 | links = null;
263 | procMacro = false;
264 | dependencies = [
265 | {
266 | name = "semver";
267 | package = "semver";
268 | default_features = true;
269 | features = [ ];
270 | kind = "build";
271 | optional = false;
272 | req = "1";
273 | target = null;
274 | source = "registry+https://github.com/rust-lang/crates.io-index";
275 | }
276 | ];
277 | };
278 | in
279 | assertEq info expected;
280 | };
281 | }
282 |
--------------------------------------------------------------------------------
/lib/resolve.nix:
--------------------------------------------------------------------------------
1 | { lib, self }:
2 | let
3 | inherit (builtins) readFile match fromTOML toJSON;
4 | inherit (lib)
5 | foldl' concatStringsSep listToAttrs filter elemAt length optional sort elem flatten
6 | hasPrefix substring
7 | attrValues mapAttrs attrNames filterAttrs composeManyExtensions assertMsg;
8 | inherit (self.semver) parseSemverReq;
9 | inherit (self.pkg-info) mkPkgInfoFromCargoToml toPkgId sanitizeDep;
10 | in rec {
11 |
12 | # Resolve the dependencies graph based on the lock file.
13 | # Output:
14 | # {
15 | # "libz-sys 0.1.0 (https://...)" = {
16 | # # name, sha256, ... All fields from pkg info.
17 | # dependencies = [
18 | # {
19 | # # name, kind, ... All fields from dependency in the pkg info.
20 | # resolved = "libc 0.1.0 (https://...)";
21 | # };
22 | # };
23 | # };
24 | # }
25 | #
26 | # Currently (rust 1.63.0), there are 3 versions of the lock file.
27 | # We supports V1, V2 and V3.
28 | # See:
29 | # https://github.com/rust-lang/cargo/blob/rust-1.63.0/src/cargo/core/resolver/resolve.rs#L56
30 | # https://github.com/rust-lang/cargo/blob/rust-1.63.0/src/cargo/core/resolver/encode.rs
31 | resolveDepsFromLock = getPkgInfo: lock: let
32 | # For git sources, they are referenced without the locked hash part after `#`.
33 | # Define: "git+https://github.com/dtolnay/semver?tag=1.0.4#ea9ea80c023ba3913b9ab0af1d983f137b4110a5"
34 | # Reference: "semver 1.0.4 (git+https://github.com/dtolnay/semver?tag=1.0.4)"
35 | removeUrlHash = s:
36 | let m = match "([^#]*)#.*" s; in
37 | if m == null then s else elemAt m 0;
38 |
39 | pkgs = map
40 | (pkg: if pkg ? source then pkg // { source = removeUrlHash pkg.source; } else pkg)
41 | lock.package;
42 |
43 | pkgsByName = foldl' (set: { name, ... }@pkg:
44 | set // { ${name} = (set.${name} or []) ++ [ pkg ]; }
45 | ) {} pkgs;
46 |
47 | resolved = listToAttrs (map resolvePkg pkgs);
48 |
49 | resolvePkg = { name, version, source ? "", dependencies ? [], ... }@args: let
50 | info = getPkgInfo args;
51 | candidates = map findPkgId dependencies;
52 |
53 | id = toPkgId args;
54 | resolvedDependencies =
55 | map (dep: dep // {
56 | resolved = selectDep candidates dep;
57 | }) info.dependencies;
58 |
59 | # Find the exact package id of a dependency key, which may omit version or source.
60 | findPkgId = key: let
61 | m = match "([^ ]+)( ([^ ]+))?( \\(([^\\)]*)\\))?" key;
62 | lockName = elemAt m 0;
63 | lockVersion = elemAt m 2;
64 | lockSource = elemAt m 4;
65 | candidates =
66 | filter (pkg:
67 | (lockVersion != null -> pkg.version == lockVersion) &&
68 | (lockSource != null -> pkg.source or null == lockSource))
69 | (pkgsByName.${lockName} or []);
70 | candidateCnt = length candidates;
71 | in
72 | if candidateCnt == 0 then
73 | throw "When resolving ${id}, locked dependency `${key}` not found"
74 | else if candidateCnt > 1 then
75 | throw ''
76 | When resolving ${id}, locked dependency `${key}` is ambiguous.
77 | Found: ${toJSON candidates}
78 | ''
79 | else
80 | elemAt candidates 0;
81 |
82 | selectDep = candidates: { name, package, req, source ? null, ... }:
83 | let
84 | # Local path or git dependencies don't have version req.
85 | checkReq = if req != null then parseSemverReq req else (ver: true);
86 | checkSource = if source != null then s: s == source else s: true;
87 |
88 | selected = filter
89 | ({ name, version, source ? null, ... }: name == package && checkReq version && checkSource source)
90 | candidates;
91 |
92 | selectedCnt = length selected;
93 | in
94 | if selectedCnt == 0 then
95 | # Cargo will omit disabled optional dependencies in lock file.
96 | # throw "When resolving ${pkgName} ${crateVersion}, dependency ${package} ${req} isn't satisfied in lock file"
97 | null
98 | else if selectedCnt > 1 then
99 | throw ''
100 | When resolving ${id}, dependency ${package} ${if req == null then "*" else req} has multiple candidates in lock file.
101 | Found: ${toJSON selected}
102 | ''
103 | else
104 | toPkgId (elemAt selected 0);
105 |
106 | in
107 | {
108 | name = toPkgId args;
109 | value = info // { dependencies = resolvedDependencies; };
110 | };
111 |
112 | in
113 | assert assertMsg (lock.version or 3 == 3) "Unsupported version of Cargo.lock: ${toString lock.version}";
114 | resolved;
115 |
116 | # Calculate the closure of each feature, with `dep:pkg` and `pkg?/feat` syntax desugared.
117 | # [String] -> { [String] } -> { [String | { dep: String, feat?: String }] }
118 | preprocessFeatures = optionalDeps: defs: let
119 | allRefs = flatten (attrValues defs);
120 | defs' =
121 | listToAttrs
122 | (map (dep: { name = dep; value = [ "dep:${dep}" ]; })
123 | (filter (dep: !elem "dep:${dep}" allRefs)
124 | optionalDeps))
125 | // defs;
126 | go = prev: feat:
127 | let
128 | m = match "([a-zA-Z0-9]+)(\\?)?/([a-zA-Z0-9]+)" feat;
129 | depName = elemAt m 0;
130 | isWeak = elemAt m 1 != null;
131 | depFeat = elemAt m 2;
132 | in if elem feat prev then
133 | prev
134 | else if defs' ? ${feat} then
135 | foldl' go ([ feat ] ++ prev) defs'.${feat}
136 | else if hasPrefix "dep:" feat then
137 | [ { dep = substring 4 (-1) feat; } ] ++ prev
138 | else if m == null then
139 | [ feat ] ++ prev
140 | else if isWeak then
141 | [ { dep = depName; feat = depFeat; } ] ++ prev
142 | else
143 | [ { dep = depName; } { dep = depName; feat = depFeat; } ] ++ prev;
144 | fixed = mapAttrs (feat: _: go [ ] feat) defs';
145 | in
146 | fixed;
147 |
148 | # Enable `features` in `prev` and do recursive update according to `defs`.
149 | # Optional dependencies must be included in `defs`.
150 | enableFeatures = pkgId: defs: prev: features:
151 | foldl' (prev: feat: let
152 | m = match "(.*)/.*" feat;
153 | mDep = elemAt m 0;
154 | nexts =
155 | if m == null then
156 | # Must be defined.
157 | defs.${feat} or (throw ''
158 | Feature '${feat}' is invalid for ${pkgId}.
159 | Possible features: ${concatStringsSep "," (attrNames defs)}
160 | '')
161 | else
162 | # Dependent features implies optional dependency to be enabled.
163 | # But non-optional dependency doesn't have coresponding feature flag.
164 | optional (defs ? ${mDep}) mDep;
165 | in
166 | if prev.${feat} or false then
167 | prev
168 | else
169 | enableFeatures pkgId defs (prev // { ${feat} = true; }) nexts
170 | ) prev features;
171 |
172 | # Resolve all features.
173 | # Note that dependent features like `foo/bar` are only available during resolution,
174 | # and will be removed in result set.
175 | #
176 | # Returns:
177 | # {
178 | # "libc 0.1.0 (https://...)" = [ "default" "foo" "bar" ];
179 | # }
180 | resolveFeatures = {
181 | # Follows the layout of the output of `resolveDepsFromLock`.
182 | pkgSet
183 | # Dependency edges (`{ name, kind, resolved, ... }`) will be checked by this filter.
184 | # Only edges returning `true` are considered and propagated.
185 | , depFilter ? dep: true
186 | # Eg. "libc 0.1.0 (https://...)"
187 | , rootId
188 | # Eg. [ "foo" "bar/baz" ]
189 | , rootFeatures
190 | }: let
191 |
192 | featureDefs = mapAttrs (id: { features, dependencies, ... }:
193 | features //
194 | listToAttrs
195 | (map (dep: { name = dep.name; value = []; })
196 | # We should collect all optional dependencies for feature def, even though they are not selected.
197 | # This happens on `rand@0.8.3`, whose `default` feature enables `rand_hc`, which is only available
198 | # for `cfg(target_os = "emscripten")`. This feature should be still enable, though optional dependency
199 | # is not.
200 | (filter (dep: dep.optional) dependencies))
201 | ) pkgSet;
202 |
203 | # initialFeatures = mapAttrs (id: defs: mapAttrs (k: v: false) defs) featureDefs;
204 | initialFeatures = mapAttrs (id: info: {}) pkgSet;
205 |
206 | # Overlay of spreading 's nested features into dependencies and enable optional dependencies.
207 | updateDepsOverlay = id: final: prev: let
208 | info = pkgSet.${id};
209 | finalFeatures = final.${id} or {};
210 | updateDep = { name, optional, resolved, default_features, features, ... }: final: prev: let
211 | depFeatures =
212 | lib.optional (default_features && featureDefs.${resolved} ? default) "default" ++
213 | features ++
214 | filter (feat: feat != null)
215 | (map (feat: let m = match "(.*)/(.*)" feat; in
216 | if m != null && elemAt m 0 == name then
217 | elemAt m 1
218 | else
219 | null
220 | ) (attrNames finalFeatures));
221 | in
222 | {
223 | ${resolved} =
224 | # This condition must be evaluated under `${resolved} =`,
225 | # or we'll enter an infinite recursion.
226 | if optional -> finalFeatures.${name} or false then
227 | enableFeatures
228 | resolved
229 | featureDefs.${resolved}
230 | prev.${resolved}
231 | depFeatures
232 | else
233 | prev.${resolved};
234 | };
235 | in
236 | composeManyExtensions
237 | (map updateDep
238 | (filter depFilter info.dependencies))
239 | final
240 | prev;
241 |
242 | rootOverlay = final: prev: {
243 | ${rootId} = enableFeatures
244 | rootId
245 | featureDefs.${rootId}
246 | initialFeatures.${rootId}
247 | rootFeatures;
248 | };
249 |
250 | final =
251 | composeManyExtensions
252 | (map updateDepsOverlay (attrNames pkgSet) ++ [ rootOverlay ])
253 | final
254 | initialFeatures;
255 |
256 | final' =
257 | mapAttrs (id: feats: filter (feat: match ".*/.*" feat == null) (attrNames feats)) final;
258 |
259 | in
260 | final';
261 |
262 | preprocess-feature-tests = { assertEq, ... }: let
263 | test = optionalDeps: featureDefs: expect:
264 | assertEq (preprocessFeatures optionalDeps featureDefs) expect;
265 | in {
266 | recursive = test [ ] { a = [ "b" ]; b = [ "a" "c" ]; c = [ ]; } {
267 | a = [ "c" "b" "a" ];
268 | b = [ "c" "a" "b" ];
269 | c = [ "c" ];
270 | };
271 | auto-dep = test [ "a" ] { b = [ "a" ]; } {
272 | a = [ { dep = "a"; } "a" ];
273 | b = [ { dep = "a"; } "a" "b" ];
274 | };
275 | manual-dep = test [ "a" ] { b = [ "dep:a" ]; } {
276 | b = [ { dep = "a"; } "b" ];
277 | };
278 | strong-dep = test [ "a" ] { b = [ "a/c" ]; } {
279 | a = [ { dep = "a"; } "a" ];
280 | b = [ { dep = "a"; } { dep = "a"; feat = "c"; } "b" ];
281 | };
282 | weak-dep = test [ "a" ] { b = [ "a?/c" ]; } {
283 | a = [ { dep = "a"; } "a" ];
284 | b = [ { dep = "a"; feat = "c"; } "b" ];
285 | };
286 | };
287 |
288 | update-feature-tests = { assertEq, ... }: let
289 | testUpdate = defs: features: expect: let
290 | init = mapAttrs (k: v: false) defs;
291 | out = enableFeatures "pkgId" defs init features;
292 | enabled = attrNames (filterAttrs (k: v: v) out);
293 | in
294 | assertEq enabled expect;
295 | in {
296 | simple1 = testUpdate { a = []; } [] [];
297 | simple2 = testUpdate { a = []; } [ "a" ] [ "a" ];
298 | simple3 = testUpdate { a = []; } [ "a" "a" ] [ "a" ];
299 | simple4 = testUpdate { a = []; b = []; } [ "a" ] [ "a" ];
300 | simple5 = testUpdate { a = []; b = []; } [ "a" "b" ] [ "a" "b" ];
301 | simple6 = testUpdate { a = []; b = []; } [ "a" "b" "a" ] [ "a" "b" ];
302 |
303 | } // (let defs = { a = []; b = [ "a" ]; }; in {
304 | link1 = testUpdate defs [ "a" ] [ "a" ];
305 | link2 = testUpdate defs [ "b" "a" ] [ "a" "b" ];
306 | link3 = testUpdate defs [ "b" ] [ "a" "b" ];
307 | link4 = testUpdate defs [ "b" "a" ] [ "a" "b" ];
308 | link5 = testUpdate defs [ "b" "b" ] [ "a" "b" ];
309 |
310 | }) // (let defs = { a = []; b = [ "a" ]; c = [ "a" ]; }; in {
311 | common1 = testUpdate defs [ "a" ] [ "a" ];
312 | common2 = testUpdate defs [ "b" ] [ "a" "b" ];
313 | common3 = testUpdate defs [ "a" "b" ] [ "a" "b" ];
314 | common4 = testUpdate defs [ "b" "a" ] [ "a" "b" ];
315 | common5 = testUpdate defs [ "b" "c" ] [ "a" "b" "c" ];
316 | common6 = testUpdate defs [ "a" "b" "c" ] [ "a" "b" "c" ];
317 | common7 = testUpdate defs [ "b" "c" "b" ] [ "a" "b" "c" ];
318 |
319 | }) // (let defs = { a = [ "b" "c" ]; b = [ "d" "e" ]; c = [ "f" "g"]; d = []; e = []; f = []; g = []; }; in {
320 | tree1 = testUpdate defs [ "a" ] [ "a" "b" "c" "d" "e" "f" "g" ];
321 | tree2 = testUpdate defs [ "b" ] [ "b" "d" "e" ];
322 | tree3 = testUpdate defs [ "d" ] [ "d" ];
323 | tree4 = testUpdate defs [ "d" "b" "g" ] [ "b" "d" "e" "g" ];
324 | tree5 = testUpdate defs [ "c" "e" "f" ] [ "c" "e" "f" "g" ];
325 |
326 | }) // (let defs = { a = [ "b" ]; b = [ "c" ]; c = [ "b" ]; }; in {
327 | cycle1 = testUpdate defs [ "b" ] [ "b" "c" ];
328 | cycle2 = testUpdate defs [ "c" ] [ "b" "c" ];
329 | cycle3 = testUpdate defs [ "a" ] [ "a" "b" "c" ];
330 | });
331 |
332 | resolve-feature-tests = { assertEq, ... }: let
333 | test = pkgSet: rootId: rootFeatures: expect: let
334 | resolved = resolveFeatures { inherit pkgSet rootId rootFeatures; };
335 | expect' = mapAttrs (id: feats: sort (a: b: a < b) feats) expect;
336 | in
337 | assertEq resolved expect';
338 |
339 | pkgSet1 = {
340 | a = {
341 | features = { foo = [ "bar" ]; bar = []; baz = [ "b" ]; };
342 | dependencies = [
343 | { name = "b"; resolved = "b-id"; optional = true; default_features = true; features = [ "a" ]; }
344 | { name = "unused"; resolved = null; optional = true; default_features = true; features = []; }
345 | ];
346 | };
347 | b-id = {
348 | features = { default = []; foo = []; bar = [ "foo" ]; a = []; };
349 | dependencies = [];
350 | };
351 | };
352 |
353 | pkgSet2 = {
354 | my-id = {
355 | features = { default = [ "tokio/macros" ]; };
356 | dependencies = [
357 | { name = "tokio"; resolved = "tokio-id"; optional = false; default_features = false; features = [ "fs" ]; }
358 | { name = "dep"; resolved = "dep-id"; optional = false; default_features = true; features = []; }
359 | ];
360 | };
361 | dep-id = {
362 | features = { default = [ "tokio/sync" ]; };
363 | dependencies = [
364 | { name = "tokio"; resolved = "tokio-id"; optional = false; default_features = false; features = [ "sync" ]; }
365 | ];
366 | };
367 | tokio-id = {
368 | features = { default = []; fs = []; sync = []; macros = []; io = []; };
369 | dependencies = [];
370 | };
371 | };
372 |
373 | in {
374 | simple = test pkgSet1 "a" [ "foo" ] {
375 | a = [ "foo" "bar" ];
376 | b-id = [ ];
377 | };
378 |
379 | depend = test pkgSet1 "a" [ "foo" "baz" ] {
380 | a = [ "foo" "bar" "baz" "b" ];
381 | b-id = [ "default" "a" ];
382 | };
383 |
384 | override = test pkgSet1 "a" [ "b/bar" ] {
385 | a = [ "b" ];
386 | b-id = [ "default" "a" "bar" "foo" ];
387 | };
388 |
389 | merge = test pkgSet2 "my-id" [ "default" ] {
390 | my-id = [ "default" ];
391 | dep-id = [ "default" ];
392 | tokio-id = [ "fs" "sync" "macros" ];
393 | };
394 | };
395 |
396 | resolve-deps-tests = { assertEq, defaultRegistries, ... }: {
397 | simple = let
398 | index = {
399 | libc."0.1.12" = { name = "libc"; version = "0.1.12"; dependencies = []; };
400 | libc."0.2.95" = { name = "libc"; version = "0.2.95"; dependencies = []; };
401 | testt."0.1.0" = {
402 | name = "testt";
403 | version = "0.1.0";
404 | dependencies = map sanitizeDep [
405 | { name = "libc"; req = "^0.1.0"; kind = "normal"; }
406 | { name = "liba"; package = "libc"; req = "^0.2.0"; kind = "normal"; }
407 | ];
408 | };
409 | };
410 |
411 | lock = {
412 | package = [
413 | {
414 | name = "libc";
415 | version = "0.1.12";
416 | source = "registry+https://github.com/rust-lang/crates.io-index";
417 | }
418 | {
419 | name = "libc";
420 | version = "0.2.95";
421 | source = "registry+https://github.com/rust-lang/crates.io-index";
422 | }
423 | {
424 | name = "testt";
425 | version = "0.1.0";
426 | dependencies = [
427 | "libc 0.1.12"
428 | "libc 0.2.95 (registry+https://github.com/rust-lang/crates.io-index)"
429 | ];
430 | }
431 | ];
432 | };
433 |
434 | expected = {
435 | "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = {
436 | name = "libc";
437 | version = "0.1.12";
438 | dependencies = [ ];
439 | };
440 | "libc 0.2.95 (registry+https://github.com/rust-lang/crates.io-index)" = {
441 | name = "libc";
442 | version = "0.2.95";
443 | dependencies = [ ];
444 | };
445 | "testt" = {
446 | name = "testt";
447 | version = "0.1.0";
448 | dependencies = [
449 | {
450 | name = "libc";
451 | package = "libc";
452 | req = "^0.1.0";
453 | resolved = "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)";
454 | kind = "normal";
455 | optional = false;
456 | features = [];
457 | default_features = true;
458 | target = null;
459 | }
460 | {
461 | name = "liba";
462 | rename = "liba";
463 | package = "libc";
464 | req = "^0.2.0";
465 | resolved = "libc 0.2.95 (registry+https://github.com/rust-lang/crates.io-index)";
466 | kind = "normal";
467 | optional = false;
468 | features = [];
469 | default_features = true;
470 | target = null;
471 | }
472 | ];
473 | };
474 | };
475 |
476 | getPkgInfo = { name, version, ... }: index.${name}.${version};
477 | resolved = resolveDepsFromLock getPkgInfo lock;
478 | in
479 | assertEq resolved expected;
480 |
481 | workspace-virtual = let
482 | lock = fromTOML (readFile ../tests/workspace-virtual/Cargo.lock);
483 | cargoTomlFoo = fromTOML (readFile ../tests/workspace-virtual/crates/foo/Cargo.toml);
484 | cargoTomlBar = fromTOML (readFile ../tests/workspace-virtual/crates/bar/Cargo.toml);
485 | infoFoo = mkPkgInfoFromCargoToml cargoTomlFoo "";
486 | infoBar = mkPkgInfoFromCargoToml cargoTomlBar "";
487 |
488 | getCrateInfo = args:
489 | if args ? source then
490 | throw "No crates.io dependency"
491 | else if args.name == "foo" then
492 | infoFoo
493 | else if args.name == "bar" then
494 | infoBar
495 | else
496 | throw "Unknow crate: ${toJSON args}";
497 |
498 | resolved = resolveDepsFromLock getCrateInfo lock;
499 | in
500 | assertEq resolved {
501 | bar = {
502 | dependencies = [];
503 | features = {};
504 | links = null;
505 | name = "bar";
506 | procMacro = false;
507 | src = "";
508 | version = "0.1.0";
509 | };
510 | foo = {
511 | dependencies = [ {
512 | default_features = true;
513 | features = [];
514 | kind = "normal";
515 | name = "bar";
516 | optional = false;
517 | package = "bar";
518 | req = null;
519 | resolved = "bar";
520 | source = null;
521 | target = null;
522 | } ];
523 | features = {};
524 | links = null;
525 | name = "foo";
526 | procMacro = false;
527 | src = "";
528 | version = "0.1.0";
529 | };
530 | };
531 | };
532 | }
533 |
--------------------------------------------------------------------------------
/lib/semver.nix:
--------------------------------------------------------------------------------
1 | { lib, ... }:
2 | let
3 | inherit (builtins) match elemAt fromJSON;
4 | inherit (lib) compare compareLists splitString all any;
5 | in rec {
6 | parseSemver = ver: let
7 | m = match "([0-9]+)\\.([0-9]+)\\.([0-9]+)(-([A-Za-z0-9.-]+))?(\\+[A-Za-z0-9.-]+)?" ver;
8 | in
9 | if m == null then
10 | throw "Invalid semver: `${ver}`"
11 | else {
12 | maj = fromJSON (elemAt m 0);
13 | min = fromJSON (elemAt m 1);
14 | pat = fromJSON (elemAt m 2);
15 | pre = elemAt m 4;
16 | };
17 |
18 | compareSemver = a: b: let
19 | m1 = parseSemver a;
20 | m2 = parseSemver b;
21 | in
22 | if m1.maj != m2.maj then
23 | if m1.maj < m2.maj then -1 else 1
24 | else if m1.min != m2.min then
25 | if m1.min < m2.min then -1 else 1
26 | else if m1.pat != m2.pat then
27 | if m1.pat < m2.pat then -1 else 1
28 | else
29 | comparePre m1.pre m2.pre;
30 |
31 | comparePre = a: b:
32 | if a == null then
33 | if b == null then
34 | 0
35 | else
36 | 1
37 | else if b == null then
38 | -1
39 | else
40 | comparePreList (splitString "." a) (splitString "." b);
41 |
42 | isNumber = s: match "[0-9]+" s != null;
43 |
44 | comparePreList = compareLists (a: b:
45 | let
46 | num1 = if isNumber a then fromJSON a else null;
47 | num2 = if isNumber b then fromJSON b else null;
48 | in
49 | if num1 != null then
50 | if num2 != null then
51 | compare num1 num2
52 | else
53 | -1
54 | else if num2 != null then
55 | 1
56 | else
57 | compare a b
58 | );
59 |
60 | parseSemverReq = req: let
61 | reqs = splitString "," req;
62 | comparators = map parseComparators reqs;
63 | in
64 | ver: all (f: f ver) comparators && (isPreVersion ver -> any (containsExactPreVersion ver) reqs);
65 |
66 | isPreVersion = ver: match "[0-9.]+-.*" ver != null;
67 | containsExactPreVersion = ver: req: let
68 | m = parseSemver ver;
69 | in
70 | match " *(=|<|<=|>|>=|~|\\^)? *${toString m.maj}\\.${toString m.min}\\.${toString m.pat}-.*" req != null;
71 |
72 | opEq = { compMaj, compMin, compPat, compPre }: { maj, min, pat, pre }:
73 | maj == compMaj &&
74 | (compMin == null || min == compMin) &&
75 | (compPat == null || pat == compPat) &&
76 | (compPre == null || comparePre pre compPre == 0);
77 |
78 | opLtGt = op: { compMaj, compMin, compPat, compPre }: { maj, min, pat, pre }:
79 | if maj != compMaj then
80 | op maj compMaj
81 | else if compMin == null then
82 | false
83 | else if min != compMin then
84 | op min compMin
85 | else if compPat == null then
86 | false
87 | else if pat != compPat then
88 | op pat compPat
89 | else
90 | op (comparePre pre compPre) 0;
91 |
92 | opTilde = { compMaj, compMin, compPat, compPre }: { maj, min, pat, pre }:
93 | maj == compMaj &&
94 | (compMin == null || min == compMin) &&
95 | (compPat == null || pat > compPat || (pat == compPat &&
96 | comparePre pre compPre >= 0));
97 |
98 | opCaret = { compMaj, compMin, compPat, compPre }: { maj, min, pat, pre }:
99 | if maj != compMaj then
100 | false
101 | else if compMin == null then
102 | true
103 | else if compPat == null then
104 | if maj > 0 then
105 | min >= compMin
106 | else
107 | min == compMin
108 | else if maj > 0 then
109 | if min != compMin then
110 | min > compMin
111 | else if pat != compPat then
112 | pat > compPat
113 | else
114 | comparePre pre compPre >= 0
115 | else if min > 0 then
116 | if min != compMin then
117 | false
118 | else if pat != compPat then
119 | pat > compPat
120 | else
121 | comparePre pre compPre >= 0
122 | else if min != compMin || pat != compPat then
123 | false
124 | else
125 | comparePre pre compPre >= 0;
126 |
127 | parseComparators = req: let
128 | toInt = s: if s == null then null else fromJSON s;
129 |
130 | star = match " *(([0-9]+)\\.(([0-9]+)\\.)?)?\\*(\\.\\*)? *" req;
131 | star0 = elemAt star 1;
132 | star1 = elemAt star 3;
133 | comp = match " *(=|>|>=|<|<=|~|\\^)? *(([0-9]+)(\\.([0-9]+)(\\.([0-9]+)(-([A-Za-z0-9.-]+))?(\\+[A-Za-z0-9.-]*)?)?)?) *" req;
134 | compOp = elemAt comp 0;
135 | compVer = elemAt comp 1;
136 | compArgs = {
137 | compMaj = toInt (elemAt comp 2);
138 | compMin = toInt (elemAt comp 4);
139 | compPat = toInt (elemAt comp 6);
140 | compPre = elemAt comp 8;
141 | };
142 |
143 | less = a: b: a < b;
144 | greater = a: b: a > b;
145 | op = {
146 | "=" = opEq compArgs;
147 | "<" = opLtGt less compArgs;
148 | "<=" = args: opLtGt less compArgs args || opEq compArgs args;
149 | ">" = opLtGt greater compArgs;
150 | ">=" = args: opLtGt greater compArgs args || opEq compArgs args;
151 | "~" = opTilde compArgs;
152 | "^" = opCaret compArgs;
153 | }.${if compOp != null then compOp else "^"};
154 |
155 | in
156 | if star != null then
157 | if star0 == null then
158 | (ver: true)
159 | else if star1 == null then
160 | (ver: match "${toString star0}\\..*" ver != null)
161 | else
162 | (ver: match "${toString star0}\\.${toString star1}\\..*" ver != null)
163 | else if comp != null then
164 | (ver: op (parseSemver ver))
165 | else
166 | throw "Invalid version comparator: `${req}`";
167 |
168 | semver-compare-tests = { assertEq, ... }: {
169 | compare-simple1 = assertEq (compareSemver "1.2.3" "1.2.2") 1;
170 | compare-simple2 = assertEq (compareSemver "1.2.3" "1.2.3") 0;
171 | compare-simple3 = assertEq (compareSemver "1.2.3" "1.2.4") (-1);
172 | compare-simple4 = assertEq (compareSemver "1.2.3" "1.1.3") 1;
173 | compare-simple5 = assertEq (compareSemver "1.2.3" "1.3.3") (-1);
174 | compare-simple6 = assertEq (compareSemver "1.2.3" "0.2.3") 1;
175 | compare-simple7 = assertEq (compareSemver "1.2.3" "2.2.3") (-1);
176 | };
177 |
178 | # From https://github.com/dtolnay/semver/blob/a03d376560e0c4d16518bc271867b1981c85acf0/tests/test_version_req.rs
179 | semver-req-tests = { assertEq, ... }: let
180 | testMatchReq = req: { yes ? [], no ? [] }: let
181 | inherit (lib) const;
182 | checker = parseSemverReq req;
183 | got = map checker (yes ++ no);
184 | expect = map (const true) yes ++ map (const false) no;
185 | in
186 | assertEq got expect;
187 | in {
188 | eq1 = testMatchReq "=1.0.0" {
189 | yes = [ "1.0.0" ];
190 | no = [ "1.0.1" "0.9.9" "0.10.0" "0.1.0" "1.0.0-pre" ];
191 | };
192 | default = testMatchReq "^1.0.0" {
193 | yes = [ "1.0.0" "1.1.0" "1.0.1" ];
194 | no = [ "0.9.9" "0.10.0" "0.1.0" "1.0.0-pre" "1.0.1-pre" ];
195 | };
196 | exact1 = testMatchReq "=1.0.0" {
197 | yes = [ "1.0.0" ];
198 | no = [ "1.0.1" "0.9.9" "0.10.0" "0.1.0" "1.0.0-pre" ];
199 | };
200 | exact2 = testMatchReq "=0.9.0" {
201 | yes = [ "0.9.0" ];
202 | no = [ "0.9.1" "1.9.0" "0.0.9" "0.9.0-pre" ];
203 | };
204 | exact3 = testMatchReq "=0.0.2" {
205 | yes = [ "0.0.2" ];
206 | no = [ "0.0.1" "0.0.3" "0.0.2-pre" ];
207 | };
208 | exact4 = testMatchReq "=0.1.0-beta2.a" {
209 | yes = [ "0.1.0-beta2.a" ];
210 | no = [ "0.9.1" "0.1.0" "0.1.1-beta2.a" "0.1.0-beta2" ];
211 | };
212 | exact5 = testMatchReq "=0.1.0" {
213 | yes = [ "0.1.0" "0.1.0+meta" "0.1.0+any" ];
214 | };
215 | gt1 = testMatchReq ">= 1.0.0" {
216 | yes = [ "1.0.0" "2.0.0" ];
217 | no = [ "0.1.0" "0.0.1" "1.0.0-pre" "2.0.0-pre" ];
218 | };
219 | gt2 = testMatchReq ">=2.1.0-alpha2" {
220 | yes = [ "2.1.0-alpha2" "2.1.0-alpha3" "2.1.0" "3.0.0" ];
221 | no = [ "2.0.0" "2.1.0-alpha1" "2.0.0-alpha2" "3.0.0-alpha2" ];
222 | };
223 | lt1 = testMatchReq "<1.0.0" {
224 | yes = [ "0.1.0" "0.0.1" ];
225 | no = [ "1.0.0" "1.0.0-beta" "1.0.1" "0.9.9-alpha" ];
226 | };
227 | le1 = testMatchReq "<= 2.1.0-alpha2" {
228 | yes = [ "2.1.0-alpha2" "2.1.0-alpha1" "2.0.0" "1.0.0" ];
229 | no = [ "2.1.0" "2.2.0-alpha1" "2.0.0-alpha2" "1.0.0-alpha2" ];
230 | };
231 | multi1 = testMatchReq ">1.0.0-alpha, <1.0.0" {
232 | yes = [ "1.0.0-beta" ];
233 | };
234 | multi2 = testMatchReq ">1.0.0-alpha, <1.0" {
235 | no = [ "1.0.0-beta" ];
236 | };
237 | multi3 = testMatchReq ">1.0.0-alpha, <1" {
238 | no = [ "1.0.0-beta" ];
239 | };
240 | multi4 = testMatchReq "> 0.0.9, <= 2.5.3" {
241 | yes = [ "0.0.10" "1.0.0" "2.5.3" ];
242 | no = [ "0.0.8" "2.5.4" ];
243 | };
244 | multi5 = testMatchReq "0.3.0, 0.4.0" {
245 | no = [ "0.0.8" "0.3.0" "0.4.0" ];
246 | };
247 | multi6 = testMatchReq "<= 0.2.0, >= 0.5.0" {
248 | no = [ "0.0.8" "0.3.0" "0.5.1" ];
249 | };
250 | multi7 = testMatchReq "^0.1.0, ^0.1.4, ^0.1.6" {
251 | yes = [ "0.1.6" "0.1.9" ];
252 | no = [ "0.1.0" "0.1.4" "0.2.0" ];
253 | };
254 | multi8 = testMatchReq ">=0.5.1-alpha3, <0.6" {
255 | yes = [ "0.5.1-alpha3" "0.5.1-alpha4" "0.5.1-beta" "0.5.1" "0.5.5" ];
256 | no = [ "0.5.1-alpha1" "0.5.2-alpha3" "0.5.5-pre" "0.5.0-pre" "0.6.0" "0.6.0-pre" ];
257 | };
258 | tilde1 = testMatchReq "~1" {
259 | yes = [ "1.0.0" "1.0.1" "1.1.1" ];
260 | no = [ "0.9.1" "2.9.0" "0.0.9" ];
261 | };
262 | tilde2 = testMatchReq "~1.2" {
263 | yes = [ "1.2.0" "1.2.1" ];
264 | no = [ "1.1.1" "1.3.0" "0.0.9" ];
265 | };
266 | tilde3 = testMatchReq "~1.2.2" {
267 | yes = [ "1.2.2" "1.2.4" ];
268 | no = [ "1.2.1" "1.9.0" "1.0.9" "2.0.1" "0.1.3" ];
269 | };
270 | tilde4 = testMatchReq "~1.2.3-beta.2" {
271 | yes = [ "1.2.3" "1.2.4" "1.2.3-beta.2" "1.2.3-beta.4" ];
272 | no = [ "1.3.3" "1.1.4" "1.2.3-beta.1" "1.2.4-beta.2" ];
273 | };
274 | caret1 = testMatchReq "^1" {
275 | yes = [ "1.1.2" "1.1.0" "1.2.1" "1.0.1" ];
276 | no = [ "0.9.1" "2.9.0" "0.1.4" "1.0.0-beta1" "0.1.0-alpha" "1.0.1-pre" ];
277 | };
278 | caret2 = testMatchReq "^1.1" {
279 | yes = [ "1.1.2" "1.1.0" "1.2.1" ];
280 | no = [ "0.9.1" "2.9.0" "1.0.1" "0.1.4" ];
281 | };
282 | caret3 = testMatchReq "^1.1.2" {
283 | yes = [ "1.1.2" "1.1.4" "1.2.1" ];
284 | 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" ];
285 | };
286 | caret4 = testMatchReq "^0.1.2" {
287 | yes = [ "0.1.2" "0.1.4" ];
288 | 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" ];
289 | };
290 | caret5 = testMatchReq "^0.5.1-alpha3" {
291 | yes = [ "0.5.1-alpha3" "0.5.1-alpha4" "0.5.1-beta" "0.5.1" "0.5.5" ];
292 | no = [ "0.5.1-alpha1" "0.5.2-alpha3" "0.5.5-pre" "0.5.0-pre" "0.6.0" ];
293 | };
294 | caret6 = testMatchReq "^0.0.2" {
295 | yes = [ "0.0.2" ];
296 | no = [ "0.9.1" "2.9.0" "1.1.1" "0.0.1" "0.1.4" ];
297 | };
298 | caret7 = testMatchReq "^0.0" {
299 | yes = [ "0.0.2" "0.0.0" ];
300 | no = [ "0.9.1" "2.9.0" "1.1.1" "0.1.4" ];
301 | };
302 | caret8 = testMatchReq "^0" {
303 | yes = [ "0.9.1" "0.0.2" "0.0.0" ];
304 | no = [ "2.9.0" "1.1.1" ];
305 | };
306 | caret9 = testMatchReq "^1.4.2-beta.5" {
307 | yes = [ "1.4.2" "1.4.3" "1.4.2-beta.5" "1.4.2-beta.6" "1.4.2-c" ];
308 | no = [ "0.9.9" "2.0.0" "1.4.2-alpha" "1.4.2-beta.4" "1.4.3-beta.5" ];
309 | };
310 | star1 = testMatchReq "*" {
311 | yes = [ "0.9.1" "2.9.0" "0.0.9" "1.0.1" "1.1.1" ];
312 | };
313 | star2 = testMatchReq "1.*" {
314 | yes = [ "1.2.0" "1.2.1" "1.1.1" "1.3.0" ];
315 | no = [ "0.0.9" ];
316 | };
317 | star3 = testMatchReq "1.2.*" {
318 | yes = [ "1.2.0" "1.2.2" "1.2.4" ];
319 | no = [ "1.9.0" "1.0.9" "2.0.1" "0.1.3" ];
320 | };
321 | pre = testMatchReq "=2.1.1-really.0" {
322 | yes = [ "2.1.1-really.0" ];
323 | };
324 | };
325 | }
326 |
--------------------------------------------------------------------------------
/lib/support.nix:
--------------------------------------------------------------------------------
1 | { lib, self }:
2 | let
3 | inherit (builtins) fromTOML toJSON match tryEval split;
4 | inherit (lib)
5 | readFile mapAttrs mapAttrs' makeOverridable warnIf
6 | isString hasPrefix
7 | filter flatten elem elemAt listToAttrs subtractLists concatStringsSep
8 | attrNames attrValues recursiveUpdate optionalAttrs;
9 | inherit (self.pkg-info) mkPkgInfoFromCargoToml getPkgInfoFromIndex toPkgId;
10 | inherit (self.resolve) resolveDepsFromLock resolveFeatures;
11 | inherit (self.target-cfg) platformToCfgs evalTargetCfgStr;
12 | inherit (self.glob) globMatchDir;
13 | in
14 | rec {
15 |
16 | # https://doc.rust-lang.org/cargo/reference/profiles.html#default-profiles
17 | defaultProfiles = rec {
18 | dev = {
19 | name = "dev";
20 | build-override = defaultBuildProfile;
21 | opt-level = 0;
22 | debug = true;
23 | debug-assertions = true;
24 | overflow-checks = true;
25 | lto = false;
26 | panic = "unwind";
27 | codegen-units = 256;
28 | rpath = false;
29 | };
30 | release = {
31 | name = "release";
32 | build-override = defaultBuildProfile;
33 | opt-level = 3;
34 | debug = false;
35 | debug-assertions = false;
36 | overflow-checks = false;
37 | lto = false;
38 | panic = "unwind";
39 | codegen-units = 16;
40 | rpath = false;
41 | };
42 | test = dev // { name = "test"; };
43 | bench = release // { name = "bench"; };
44 | };
45 |
46 | defaultBuildProfile = {
47 | opt-level = 0;
48 | codegen-units = 256;
49 | };
50 |
51 | profilesFromManifest = manifest:
52 | let
53 | knownFields = [
54 | "name"
55 | "inherits"
56 | # "package" # Unsupported yet.
57 | "build-override"
58 |
59 | "opt-level"
60 | "debug"
61 | # split-debug-info # Unsupported.
62 | "strip"
63 | "debug-assertions"
64 | "overflow-checks"
65 | "lto"
66 | "panic"
67 | # incremental # Unsupported.
68 | "codegen-units"
69 | "rpath"
70 | ];
71 |
72 | profiles = mapAttrs (name: p:
73 | let unknown = removeAttrs p knownFields; in
74 | warnIf (unknown != {}) "Unsupported fields of profile ${name}: ${toString (attrNames unknown)}"
75 | (optionalAttrs (p ? inherits) profiles.${p.inherits} // p)
76 | ) (recursiveUpdate defaultProfiles (manifest.profile or {}));
77 |
78 | in profiles;
79 |
80 | mkRustPackageOrWorkspace =
81 | { defaultRegistries, pkgsBuildHost, buildRustCrate, stdenv }@default:
82 | { src # : Path
83 | , gitSrcs ? {} # : Attrset Path
84 | , buildCrateOverrides ? {} # : Attrset (Attrset _)
85 | , extraRegistries ? {} # : Attrset Registry
86 | , registries ? defaultRegistries // extraRegistries
87 |
88 | , rustc ? pkgsBuildHost.rustc
89 | , stdenv ? default.stdenv
90 | }:
91 | let
92 | manifest = fromTOML (readFile (src + "/Cargo.toml"));
93 |
94 | profiles = profilesFromManifest manifest;
95 |
96 | selected = flatten (map (glob: globMatchDir glob src) manifest.workspace.members);
97 | excluded = map sanitizeRelativePath (manifest.workspace.exclude or []);
98 | members = subtractLists excluded selected;
99 |
100 | lock = fromTOML (readFile (src + "/Cargo.lock"));
101 | # We don't distinguish between v1 and v2. But v3 is different from both.
102 | lockVersionSet = { lockVersion = lock.version or 2; };
103 |
104 | localSrcInfos =
105 | listToAttrs
106 | (map (relativePath:
107 | let
108 | memberRoot = src + ("/" + relativePath);
109 | memberManifest = fromTOML (readFile (memberRoot + "/Cargo.toml")) // lockVersionSet;
110 | in {
111 | name = toPkgId memberManifest.package;
112 | value = mkPkgInfoFromCargoToml memberManifest memberRoot;
113 | }
114 | ) (if manifest ? workspace then members else [ "" ]));
115 |
116 | in mkRustPackageSet {
117 | gitSrcInfos = mapAttrs (url: src:
118 | mkPkgInfoFromCargoToml (fromTOML (readFile (src + "/Cargo.toml")) // lockVersionSet) src
119 | ) gitSrcs;
120 |
121 | inherit lock profiles localSrcInfos buildRustCrate buildCrateOverrides registries rustc stdenv;
122 | };
123 |
124 | # -> { = { = ; }; }
125 | mkRustPackageSet =
126 | { lock # :
127 | , localSrcInfos # : Attrset PkgInfo
128 | , gitSrcInfos # : Attrset PkgInfo
129 | , profiles # : Attrset Profile
130 | , buildCrateOverrides # : Attrset (Attrset _)
131 | , buildRustCrate # : Attrset -> Derivation
132 | , registries # : Attrset Registry
133 |
134 | # FIXME: Cross compilation.
135 | , rustc
136 | , stdenv
137 | }:
138 | let
139 |
140 | getPkgInfo = { source ? null, name, version, ... }@args: let
141 | m = match "(registry|git)\\+([^#]*).*" source;
142 | kind = elemAt m 0;
143 | url = elemAt m 1;
144 | in
145 | # Local crates have no `source`.
146 | if source == null then
147 | localSrcInfos.${toPkgId args}
148 | or (throw "Local crate is outside the workspace: ${toPkgId args}")
149 | // { isLocalPkg = true; }
150 | else if m == null then
151 | throw "Invalid source: ${source}"
152 | else if kind == "registry" then
153 | getPkgInfoFromIndex
154 | (registries.${url} or
155 | (throw "Registry `${url}` not found. Please define it in `extraRegistries`."))
156 | args
157 | // { inherit source; } # `source` is for crate id, which is used for overrides.
158 | else if kind == "git" then
159 | gitSrcInfos.${url}
160 | or (throw "Git source `${url}` not found. Please define it in `gitSrcs`.")
161 | else
162 | throw "Invalid source: ${source}";
163 |
164 | hostCfgs = platformToCfgs stdenv.hostPlatform;
165 |
166 | pkgSetRaw = resolveDepsFromLock getPkgInfo lock;
167 | pkgSet = mapAttrs (id: info: info // {
168 | dependencies = map (dep: dep // {
169 | targetEnabled = dep.target != null -> evalTargetCfgStr hostCfgs dep.target;
170 | }) info.dependencies;
171 | }) pkgSetRaw;
172 |
173 | selectDeps = pkgs: deps: features: selectKind: onlyLinks:
174 | map
175 | (dep: { rename = dep.rename or null; drv = pkgs.${dep.resolved}; })
176 | (filter
177 | ({ kind, name, optional, targetEnabled, resolved, ... }@dep:
178 | targetEnabled && kind == selectKind
179 | && (optional -> elem name features)
180 | && (if resolved == null then throw "Unresolved dependency: ${toJSON dep}" else true)
181 | && (onlyLinks -> pkgSet.${resolved}.links != null))
182 | deps);
183 |
184 | buildRustCrate' = info: args:
185 | let
186 | # TODO: Proc macro crates should behave differently in dependency resolution.
187 | # But this override is applied just before the `buildRustCrate` call.
188 | args' = args // (info.__override or lib.id) args;
189 | args'' = args' // (buildCrateOverrides.${toPkgId info} or lib.id) args';
190 | in
191 | buildRustCrate args'';
192 |
193 | mkPkg = profile: rootId: makeOverridable (
194 | { features }:
195 | let
196 | rootFeatures = if features != null then features
197 | else if pkgSet.${rootId}.features ? default then [ "default" ]
198 | else [];
199 |
200 | resolvedBuildFeatures = resolveFeatures {
201 | inherit pkgSet rootId rootFeatures;
202 | depFilter = dep: dep.targetEnabled && dep.kind == "normal" || dep.kind == "build";
203 | };
204 | resolvedNormalFeatures = resolveFeatures {
205 | inherit pkgSet rootId rootFeatures;
206 | depFilter = dep: dep.targetEnabled && dep.kind == "normal";
207 | };
208 |
209 | pkgsBuild = mapAttrs (id: features: let info = pkgSet.${id}; in
210 | if features != null then
211 | buildRustCrate' info {
212 | inherit (info) version src procMacro;
213 | inherit features profile rustc;
214 | pname = info.name;
215 | capLints = if localSrcInfos ? id then null else "allow";
216 | buildDependencies = selectDeps pkgsBuild info.dependencies features "build" false;
217 | # Build dependency's normal dependency is still build dependency.
218 | dependencies = selectDeps pkgsBuild info.dependencies features "normal" false;
219 | linksDependencies = selectDeps pkgsBuild info.dependencies features "normal" true;
220 | }
221 | else
222 | null
223 | ) resolvedBuildFeatures;
224 |
225 | pkgs = mapAttrs (id: features: let info = pkgSet.${id}; in
226 | if features != null then
227 | buildRustCrate' info {
228 | inherit (info) version src links procMacro;
229 | inherit features profile rustc;
230 | pname = info.name;
231 | capLints = if localSrcInfos ? id then null else "allow";
232 | buildDependencies = selectDeps pkgsBuild info.dependencies features "build" false;
233 | dependencies = selectDeps pkgs info.dependencies features "normal" false;
234 | linksDependencies = selectDeps pkgs info.dependencies features "normal" true;
235 | }
236 | else
237 | null
238 | ) resolvedNormalFeatures;
239 | in
240 | pkgs.${rootId}
241 | ) {
242 | features = null;
243 | };
244 |
245 | in
246 | mapAttrs (_: profile:
247 | mapAttrs' (pkgId: pkgInfo: {
248 | name = pkgInfo.name;
249 | value = mkPkg profile pkgId;
250 | }) localSrcInfos
251 | ) profiles;
252 |
253 | sanitizeRelativePath = path:
254 | if hasPrefix "/" path then
255 | throw "Absolute path is not allowed: ${path}"
256 | else
257 | concatStringsSep "/"
258 | (filter
259 | (s:
260 | isString s && s != "" && s != "." &&
261 | (if match ''.*[[?*].*|\.\.'' s != null then throw ''
262 | Globing and `..` are not allowed: ${path}
263 | '' else true))
264 | (split ''[\/]'' path));
265 |
266 | build-from-src-dry-tests = { assertEq, pkgs, defaultRegistries, ... }: let
267 | inherit (builtins) head listToAttrs;
268 |
269 | mkPackage = pkgs.callPackage mkRustPackageOrWorkspace {
270 | inherit defaultRegistries;
271 | buildRustCrate = args: args;
272 | };
273 |
274 | build = src: args:
275 | head (attrValues (mkPackage ({ inherit src; } // args)).dev);
276 |
277 | in
278 | {
279 | features = let ret = build ../tests/features {}; in
280 | assertEq ret.features [ "a" "default" "semver" ]; # FIXME
281 |
282 | dependency-features = let
283 | ret = build ../tests/features { };
284 | semver = (head ret.dependencies).drv;
285 | serde = (head semver.dependencies).drv;
286 | in assertEq
287 | [ semver.features serde.features ]
288 | [ [ "default" "serde" "std" ] [ /* Don't trigger default features */ ] ];
289 |
290 | dependency-overrided = let
291 | ret = build ../tests/features {
292 | buildCrateOverrides."" = old: { a = "b"; };
293 | buildCrateOverrides."serde 1.0.139 (registry+https://github.com/rust-lang/crates.io-index)" = old: {
294 | buildInputs = [ "some-inputs" ];
295 | };
296 | };
297 | semver = (head ret.dependencies).drv;
298 | serde = (head semver.dependencies).drv;
299 | in
300 | assertEq serde.buildInputs [ "some-inputs" ];
301 |
302 | dependency-kinds = let
303 | mkSrc = from: { __toString = _: ../tests/fake-semver; inherit from; };
304 | gitSrcs = {
305 | "https://github.com/dtolnay/semver?tag=1.0.0" = mkSrc "tag";
306 | "http://github.com/dtolnay/semver" = mkSrc "branch"; # v1, v2
307 | "http://github.com/dtolnay/semver?branch=master" = mkSrc "branch"; # v3
308 | "ssh://git@github.com/dtolnay/semver?rev=a2ce5777dcd455246e4650e36dde8e2e96fcb3fd" = mkSrc "rev";
309 | "ssh://git@github.com/dtolnay/semver" = mkSrc "nothing";
310 | };
311 | extraRegistries = {
312 | "https://www.github.com/rust-lang/crates.io-index" =
313 | head (attrValues defaultRegistries);
314 | };
315 |
316 | ret = build ../tests/dependency-v3 {
317 | inherit gitSrcs extraRegistries;
318 | };
319 | ret' = listToAttrs
320 | (map (dep: {
321 | name = if dep.rename != null then dep.rename else dep.drv.pname;
322 | value = dep.drv.src.from or dep.drv.src.name;
323 | }) ret.dependencies);
324 | in
325 | assertEq ret' {
326 | cratesio = "crate-semver-1.0.12.tar.gz";
327 | git_branch = "branch";
328 | git_head = "nothing";
329 | git_rev = "rev";
330 | git_tag = "tag";
331 | registry_index = "crate-semver-1.0.12.tar.gz";
332 | };
333 |
334 | libz-propagated = let
335 | ret = build ../tests/libz-dynamic {};
336 | libz = (head ret.dependencies).drv;
337 | in
338 | assertEq (head libz.propagatedBuildInputs).pname "zlib";
339 |
340 | libz-link = let
341 | ret = build ../tests/libz-dynamic {};
342 | libz = (head ret.dependencies).drv;
343 | libz' = (head ret.linksDependencies).drv;
344 | in
345 | assertEq [ libz.links libz'.links ] [ "z" "z" ];
346 | };
347 |
348 | sanitize-relative-path-tests = { assertEq, ... }: let
349 | assertOk = raw: expect: assertEq (tryEval (sanitizeRelativePath raw)) { success = true; value = expect; };
350 | assertInvalid = raw: assertEq (tryEval (sanitizeRelativePath raw)) { success = false; value = false; };
351 | in
352 | {
353 | empty = assertOk "" "";
354 | simple1 = assertOk "foo" "foo";
355 | simple2 = assertOk "foo/bar" "foo/bar";
356 | dot1 = assertOk "." "";
357 | dot2 = assertOk "./././" "";
358 | dot3 = assertOk "./foo/./bar/" "foo/bar";
359 |
360 | dotdot1 = assertInvalid "..";
361 | dotdot2 = assertInvalid "./foo/..";
362 | dotdot3 = assertInvalid "../bar";
363 | root1 = assertInvalid "/";
364 | root2 = assertInvalid "/foo";
365 | };
366 | }
367 |
--------------------------------------------------------------------------------
/lib/target-cfg.nix:
--------------------------------------------------------------------------------
1 | { lib, ... }:
2 | let
3 | inherit (builtins) match tryEval;
4 | inherit (lib)
5 | concatStrings
6 | length elem elemAt any all sort flatten isList
7 | optionalAttrs mapAttrsToList;
8 | in
9 | rec {
10 | # https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch
11 | platformToTargetArch = platform:
12 | if platform.isAarch32 then "arm"
13 | else platform.parsed.cpu.name;
14 |
15 | # https://doc.rust-lang.org/reference/conditional-compilation.html#target_os
16 | platformToTargetOs = platform:
17 | if platform.isDarwin then "macos"
18 | else platform.parsed.kernel.name;
19 |
20 | # https://github.com/rust-lang/rust/blob/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/compiler/rustc_session/src/config.rs#L835
21 | # https://doc.rust-lang.org/reference/conditional-compilation.html
22 | platformToCfgAttrs = platform: {
23 | # Arch info.
24 | # https://github.com/NixOS/nixpkgs/blob/c63d4270feed5eb6c578fe2d9398d3f6f2f96811/pkgs/build-support/rust/build-rust-crate/configure-crate.nix#L126
25 | target_arch = platformToTargetArch platform;
26 | target_endian = if platform.isLittleEndian then "little"
27 | else if platform.isBigEndian then "big"
28 | else throw "Unknow target_endian for ${platform.config}";
29 | target_env = if platform.isNone then ""
30 | else if platform.libc == "glibc" then "gnu"
31 | else if platform.isMusl then "musl"
32 | else if platform.isDarwin then "" # Empty
33 | else lib.trace platform (throw "Unknow target_env for ${platform.config}");
34 | target_family = if platform.isUnix then "unix"
35 | else if platform.isWindows then "windows"
36 | else null;
37 | target_os = platformToTargetOs platform;
38 | target_pointer_width = toString platform.parsed.cpu.bits;
39 | target_vendor = platform.parsed.vendor.name;
40 | } // optionalAttrs platform.isx86 {
41 | # These features are assume to be available.
42 | target_feature = [ "fxsr" "sse" "sse2" ];
43 | } // optionalAttrs platform.isUnix {
44 | unix = true;
45 | } // optionalAttrs platform.isWindows {
46 | windows = true;
47 | };
48 |
49 | platformToCfgs = platform:
50 | flatten (
51 | mapAttrsToList (key: value:
52 | if value == true then { inherit key; }
53 | else if isList value then map (value: { inherit key value; }) value
54 | else { inherit key value; }
55 | ) (platformToCfgAttrs platform));
56 |
57 | # cfgs: [
58 | # { key = "atom1"; }
59 | # { key = "atom2"; }
60 | # { key = "feature"; value = "foo"; }
61 | # { key = "feature"; value = "bar"; }
62 | # ]
63 | evalTargetCfgStr = cfgs: s:
64 | evalCfgExpr cfgs (parseTargetCfgExpr s);
65 |
66 | # Cargo's parse is stricter than rustc's.
67 | # - Must starts with `cfg(` and ends with `)`. No spaces are allowed before and after.
68 | # - Identifiers must follows /[A-Za-z_][A-Za-z_0-9]*/.
69 | # - Raw identifiers, raw strings, escapes in strings are not allowed.
70 | #
71 | # The target can also be a simple target name like `aarch64-unknown-linux-gnu`, which will be parsed
72 | # as if it's `cfg(target = "...")`.
73 | #
74 | # https://github.com/rust-lang/cargo/blob/dcc95871605785c2c1f2279a25c6d3740301c468/crates/cargo-platform/src/cfg.rs
75 | parseTargetCfgExpr = cfg: let
76 | fail = reason: throw "${reason}, when parsing `${cfg}";
77 |
78 | go = { fn, values, afterComma, prev }@stack: s: let
79 | m = match ''((all|any|not) *\( *|(\)) *|(,) *|([A-Za-z_][A-Za-z_0-9]*) *(= *"([^"]*)" *)?)(.*)'' s;
80 | mFn = elemAt m 1;
81 | mClose = elemAt m 2;
82 | mComma = elemAt m 3;
83 | mIdent = elemAt m 4;
84 | mString = elemAt m 6;
85 | mRest = elemAt m 7;
86 | in
87 | if s == "" then
88 | stack
89 | else if m == null then
90 | fail "No parse `${s}`"
91 | # else if builtins.trace ([ stack m ]) (mFn != null) then
92 | else if mFn != null then
93 | if !afterComma then
94 | fail "Missing comma before `${mFn}` at `${s}"
95 | else
96 | go { fn = mFn; values = []; afterComma = true; prev = stack; } mRest
97 | else if mClose != null then
98 | if prev == null then
99 | fail "Unexpected `)` at `${s}`"
100 | else if fn == "not" && length values == 0 then
101 | fail "`not` must have exact one argument, got 0"
102 | else if prev.fn == "not" && length prev.values != 0 then
103 | fail "`not` must have exact one argument, got at least 2"
104 | else
105 | go (prev // { values = prev.values ++ [ { inherit (stack) fn values; } ]; afterComma = false; }) mRest
106 | else if mComma != null then
107 | if afterComma then
108 | fail "Unexpected `,` at `${s}`"
109 | else
110 | go (stack // { afterComma = true; }) mRest
111 | else
112 | if !afterComma then
113 | fail "Missing comma before identifier `${mIdent}` at `${s}"
114 | else if fn == "not" && length values != 0 then
115 | fail "`not` must have exact one argument, got at least 2"
116 | else
117 | let kv =
118 | if mString != null then { key = mIdent; value = mString; }
119 | else { key = mIdent; };
120 | in
121 | go (stack // { afterComma = false; values = values ++ [ kv ]; }) mRest;
122 |
123 | mSimpleTarget = match "[A-Za-z_0-9_.-]+" cfg;
124 |
125 | mCfg = match ''cfg\( *(.*)\)'' cfg;
126 | mCfgInner = elemAt mCfg 0;
127 | ret = go { fn = "cfg"; values = []; afterComma = true; prev = null; } mCfgInner;
128 |
129 | in
130 | if mSimpleTarget != null then
131 | { key = "target"; value = cfg; }
132 | else if mCfg == null then
133 | fail "Cfg expr must be a simple target string, or start with `cfg(` and end with `)`"
134 | else if ret.prev != null then
135 | fail "Missing `)`"
136 | else if length ret.values != 1 then
137 | fail "`cfg` must have exact one argument, got ${toString (length ret.values)}"
138 | else
139 | elemAt ret.values 0;
140 |
141 | evalCfgExpr = cfgs: tree:
142 | if !(tree ? fn) then
143 | elem tree cfgs
144 | else if tree.fn == "all" then
145 | all (evalCfgExpr cfgs) tree.values
146 | else if tree.fn == "any" then
147 | any (evalCfgExpr cfgs) tree.values
148 | else
149 | !evalCfgExpr cfgs (elemAt tree.values 0);
150 |
151 | cfg-parser-tests = { assertEq, ... }: let
152 | shouldParse = cfg: expect:
153 | assertEq (tryEval (parseTargetCfgExpr cfg)) { success = true; value = expect; };
154 | shouldNotParse = cfg:
155 | assertEq (tryEval (parseTargetCfgExpr cfg)) { success = false; value = false; };
156 | in {
157 | simple-target1 = shouldParse "thumbv8m.base-none-eabi"
158 | { key = "target"; value = "thumbv8m.base-none-eabi"; };
159 | simple-target2 = shouldParse "aarch64-unknown-linux-gnu"
160 | { key = "target"; value = "aarch64-unknown-linux-gnu"; };
161 |
162 | simple1 = shouldParse "cfg(atom)" { key = "atom"; };
163 | simple2 = shouldParse ''cfg(k = "v")'' { key = "k"; value = "v"; };
164 | complex = shouldParse ''cfg( all ( not ( a , ) , b , all ( ) , any ( c , d = "e" ) , ) )''
165 | {
166 | fn = "all";
167 | values = [
168 | {
169 | fn = "not";
170 | values = [ { key = "a"; } ];
171 | }
172 | { key = "b"; }
173 | {
174 | fn = "all";
175 | values = [];
176 | }
177 | {
178 | fn = "any";
179 | values = [
180 | { key = "c"; }
181 | { key = "d"; value = "e"; }
182 | ];
183 | }
184 | ];
185 | };
186 |
187 | invalid-cfg1 = shouldNotParse "cfg (a)";
188 | invalid-cfg2 = shouldNotParse "cfg()";
189 | invalid-cfg3 = shouldNotParse "cfg(a,b)";
190 | invalid-not1 = shouldNotParse "cfg(not(a,b))";
191 | invalid-not2 = shouldNotParse "cfg(not())";
192 | invalid-comma1 = shouldNotParse "cfg(all(,))";
193 | invalid-comma2 = shouldNotParse "cfg(all(a,,b))";
194 | invalid-comma3 = shouldNotParse "cfg(all(a,b,,))";
195 | invalid-comma4 = shouldNotParse "cfg(all(a b))";
196 | invalid-comma5 = shouldNotParse "cfg(all(any() any()))";
197 | invalid-paren1 = shouldNotParse "cfg(all(a)))";
198 | invalid-paren2 = shouldNotParse "cfg(all(a)";
199 | };
200 |
201 | cfg-eval-tests = { assertEq, ... }: let
202 | cfgs = [
203 | { key = "foo"; }
204 | { key = "bar"; }
205 | { key = "feature"; value = "foo"; }
206 | { key = "feature"; value = "bar"; }
207 | ];
208 | test = cfg: expect: assertEq (evalTargetCfgStr cfgs cfg) expect;
209 | in {
210 | simple1 = test ''cfg(foo)'' true;
211 | simple2 = test ''cfg(baz)'' false;
212 | simple3 = test ''cfg(feature = "foo")'' true;
213 | simple4 = test ''cfg(foo = "")'' false;
214 | simple5 = test ''cfg(wtf = "foo")'' false;
215 |
216 | all1 = test ''cfg(all())'' true;
217 | all2 = test ''cfg(all(foo))'' true;
218 | all3 = test ''cfg(all(baz))'' false;
219 | all4 = test ''cfg(all(foo,bar))'' true;
220 | all5 = test ''cfg(all(foo,bar,baz))'' false;
221 | all6 = test ''cfg(all(foo,baz,bar))'' false;
222 | all7 = test ''cfg(all(baz,foo))'' false;
223 | all8 = test ''cfg(all(baz,feature="foo"))'' false;
224 | all9 = test ''cfg(all(baz,feature="wtf"))'' false;
225 | all10 = test ''cfg(all(foo,feature="foo"))'' true;
226 |
227 | any1 = test ''cfg(any())'' false;
228 | any2 = test ''cfg(any(foo))'' true;
229 | any3 = test ''cfg(any(baz))'' false;
230 | any4 = test ''cfg(any(foo,bar))'' true;
231 | any5 = test ''cfg(any(foo,bar,baz))'' true;
232 | any6 = test ''cfg(any(foo,baz,bar))'' true;
233 | any7 = test ''cfg(any(baz,foo))'' true;
234 | any8 = test ''cfg(any(baz,feature="foo"))'' true;
235 | any9 = test ''cfg(any(baz,feature="wtf"))'' false;
236 | any10 = test ''cfg(any(foo,feature="wtf"))'' true;
237 |
238 | not1 = test ''cfg(not(foo))'' false;
239 | not2 = test ''cfg(not(wtf))'' true;
240 | };
241 |
242 | platform-cfg-tests = { assertEq, ... }: let
243 | inherit (lib.systems) elaborate;
244 | test = config: expect: let
245 | cfgs = platformToCfgs (elaborate config);
246 | strs = map ({ key, value ? null }:
247 | if value != null then "${key}=\"${value}\"\n" else "${key}\n"
248 | ) cfgs;
249 | got = concatStrings (sort (a: b: a < b) strs);
250 | in
251 | assertEq got expect;
252 |
253 | in {
254 | attrs-x86_64-linux = assertEq (platformToCfgAttrs (elaborate "x86_64-unknown-linux-gnu")) {
255 | target_arch = "x86_64";
256 | target_endian = "little";
257 | target_env = "gnu";
258 | target_family = "unix";
259 | target_feature = ["fxsr" "sse" "sse2"];
260 | target_os = "linux";
261 | target_pointer_width = "64";
262 | target_vendor = "unknown";
263 | unix = true;
264 | };
265 |
266 | cfg-x86_64-linux = test "x86_64-unknown-linux-gnu" ''
267 | target_arch="x86_64"
268 | target_endian="little"
269 | target_env="gnu"
270 | target_family="unix"
271 | target_feature="fxsr"
272 | target_feature="sse"
273 | target_feature="sse2"
274 | target_os="linux"
275 | target_pointer_width="64"
276 | target_vendor="unknown"
277 | unix
278 | '';
279 |
280 | cfg-aarch64-linux = test "aarch64-unknown-linux-gnu" ''
281 | target_arch="aarch64"
282 | target_endian="little"
283 | target_env="gnu"
284 | target_family="unix"
285 | target_os="linux"
286 | target_pointer_width="64"
287 | target_vendor="unknown"
288 | unix
289 | '';
290 | };
291 | }
292 |
--------------------------------------------------------------------------------
/noc/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "aho-corasick"
7 | version = "0.7.18"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
10 | dependencies = [
11 | "memchr",
12 | ]
13 |
14 | [[package]]
15 | name = "anyhow"
16 | version = "1.0.63"
17 | source = "registry+https://github.com/rust-lang/crates.io-index"
18 | checksum = "a26fa4d7e3f2eebadf743988fc8aec9fa9a9e82611acafd77c1462ed6262440a"
19 |
20 | [[package]]
21 | name = "askama"
22 | version = "0.11.1"
23 | source = "registry+https://github.com/rust-lang/crates.io-index"
24 | checksum = "fb98f10f371286b177db5eeb9a6e5396609555686a35e1d4f7b9a9c6d8af0139"
25 | dependencies = [
26 | "askama_derive",
27 | "askama_escape",
28 | "askama_shared",
29 | ]
30 |
31 | [[package]]
32 | name = "askama_derive"
33 | version = "0.11.2"
34 | source = "registry+https://github.com/rust-lang/crates.io-index"
35 | checksum = "87bf87e6e8b47264efa9bde63d6225c6276a52e05e91bf37eaa8afd0032d6b71"
36 | dependencies = [
37 | "askama_shared",
38 | "proc-macro2",
39 | "syn",
40 | ]
41 |
42 | [[package]]
43 | name = "askama_escape"
44 | version = "0.10.3"
45 | source = "registry+https://github.com/rust-lang/crates.io-index"
46 | checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
47 |
48 | [[package]]
49 | name = "askama_shared"
50 | version = "0.12.2"
51 | source = "registry+https://github.com/rust-lang/crates.io-index"
52 | checksum = "bf722b94118a07fcbc6640190f247334027685d4e218b794dbfe17c32bf38ed0"
53 | dependencies = [
54 | "askama_escape",
55 | "mime",
56 | "mime_guess",
57 | "nom",
58 | "proc-macro2",
59 | "quote",
60 | "syn",
61 | ]
62 |
63 | [[package]]
64 | name = "atty"
65 | version = "0.2.14"
66 | source = "registry+https://github.com/rust-lang/crates.io-index"
67 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
68 | dependencies = [
69 | "hermit-abi",
70 | "libc",
71 | "winapi",
72 | ]
73 |
74 | [[package]]
75 | name = "autocfg"
76 | version = "1.1.0"
77 | source = "registry+https://github.com/rust-lang/crates.io-index"
78 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
79 |
80 | [[package]]
81 | name = "bitflags"
82 | version = "1.3.2"
83 | source = "registry+https://github.com/rust-lang/crates.io-index"
84 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
85 |
86 | [[package]]
87 | name = "cargo_toml"
88 | version = "0.11.5"
89 | source = "registry+https://github.com/rust-lang/crates.io-index"
90 | checksum = "5809dd3e6444651fd1cdd3dbec71eca438c439a0fcc8081674a14da0afe50185"
91 | dependencies = [
92 | "serde",
93 | "serde_derive",
94 | "toml",
95 | ]
96 |
97 | [[package]]
98 | name = "clap"
99 | version = "3.2.20"
100 | source = "registry+https://github.com/rust-lang/crates.io-index"
101 | checksum = "23b71c3ce99b7611011217b366d923f1d0a7e07a92bb2dbf1e84508c673ca3bd"
102 | dependencies = [
103 | "atty",
104 | "bitflags",
105 | "clap_derive",
106 | "clap_lex",
107 | "indexmap",
108 | "once_cell",
109 | "strsim",
110 | "termcolor",
111 | "textwrap",
112 | ]
113 |
114 | [[package]]
115 | name = "clap_derive"
116 | version = "3.2.18"
117 | source = "registry+https://github.com/rust-lang/crates.io-index"
118 | checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"
119 | dependencies = [
120 | "heck",
121 | "proc-macro-error",
122 | "proc-macro2",
123 | "quote",
124 | "syn",
125 | ]
126 |
127 | [[package]]
128 | name = "clap_lex"
129 | version = "0.2.4"
130 | source = "registry+https://github.com/rust-lang/crates.io-index"
131 | checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
132 | dependencies = [
133 | "os_str_bytes",
134 | ]
135 |
136 | [[package]]
137 | name = "dissimilar"
138 | version = "1.0.4"
139 | source = "registry+https://github.com/rust-lang/crates.io-index"
140 | checksum = "8c97b9233581d84b8e1e689cdd3a47b6f69770084fc246e86a7f78b0d9c1d4a5"
141 |
142 | [[package]]
143 | name = "expect-test"
144 | version = "1.4.0"
145 | source = "registry+https://github.com/rust-lang/crates.io-index"
146 | checksum = "1d4661aca38d826eb7c72fe128e4238220616de4c0cc00db7bfc38e2e1364dd3"
147 | dependencies = [
148 | "dissimilar",
149 | "once_cell",
150 | ]
151 |
152 | [[package]]
153 | name = "glob"
154 | version = "0.3.0"
155 | source = "registry+https://github.com/rust-lang/crates.io-index"
156 | checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
157 |
158 | [[package]]
159 | name = "hashbrown"
160 | version = "0.12.3"
161 | source = "registry+https://github.com/rust-lang/crates.io-index"
162 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
163 |
164 | [[package]]
165 | name = "heck"
166 | version = "0.4.0"
167 | source = "registry+https://github.com/rust-lang/crates.io-index"
168 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
169 |
170 | [[package]]
171 | name = "hermit-abi"
172 | version = "0.1.19"
173 | source = "registry+https://github.com/rust-lang/crates.io-index"
174 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
175 | dependencies = [
176 | "libc",
177 | ]
178 |
179 | [[package]]
180 | name = "indexmap"
181 | version = "1.9.1"
182 | source = "registry+https://github.com/rust-lang/crates.io-index"
183 | checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
184 | dependencies = [
185 | "autocfg",
186 | "hashbrown",
187 | ]
188 |
189 | [[package]]
190 | name = "libc"
191 | version = "0.2.132"
192 | source = "registry+https://github.com/rust-lang/crates.io-index"
193 | checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
194 |
195 | [[package]]
196 | name = "memchr"
197 | version = "2.5.0"
198 | source = "registry+https://github.com/rust-lang/crates.io-index"
199 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
200 |
201 | [[package]]
202 | name = "mime"
203 | version = "0.3.16"
204 | source = "registry+https://github.com/rust-lang/crates.io-index"
205 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
206 |
207 | [[package]]
208 | name = "mime_guess"
209 | version = "2.0.4"
210 | source = "registry+https://github.com/rust-lang/crates.io-index"
211 | checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
212 | dependencies = [
213 | "mime",
214 | "unicase",
215 | ]
216 |
217 | [[package]]
218 | name = "minimal-lexical"
219 | version = "0.2.1"
220 | source = "registry+https://github.com/rust-lang/crates.io-index"
221 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
222 |
223 | [[package]]
224 | name = "nocargo"
225 | version = "0.0.0"
226 | dependencies = [
227 | "anyhow",
228 | "askama",
229 | "cargo_toml",
230 | "clap",
231 | "expect-test",
232 | "glob",
233 | "once_cell",
234 | "regex",
235 | "toml",
236 | ]
237 |
238 | [[package]]
239 | name = "nom"
240 | version = "7.1.1"
241 | source = "registry+https://github.com/rust-lang/crates.io-index"
242 | checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
243 | dependencies = [
244 | "memchr",
245 | "minimal-lexical",
246 | ]
247 |
248 | [[package]]
249 | name = "once_cell"
250 | version = "1.14.0"
251 | source = "registry+https://github.com/rust-lang/crates.io-index"
252 | checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0"
253 |
254 | [[package]]
255 | name = "os_str_bytes"
256 | version = "6.3.0"
257 | source = "registry+https://github.com/rust-lang/crates.io-index"
258 | checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
259 |
260 | [[package]]
261 | name = "proc-macro-error"
262 | version = "1.0.4"
263 | source = "registry+https://github.com/rust-lang/crates.io-index"
264 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
265 | dependencies = [
266 | "proc-macro-error-attr",
267 | "proc-macro2",
268 | "quote",
269 | "syn",
270 | "version_check",
271 | ]
272 |
273 | [[package]]
274 | name = "proc-macro-error-attr"
275 | version = "1.0.4"
276 | source = "registry+https://github.com/rust-lang/crates.io-index"
277 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
278 | dependencies = [
279 | "proc-macro2",
280 | "quote",
281 | "version_check",
282 | ]
283 |
284 | [[package]]
285 | name = "proc-macro2"
286 | version = "1.0.43"
287 | source = "registry+https://github.com/rust-lang/crates.io-index"
288 | checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
289 | dependencies = [
290 | "unicode-ident",
291 | ]
292 |
293 | [[package]]
294 | name = "quote"
295 | version = "1.0.21"
296 | source = "registry+https://github.com/rust-lang/crates.io-index"
297 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
298 | dependencies = [
299 | "proc-macro2",
300 | ]
301 |
302 | [[package]]
303 | name = "regex"
304 | version = "1.6.0"
305 | source = "registry+https://github.com/rust-lang/crates.io-index"
306 | checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
307 | dependencies = [
308 | "aho-corasick",
309 | "memchr",
310 | "regex-syntax",
311 | ]
312 |
313 | [[package]]
314 | name = "regex-syntax"
315 | version = "0.6.27"
316 | source = "registry+https://github.com/rust-lang/crates.io-index"
317 | checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
318 |
319 | [[package]]
320 | name = "serde"
321 | version = "1.0.144"
322 | source = "registry+https://github.com/rust-lang/crates.io-index"
323 | checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
324 |
325 | [[package]]
326 | name = "serde_derive"
327 | version = "1.0.144"
328 | source = "registry+https://github.com/rust-lang/crates.io-index"
329 | checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
330 | dependencies = [
331 | "proc-macro2",
332 | "quote",
333 | "syn",
334 | ]
335 |
336 | [[package]]
337 | name = "strsim"
338 | version = "0.10.0"
339 | source = "registry+https://github.com/rust-lang/crates.io-index"
340 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
341 |
342 | [[package]]
343 | name = "syn"
344 | version = "1.0.99"
345 | source = "registry+https://github.com/rust-lang/crates.io-index"
346 | checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
347 | dependencies = [
348 | "proc-macro2",
349 | "quote",
350 | "unicode-ident",
351 | ]
352 |
353 | [[package]]
354 | name = "termcolor"
355 | version = "1.1.3"
356 | source = "registry+https://github.com/rust-lang/crates.io-index"
357 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
358 | dependencies = [
359 | "winapi-util",
360 | ]
361 |
362 | [[package]]
363 | name = "textwrap"
364 | version = "0.15.0"
365 | source = "registry+https://github.com/rust-lang/crates.io-index"
366 | checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
367 |
368 | [[package]]
369 | name = "toml"
370 | version = "0.5.9"
371 | source = "registry+https://github.com/rust-lang/crates.io-index"
372 | checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
373 | dependencies = [
374 | "serde",
375 | ]
376 |
377 | [[package]]
378 | name = "unicase"
379 | version = "2.6.0"
380 | source = "registry+https://github.com/rust-lang/crates.io-index"
381 | checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
382 | dependencies = [
383 | "version_check",
384 | ]
385 |
386 | [[package]]
387 | name = "unicode-ident"
388 | version = "1.0.3"
389 | source = "registry+https://github.com/rust-lang/crates.io-index"
390 | checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
391 |
392 | [[package]]
393 | name = "version_check"
394 | version = "0.9.4"
395 | source = "registry+https://github.com/rust-lang/crates.io-index"
396 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
397 |
398 | [[package]]
399 | name = "winapi"
400 | version = "0.3.9"
401 | source = "registry+https://github.com/rust-lang/crates.io-index"
402 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
403 | dependencies = [
404 | "winapi-i686-pc-windows-gnu",
405 | "winapi-x86_64-pc-windows-gnu",
406 | ]
407 |
408 | [[package]]
409 | name = "winapi-i686-pc-windows-gnu"
410 | version = "0.4.0"
411 | source = "registry+https://github.com/rust-lang/crates.io-index"
412 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
413 |
414 | [[package]]
415 | name = "winapi-util"
416 | version = "0.1.5"
417 | source = "registry+https://github.com/rust-lang/crates.io-index"
418 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
419 | dependencies = [
420 | "winapi",
421 | ]
422 |
423 | [[package]]
424 | name = "winapi-x86_64-pc-windows-gnu"
425 | version = "0.4.0"
426 | source = "registry+https://github.com/rust-lang/crates.io-index"
427 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
428 |
--------------------------------------------------------------------------------
/noc/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "nocargo"
3 | version = "0.0.0"
4 | edition = "2021"
5 | license = "MIT"
6 | description = "Helper program for github.com/oxalica/nocargo"
7 | repository = "https://github.com/oxalica/nocargo"
8 |
9 | [[bin]]
10 | name = "noc"
11 | path = "src/main.rs"
12 |
13 | [dependencies]
14 | anyhow = "1.0.40"
15 | askama = { version = "0.11.1", default-features = false }
16 | cargo_toml = "0.11.5"
17 | clap = { version = "3.2.8", features = ["derive"] }
18 | glob = "0.3.0"
19 | once_cell = "1.12.0"
20 | regex = "1.5.4"
21 | toml = "0.5.9"
22 |
23 | [dev-dependencies]
24 | expect-test = "1.3.0"
25 |
--------------------------------------------------------------------------------
/noc/src/main.rs:
--------------------------------------------------------------------------------
1 | use anyhow::Result;
2 | use clap::Parser;
3 |
4 | mod init;
5 |
6 | trait App {
7 | fn run(self) -> Result<()>;
8 | }
9 |
10 | #[derive(Parser)]
11 | #[clap(version, about, long_about = None)]
12 | enum Args {
13 | Init(init::Args),
14 | }
15 |
16 | impl App for Args {
17 | fn run(self) -> Result<()> {
18 | match self {
19 | Self::Init(args) => args.run(),
20 | }
21 | }
22 | }
23 |
24 | fn main() -> Result<()> {
25 | Args::from_args().run()
26 | }
27 |
--------------------------------------------------------------------------------
/noc/templates/init-flake.nix:
--------------------------------------------------------------------------------
1 | # See more usages of nocargo at https://github.com/oxalica/nocargo#readme
2 | {
3 | {%- if let Some((pkg_name, _)) = main_pkg %}
4 | description = "Rust package {{ pkg_name|nix_escape }}";
5 | {%- else %}
6 | description = "My Rust packages";
7 | {%- endif %}
8 |
9 | inputs = {
10 | nixpkgs.url = "github:NixOS/nixpkgs";
11 | flake-utils.url = "github:numtide/flake-utils";
12 | nocargo = {
13 | url = "github:oxalica/nocargo";
14 | inputs.nixpkgs.follows = "nixpkgs";
15 | # inputs.registry-crates-io.follows = "registry-crates-io";
16 | };
17 | # Optionally, you can override crates.io index to get cutting-edge packages.
18 | # registry-crates-io = { url = "github:rust-lang/crates.io-index"; flake = false; };
19 | {%- for (_, flake_ref) in registries %}
20 | registry-{{ loop.index }} = { url = "{{ flake_ref|nix_escape }}"; flake = false; };
21 | {%- endfor %}
22 | {%- for (_, flake_ref) in git_srcs %}
23 | git-{{ loop.index }} = { url = "{{ flake_ref|nix_escape }}"; flake = false; };
24 | {%- endfor %}
25 | };
26 |
27 | outputs = { nixpkgs, flake-utils, nocargo, ... }@inputs:
28 | flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" ] (system:
29 | let
30 | ws = nocargo.lib.${system}.mkRustPackageOrWorkspace {
31 | src = ./.;
32 | {%- if !registries.is_empty() %}
33 |
34 | # Referenced external registries other than crates.io.
35 | extraRegistries = {
36 | {%- for (source_id, _) in registries %}
37 | "{{ source_id|nix_escape }}" = nocargo.lib.${system}.mkIndex inputs.registry-{{ loop.index }} {};
38 | {%- endfor %}
39 | };
40 | {%- endif %}
41 | {%- if !git_srcs.is_empty() %}
42 |
43 | # Referenced external rust packages from git.
44 | gitSrcs = {
45 | {%- for (source_id, _) in git_srcs %}
46 | "{{ source_id|nix_escape }}" = inputs.git-{{ loop.index }};
47 | {%- endfor %}
48 | };
49 | {%- endif %}
50 | };
51 | in rec {
52 | {%- if is_workspace %}
53 | packages = {% if let Some((pkg_name, prod)) = main_pkg %}{
54 | default = packages.{{ pkg_name|ident_or_str }}{% if prod.binary %}.bin{% endif %};
55 | } // {% endif %}ws.release
56 | // nixpkgs.lib.mapAttrs' (name: value: { name = "${name}-dev"; inherit value; }) ws.dev;
57 | {%- else if let Some((pkg_name, prod)) = main_pkg %}
58 | packages = {
59 | default = packages.{{ pkg_name|ident_or_str }};
60 | {{ pkg_name|ident_or_str }} = ws.release.{{ pkg_name|ident_or_str }}{% if prod.binary %}.bin{% endif %};
61 | {{ pkg_name|ident_or_str }}-dev = ws.dev.{{ pkg_name|ident_or_str }}{% if prod.binary %}.bin{% endif %};
62 | };
63 | {%- endif %}
64 | });
65 | }
66 |
--------------------------------------------------------------------------------
/scripts/cratesio-utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env nix-shell
2 | #!nix-shell -i python3 -p cargo "python3.withPackages (ps: with ps; [ aiohttp toml ])"
3 | from io import BytesIO
4 | from pathlib import Path
5 | from typing import Callable, Optional
6 | from typing_extensions import Self
7 | from urllib import request
8 | import aiohttp
9 | import argparse
10 | import asyncio
11 | import csv
12 | import os
13 | import re
14 | import sqlite3
15 | import subprocess
16 | import sys
17 | import tarfile
18 | import toml
19 |
20 | # Check dependencies.
21 | subprocess.check_call(['cargo', '--version'], stdout=subprocess.DEVNULL)
22 |
23 | # CRATE_TARBALL_URL = 'https://crates.io/api/v1/crates/{name}/{version}/download' # -> 302
24 | CRATE_TARBALL_URL = 'https://static.crates.io/crates/{name}/{name}-{version}.crate' # -> 200 application/gzip
25 |
26 | POPULAR_CRATES_MANIFEST_PATH = Path(__file__).parent.parent / 'cache' / 'Cargo.toml'
27 | PROC_MACRO_LIST_PATH = Path(__file__).parent.parent / 'crates-io-override' / 'proc-macro.nix'
28 |
29 | CACHE_DIR = Path(os.environ.get('XDG_CACHE_HOME') or (Path.home() / '.cache')) / 'cratesio'
30 | CRATES_TOML_DIR = Path(os.environ.get('CRATES_TOML_DIR') or (CACHE_DIR / 'toml'))
31 |
32 | def noisily(*args) -> None:
33 | print(*args, file=sys.stderr)
34 |
35 | class CratesioDB(sqlite3.Connection):
36 | DB_URL = 'https://static.crates.io/db-dump.tar.gz'
37 |
38 | DB_DIR = CACHE_DIR
39 | DB_PATH = DB_DIR / 'db.sqlite'
40 | MTIME_PATH = DB_DIR / 'mtime.txt'
41 |
42 | INIT_SQL = r'''
43 | PRAGMA journal_mode = off;
44 | PRAGMA cache_size = -{cache_kb};
45 |
46 | CREATE TABLE IF NOT EXISTS crates (
47 | id INTEGER NOT NULL PRIMARY KEY,
48 | name TEXT NOT NULL,
49 | created_at TEXT NOT NULL,
50 | updated_at TEXT NOT NULL,
51 | downloads INTEGER NOT NULL
52 | ) STRICT;
53 | CREATE TABLE IF NOT EXISTS versions (
54 | id INTEGER NOT NULL PRIMARY KEY,
55 | crate_id INTEGER NOT NULL,
56 | num TEXT NOT NULL,
57 | updated_at TEXT NOT NULL,
58 | created_at TEXT NOT NULL,
59 | downloads INTEGER NOT NULL,
60 | features TEXT NOT NULL,
61 | yanked INTEGER NOT NULL,
62 | license TEXT
63 | ) STRICT;
64 | CREATE TABLE IF NOT EXISTS version_downloads (
65 | version_id INTEGER NOT NULL,
66 | downloads INTEGER NOT NULL,
67 | date TEXT NOT NULL,
68 | PRIMARY KEY (version_id, date)
69 | ) STRICT, WITHOUT ROWID;
70 | '''
71 |
72 | INIT_INDEX_SQL = r'''
73 | CREATE INDEX IF NOT EXISTS versions_ix_crate_to_version ON versions
74 | (crate_id, id);
75 | ANALYZE;
76 | '''
77 |
78 | INSERTERS: dict[str, tuple[str, Callable[[dict[str, str]], tuple]]] = {
79 | 'crates.csv': (
80 | 'INSERT INTO crates VALUES (?,?,?,?,?)',
81 | lambda row: (
82 | int(row['id']),
83 | row['name'],
84 | row['created_at'],
85 | row['updated_at'],
86 | int(row['downloads']),
87 | ),
88 | ),
89 | 'versions.csv': (
90 | 'INSERT INTO versions VALUES (?,?,?,?,?,?,?,?,?)',
91 | lambda row: (
92 | int(row['id']),
93 | int(row['crate_id']),
94 | row['num'],
95 | row['updated_at'],
96 | row['created_at'],
97 | int(row['downloads']),
98 | row['features'],
99 | row['yanked'] == 't',
100 | row['license'],
101 | ),
102 | ),
103 | 'version_downloads.csv': (
104 | 'INSERT INTO version_downloads VALUES (?,?,?)',
105 | lambda row: (
106 | int(row['version_id']),
107 | int(row['downloads']),
108 | row['date'],
109 | )
110 | ),
111 | }
112 |
113 | def __init__(self, *, check: bool=True, cache_mb: int=1024) -> None:
114 | assert not check or self.MTIME_PATH.exists(), 'Database not initialized, please run `sync` subcommand'
115 | super().__init__(str(self.DB_PATH))
116 | self.executescript(self.INIT_SQL.format(cache_kb=cache_mb * 1024))
117 |
118 | @classmethod
119 | def sync(cls, *, cache_mb: int=1024) -> None:
120 | cls.DB_DIR.mkdir(exist_ok=True)
121 | last_mtime = cls.MTIME_PATH.read_text() if cls.MTIME_PATH.exists() else None
122 |
123 | noisily('Synchronizing database')
124 | with request.urlopen(cls.DB_URL) as resp:
125 | assert resp.status == 200, f'HTTP failure {resp.status}'
126 | mtime: str = resp.headers['last-modified']
127 | if mtime == last_mtime:
128 | noisily(f'Database is up-to-date at {mtime}')
129 | return
130 |
131 | noisily(f'Fetching and importing database dump at {mtime}, previous at {last_mtime}')
132 | cls.DB_PATH.unlink(missing_ok=True)
133 | db = CratesioDB(check=False, cache_mb=cache_mb)
134 |
135 | csv.field_size_limit(2 ** 30) # There are large fields.
136 | with tarfile.open(fileobj=resp, mode='r|gz') as tar:
137 | for member in tar:
138 | name = member.path.split('/')[-1]
139 | if name in cls.INSERTERS:
140 | sql, preprocessor = cls.INSERTERS[name]
141 | fileobj = tar.extractfile(member)
142 | assert fileobj is not None
143 | rdr = csv.DictReader(map(bytes.decode, fileobj))
144 | db.executemany(sql, map(preprocessor, rdr))
145 |
146 | noisily(f'Creating indices')
147 | db.executescript(cls.INIT_INDEX_SQL)
148 | db.commit()
149 | db.close()
150 |
151 | cls.MTIME_PATH.write_text(mtime)
152 | noisily('Database initialized')
153 |
154 | def update_popular_crates(db: CratesioDB, *, time: str, limit: int) -> None:
155 | class VersMajor(tuple):
156 | RE_SEMVER = re.compile(r'^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$')
157 |
158 | def __new__(cls: type[Self], s: str) -> Self:
159 | m = cls.RE_SEMVER.match(s)
160 | assert m is not None, f'Invalid semver: {s}'
161 | maj, min, pat = map(int, (m[1], m[2], m[3]))
162 | vers = (maj,) if maj else (maj, min) if min else (maj, min, pat)
163 | return super().__new__(cls, vers)
164 |
165 | def __str__(self) -> str:
166 | return '.'.join(map(str, self))
167 |
168 | # Reversed.
169 | def __lt__(self, rhs: Self) -> bool:
170 | return super().__gt__(rhs)
171 |
172 | noisily('Querying database')
173 | cursor = db.cursor()
174 | cursor.execute('''
175 | SELECT crates.name, versions.num
176 | FROM version_downloads
177 | JOIN versions ON versions.id == version_id
178 | JOIN crates ON crates.id == versions.crate_id
179 | WHERE version_downloads.date > DATE('now', ?)
180 | GROUP BY version_id
181 | ORDER BY SUM(version_downloads.downloads) DESC
182 | ''', (time,))
183 |
184 | crates_set: set[tuple[str, VersMajor]] = set()
185 | for row in cursor:
186 | crates_set.add((str(row[0]), VersMajor(row[1])))
187 | if len(crates_set) == limit:
188 | break
189 |
190 | crates = sorted(crates_set)
191 |
192 | noisily('======== Start of top crates')
193 | for name, vers in crates:
194 | noisily(name, vers)
195 | noisily('======== End of top crates')
196 |
197 | out_path = POPULAR_CRATES_MANIFEST_PATH
198 | tmp_path = out_path.with_suffix('.tmp')
199 | with out_path.open('r') as fin, tmp_path.open('w') as fout:
200 | for line in fin:
201 | fout.write(line)
202 | if line.startswith('[dependencies]'):
203 | break
204 |
205 | last_name: Optional[str] = None
206 | crate_idx = 1
207 | for name, vers in crates:
208 | if last_name != name:
209 | crate_idx = 1
210 | fout.write(f'{name} = "{vers}"\n')
211 | else:
212 | crate_idx += 1
213 | fout.write(f'{name}-{crate_idx} = {{ package = "{name}", version = "{vers}" }}\n')
214 | last_name = name
215 | tmp_path.replace(out_path)
216 |
217 | noisily('Updating lock file')
218 | subprocess.check_call(['cargo', 'update', f'--manifest-path={out_path}'])
219 |
220 | noisily('Verifying crates metadata')
221 | subprocess.check_call(
222 | ['cargo', 'metadata', f'--manifest-path={out_path}', '--format-version=1'],
223 | stdout=subprocess.DEVNULL,
224 | )
225 |
226 | def list_popular_crates(db: CratesioDB, *, time: str, pat: Optional[str]) -> None:
227 | noisily('Querying database')
228 | cursor = db.cursor()
229 | cursor.execute('''
230 | SELECT crates.name, SUM(version_downloads.downloads) AS crate_downloads
231 | FROM crates
232 | JOIN versions ON versions.crate_id == crates.id
233 | JOIN version_downloads ON version_downloads.version_id == versions.id
234 | WHERE crates.name LIKE ? AND version_downloads.date > DATE('now', ?)
235 | GROUP BY versions.crate_id
236 | ORDER BY crate_downloads DESC
237 | ''', (pat if pat is not None else '%', time))
238 | try:
239 | for row in cursor:
240 | print(row[0], row[1])
241 | except BrokenPipeError as _:
242 | # Suppress backtrace.
243 | exit(1)
244 |
245 | def update_proc_macro_crates(db: CratesioDB, *, concurrency: int) -> None:
246 | CRATES_TOML_DIR.mkdir(exist_ok=True)
247 |
248 | noisily('Querying database')
249 | cursor = db.cursor()
250 | cursor.execute('''
251 | SELECT crates.name, versions.num AS max_vers, MAX(versions.created_at)
252 | FROM versions
253 | JOIN crates ON crates.id == crate_id
254 | WHERE NOT yanked
255 | GROUP BY crate_id
256 | ORDER BY crates.name ASC
257 | ''')
258 |
259 | RE_ITEMS = re.compile(r'"([^"]*)"', re.S)
260 | proc_macro_crates: set[str] = set(m[1] for m in RE_ITEMS.finditer(PROC_MACRO_LIST_PATH.read_text()))
261 |
262 | async def load_or_fetch(sema: asyncio.Semaphore, name: str, version: str) -> None:
263 | try:
264 | out_path = CRATES_TOML_DIR / f'{name}.toml'
265 | if not out_path.exists():
266 | noisily(f'GET {name} {version}')
267 | url = CRATE_TARBALL_URL.format(name=name, version=version)
268 | async with aiohttp.request(method='GET', url=url) as resp:
269 | resp.raise_for_status()
270 | data = await resp.read()
271 |
272 | with tarfile.open(mode='r|gz', fileobj=BytesIO(data)) as tar:
273 | for member in tar:
274 | segments = member.path.split('/')
275 | if len(segments) == 2 and segments[-1] == 'Cargo.toml':
276 | f = tar.extractfile(member)
277 | assert f is not None
278 | tmp_path = out_path.with_suffix('.tmp')
279 | tmp_path.write_bytes(f.read())
280 | tmp_path.replace(out_path)
281 | break
282 | else:
283 | assert False, 'No Cargo.toml found'
284 | except (aiohttp.ClientError, tarfile.TarError, UnicodeDecodeError, AssertionError) as exc:
285 | print(f'For {name} {version}: {exc}', file=sys.stderr)
286 | return
287 | finally:
288 | sema.release()
289 |
290 | try:
291 | # Must exist if we are here.
292 | with open(out_path, 'r') as fin:
293 | manifest = toml.load(fin)
294 | lib = manifest.get('lib', {})
295 | if isinstance(lib, dict) and lib.get('proc-macro', False) is True:
296 | proc_macro_crates.add(name)
297 | except (UnicodeDecodeError, toml.TomlDecodeError) as exc:
298 | print(f'For cached {name}: {exc}', file=sys.stderr)
299 | return
300 |
301 | async def proc() -> None:
302 | sema = asyncio.Semaphore(concurrency)
303 | for row in cursor:
304 | name: str = row[0]
305 | vers: str = row[1]
306 | await sema.acquire()
307 | asyncio.create_task(load_or_fetch(sema, name, vers))
308 |
309 | # Wait until all done.
310 | for _ in range(concurrency):
311 | await sema.acquire()
312 |
313 | noisily('Retrieving metadata')
314 | asyncio.run(proc())
315 |
316 | noisily(f'Writing to {PROC_MACRO_LIST_PATH}')
317 | with PROC_MACRO_LIST_PATH.open('w') as fout:
318 | fout.write('[\n')
319 | for name in sorted(proc_macro_crates):
320 | assert '"' not in name
321 | fout.write(f'"{name}"\n')
322 | fout.write(']\n')
323 |
324 | def main() -> None:
325 | parser = argparse.ArgumentParser(description='Metadata updater and utilities using crates.io database dump')
326 | parser.add_argument('--cache-mb', type=int, required=False, default=1024, help='Sqlite cache size in MiB')
327 | subparser = parser.add_subparsers(required=True)
328 |
329 | p = subparser.add_parser('sync', help='Synchronize or initialize the database')
330 | p.set_defaults(sync=True)
331 |
332 | p = subparser.add_parser('update-popular-crates', help='Update popular crates cache')
333 | p.add_argument('--limit', type=int, required=False, default=256, help='Number of top crates to cache')
334 | p.add_argument('--time', type=str, default='-90 days', help='Time period to count recent downloads')
335 | p.set_defaults(fn=update_popular_crates)
336 |
337 | p = subparser.add_parser('list-popular-crates', help='Print popular crates with download counts in a given period')
338 | p.add_argument('--time', type=str, default='-90 days', help='Time period to count recent downloads')
339 | p.add_argument('--pat', type=str, default=None, help='Crate name pattern to filter for SQL "LIKE"')
340 | p.set_defaults(fn=list_popular_crates)
341 |
342 | p = subparser.add_parser('update-proc-macro-crates', help='Update the list of proc-macro crates')
343 | p.add_argument('--concurrency', type=int, default=16, help='Connection concurrency')
344 | p.set_defaults(fn=update_proc_macro_crates)
345 |
346 | args = parser.parse_args()
347 | if 'sync' in args:
348 | CratesioDB.sync(cache_mb=args.cache_mb)
349 | else:
350 | subargs = vars(args)
351 | with CratesioDB(cache_mb=subargs.pop('cache_mb')) as db:
352 | subargs.pop('fn')(db, **subargs)
353 |
354 | main()
355 |
--------------------------------------------------------------------------------
/tests/build-deps/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "build-deps"
7 | version = "0.0.0"
8 | dependencies = [
9 | "semver",
10 | ]
11 |
12 | [[package]]
13 | name = "semver"
14 | version = "1.0.12"
15 | source = "registry+https://github.com/rust-lang/crates.io-index"
16 | checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
17 |
--------------------------------------------------------------------------------
/tests/build-deps/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "build-deps"
3 | version = "0.0.0"
4 | edition = "2015"
5 |
6 | [build-dependencies]
7 | semver = "1"
8 |
--------------------------------------------------------------------------------
/tests/build-deps/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | let s = semver::Version::new(1, 2, 3).to_string();
3 | println!("cargo:rustc-cfg=result={:?}", s);
4 | }
5 |
--------------------------------------------------------------------------------
/tests/build-deps/src/main.rs:
--------------------------------------------------------------------------------
1 | #[cfg(result = "1.2.3")]
2 | fn main() {
3 | println!("Hello, world!");
4 | }
5 |
--------------------------------------------------------------------------------
/tests/build-feature-env-vars/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "build-feature-env-vars"
7 | version = "0.0.0"
8 |
--------------------------------------------------------------------------------
/tests/build-feature-env-vars/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "build-feature-env-vars"
3 | version = "0.0.0"
4 | edition = "2015"
5 |
6 | [features]
7 | default = ["foo-bar"]
8 | foo-bar = []
9 | quux = []
10 |
--------------------------------------------------------------------------------
/tests/build-feature-env-vars/build.rs:
--------------------------------------------------------------------------------
1 | use std::env::var;
2 |
3 | fn main() {
4 | if var("CARGO_FEATURE_QUUX").is_err() {
5 | if let Ok(s) = var("CARGO_FEATURE_FOO_BAR") {
6 | println!("cargo:rustc-cfg=result={:?}", s);
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/tests/build-feature-env-vars/src/main.rs:
--------------------------------------------------------------------------------
1 | #[cfg(result = "1")]
2 | fn main() {
3 | println!("Hello, world!");
4 | }
5 |
--------------------------------------------------------------------------------
/tests/cap-lints/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "cap-lints"
7 | version = "0.0.0"
8 | dependencies = [
9 | "want",
10 | ]
11 |
12 | [[package]]
13 | name = "cfg-if"
14 | version = "1.0.0"
15 | source = "registry+https://github.com/rust-lang/crates.io-index"
16 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
17 |
18 | [[package]]
19 | name = "log"
20 | version = "0.4.17"
21 | source = "registry+https://github.com/rust-lang/crates.io-index"
22 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
23 | dependencies = [
24 | "cfg-if",
25 | ]
26 |
27 | [[package]]
28 | name = "try-lock"
29 | version = "0.2.3"
30 | source = "registry+https://github.com/rust-lang/crates.io-index"
31 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
32 |
33 | [[package]]
34 | name = "want"
35 | version = "0.3.0"
36 | source = "registry+https://github.com/rust-lang/crates.io-index"
37 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
38 | dependencies = [
39 | "log",
40 | "try-lock",
41 | ]
42 |
--------------------------------------------------------------------------------
/tests/cap-lints/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "cap-lints"
3 | version = "0.0.0"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | # 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
8 | # --> src/lib.rs:209:63
9 | # |
10 | # 209 | if let Some(mut locked) = self.inner.task.try_lock_order(SeqCst, SeqCst) {
11 | # | ^^^^^^^^^^^^^^
12 | # |
13 | # note: the lint level is defined here
14 | # --> src/lib.rs:2:9
15 | # |
16 | # 2 | #![deny(warnings)]
17 | # | ^^^^^^^^
18 | # = note: `#[deny(deprecated)]` implied by `#[deny(warnings)]`
19 | #
20 | # https://github.com/seanmonstar/want/blob/v0.3.0/src/lib.rs#L352
21 | want = "=0.3.0"
22 |
--------------------------------------------------------------------------------
/tests/cap-lints/src/main.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | let _ = want::new();
3 | println!("Hello, world!");
4 | }
5 |
--------------------------------------------------------------------------------
/tests/crate-names/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "RustyXML"
7 | version = "0.3.0"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "8b5ace29ee3216de37c0546865ad08edef58b0f9e76838ed8959a84a990e58c5"
10 |
11 | [[package]]
12 | name = "b"
13 | version = "0.2.0"
14 | source = "registry+https://github.com/rust-lang/crates.io-index"
15 | checksum = "ad71822f94ff0251011da9d7c63248c2520e6a69e56d457be0679b4fe81cbada"
16 |
17 | [[package]]
18 | name = "cc"
19 | version = "1.0.73"
20 | source = "registry+https://github.com/rust-lang/crates.io-index"
21 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
22 |
23 | [[package]]
24 | name = "crate-names"
25 | version = "0.0.0"
26 | dependencies = [
27 | "RustyXML",
28 | "b",
29 | "cc",
30 | "fnv",
31 | ]
32 |
33 | [[package]]
34 | name = "fnv"
35 | version = "1.0.7"
36 | source = "registry+https://github.com/rust-lang/crates.io-index"
37 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
38 |
--------------------------------------------------------------------------------
/tests/crate-names/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "crate-names"
3 | version = "0.0.0"
4 | edition = "2018"
5 |
6 | [dependencies]
7 | b = "=0.2.0"
8 | cc = "=1.0.73"
9 | fnv = "=1.0.7"
10 | RustyXML = "=0.3.0" # Should be lowered.
11 |
--------------------------------------------------------------------------------
/tests/crate-names/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::hash::Hasher;
2 |
3 | fn main() {
4 | assert_eq!(b::B, "🅱️");
5 | let _ = cc::Build::new();
6 | assert_eq!(fnv::FnvHasher::with_key(42).finish(), 42);
7 | assert_eq!(xml::escape("<"), "<");
8 | println!("Hello, world!");
9 | }
10 |
--------------------------------------------------------------------------------
/tests/custom-lib-name/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "angle"
7 | version = "0.4.0"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "ba2ba87287a952ce1802117f6201af6ff3f32dd372f42dbf53217e900bac7881"
10 | dependencies = [
11 | "num-traits",
12 | "serde",
13 | "serde_derive",
14 | ]
15 |
16 | [[package]]
17 | name = "autocfg"
18 | version = "1.1.0"
19 | source = "registry+https://github.com/rust-lang/crates.io-index"
20 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
21 |
22 | [[package]]
23 | name = "color-rs"
24 | version = "0.5.0"
25 | source = "registry+https://github.com/rust-lang/crates.io-index"
26 | checksum = "00c7ad08f8b0b391ab2e94aea63cd01f1d821775caee42c0eb62367872a18a5f"
27 | dependencies = [
28 | "angle",
29 | "num-traits",
30 | "serde",
31 | "serde_derive",
32 | ]
33 |
34 | [[package]]
35 | name = "color-rs"
36 | version = "0.6.1"
37 | source = "registry+https://github.com/rust-lang/crates.io-index"
38 | checksum = "59f43d9768f46589e0b9f246486f49a5b841b29651503c919d0ed3f1e679fc70"
39 | dependencies = [
40 | "angle",
41 | "half",
42 | "num-traits",
43 | "serde",
44 | "serde_derive",
45 | ]
46 |
47 | [[package]]
48 | name = "custom-lib-name"
49 | version = "0.0.0"
50 | dependencies = [
51 | "color-rs 0.5.0",
52 | "color-rs 0.6.1",
53 | ]
54 |
55 | [[package]]
56 | name = "half"
57 | version = "1.8.2"
58 | source = "registry+https://github.com/rust-lang/crates.io-index"
59 | checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
60 |
61 | [[package]]
62 | name = "num-traits"
63 | version = "0.2.15"
64 | source = "registry+https://github.com/rust-lang/crates.io-index"
65 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
66 | dependencies = [
67 | "autocfg",
68 | ]
69 |
70 | [[package]]
71 | name = "proc-macro2"
72 | version = "1.0.40"
73 | source = "registry+https://github.com/rust-lang/crates.io-index"
74 | checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
75 | dependencies = [
76 | "unicode-ident",
77 | ]
78 |
79 | [[package]]
80 | name = "quote"
81 | version = "1.0.20"
82 | source = "registry+https://github.com/rust-lang/crates.io-index"
83 | checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
84 | dependencies = [
85 | "proc-macro2",
86 | ]
87 |
88 | [[package]]
89 | name = "serde"
90 | version = "1.0.139"
91 | source = "registry+https://github.com/rust-lang/crates.io-index"
92 | checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6"
93 |
94 | [[package]]
95 | name = "serde_derive"
96 | version = "1.0.139"
97 | source = "registry+https://github.com/rust-lang/crates.io-index"
98 | checksum = "dc1d3230c1de7932af58ad8ffbe1d784bd55efd5a9d84ac24f69c72d83543dfb"
99 | dependencies = [
100 | "proc-macro2",
101 | "quote",
102 | "syn",
103 | ]
104 |
105 | [[package]]
106 | name = "syn"
107 | version = "1.0.98"
108 | source = "registry+https://github.com/rust-lang/crates.io-index"
109 | checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
110 | dependencies = [
111 | "proc-macro2",
112 | "quote",
113 | "unicode-ident",
114 | ]
115 |
116 | [[package]]
117 | name = "unicode-ident"
118 | version = "1.0.1"
119 | source = "registry+https://github.com/rust-lang/crates.io-index"
120 | checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
121 |
--------------------------------------------------------------------------------
/tests/custom-lib-name/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "custom-lib-name"
3 | version = "0.0.0"
4 | edition = "2018"
5 |
6 | [lib]
7 | name = "custom"
8 |
9 | [dependencies]
10 | color-rs = "=0.6.1"
11 | renamed = { package = "color-rs", version = "=0.5.0" }
12 |
--------------------------------------------------------------------------------
/tests/custom-lib-name/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub fn custom() -> i32 {
2 | 42
3 | }
4 |
--------------------------------------------------------------------------------
/tests/custom-lib-name/src/main.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | assert_eq!(color::consts::PURPLE, color::Rgb::new(0x80, 0x00, 0x80));
3 | assert_eq!(renamed::consts::PURPLE, renamed::Rgb::new(0x80, 0x00, 0x80));
4 | assert_eq!(custom::custom(), 42);
5 | println!("Hello, world!");
6 | }
7 |
--------------------------------------------------------------------------------
/tests/default.nix:
--------------------------------------------------------------------------------
1 | { pkgs, self, inputs, defaultRegistries }:
2 | let
3 | inherit (pkgs.lib) mapAttrs attrNames attrValues assertMsg head mapAttrsToList;
4 | inherit (self.lib.${pkgs.system}) mkRustPackageOrWorkspace;
5 | inherit (self.packages.${pkgs.system}) noc;
6 |
7 | git-semver-1-0-0 = builtins.fetchTarball {
8 | url = "https://github.com/dtolnay/semver/archive/1.0.0/master.tar.gz";
9 | sha256 = "0s7gwj5l0h98spgm7vyxak9z3hgrachwxbnf1fpry5diz939x8n4";
10 | };
11 |
12 | git-semver-1-0-12 = builtins.fetchTarball {
13 | url = "https://github.com/dtolnay/semver/archive/1.0.4/master.tar.gz";
14 | sha256 = "1l2nkfmjgz2zkqw03hmy66q0v1rxvs7fc4kh63ph4lf1924wrmix";
15 | };
16 |
17 | gitSrcs = {
18 | "https://github.com/dtolnay/semver?tag=1.0.0" = git-semver-1-0-0;
19 | "http://github.com/dtolnay/semver" = git-semver-1-0-12; # v1, v2
20 | "http://github.com/dtolnay/semver?branch=master" = git-semver-1-0-12; # v3
21 | "ssh://git@github.com/dtolnay/semver?rev=a2ce5777dcd455246e4650e36dde8e2e96fcb3fd" = git-semver-1-0-0;
22 | "ssh://git@github.com/dtolnay/semver" = git-semver-1-0-12;
23 | };
24 |
25 | extraRegistries = {
26 | "https://www.github.com/rust-lang/crates.io-index" =
27 | head (attrValues defaultRegistries);
28 | };
29 |
30 | shouldBeHelloWorld = drv: pkgs.runCommand "${drv.name}" {} ''
31 | binaries=(${drv.bin}/bin/*)
32 | [[ ''${#binaries[@]} == 1 ]]
33 | got="$(''${binaries[0]})"
34 | expect="Hello, world!"
35 | echo "Got : $got"
36 | echo "Expect: $expect"
37 | [[ "$got" == "$expect" ]]
38 | touch $out
39 | '';
40 |
41 | mkHelloWorldTest = src:
42 | let
43 | ws = mkRustPackageOrWorkspace {
44 | inherit src gitSrcs extraRegistries;
45 | };
46 | profiles = mapAttrs (_: pkgs: shouldBeHelloWorld (head (attrValues pkgs))) ws;
47 | in {
48 | inherit (profiles) dev release;
49 | };
50 |
51 | mkWorkspaceTest = src: expectMembers: let
52 | ws = mkRustPackageOrWorkspace { inherit src; };
53 | gotMembers = attrNames ws.dev;
54 | in
55 | assert assertMsg (gotMembers == expectMembers) ''
56 | Member assertion failed.
57 | expect: ${toString expectMembers}
58 | got: ${toString gotMembers}
59 | '';
60 | ws;
61 |
62 | # Recursive Nix setup.
63 | # https://github.com/NixOS/nixpkgs/blob/e966ab3965a656efdd40b6ae0d8cec6183972edc/pkgs/top-level/make-tarball.nix#L45-L48
64 | mkGenInit = name: path:
65 | pkgs.runCommand "gen-${name}" {
66 | nativeBuildInputs = [ noc pkgs.nix ];
67 | checkFlags =
68 | mapAttrsToList (from: to: "--override-input ${from} ${to}") {
69 | inherit (inputs) nixpkgs flake-utils;
70 | nocargo = self;
71 | "nocargo/registry-crates-io" = inputs.registry-crates-io;
72 | registry-1 = inputs.registry-crates-io;
73 | git-1 = git-semver-1-0-0;
74 | git-2 = git-semver-1-0-0;
75 | git-3 = git-semver-1-0-0;
76 | git-4 = git-semver-1-0-0;
77 | };
78 | } ''
79 | cp -r ${path} src
80 | chmod -R u+w src
81 | cd src
82 |
83 | echo "generating flake.nix"
84 | noc init
85 | cat flake.nix
86 | install -D flake.nix $out/flake.nix
87 |
88 | echo "checking with 'nix flake check'"
89 | export NIX_STATE_DIR=$TMPDIR/nix/var
90 | export NIX_PATH=
91 | export HOME=$TMPDIR
92 | nix-store --init
93 | nixFlags=(
94 | --offline
95 | --option build-users-group ""
96 | --option experimental-features "ca-derivations nix-command flakes"
97 | --store $TMPDIR/nix/store
98 | )
99 |
100 | nix flake check \
101 | --no-build \
102 | --show-trace \
103 | $checkFlags \
104 | "''${nixFlags[@]}"
105 | '';
106 |
107 | in
108 | {
109 | _1000-hello-worlds = mapAttrs (name: path: mkHelloWorldTest path) {
110 | build-deps = ./build-deps;
111 | build-feature-env-vars = ./build-feature-env-vars;
112 | cap-lints = ./cap-lints;
113 | crate-names = ./crate-names;
114 | custom-lib-name = ./custom-lib-name;
115 | dependency-v1 = ./dependency-v1;
116 | dependency-v2 = ./dependency-v2;
117 | dependency-v3 = ./dependency-v3;
118 | features = ./features;
119 | libz-dynamic = ./libz-dynamic;
120 | libz-static = ./libz-static;
121 | lto-fat = ./lto-fat;
122 | lto-proc-macro = ./lto-proc-macro;
123 | lto-thin = ./lto-thin;
124 | tokio-app = ./tokio-app;
125 | } // {
126 | workspace-inline = mkWorkspaceTest ./workspace-inline [ "bar" "baz" "foo" ];
127 | workspace-proc-macro-lto = mkWorkspaceTest ./workspace-proc-macro-lto [ "acro" "procm" ];
128 | workspace-virtual = mkWorkspaceTest ./workspace-virtual [ "bar" "foo" ];
129 | };
130 |
131 | _1100-gen-init = mapAttrs mkGenInit {
132 | dependency-v1 = ./dependency-v1;
133 | dependency-v2 = ./dependency-v2;
134 | dependency-v3 = ./dependency-v3;
135 | features = ./features;
136 |
137 | workspace-virtual = ./workspace-virtual;
138 | workspace-inline = ./workspace-inline;
139 | };
140 | }
141 |
--------------------------------------------------------------------------------
/tests/dependency-v1/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | [[package]]
4 | name = "dependencies"
5 | version = "0.0.0"
6 | dependencies = [
7 | "semver 1.0.0 (git+https://github.com/dtolnay/semver?tag=1.0.0)",
8 | "semver 1.0.0 (git+ssh://git@github.com/dtolnay/semver?rev=a2ce5777dcd455246e4650e36dde8e2e96fcb3fd)",
9 | "semver 1.0.12 (git+http://github.com/dtolnay/semver)",
10 | "semver 1.0.12 (git+ssh://git@github.com/dtolnay/semver)",
11 | "semver 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
12 | "semver 1.0.12 (registry+https://www.github.com/rust-lang/crates.io-index)",
13 | ]
14 |
15 | [[package]]
16 | name = "semver"
17 | version = "1.0.0"
18 | source = "git+https://github.com/dtolnay/semver?tag=1.0.0#a2ce5777dcd455246e4650e36dde8e2e96fcb3fd"
19 |
20 | [[package]]
21 | name = "semver"
22 | version = "1.0.0"
23 | source = "git+ssh://git@github.com/dtolnay/semver?rev=a2ce5777dcd455246e4650e36dde8e2e96fcb3fd#a2ce5777dcd455246e4650e36dde8e2e96fcb3fd"
24 |
25 | [[package]]
26 | name = "semver"
27 | version = "1.0.12"
28 | source = "git+http://github.com/dtolnay/semver#a6425e6f41ddc81c6d6dd60c68248e0f0ef046c7"
29 |
30 | [[package]]
31 | name = "semver"
32 | version = "1.0.12"
33 | source = "git+ssh://git@github.com/dtolnay/semver#a6425e6f41ddc81c6d6dd60c68248e0f0ef046c7"
34 |
35 | [[package]]
36 | name = "semver"
37 | version = "1.0.12"
38 | source = "registry+https://github.com/rust-lang/crates.io-index"
39 |
40 | [[package]]
41 | name = "semver"
42 | version = "1.0.12"
43 | source = "registry+https://www.github.com/rust-lang/crates.io-index"
44 |
45 | [metadata]
46 | "checksum semver 1.0.0 (git+https://github.com/dtolnay/semver?tag=1.0.0)" = ""
47 | "checksum semver 1.0.0 (git+ssh://git@github.com/dtolnay/semver?rev=a2ce5777dcd455246e4650e36dde8e2e96fcb3fd)" = ""
48 | "checksum semver 1.0.12 (git+http://github.com/dtolnay/semver)" = ""
49 | "checksum semver 1.0.12 (git+ssh://git@github.com/dtolnay/semver)" = ""
50 | "checksum semver 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
51 | "checksum semver 1.0.12 (registry+https://www.github.com/rust-lang/crates.io-index)" = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
52 |
--------------------------------------------------------------------------------
/tests/dependency-v1/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "dependencies"
3 | version = "0.0.0"
4 | edition = "2018"
5 |
6 | [dependencies]
7 | cratesio = { package = "semver", version = "1" }
8 | registry-index = { package = "semver", version = "1", registry-index = "https://www.github.com/rust-lang/crates.io-index" }
9 | git-tag = { package = "semver", git = "https://github.com/dtolnay/semver", tag = "1.0.0" }
10 | git-branch = { package = "semver", git = "http://github.com/dtolnay/semver", branch = "master" }
11 | git-rev = { package = "semver", git = "ssh://git@github.com/dtolnay/semver", rev = "a2ce5777dcd455246e4650e36dde8e2e96fcb3fd" }
12 | git-head = { package = "semver", git = "ssh://git@github.com/dtolnay/semver" }
13 |
--------------------------------------------------------------------------------
/tests/dependency-v1/README.md:
--------------------------------------------------------------------------------
1 | This `Cargo.lock` is generated by rust 1.37.0.
2 |
--------------------------------------------------------------------------------
/tests/dependency-v1/src/main.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | cratesio::Version::parse("1.2.3").unwrap();
3 | registry_index::Version::parse("1.2.3").unwrap();
4 | git_tag::Version::parse("1.2.3").unwrap();
5 | git_branch::Version::parse("1.2.3").unwrap();
6 | git_rev::Version::parse("1.2.3").unwrap();
7 | git_head::Version::parse("1.2.3").unwrap();
8 | println!("Hello, world!");
9 | }
10 |
--------------------------------------------------------------------------------
/tests/dependency-v2/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | [[package]]
4 | name = "dependencies"
5 | version = "0.0.0"
6 | dependencies = [
7 | "semver 1.0.0 (git+https://github.com/dtolnay/semver?tag=1.0.0)",
8 | "semver 1.0.0 (git+ssh://git@github.com/dtolnay/semver?rev=a2ce5777dcd455246e4650e36dde8e2e96fcb3fd)",
9 | "semver 1.0.12 (git+http://github.com/dtolnay/semver)",
10 | "semver 1.0.12 (git+ssh://git@github.com/dtolnay/semver)",
11 | "semver 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
12 | "semver 1.0.12 (registry+https://www.github.com/rust-lang/crates.io-index)",
13 | ]
14 |
15 | [[package]]
16 | name = "semver"
17 | version = "1.0.0"
18 | source = "git+https://github.com/dtolnay/semver?tag=1.0.0#a2ce5777dcd455246e4650e36dde8e2e96fcb3fd"
19 |
20 | [[package]]
21 | name = "semver"
22 | version = "1.0.0"
23 | source = "git+ssh://git@github.com/dtolnay/semver?rev=a2ce5777dcd455246e4650e36dde8e2e96fcb3fd#a2ce5777dcd455246e4650e36dde8e2e96fcb3fd"
24 |
25 | [[package]]
26 | name = "semver"
27 | version = "1.0.12"
28 | source = "git+http://github.com/dtolnay/semver#a6425e6f41ddc81c6d6dd60c68248e0f0ef046c7"
29 |
30 | [[package]]
31 | name = "semver"
32 | version = "1.0.12"
33 | source = "git+ssh://git@github.com/dtolnay/semver#a6425e6f41ddc81c6d6dd60c68248e0f0ef046c7"
34 |
35 | [[package]]
36 | name = "semver"
37 | version = "1.0.12"
38 | source = "registry+https://github.com/rust-lang/crates.io-index"
39 | checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
40 |
41 | [[package]]
42 | name = "semver"
43 | version = "1.0.12"
44 | source = "registry+https://www.github.com/rust-lang/crates.io-index"
45 | checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
46 |
--------------------------------------------------------------------------------
/tests/dependency-v2/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "dependencies"
3 | version = "0.0.0"
4 | edition = "2018"
5 |
6 | [dependencies]
7 | cratesio = { package = "semver", version = "1" }
8 | registry-index = { package = "semver", version = "1", registry-index = "https://www.github.com/rust-lang/crates.io-index" }
9 | git-tag = { package = "semver", git = "https://github.com/dtolnay/semver", tag = "1.0.0" }
10 | git-branch = { package = "semver", git = "http://github.com/dtolnay/semver", branch = "master" }
11 | git-rev = { package = "semver", git = "ssh://git@github.com/dtolnay/semver", rev = "a2ce5777dcd455246e4650e36dde8e2e96fcb3fd" }
12 | git-head = { package = "semver", git = "ssh://git@github.com/dtolnay/semver" }
13 |
--------------------------------------------------------------------------------
/tests/dependency-v2/README.md:
--------------------------------------------------------------------------------
1 | This `Cargo.lock` is generated by rust 1.41.0.
2 |
--------------------------------------------------------------------------------
/tests/dependency-v2/src/main.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | cratesio::Version::parse("1.2.3").unwrap();
3 | registry_index::Version::parse("1.2.3").unwrap();
4 | git_tag::Version::parse("1.2.3").unwrap();
5 | git_branch::Version::parse("1.2.3").unwrap();
6 | git_rev::Version::parse("1.2.3").unwrap();
7 | git_head::Version::parse("1.2.3").unwrap();
8 | println!("Hello, world!");
9 | }
10 |
--------------------------------------------------------------------------------
/tests/dependency-v3/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "dependencies"
7 | version = "0.0.0"
8 | dependencies = [
9 | "semver 1.0.0 (git+https://github.com/dtolnay/semver?tag=1.0.0)",
10 | "semver 1.0.0 (git+ssh://git@github.com/dtolnay/semver?rev=a2ce5777dcd455246e4650e36dde8e2e96fcb3fd)",
11 | "semver 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
12 | "semver 1.0.12 (registry+https://www.github.com/rust-lang/crates.io-index)",
13 | "semver 1.0.12 (git+http://github.com/dtolnay/semver?branch=master)",
14 | "semver 1.0.12 (git+ssh://git@github.com/dtolnay/semver)",
15 | ]
16 |
17 | [[package]]
18 | name = "semver"
19 | version = "1.0.0"
20 | source = "git+https://github.com/dtolnay/semver?tag=1.0.0#a2ce5777dcd455246e4650e36dde8e2e96fcb3fd"
21 |
22 | [[package]]
23 | name = "semver"
24 | version = "1.0.0"
25 | source = "git+ssh://git@github.com/dtolnay/semver?rev=a2ce5777dcd455246e4650e36dde8e2e96fcb3fd#a2ce5777dcd455246e4650e36dde8e2e96fcb3fd"
26 |
27 | [[package]]
28 | name = "semver"
29 | version = "1.0.12"
30 | source = "registry+https://github.com/rust-lang/crates.io-index"
31 | checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
32 |
33 | [[package]]
34 | name = "semver"
35 | version = "1.0.12"
36 | source = "registry+https://www.github.com/rust-lang/crates.io-index"
37 | checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
38 |
39 | [[package]]
40 | name = "semver"
41 | version = "1.0.12"
42 | source = "git+http://github.com/dtolnay/semver?branch=master#a6425e6f41ddc81c6d6dd60c68248e0f0ef046c7"
43 |
44 | [[package]]
45 | name = "semver"
46 | version = "1.0.12"
47 | source = "git+ssh://git@github.com/dtolnay/semver#a6425e6f41ddc81c6d6dd60c68248e0f0ef046c7"
48 |
--------------------------------------------------------------------------------
/tests/dependency-v3/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "dependencies"
3 | version = "0.0.0"
4 | edition = "2018"
5 |
6 | [dependencies]
7 | cratesio = { package = "semver", version = "1" }
8 | registry-index = { package = "semver", version = "1", registry-index = "https://www.github.com/rust-lang/crates.io-index" }
9 | git-tag = { package = "semver", git = "https://github.com/dtolnay/semver", tag = "1.0.0" }
10 | git-branch = { package = "semver", git = "http://github.com/dtolnay/semver", branch = "master" }
11 | git-rev = { package = "semver", git = "ssh://git@github.com/dtolnay/semver", rev = "a2ce5777dcd455246e4650e36dde8e2e96fcb3fd" }
12 | git-head = { package = "semver", git = "ssh://git@github.com/dtolnay/semver" }
13 |
--------------------------------------------------------------------------------
/tests/dependency-v3/src/main.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | cratesio::Version::parse("1.2.3").unwrap();
3 | registry_index::Version::parse("1.2.3").unwrap();
4 | git_tag::Version::parse("1.2.3").unwrap();
5 | git_branch::Version::parse("1.2.3").unwrap();
6 | git_rev::Version::parse("1.2.3").unwrap();
7 | git_head::Version::parse("1.2.3").unwrap();
8 | println!("Hello, world!");
9 | }
10 |
--------------------------------------------------------------------------------
/tests/fake-semver/Cargo.toml:
--------------------------------------------------------------------------------
1 | # This crate is for tests in `support.nix` and will not be really built.
2 | [package]
3 | name = "semver"
4 | version = "1.0.0"
5 | edition = "2018"
6 |
7 | [dependencies]
8 |
--------------------------------------------------------------------------------
/tests/fake-semver/src/lib.rs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tests/features/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "semver"
7 | version = "1.0.12"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
10 | dependencies = [
11 | "serde",
12 | ]
13 |
14 | [[package]]
15 | name = "serde"
16 | version = "1.0.139"
17 | source = "registry+https://github.com/rust-lang/crates.io-index"
18 | checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6"
19 |
20 | [[package]]
21 | name = "simple-features"
22 | version = "0.0.0"
23 | dependencies = [
24 | "semver",
25 | ]
26 |
--------------------------------------------------------------------------------
/tests/features/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "simple-features"
3 | version = "0.0.0"
4 | edition = "2018"
5 |
6 | [dependencies]
7 | semver = { version = "1.0.12", optional = true }
8 |
9 | [features]
10 | default = [ "a" ]
11 | a = ["default", "semver/serde"]
12 | b = []
13 |
--------------------------------------------------------------------------------
/tests/features/src/main.rs:
--------------------------------------------------------------------------------
1 | #[cfg(all(feature = "a", not(feature = "b")))]
2 | fn main() {
3 | println!("Hello, world!");
4 | }
5 |
--------------------------------------------------------------------------------
/tests/libz-dynamic/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "cc"
7 | version = "1.0.73"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
10 |
11 | [[package]]
12 | name = "libz-dynamic"
13 | version = "0.0.0"
14 | dependencies = [
15 | "libz-sys",
16 | ]
17 |
18 | [[package]]
19 | name = "libz-sys"
20 | version = "1.1.6"
21 | source = "registry+https://github.com/rust-lang/crates.io-index"
22 | checksum = "92e7e15d7610cce1d9752e137625f14e61a28cd45929b6e12e47b50fe154ee2e"
23 | dependencies = [
24 | "cc",
25 | "pkg-config",
26 | "vcpkg",
27 | ]
28 |
29 | [[package]]
30 | name = "pkg-config"
31 | version = "0.3.25"
32 | source = "registry+https://github.com/rust-lang/crates.io-index"
33 | checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
34 |
35 | [[package]]
36 | name = "vcpkg"
37 | version = "0.2.15"
38 | source = "registry+https://github.com/rust-lang/crates.io-index"
39 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
40 |
--------------------------------------------------------------------------------
/tests/libz-dynamic/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "libz-dynamic"
3 | version = "0.0.0"
4 | edition = "2018"
5 |
6 | [dependencies]
7 | libz-sys = { version = "=1.1.6", default-features = false }
8 |
--------------------------------------------------------------------------------
/tests/libz-dynamic/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | std::env::var("DEP_Z_INCLUDE").expect_err("Dynamic linking should not set DEP_Z_INCLUDE");
3 | println!("cargo:rustc-env=OKAY=");
4 | }
5 |
--------------------------------------------------------------------------------
/tests/libz-dynamic/src/main.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | let crc_init = unsafe { libz_sys::crc32(0, "foo".as_ptr() as _, 3) };
3 | assert_eq!(crc_init, 2356372769);
4 | assert_eq!(env!("OKAY"), "");
5 | println!("Hello, world!");
6 | }
7 |
--------------------------------------------------------------------------------
/tests/libz-static/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "cc"
7 | version = "1.0.73"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
10 |
11 | [[package]]
12 | name = "libz-static"
13 | version = "0.0.0"
14 | dependencies = [
15 | "libz-sys",
16 | ]
17 |
18 | [[package]]
19 | name = "libz-sys"
20 | version = "1.1.3"
21 | source = "registry+https://github.com/rust-lang/crates.io-index"
22 | checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66"
23 | dependencies = [
24 | "cc",
25 | "pkg-config",
26 | "vcpkg",
27 | ]
28 |
29 | [[package]]
30 | name = "pkg-config"
31 | version = "0.3.25"
32 | source = "registry+https://github.com/rust-lang/crates.io-index"
33 | checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
34 |
35 | [[package]]
36 | name = "vcpkg"
37 | version = "0.2.15"
38 | source = "registry+https://github.com/rust-lang/crates.io-index"
39 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
40 |
--------------------------------------------------------------------------------
/tests/libz-static/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "libz-static"
3 | version = "0.0.0"
4 | edition = "2018"
5 |
6 | [dependencies]
7 | libz-sys = { version = "=1.1.3", default-features = false, features = ["static"] }
8 |
--------------------------------------------------------------------------------
/tests/libz-static/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | let z_include = std::env::var("DEP_Z_INCLUDE").unwrap();
3 | let header = std::fs::read_to_string(z_include + "/zlib.h").unwrap();
4 | let mut lines = header.lines();
5 | assert_eq!(
6 | lines.next().unwrap(),
7 | "/* zlib.h -- interface of the 'zlib' general purpose compression library"
8 | );
9 | assert_eq!(
10 | lines.next().unwrap(),
11 | " version 1.2.11, January 15th, 2017",
12 | "bundled libz MUST be 1.2.11",
13 | );
14 | println!("cargo:rustc-env=OKAY=");
15 | }
16 |
--------------------------------------------------------------------------------
/tests/libz-static/src/lib.rs:
--------------------------------------------------------------------------------
1 | // Only available in the build script.
2 | const _: [(); 1] = [(); option_env!("DEP_Z_INCLUDE").is_none() as usize];
3 |
--------------------------------------------------------------------------------
/tests/libz-static/src/main.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | // Only available in the build script.
3 | assert!(option_env!("DEP_Z_INCLUDE").is_none());
4 |
5 | let crc_init = unsafe { libz_sys::crc32(0, "foo".as_ptr() as _, 3) };
6 | assert_eq!(crc_init, 2356372769);
7 | assert_eq!(env!("OKAY"), "");
8 | println!("Hello, world!");
9 | }
10 |
--------------------------------------------------------------------------------
/tests/lto-fat/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "lto-fat"
7 | version = "0.0.0"
8 | dependencies = [
9 | "semver",
10 | ]
11 |
12 | [[package]]
13 | name = "semver"
14 | version = "1.0.13"
15 | source = "registry+https://github.com/rust-lang/crates.io-index"
16 | checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711"
17 |
--------------------------------------------------------------------------------
/tests/lto-fat/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "lto-fat"
3 | version = "0.0.0"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | semver = "1"
8 |
9 | [profile.release]
10 | lto = "fat"
11 |
--------------------------------------------------------------------------------
/tests/lto-fat/src/main.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | assert_eq!(
3 | semver::Version::parse("1.2.3").unwrap().to_string(),
4 | "1.2.3"
5 | );
6 | println!("Hello, world!");
7 | }
8 |
--------------------------------------------------------------------------------
/tests/lto-proc-macro/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "lto-proc-macro"
7 | version = "0.0.0"
8 | dependencies = [
9 | "thiserror",
10 | ]
11 |
12 | [[package]]
13 | name = "proc-macro2"
14 | version = "1.0.43"
15 | source = "registry+https://github.com/rust-lang/crates.io-index"
16 | checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
17 | dependencies = [
18 | "unicode-ident",
19 | ]
20 |
21 | [[package]]
22 | name = "quote"
23 | version = "1.0.21"
24 | source = "registry+https://github.com/rust-lang/crates.io-index"
25 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
26 | dependencies = [
27 | "proc-macro2",
28 | ]
29 |
30 | [[package]]
31 | name = "syn"
32 | version = "1.0.99"
33 | source = "registry+https://github.com/rust-lang/crates.io-index"
34 | checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
35 | dependencies = [
36 | "proc-macro2",
37 | "quote",
38 | "unicode-ident",
39 | ]
40 |
41 | [[package]]
42 | name = "thiserror"
43 | version = "1.0.35"
44 | source = "registry+https://github.com/rust-lang/crates.io-index"
45 | checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85"
46 | dependencies = [
47 | "thiserror-impl",
48 | ]
49 |
50 | [[package]]
51 | name = "thiserror-impl"
52 | version = "1.0.35"
53 | source = "registry+https://github.com/rust-lang/crates.io-index"
54 | checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783"
55 | dependencies = [
56 | "proc-macro2",
57 | "quote",
58 | "syn",
59 | ]
60 |
61 | [[package]]
62 | name = "unicode-ident"
63 | version = "1.0.4"
64 | source = "registry+https://github.com/rust-lang/crates.io-index"
65 | checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
66 |
--------------------------------------------------------------------------------
/tests/lto-proc-macro/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "lto-proc-macro"
3 | version = "0.0.0"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | thiserror = "1"
8 |
9 | [profile.release]
10 | lto = "thin"
11 |
--------------------------------------------------------------------------------
/tests/lto-proc-macro/src/main.rs:
--------------------------------------------------------------------------------
1 | #[derive(Debug, thiserror::Error)]
2 | enum Error {
3 | #[error("Hello, {0}!")]
4 | Hello(&'static str),
5 | }
6 |
7 | fn main() {
8 | println!("{}", Error::Hello("world"));
9 | }
10 |
--------------------------------------------------------------------------------
/tests/lto-thin/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "lto-thin"
7 | version = "0.0.0"
8 | dependencies = [
9 | "semver",
10 | ]
11 |
12 | [[package]]
13 | name = "semver"
14 | version = "1.0.13"
15 | source = "registry+https://github.com/rust-lang/crates.io-index"
16 | checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711"
17 |
--------------------------------------------------------------------------------
/tests/lto-thin/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "lto-thin"
3 | version = "0.0.0"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | semver = "1"
8 |
9 | [profile.release]
10 | lto = "thin"
11 |
--------------------------------------------------------------------------------
/tests/lto-thin/src/main.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | assert_eq!(
3 | semver::Version::parse("1.2.3").unwrap().to_string(),
4 | "1.2.3"
5 | );
6 | println!("Hello, world!");
7 | }
8 |
--------------------------------------------------------------------------------
/tests/tokio-app/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "autocfg"
7 | version = "1.1.0"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
10 |
11 | [[package]]
12 | name = "hermit-abi"
13 | version = "0.1.19"
14 | source = "registry+https://github.com/rust-lang/crates.io-index"
15 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
16 | dependencies = [
17 | "libc",
18 | ]
19 |
20 | [[package]]
21 | name = "libc"
22 | version = "0.2.126"
23 | source = "registry+https://github.com/rust-lang/crates.io-index"
24 | checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
25 |
26 | [[package]]
27 | name = "num_cpus"
28 | version = "1.13.1"
29 | source = "registry+https://github.com/rust-lang/crates.io-index"
30 | checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
31 | dependencies = [
32 | "hermit-abi",
33 | "libc",
34 | ]
35 |
36 | [[package]]
37 | name = "once_cell"
38 | version = "1.13.0"
39 | source = "registry+https://github.com/rust-lang/crates.io-index"
40 | checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
41 |
42 | [[package]]
43 | name = "pin-project-lite"
44 | version = "0.2.9"
45 | source = "registry+https://github.com/rust-lang/crates.io-index"
46 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
47 |
48 | [[package]]
49 | name = "proc-macro2"
50 | version = "1.0.40"
51 | source = "registry+https://github.com/rust-lang/crates.io-index"
52 | checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
53 | dependencies = [
54 | "unicode-ident",
55 | ]
56 |
57 | [[package]]
58 | name = "quote"
59 | version = "1.0.20"
60 | source = "registry+https://github.com/rust-lang/crates.io-index"
61 | checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
62 | dependencies = [
63 | "proc-macro2",
64 | ]
65 |
66 | [[package]]
67 | name = "syn"
68 | version = "1.0.98"
69 | source = "registry+https://github.com/rust-lang/crates.io-index"
70 | checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
71 | dependencies = [
72 | "proc-macro2",
73 | "quote",
74 | "unicode-ident",
75 | ]
76 |
77 | [[package]]
78 | name = "tokio"
79 | version = "1.20.0"
80 | source = "registry+https://github.com/rust-lang/crates.io-index"
81 | checksum = "57aec3cfa4c296db7255446efb4928a6be304b431a806216105542a67b6ca82e"
82 | dependencies = [
83 | "autocfg",
84 | "num_cpus",
85 | "once_cell",
86 | "pin-project-lite",
87 | "tokio-macros",
88 | ]
89 |
90 | [[package]]
91 | name = "tokio-app"
92 | version = "0.0.0"
93 | dependencies = [
94 | "tokio",
95 | ]
96 |
97 | [[package]]
98 | name = "tokio-macros"
99 | version = "1.8.0"
100 | source = "registry+https://github.com/rust-lang/crates.io-index"
101 | checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484"
102 | dependencies = [
103 | "proc-macro2",
104 | "quote",
105 | "syn",
106 | ]
107 |
108 | [[package]]
109 | name = "unicode-ident"
110 | version = "1.0.1"
111 | source = "registry+https://github.com/rust-lang/crates.io-index"
112 | checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
113 |
--------------------------------------------------------------------------------
/tests/tokio-app/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "tokio-app"
3 | version = "0.0.0"
4 | edition = "2018"
5 |
6 | [dependencies]
7 | tokio = { version = "1", features = [ "rt-multi-thread", "macros", "time" ], default-features = false }
8 |
--------------------------------------------------------------------------------
/tests/tokio-app/src/main.rs:
--------------------------------------------------------------------------------
1 | #[tokio::main]
2 | async fn main() {
3 | tokio::time::sleep(std::time::Duration::from_millis(100)).await;
4 | println!("Hello, world!");
5 | }
6 |
--------------------------------------------------------------------------------
/tests/workspace-inline/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "bar"
7 | version = "0.1.0"
8 |
9 | [[package]]
10 | name = "baz"
11 | version = "0.1.0"
12 | dependencies = [
13 | "bar",
14 | ]
15 |
16 | [[package]]
17 | name = "foo"
18 | version = "0.1.0"
19 | dependencies = [
20 | "bar",
21 | ]
22 |
--------------------------------------------------------------------------------
/tests/workspace-inline/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "foo"
3 | version = "0.1.0"
4 | edition = "2018"
5 |
6 | [dependencies]
7 | bar = { path = "bar" }
8 |
9 | [workspace]
10 | members = [".", "b*"]
11 |
--------------------------------------------------------------------------------
/tests/workspace-inline/bar/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "bar"
3 | version = "0.1.0"
4 | edition = "2018"
5 |
6 | [dependencies]
7 |
--------------------------------------------------------------------------------
/tests/workspace-inline/bar/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub fn hello() -> &'static str {
2 | "Hello"
3 | }
4 |
--------------------------------------------------------------------------------
/tests/workspace-inline/baz/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "baz"
3 | version = "0.1.0"
4 | edition = "2018"
5 |
6 | [dependencies]
7 | bar = { path = "../bar" }
8 |
--------------------------------------------------------------------------------
/tests/workspace-inline/baz/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub fn show(s: String) {
2 | assert!(s.starts_with(bar::hello()));
3 | println!("{}", s);
4 | }
5 |
--------------------------------------------------------------------------------
/tests/workspace-inline/src/main.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | println!("{}, world!", bar::hello());
3 | }
4 |
--------------------------------------------------------------------------------
/tests/workspace-proc-macro-lto/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "acro"
7 | version = "0.1.0"
8 | dependencies = [
9 | "procm",
10 | ]
11 |
12 | [[package]]
13 | name = "procm"
14 | version = "0.1.0"
15 |
--------------------------------------------------------------------------------
/tests/workspace-proc-macro-lto/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "acro"
3 | version = "0.1.0"
4 | edition = "2018"
5 |
6 | [dependencies]
7 | procm = { path = "./procm" }
8 |
9 | [workspace]
10 | # FIXME: "." is required.
11 | members = ["procm", "."]
12 |
13 | [profile.release]
14 | lto = "thin"
15 |
--------------------------------------------------------------------------------
/tests/workspace-proc-macro-lto/procm/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "procm"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [lib]
7 | proc-macro = true
8 |
--------------------------------------------------------------------------------
/tests/workspace-proc-macro-lto/procm/src/lib.rs:
--------------------------------------------------------------------------------
1 | use proc_macro::TokenStream;
2 |
3 | #[proc_macro]
4 | pub fn acro(input: TokenStream) -> TokenStream {
5 | format!(r#"fn main() {{ println!({}) }}"#, input).parse().unwrap()
6 | }
7 |
--------------------------------------------------------------------------------
/tests/workspace-proc-macro-lto/src/lib.rs:
--------------------------------------------------------------------------------
1 | procm::acro!("Hello, world!");
2 |
--------------------------------------------------------------------------------
/tests/workspace-virtual/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "bar"
7 | version = "0.1.0"
8 |
9 | [[package]]
10 | name = "foo"
11 | version = "0.1.0"
12 | dependencies = [
13 | "bar",
14 | ]
15 |
--------------------------------------------------------------------------------
/tests/workspace-virtual/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = [ "./crates/*" ]
3 | exclude = [ "./crates/./exc" ]
4 |
--------------------------------------------------------------------------------
/tests/workspace-virtual/crates/bar/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "bar"
3 | version = "0.1.0"
4 | edition = "2018"
5 |
6 | [dependencies]
7 |
--------------------------------------------------------------------------------
/tests/workspace-virtual/crates/bar/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub fn world() -> &'static str {
2 | "world"
3 | }
4 |
--------------------------------------------------------------------------------
/tests/workspace-virtual/crates/exc/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "exc"
3 | version = "0.1.0"
4 | edition = "2018"
5 |
6 | [dependencies]
7 |
--------------------------------------------------------------------------------
/tests/workspace-virtual/crates/exc/src/main.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | println!("Hello, world!");
3 | }
4 |
--------------------------------------------------------------------------------
/tests/workspace-virtual/crates/foo/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "foo"
3 | version = "0.1.0"
4 | edition = "2018"
5 |
6 | [dependencies]
7 | bar = { path = "../bar" }
8 |
--------------------------------------------------------------------------------
/tests/workspace-virtual/crates/foo/src/main.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | println!("Hello, {}!", bar::world());
3 | }
4 |
--------------------------------------------------------------------------------
/toml2json/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "itoa"
7 | version = "1.0.3"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
10 |
11 | [[package]]
12 | name = "ryu"
13 | version = "1.0.11"
14 | source = "registry+https://github.com/rust-lang/crates.io-index"
15 | checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
16 |
17 | [[package]]
18 | name = "serde"
19 | version = "1.0.144"
20 | source = "registry+https://github.com/rust-lang/crates.io-index"
21 | checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
22 |
23 | [[package]]
24 | name = "serde_json"
25 | version = "1.0.85"
26 | source = "registry+https://github.com/rust-lang/crates.io-index"
27 | checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
28 | dependencies = [
29 | "itoa",
30 | "ryu",
31 | "serde",
32 | ]
33 |
34 | [[package]]
35 | name = "toml"
36 | version = "0.5.9"
37 | source = "registry+https://github.com/rust-lang/crates.io-index"
38 | checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
39 | dependencies = [
40 | "serde",
41 | ]
42 |
43 | [[package]]
44 | name = "toml2json"
45 | version = "1.0.0"
46 | dependencies = [
47 | "serde_json",
48 | "toml",
49 | ]
50 |
--------------------------------------------------------------------------------
/toml2json/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "toml2json"
3 | version = "1.0.0"
4 | rust-version = "1.36"
5 |
6 | [dependencies]
7 | serde_json = "1.0.0"
8 | toml = "0.5"
9 |
--------------------------------------------------------------------------------
/toml2json/README.md:
--------------------------------------------------------------------------------
1 | ## toml2json: minimal impl for toml -> json
2 |
3 | This utility program is a dependency of every rust crate derivation.
4 | Thus it's is designed to be simple and have minimal dependencies, instead of using `remarshal` which
5 | pulls in tons of python packages.
6 |
--------------------------------------------------------------------------------
/toml2json/default.nix:
--------------------------------------------------------------------------------
1 | { stdenv, fetchurl, rustc }:
2 | let
3 | fetch = name: version: sha256:
4 | fetchurl {
5 | name = "crate-${name}-${version}.tar.gz";
6 | url = "https://crates.io/api/v1/crates/${name}/${version}/download";
7 | inherit sha256;
8 | };
9 |
10 | manifest = builtins.fromTOML (builtins.readFile ./Cargo.toml);
11 | lock = builtins.fromTOML (builtins.readFile ./Cargo.lock);
12 |
13 | in stdenv.mkDerivation {
14 | pname = manifest.package.name;
15 | version = manifest.package.version;
16 |
17 | srcs = map ({ name, version, checksum ? null, ... }: if checksum != null then fetch name version checksum else null) lock.package;
18 |
19 | sourceRoot = ".";
20 |
21 | nativeBuildInputs = [ rustc ];
22 |
23 | buildPhase = ''
24 | buildFlagsArray+=(
25 | --color=always
26 | --out-dir .
27 | -L .
28 | -C codegen-units=1
29 | -C opt-level=3
30 | --cap-lints allow
31 | )
32 |
33 | run() {
34 | echo "rustc $* ''${buildFlagsArray[*]}"
35 | rustc "$@" "''${buildFlagsArray[@]}"
36 | }
37 |
38 | run itoa-*/src/lib.rs --crate-name itoa --crate-type lib \
39 | --cfg 'feature="default"' --cfg 'feature="std"'
40 | run ryu-*/src/lib.rs --crate-name ryu --crate-type lib
41 | run serde-*/src/lib.rs --crate-name serde --crate-type lib \
42 | --cfg 'feature="default"' --cfg 'feature="std"'
43 | run serde_json-*/src/lib.rs --crate-name serde_json --crate-type lib \
44 | --edition=2018 \
45 | --cfg 'feature="default"' --cfg 'feature="std"' \
46 | --extern itoa=libitoa.rlib \
47 | --extern ryu=libryu.rlib \
48 | --extern serde=libserde.rlib
49 | run toml-*/src/lib.rs --crate-name toml --crate-type lib \
50 | --edition=2018 \
51 | --extern serde=libserde.rlib
52 | run ${./src/main.rs} --crate-name toml2json --crate-type bin \
53 | --extern serde_json=./libserde_json.rlib \
54 | --extern toml=libtoml.rlib
55 | '';
56 |
57 | testToml = ''
58 | [hello]
59 | world = "good"
60 | [target."cfg(target = \"good\")"]
61 | foo = "bar"
62 | '';
63 |
64 | testJson = ''{"hello":{"world":"good"},"target":{"cfg(target = \"good\")":{"foo":"bar"}}}'';
65 |
66 | doCheck = true;
67 | checkPhase = ''
68 | ./toml2json <<<"$testToml" >out.json
69 | echo "Got : $(cat out.json)"
70 | echo "Expect: $testJson"
71 | [[ "$(cat out.json)" == "$testJson" ]]
72 | '';
73 |
74 | installPhase = ''
75 | mkdir -p $out/bin
76 | cp -t $out/bin ./toml2json
77 | '';
78 | }
79 |
--------------------------------------------------------------------------------
/toml2json/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::io::{stdin, stdout, Read, Write};
2 |
3 | fn main() -> Result<(), Box> {
4 | let mut input = Vec::new();
5 | stdin().lock().read_to_end(&mut input)?;
6 | let data: serde_json::Value = toml::from_slice(&input)?;
7 | let mut output = serde_json::to_vec(&data)?;
8 | output.push(b'\n');
9 | stdout().lock().write_all(&output)?;
10 | Ok(())
11 | }
12 |
--------------------------------------------------------------------------------