Seed the ClojureDart caches out of the flake
cljd-deps already fetches and hashes everything `clojure -M:cljd compile` reaches the network for, but only the sandbox build consumed it: both `just apk` and `just flutter-desktop` re-resolved Maven, gitlibs and pub from upstream on every run, into the real ~/.m2 and ~/.gitlibs — neither of which follows HOME, because the JVM reads user.home out of /etc/passwd. Both shells name the output now, and both recipes seed from it once into flutter/.home the way `apk` already seeds ANDROID_HOME, then leave it alone. One cache between the two frontends: same compiler, same deps.edn, so a second copy would be a second answer to what it resolved against. Not --offline, unlike the sandbox build. These are a warm start rather than a pin, and a pin would mean re-hashing cljd-deps before a new line in flutter/deps.edn compiled at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
f2e12af parent: 553469e modified
flake.nix +29 -1 | @@ -891,7 +891,13 @@ | ||
| 891 | 891 | # than as a version mismatch. |
| 892 | 892 | flutter = pkgs.mkShellNoCC { |
| 893 | 893 | name = "frq-flutter"; |
| 894 | - packages = [ pkgs.clojure pkgs.jdk17 pkgs.flutter pkgs.just ]; | |
| 894 | + | |
| 895 | + # git, because tools.deps resolves the ClojureDart dependency | |
| 896 | + # through tools.gitlibs even when every byte of it is already in | |
| 897 | + # the seeded cache — the same reason flutter-desktop-unwrapped | |
| 898 | + # names it. The host's git has always been there to answer; naming | |
| 899 | + # it means the shell does not depend on that. | |
| 900 | + packages = [ pkgs.clojure pkgs.jdk17 pkgs.flutter pkgs.just pkgs.git ]; | |
| 895 | 901 | |
| 896 | 902 | # Where the recipe copies from. Naming it here is also what makes |
| 897 | 903 | # entering the shell build it, so the first `just apk` does not |
| @@ -899,6 +905,19 @@ | ||
| 899 | 905 | # why. |
| 900 | 906 | FRQ_ANDROID_SDK = |
| 901 | 907 | "${androidSdkFor pkgs.stdenv.hostPlatform.system}/libexec/android-sdk"; |
| 908 | + | |
| 909 | + # The Maven, gitlibs and pub caches the ClojureDart compile would | |
| 910 | + # otherwise fetch, plus the analyzer project it writes under | |
| 911 | + # .clojuredart. Here for both of FRQ_ANDROID_SDK's reasons: it is | |
| 912 | + # where the recipe copies from, and naming it is what makes | |
| 913 | + # entering the shell build it. | |
| 914 | + # | |
| 915 | + # The compile still runs online. These are a warm start and not a | |
| 916 | + # pin — `--offline` would be, and would turn adding a line to | |
| 917 | + # flutter/deps.edn into a re-hash of cljd-deps before anything | |
| 918 | + # compiled again. The sandbox build takes that trade because it | |
| 919 | + # has no network; the loop someone edits in should not. | |
| 920 | + FRQ_CLJD_DEPS = "${self.packages.${pkgs.stdenv.hostPlatform.system}.cljd-deps}"; | |
| 902 | 921 | }; |
| 903 | 922 | |
| 904 | 923 | # The other desktop GUI. Same ClojureDart half as the APK — one |
| @@ -945,6 +964,15 @@ | ||
| 945 | 964 | # the store's. The recipe reads this exactly as `run` does. |
| 946 | 965 | NIXGL = "${nixGLFor pkgs}/bin/nixGLIntel"; |
| 947 | 966 | |
| 967 | + # The `flutter` shell's, deliberately the same one and for the | |
| 968 | + # same reason clojure and flutter are: the ClojureDart half of | |
| 969 | + # both builds is one compile over one deps.edn, so a second set of | |
| 970 | + # caches would be a second answer to what it resolved against. | |
| 971 | + # This recipe reads the variable directly — it is inside this | |
| 972 | + # shell before it does any of the work — where `just apk` reaches | |
| 973 | + # for the flake output itself. | |
| 974 | + FRQ_CLJD_DEPS = "${self.packages.${pkgs.stdenv.hostPlatform.system}.cljd-deps}"; | |
| 975 | + | |
| 948 | 976 | # The recipe's re-entry test, the way JOLT_NATIVE_LIB is the |
| 949 | 977 | # default shell's. Nothing else sets it, so `just flutter-desktop` |
| 950 | 978 | # outside the shell re-enters and lands back on the same recipe — |
| @@ -891,7 +891,13 @@ | |||
| 891 | # than as a version mismatch. | 891 | # than as a version mismatch. |
| 892 | flutter = pkgs.mkShellNoCC { | 892 | flutter = pkgs.mkShellNoCC { |
| 893 | name = "frq-flutter"; | 893 | name = "frq-flutter"; |
| 894 | - packages = [ pkgs.clojure pkgs.jdk17 pkgs.flutter pkgs.just ]; | 894 | + |
| 895 | + # git, because tools.deps resolves the ClojureDart dependency | ||
| 896 | + # through tools.gitlibs even when every byte of it is already in | ||
| 897 | + # the seeded cache — the same reason flutter-desktop-unwrapped | ||
| 898 | + # names it. The host's git has always been there to answer; naming | ||
| 899 | + # it means the shell does not depend on that. | ||
| 900 | + packages = [ pkgs.clojure pkgs.jdk17 pkgs.flutter pkgs.just pkgs.git ]; | ||
| 895 | 901 | ||
| 896 | # Where the recipe copies from. Naming it here is also what makes | 902 | # Where the recipe copies from. Naming it here is also what makes |
| 897 | # entering the shell build it, so the first `just apk` does not | 903 | # entering the shell build it, so the first `just apk` does not |
| @@ -899,6 +905,19 @@ | |||
| 899 | # why. | 905 | # why. |
| 900 | FRQ_ANDROID_SDK = | 906 | FRQ_ANDROID_SDK = |
| 901 | "${androidSdkFor pkgs.stdenv.hostPlatform.system}/libexec/android-sdk"; | 907 | "${androidSdkFor pkgs.stdenv.hostPlatform.system}/libexec/android-sdk"; |
| 908 | + | ||
| 909 | + # The Maven, gitlibs and pub caches the ClojureDart compile would | ||
| 910 | + # otherwise fetch, plus the analyzer project it writes under | ||
| 911 | + # .clojuredart. Here for both of FRQ_ANDROID_SDK's reasons: it is | ||
| 912 | + # where the recipe copies from, and naming it is what makes | ||
| 913 | + # entering the shell build it. | ||
| 914 | + # | ||
| 915 | + # The compile still runs online. These are a warm start and not a | ||
| 916 | + # pin — `--offline` would be, and would turn adding a line to | ||
| 917 | + # flutter/deps.edn into a re-hash of cljd-deps before anything | ||
| 918 | + # compiled again. The sandbox build takes that trade because it | ||
| 919 | + # has no network; the loop someone edits in should not. | ||
| 920 | + FRQ_CLJD_DEPS = "${self.packages.${pkgs.stdenv.hostPlatform.system}.cljd-deps}"; | ||
| 902 | }; | 921 | }; |
| 903 | 922 | ||
| 904 | # The other desktop GUI. Same ClojureDart half as the APK — one | 923 | # The other desktop GUI. Same ClojureDart half as the APK — one |
| @@ -945,6 +964,15 @@ | |||
| 945 | # the store's. The recipe reads this exactly as `run` does. | 964 | # the store's. The recipe reads this exactly as `run` does. |
| 946 | NIXGL = "${nixGLFor pkgs}/bin/nixGLIntel"; | 965 | NIXGL = "${nixGLFor pkgs}/bin/nixGLIntel"; |
| 947 | 966 | ||
| 967 | + # The `flutter` shell's, deliberately the same one and for the | ||
| 968 | + # same reason clojure and flutter are: the ClojureDart half of | ||
| 969 | + # both builds is one compile over one deps.edn, so a second set of | ||
| 970 | + # caches would be a second answer to what it resolved against. | ||
| 971 | + # This recipe reads the variable directly — it is inside this | ||
| 972 | + # shell before it does any of the work — where `just apk` reaches | ||
| 973 | + # for the flake output itself. | ||
| 974 | + FRQ_CLJD_DEPS = "${self.packages.${pkgs.stdenv.hostPlatform.system}.cljd-deps}"; | ||
| 975 | + | ||
| 948 | # The recipe's re-entry test, the way JOLT_NATIVE_LIB is the | 976 | # The recipe's re-entry test, the way JOLT_NATIVE_LIB is the |
| 949 | # default shell's. Nothing else sets it, so `just flutter-desktop` | 977 | # default shell's. Nothing else sets it, so `just flutter-desktop` |
| 950 | # outside the shell re-enters and lands back on the same recipe — | 978 | # outside the shell re-enters and lands back on the same recipe — |
modified
justfile +79 -2 | @@ -99,12 +99,52 @@ apk action="build": | ||
| 99 | 99 | chmod -R u+w "$ANDROID_HOME" |
| 100 | 100 | fi |
| 101 | 101 | |
| 102 | + # The compile's three caches, same flake-output-and-copy shape as the SDK | |
| 103 | + # and for the same reason: tools.deps and pub both write into theirs, and | |
| 104 | + # the store is read-only. What this buys is the "Resolving dependencies… | |
| 105 | + # Downloading packages…" that used to open every run. | |
| 106 | + deps="$(nix build --no-link --print-out-paths \ | |
| 107 | + "{{justfile_directory()}}#cljd-deps")" | |
| 108 | + | |
| 109 | + # Neither of these follows HOME. Both are read off the JVM's user.home, | |
| 110 | + # which comes from /etc/passwd rather than the environment — so moving | |
| 111 | + # HOME below is not enough to move them, and left alone they would be the | |
| 112 | + # real ~/.m2 and ~/.gitlibs, shared with every other project on the box. | |
| 113 | + export GITLIBS="$HOME/gitlibs" | |
| 114 | + m2="$HOME/m2" | |
| 115 | + export PUB_CACHE="$HOME/.pub-cache" | |
| 116 | + | |
| 117 | + # Seeded once each and then left alone, exactly as ANDROID_HOME is: after | |
| 118 | + # the first compile these hold whatever the working tree has asked for | |
| 119 | + # since, and re-copying would throw that away. | |
| 120 | + seed() { | |
| 121 | + [ -e "$2" ] && return 0 | |
| 122 | + mkdir -p "$(dirname "$2")" | |
| 123 | + cp -r "$deps/$1" "$2" | |
| 124 | + chmod -R u+w "$2" | |
| 125 | + } | |
| 126 | + seed m2 "$m2" | |
| 127 | + seed gitlibs "$GITLIBS" | |
| 128 | + seed pub-cache "$PUB_CACHE" | |
| 129 | + seed clojuredart/cache "$PWD/.clojuredart/cache" | |
| 130 | + | |
| 102 | 131 | # Also the flake's. `nix shell nixpkgs#...` read the registry, which is a |
| 103 | 132 | # different and unlocked nixpkgs — the Flutter that built the APK could |
| 104 | 133 | # move under it without flake.lock changing a line. |
| 105 | 134 | flutter="nix develop {{justfile_directory()}}#flutter --command" |
| 106 | 135 | |
| 107 | - $flutter clojure -M:cljd compile | |
| 136 | + # cljd-deps ships the analyzer project unresolved — a fixed-output | |
| 137 | + # derivation may not name the store, and a resolved pub project is | |
| 138 | + # nothing but store paths. So it is resolved here instead, offline, | |
| 139 | + # against the cache that derivation did fetch. ClojureDart reaches for the | |
| 140 | + # network only when bin/analyzer.dart is missing, and after this it is not. | |
| 141 | + for helper in .clojuredart/cache/*/cljd_helper; do | |
| 142 | + [ -d "$helper" ] || continue | |
| 143 | + [ -e "$helper/.dart_tool/package_config.json" ] && continue | |
| 144 | + ( cd "$helper" && $flutter flutter pub get --offline ) | |
| 145 | + done | |
| 146 | + | |
| 147 | + $flutter clojure -Sdeps "{:mvn/local-repo \"$m2\"}" -M:cljd compile | |
| 108 | 148 | |
| 109 | 149 | # Rewritten every run: it carries absolute store paths, and the flutter |
| 110 | 150 | # one moves whenever nixpkgs does. |
| @@ -311,7 +351,44 @@ flutter-desktop action="build": | ||
| 311 | 351 | fi |
| 312 | 352 | cd flutter |
| 313 | 353 | |
| 314 | - clojure -M:cljd compile | |
| 354 | + # The same three caches `just apk` seeds, in the same place and out of the | |
| 355 | + # same flake output — one compiler, one set of dependencies, and no reason | |
| 356 | + # for the two frontends to keep a copy each. `.home/` is `apk`'s directory | |
| 357 | + # by name and this is the only thing put there from here, which is the | |
| 358 | + # point: whichever recipe runs first pays for the copy and the other finds | |
| 359 | + # it warm. | |
| 360 | + # | |
| 361 | + # No `nix build` here, unlike `apk`: this recipe is already inside the | |
| 362 | + # shell that names FRQ_CLJD_DEPS by the time it gets this far, and `apk` | |
| 363 | + # needs the path before it enters anything. | |
| 364 | + # | |
| 365 | + # m2 and gitlibs are set here for the reason they are set there — the JVM | |
| 366 | + # reads user.home out of /etc/passwd, so neither follows HOME and left | |
| 367 | + # alone they are the real ~/.m2 and ~/.gitlibs. | |
| 368 | + export PUB_CACHE="$PWD/.home/.pub-cache" | |
| 369 | + export GITLIBS="$PWD/.home/gitlibs" | |
| 370 | + m2="$PWD/.home/m2" | |
| 371 | + | |
| 372 | + seed() { | |
| 373 | + [ -e "$2" ] && return 0 | |
| 374 | + mkdir -p "$(dirname "$2")" | |
| 375 | + cp -r "$FRQ_CLJD_DEPS/$1" "$2" | |
| 376 | + chmod -R u+w "$2" | |
| 377 | + } | |
| 378 | + seed m2 "$m2" | |
| 379 | + seed gitlibs "$GITLIBS" | |
| 380 | + seed pub-cache "$PUB_CACHE" | |
| 381 | + seed clojuredart/cache "$PWD/.clojuredart/cache" | |
| 382 | + | |
| 383 | + # Resolved here rather than in cljd-deps, which was not allowed to name | |
| 384 | + # the store — see the same loop in `apk`. | |
| 385 | + for helper in .clojuredart/cache/*/cljd_helper; do | |
| 386 | + [ -d "$helper" ] || continue | |
| 387 | + [ -e "$helper/.dart_tool/package_config.json" ] && continue | |
| 388 | + ( cd "$helper" && flutter pub get --offline ) | |
| 389 | + done | |
| 390 | + | |
| 391 | + clojure -Sdeps "{:mvn/local-repo \"$m2\"}" -M:cljd compile | |
| 315 | 392 | flutter build linux --debug |
| 316 | 393 | |
| 317 | 394 | # x64/arm64 is Flutter's own name for the host arch, not uname's. |
| @@ -99,12 +99,52 @@ apk action="build": | |||
| 99 | chmod -R u+w "$ANDROID_HOME" | 99 | chmod -R u+w "$ANDROID_HOME" |
| 100 | fi | 100 | fi |
| 101 | 101 | ||
| 102 | + # The compile's three caches, same flake-output-and-copy shape as the SDK | ||
| 103 | + # and for the same reason: tools.deps and pub both write into theirs, and | ||
| 104 | + # the store is read-only. What this buys is the "Resolving dependencies… | ||
| 105 | + # Downloading packages…" that used to open every run. | ||
| 106 | + deps="$(nix build --no-link --print-out-paths \ | ||
| 107 | + "{{justfile_directory()}}#cljd-deps")" | ||
| 108 | + | ||
| 109 | + # Neither of these follows HOME. Both are read off the JVM's user.home, | ||
| 110 | + # which comes from /etc/passwd rather than the environment — so moving | ||
| 111 | + # HOME below is not enough to move them, and left alone they would be the | ||
| 112 | + # real ~/.m2 and ~/.gitlibs, shared with every other project on the box. | ||
| 113 | + export GITLIBS="$HOME/gitlibs" | ||
| 114 | + m2="$HOME/m2" | ||
| 115 | + export PUB_CACHE="$HOME/.pub-cache" | ||
| 116 | + | ||
| 117 | + # Seeded once each and then left alone, exactly as ANDROID_HOME is: after | ||
| 118 | + # the first compile these hold whatever the working tree has asked for | ||
| 119 | + # since, and re-copying would throw that away. | ||
| 120 | + seed() { | ||
| 121 | + [ -e "$2" ] && return 0 | ||
| 122 | + mkdir -p "$(dirname "$2")" | ||
| 123 | + cp -r "$deps/$1" "$2" | ||
| 124 | + chmod -R u+w "$2" | ||
| 125 | + } | ||
| 126 | + seed m2 "$m2" | ||
| 127 | + seed gitlibs "$GITLIBS" | ||
| 128 | + seed pub-cache "$PUB_CACHE" | ||
| 129 | + seed clojuredart/cache "$PWD/.clojuredart/cache" | ||
| 130 | + | ||
| 102 | # Also the flake's. `nix shell nixpkgs#...` read the registry, which is a | 131 | # Also the flake's. `nix shell nixpkgs#...` read the registry, which is a |
| 103 | # different and unlocked nixpkgs — the Flutter that built the APK could | 132 | # different and unlocked nixpkgs — the Flutter that built the APK could |
| 104 | # move under it without flake.lock changing a line. | 133 | # move under it without flake.lock changing a line. |
| 105 | flutter="nix develop {{justfile_directory()}}#flutter --command" | 134 | flutter="nix develop {{justfile_directory()}}#flutter --command" |
| 106 | 135 | ||
| 107 | - $flutter clojure -M:cljd compile | 136 | + # cljd-deps ships the analyzer project unresolved — a fixed-output |
| 137 | + # derivation may not name the store, and a resolved pub project is | ||
| 138 | + # nothing but store paths. So it is resolved here instead, offline, | ||
| 139 | + # against the cache that derivation did fetch. ClojureDart reaches for the | ||
| 140 | + # network only when bin/analyzer.dart is missing, and after this it is not. | ||
| 141 | + for helper in .clojuredart/cache/*/cljd_helper; do | ||
| 142 | + [ -d "$helper" ] || continue | ||
| 143 | + [ -e "$helper/.dart_tool/package_config.json" ] && continue | ||
| 144 | + ( cd "$helper" && $flutter flutter pub get --offline ) | ||
| 145 | + done | ||
| 146 | + | ||
| 147 | + $flutter clojure -Sdeps "{:mvn/local-repo \"$m2\"}" -M:cljd compile | ||
| 108 | 148 | ||
| 109 | # Rewritten every run: it carries absolute store paths, and the flutter | 149 | # Rewritten every run: it carries absolute store paths, and the flutter |
| 110 | # one moves whenever nixpkgs does. | 150 | # one moves whenever nixpkgs does. |
| @@ -311,7 +351,44 @@ flutter-desktop action="build": | |||
| 311 | fi | 351 | fi |
| 312 | cd flutter | 352 | cd flutter |
| 313 | 353 | ||
| 314 | - clojure -M:cljd compile | 354 | + # The same three caches `just apk` seeds, in the same place and out of the |
| 355 | + # same flake output — one compiler, one set of dependencies, and no reason | ||
| 356 | + # for the two frontends to keep a copy each. `.home/` is `apk`'s directory | ||
| 357 | + # by name and this is the only thing put there from here, which is the | ||
| 358 | + # point: whichever recipe runs first pays for the copy and the other finds | ||
| 359 | + # it warm. | ||
| 360 | + # | ||
| 361 | + # No `nix build` here, unlike `apk`: this recipe is already inside the | ||
| 362 | + # shell that names FRQ_CLJD_DEPS by the time it gets this far, and `apk` | ||
| 363 | + # needs the path before it enters anything. | ||
| 364 | + # | ||
| 365 | + # m2 and gitlibs are set here for the reason they are set there — the JVM | ||
| 366 | + # reads user.home out of /etc/passwd, so neither follows HOME and left | ||
| 367 | + # alone they are the real ~/.m2 and ~/.gitlibs. | ||
| 368 | + export PUB_CACHE="$PWD/.home/.pub-cache" | ||
| 369 | + export GITLIBS="$PWD/.home/gitlibs" | ||
| 370 | + m2="$PWD/.home/m2" | ||
| 371 | + | ||
| 372 | + seed() { | ||
| 373 | + [ -e "$2" ] && return 0 | ||
| 374 | + mkdir -p "$(dirname "$2")" | ||
| 375 | + cp -r "$FRQ_CLJD_DEPS/$1" "$2" | ||
| 376 | + chmod -R u+w "$2" | ||
| 377 | + } | ||
| 378 | + seed m2 "$m2" | ||
| 379 | + seed gitlibs "$GITLIBS" | ||
| 380 | + seed pub-cache "$PUB_CACHE" | ||
| 381 | + seed clojuredart/cache "$PWD/.clojuredart/cache" | ||
| 382 | + | ||
| 383 | + # Resolved here rather than in cljd-deps, which was not allowed to name | ||
| 384 | + # the store — see the same loop in `apk`. | ||
| 385 | + for helper in .clojuredart/cache/*/cljd_helper; do | ||
| 386 | + [ -d "$helper" ] || continue | ||
| 387 | + [ -e "$helper/.dart_tool/package_config.json" ] && continue | ||
| 388 | + ( cd "$helper" && flutter pub get --offline ) | ||
| 389 | + done | ||
| 390 | + | ||
| 391 | + clojure -Sdeps "{:mvn/local-repo \"$m2\"}" -M:cljd compile | ||
| 315 | flutter build linux --debug | 392 | flutter build linux --debug |
| 316 | 393 | ||
| 317 | # x64/arm64 is Flutter's own name for the host arch, not uname's. | 394 | # x64/arm64 is Flutter's own name for the host arch, not uname's. |