gradle: add setup hook

chayleaf c12b2a0b 62d13413

+1080 -4
+189
doc/languages-frameworks/gradle.section.md
···
··· 1 + # Gradle {#gradle} 2 + 3 + Gradle is a popular build tool for Java/Kotlin. Gradle itself doesn't 4 + currently provide tools to make dependency resolution reproducible, so 5 + nixpkgs has a proxy designed for intercepting Gradle web requests to 6 + record dependencies so they can be restored in a reproducible fashion. 7 + 8 + ## Building a Gradle package {#building-a-gradle-package} 9 + 10 + Here's how a typical derivation will look like: 11 + 12 + ```nix 13 + stdenv.mkDerivation (finalAttrs: { 14 + pname = "pdftk"; 15 + version = "3.3.3"; 16 + 17 + src = fetchFromGitLab { 18 + owner = "pdftk-java"; 19 + repo = "pdftk"; 20 + rev = "v${finalAttrs.version}"; 21 + hash = "sha256-ciKotTHSEcITfQYKFZ6sY2LZnXGChBJy0+eno8B3YHY="; 22 + }; 23 + 24 + nativeBuildInputs = [ gradle ]; 25 + 26 + # if the package has dependencies, mitmCache must be set 27 + mitmCache = gradle.fetchDeps { 28 + inherit (finalAttrs) pname; 29 + data = ./deps.json; 30 + }; 31 + 32 + # this is required for using mitm-cache on Darwin 33 + __darwinAllowLocalNetworking = true; 34 + 35 + gradleFlags = [ "-Dfile.encoding=utf-8" ]; 36 + 37 + # defaults to "assemble" 38 + gradleBuildTask = "shadowJar"; 39 + 40 + # will run the gradleCheckTask (defaults to "test") 41 + doCheck = true; 42 + 43 + installPhase = '' 44 + mkdir -p $out/{bin,share/pdftk} 45 + cp build/libs/pdftk-all.jar $out/share/pdftk 46 + 47 + makeWrapper ${jre}/bin/java $out/bin/pdftk \ 48 + --add-flags "-jar $out/share/pdftk/pdftk-all.jar" 49 + 50 + cp ${finalAttrs.src}/pdftk.1 $out/share/man/man1 51 + ''; 52 + 53 + meta.sourceProvenance = with lib.sourceTypes; [ 54 + fromSource 55 + binaryBytecode # mitm cache 56 + ]; 57 + }) 58 + ``` 59 + 60 + To update (or initialize) dependencies, run the update script via 61 + something like `$(nix-build -A <pname>.mitmCache.updateScript)` 62 + (`nix-build` builds the `updateScript`, `$(...)` runs the script at the 63 + path printed by `nix-build`). 64 + 65 + If your package can't be evaluated using a simple `pkgs.<pname>` 66 + expression (for example, if your package isn't located in nixpkgs, or if 67 + you want to override some of its attributes), you will usually have to 68 + pass `pkg` instead of `pname` to `gradle.fetchDeps`. There are two ways 69 + of doing it. 70 + 71 + The first is to add the derivation arguments required for getting the 72 + package. Using the pdftk example above: 73 + 74 + ```nix 75 + { lib 76 + , stdenv 77 + # ... 78 + , pdftk 79 + }: 80 + 81 + stdenv.mkDerivation (finalAttrs: { 82 + # ... 83 + mitmCache = gradle.fetchDeps { 84 + pkg = pdftk; 85 + data = ./deps.json; 86 + }; 87 + }) 88 + ``` 89 + 90 + This allows you to `override` any arguments of the `pkg` used for 91 + the update script (for example, `pkg = pdftk.override { enableSomeFlag = 92 + true };`), so this is the preferred way. 93 + 94 + The second is to create a `let` binding for the package, like this: 95 + 96 + ```nix 97 + let self = stdenv.mkDerivation { 98 + # ... 99 + mitmCache = gradle.fetchDeps { 100 + pkg = self; 101 + data = ./deps.json; 102 + }; 103 + }; in self 104 + ``` 105 + 106 + This is useful if you can't easily pass the derivation as its own 107 + argument, or if your `mkDerivation` call is responsible for building 108 + multiple packages. 109 + 110 + In the former case, the update script will stay the same even if the 111 + derivation is called with different arguments. In the latter case, the 112 + update script will change depending on the derivation arguments. It's up 113 + to you to decide which one would work best for your derivation. 114 + 115 + ## Update Script {#gradle-update-script} 116 + 117 + The update script does the following: 118 + 119 + - Build the derivation's source via `pkgs.srcOnly` 120 + - Enter a `nix-shell` for the derivation in a `bwrap` sandbox (the 121 + sandbox is only used on Linux) 122 + - Set the `IN_GRADLE_UPDATE_DEPS` environment variable to `1` 123 + - Run the derivation's `unpackPhase`, `patchPhase`, `configurePhase` 124 + - Run the derivation's `gradleUpdateScript` (the Gradle setup hook sets 125 + a default value for it, which runs `preBuild`, `preGradleUpdate` 126 + hooks, fetches the dependencies using `gradleUpdateTask`, and finally 127 + runs the `postGradleUpdate` hook) 128 + - Finally, store all of the fetched files' hashes in the lockfile. They 129 + may be `.jar`/`.pom` files from Maven repositories, or they may be 130 + files otherwise used for building the package. 131 + 132 + `fetchDeps` takes the following arguments: 133 + 134 + - `attrPath` - the path to the package in nixpkgs (for example, 135 + `"javaPackages.openjfx22"`). Used for update script metadata. 136 + - `pname` - an alias for `attrPath` for convenience. This is what you 137 + will generally use instead of `pkg` or `attrPath`. 138 + - `pkg` - the package to be used for fetching the dependencies. Defaults 139 + to `getAttrFromPath (splitString "." attrPath) pkgs`. 140 + - `bwrapFlags` - allows you to override bwrap flags (only relevant for 141 + downstream, non-nixpkgs projects) 142 + - `data` - path to the dependencies lockfile (can be relative to the 143 + package, can be absolute). In nixpkgs, it's discouraged to have the 144 + lockfiles be named anything other `deps.json`, consider creating 145 + subdirectories if your package requires multiple `deps.json` files. 146 + 147 + ## Environment {#gradle-environment} 148 + 149 + The Gradle setup hook accepts the following environment variables: 150 + 151 + - `mitmCache` - the MITM proxy cache imported using `gradle.fetchDeps` 152 + - `gradleFlags` - command-line flags to be used for every Gradle 153 + invocation (this simply registers a function that uses the necessary 154 + flags). 155 + - You can't use `gradleFlags` for flags that contain spaces, in that 156 + case you must add `gradleFlagsArray+=("-flag with spaces")` to the 157 + derivation's bash code instead. 158 + - If you want to build the package using a specific Java version, you 159 + can pass `"-Dorg.gradle.java.home=${jdk}"` as one of the flags. 160 + - `gradleBuildTask` - the Gradle task (or tasks) to be used for building 161 + the package. Defaults to `assemble`. 162 + - `gradleCheckTask` - the Gradle task (or tasks) to be used for checking 163 + the package if `doCheck` is set to `true`. Defaults to `test`. 164 + - `gradleUpdateTask` - the Gradle task (or tasks) to be used for 165 + fetching all of the package's dependencies in 166 + `mitmCache.updateScript`. Defaults to `nixDownloadDeps`. 167 + - `gradleUpdateScript` - the code to run for fetching all of the 168 + package's dependencies in `mitmCache.updateScript`. Defaults to 169 + running the `preBuild` and `preGradleUpdate` hooks, running the 170 + `gradleUpdateTask`, and finally running the `postGradleUpdate` hook. 171 + - `gradleInitScript` - path to the `--init-script` to pass to Gradle. By 172 + default, a simple init script that enables reproducible archive 173 + creation is used. 174 + - Note that reproducible archives might break some builds. One example 175 + of an error caused by it is `Could not create task ':jar'. Replacing 176 + an existing task that may have already been used by other plugins is 177 + not supported`. If you get such an error, the easiest "fix" is 178 + disabling reproducible archives altogether by setting 179 + `gradleInitScript` to something like `writeText 180 + "empty-init-script.gradle" ""` 181 + - `enableParallelBuilding` / `enableParallelChecking` / 182 + `enableParallelUpdating` - pass `--parallel` to Gradle in the 183 + build/check phase or in the update script. Defaults to true. If the 184 + build fails for mysterious reasons, consider setting this to false. 185 + - `dontUseGradleConfigure` / `dontUseGradleBuild` / `dontUseGradleCheck` 186 + \- force disable the Gradle setup hook for certain phases. 187 + - Note that if you disable the configure hook, you may face issues 188 + such as `Failed to load native library 'libnative-platform.so'`, 189 + because the configure hook is responsible for initializing Gradle.
+245
pkgs/development/tools/build-managers/gradle/README.md
···
··· 1 + # Gradle Setup Hook 2 + 3 + ## Introduction 4 + 5 + Gradle build scripts are written in a DSL, computing the list of Gradle 6 + dependencies is a turing-complete task, not just in theory but in 7 + practice. Fetching all of the dependencies often requires building some 8 + native code, running some commands to check the host platform, or just 9 + fetching some files using either JVM code or commands like `curl` or 10 + `wget`. 11 + 12 + This practice is widespread and isn't considered a bad practice in the 13 + Java world, so all we can do is run Gradle to check what dependencies 14 + end up being fetched, and allow derivation authors to apply workarounds 15 + so they can run the code necessary for fetching the dependencies our 16 + script doesn't fetch. 17 + 18 + "Run Gradle to check what dependencies end up being fetched" isn't a 19 + straightforward task. For example, Gradle usually uses Maven 20 + repositories, which have features such as "snapshots", a way to always 21 + use the latest version of a dependency as opposed to a fixed version. 22 + Obviously, this is horrible for reproducibility. Additionally, Gradle 23 + doesn't offer a way to export the list of dependency URLs and hashes (it 24 + does in a way, but it's far from being complete, and as such is useless 25 + for nixpkgs). Even if did, it would be annoying to use considering 26 + fetching non-Gradle dependendencies in Gradle scripts is commonplace. 27 + 28 + That's why the setup hook uses mitm-cache, a program designed for 29 + intercepting all HTTP requests, recording all the files that were 30 + accessed, creating a Nix derivation with all of them, and then allowing 31 + the Gradle derivation to access these files. 32 + 33 + ## Maven Repositories 34 + 35 + (Reference: [Repository 36 + Layout](https://cwiki.apache.org/confluence/display/MAVENOLD/Repository+Layout+-+Final)) 37 + 38 + Most of Gradle dependencies are fetched from Maven repositories. For 39 + each dependency, Gradle finds the first repo where it can successfully 40 + fetch that dependency, and uses that repo for it. Different repos might 41 + actually return different files for the same artifact because of e.g. 42 + pom normalization. Different repos may be used for the same artifact 43 + even across a single package (for example, if two build scripts define 44 + repositories in a different order). 45 + 46 + The artifact metadata is specified in a .pom file, and the artifacts 47 + themselves are typically .jar files. The URL format is as follows: 48 + 49 + `<repo>/<group-id>/<artifact-id>/<base-version>/<artifact-id>-<version>[-<classifier>].<ext>` 50 + 51 + For example: 52 + 53 + - `https://repo.maven.apache.org/maven2/org/slf4j/slf4j-api/2.0.9/slf4j-api-2.0.9.pom` 54 + - `https://oss.sonatype.org/content/groups/public/com/tobiasdiez/easybind/2.2.1-SNAPSHOT/easybind-2.2.1-20230117.075740-16.pom` 55 + 56 + Where: 57 + 58 + - `<repo>` is the repo base (`https://repo.maven.apache.org/maven2`) 59 + - `<group-id>` is the group ID with dots replaced with slashes 60 + (`org.slf4j` -> `org/slf4j`) 61 + - `<artifact-id>` is the artifact ID (`slf4j-api`) 62 + - `<base-version>` is the artifact version (`2.0.9` for normal 63 + artifacts, `2.2.1-SNAPSHOT` for snapshots) 64 + - `<version>` is the artifact version - can be either `<base-version>` 65 + or `<version-base>-<timestamp>-<build-num>` (`2.0.9` for normal 66 + artifacts, and either `2.2.1-SNAPSHOT` or `2.2.1-20230117.075740-16` 67 + for snapshots) 68 + - `<version-base>` - `<base-version>` without the `-SNAPSHOT` suffix 69 + - `<timestamp>` - artifact build timestamp in the `YYYYMMDD.HHMMSS` 70 + format (UTC) 71 + - `<build-num>` - a counter that's incremented by 1 for each new 72 + snapshot build 73 + - `<classifier>` is an optional classifier for allowing a single .pom to 74 + refer to multiple .jar files. .pom files don't have classifiers, as 75 + they describe metadata. 76 + - `<ext>` is the extension. .pom 77 + 78 + Note that the artifact ID can contain `-`, so you can't extract the 79 + artifact ID and version from just the file name. 80 + 81 + Additionally, the files in the repository may have associated signature 82 + files, formed by appending `.asc` to the filename, and hashsum files, 83 + formed by appending `.md5` or `.sha1` to the filename. The signatures 84 + are harmless, but the `.md5`/`.sha1` files are rejected. 85 + 86 + The reasoning is as follows - consider two files `a.jar` and `b.jar`, 87 + that have the same hash. Gradle will fetch `a.jar.sha1`, find out that 88 + it hasn't yet downloaded a file with this hash, and then fetch `a.jar`, 89 + and finally download `b.jar.sha1`, locate it in its cache, and then 90 + *not* download `b.jar`. This means `b.jar` won't be stored in the MITM 91 + cache. Then, consider that on a later invocation, the fetching order 92 + changed, whether it was because of a running on different system, 93 + changed behavior after a Gradle update, or any other source of 94 + nondeterminism - `b.jar` is fetched before `a.jar`. Gradle will first 95 + fetch `b.jar.sha1`, not find it in its cache, attempt to fetch `b.jar`, 96 + and fail, as the cache doesn't have that file. 97 + 98 + For the same reason, the proxy strips all checksum/etag headers. An 99 + alternative would be to make the proxy remember previous checksums and 100 + etags, but that would complicate the implementation - however, such a 101 + feature can be implemented if necessary. Note that checksum/etag header 102 + stripping is hardcoded, but `.md5/.sha1` file rejection is configured 103 + via CLI arguments. 104 + 105 + **Caveat**: Gradle .module files also contain file hashes, in md5, sha1, 106 + sha256, sha512 formats. It posed no problem as of yet, but it might in 107 + the future. If it does pose problems, the deps derivation code can be 108 + extended to find all checksums in .module files and copy existing files 109 + there if their hash matches. 110 + 111 + ## Snapshots 112 + 113 + Snapshots are a way to publish the very latest, unstable version of a 114 + dependency that constantly changes. Any project that depends on a 115 + snapshot will depend on this rolling version, rather than a fixed 116 + version. It's easy to understand why this is a bad idea for reproducible 117 + builds. Still, they can be dealt with by the logic in `gradle.fetchDeps` 118 + and `gradle.updateDeps`. 119 + 120 + First, as you can see above, while normal artifacts have the same 121 + `base-version` and `version`, for snapshots it usually (but not 122 + necessarily) differs. 123 + 124 + Second, for figuring out where to download the snapshot, Gradle consults 125 + `maven-metadata.xml`. With that in mind... 126 + 127 + ## Maven Metadata 128 + 129 + (Reference: [Maven 130 + Metadata](https://maven.apache.org/repositories/metadata.html), 131 + [Metadata](https://maven.apache.org/ref/3.9.8/maven-repository-metadata/repository-metadata.html) 132 + 133 + Maven metadata files are called `maven-metadata.xml`. 134 + 135 + There are three levels of metadata: "G level", "A level", "V level", 136 + representing group, artifact, or version metadata. 137 + 138 + G level metadata is currently unsupported. It's only used for Maven 139 + plugins, which Gradle presumably doesn't use. 140 + 141 + A level metadata is used for getting the version list for an artifact. 142 + It's an xml with the following items: 143 + 144 + - `<groupId>` - group ID 145 + - `<artifactId>` - artifact ID 146 + - `<versioning>` 147 + - `<latest>` - the very latest base version (e.g. `2.2.1-SNAPSHOT`) 148 + - `<release>` - the latest non-snapshot version 149 + - `<versions>` - the version list, each in a `<version>` tag 150 + - `<lastUpdated>` - the metadata update timestamp (UTC, 151 + `YYYYMMDDHHMMSS`) 152 + 153 + V level metadata is used for listing the snapshot versions. It has the 154 + following items: 155 + 156 + - `<groupId>` - group ID 157 + - `<artifactId>` - artifact ID 158 + - `<versioning>` 159 + - `<lastUpdated>` - the metadata update timestamp (UTC, 160 + `YYYYMMDDHHMMSS`) 161 + - `<snapshot>` - info about the latest snapshot version 162 + - `<timestamp>` - build timestamp (UTC, `YYYYMMDD.HHMMSS`) 163 + - `<buildNumber>` - build number 164 + - `<snapshotVersions>` - the list of all available snapshot file info, 165 + each info is enclosed in a `<snapshotVersion>` 166 + - `<classifier>` - classifier (optional) 167 + - `<extension>` - file extension 168 + - `<value>` - snapshot version (as opposed to base version) 169 + - `<updated>` - snapshot build timestamp (UTC, `YYYYMMDDHHMMSS`) 170 + 171 + ## Lockfile Format 172 + 173 + The mitm-cache lockfile format is described in the [mitm-cache 174 + README](https://github.com/chayleaf/mitm-cache#readme). 175 + 176 + The nixpkgs Gradle lockfile format is more complicated: 177 + 178 + ```json 179 + { 180 + "!comment": "This is a nixpkgs Gradle dependency lockfile. For more details, refer to the Gradle section in the nixpkgs manual.", 181 + "!version": 1, 182 + "https://oss.sonatype.org/content/repositories/snapshots/com/badlogicgames/gdx-controllers": { 183 + "gdx-controllers#gdx-controllers-core/2.2.4-20231021.200112-6/SNAPSHOT": { 184 + 185 + "jar": "sha256-Gdz2J1IvDJFktUD2XeGNS0SIrOyym19X/+dCbbbe3/U=", 186 + "pom": "sha256-90QW/Mtz1jbDUhKjdJ88ekhulZR2a7eCaEJoswmeny4=" 187 + }, 188 + "gdx-controllers-core/2.2.4-SNAPSHOT/maven-metadata": { 189 + "xml": { 190 + "groupId": "com.badlogicgames.gdx-controllers" 191 + } 192 + } 193 + }, 194 + "https://repo.maven.apache.org/maven2": { 195 + "com/badlogicgames/gdx#gdx-backend-lwjgl3/1.12.1": { 196 + "jar": "sha256-B3OwjHfBoHcJPFlyy4u2WJuRe4ZF/+tKh7gKsDg41o0=", 197 + "module": "sha256-9O7d2ip5+E6OiwN47WWxC8XqSX/mT+b0iDioCRTTyqc=", 198 + "pom": "sha256-IRSihaCUPC2d0QzB0MVDoOWM1DXjcisTYtnaaxR9SRo=" 199 + } 200 + } 201 + } 202 + ``` 203 + 204 + `!comment` is a human-readable description explaining what the file is, 205 + `!version` is the lockfile version (note that while it shares the name 206 + with mitm-cache's `!version`, they don't actually have to be in sync and 207 + can be bumped separately). 208 + 209 + The other keys are parts of a URL. Each URL is split into three parts. 210 + They are joined like this: `<part1>/<part2>.<part3>`. 211 + 212 + Some URLs may have a `#` in them. In that case, the part after `#` is 213 + parsed as `#<artifact-id>/<version>[/SNAPSHOT][/<classifier>].<ext>` and 214 + expanded into 215 + `<artifact-id>/<base-version>/<artifact-id>-<version>[-<classifier>].<ext>`. 216 + 217 + Each URL has a value associated with it. The value may be: 218 + 219 + - an SRI hash (string) 220 + - for `maven-metadata.xml` - an attrset containing the parts of the 221 + metadata that can't be generated in Nix code (e.g. `groupId`, which is 222 + challenging to parse from a URL because it's not always possible to 223 + discern where the repo base ends and the group ID begins). 224 + 225 + `compress-deps-json.py` converts the JSON from mitm-cache format into 226 + nixpkgs Gradle lockfile format. `fetch.nix` does the opposite. 227 + 228 + ## Security Considerations 229 + 230 + Lockfiles won't be human-reviewed. They must be tampering-resistant. 231 + That's why it's imperative that nobody can inject their own contents 232 + into the lockfiles. 233 + 234 + This is achieved in a very simple way - the `deps.json` only contains 235 + the following: 236 + 237 + - `maven-metadata.xml` URLs and small pieces of the contained metadata 238 + (most of it will be generated in Nix, i.e. the area of injection is 239 + minimal, and the parts that aren't generated in Nix are validated). 240 + - artifact/other file URLs and associated hashes (Nix will complain if 241 + the hash doesn't match, and Gradle won't even access the URL if it 242 + doesn't match) 243 + 244 + Please be mindful of the above when working on Gradle support for 245 + nixpkgs.
+163
pkgs/development/tools/build-managers/gradle/compress-deps-json.py
···
··· 1 + import json 2 + import sys 3 + 4 + from typing import Dict, Set 5 + 6 + # this compresses MITM URL lists with Gradle-specific optimizations 7 + # specifically, it splits each url into up to 3 parts - they will be 8 + # concatenated like part1/part2.part3 or part1.part2 9 + # part3 is simply always the file extension, but part1 and part2 is 10 + # optimized using special heuristics 11 + # additionally, if part2 ends with /a/b/{a}-{b}, the all occurences of 12 + # /{a}/{b}/ are replaced with # 13 + # finally, anything that ends with = is considered SHA256, anything that 14 + # starts with http is considered a redirect URL, anything else is 15 + # considered text 16 + 17 + with open(sys.argv[1], "rt") as f: 18 + data: dict = json.load(f) 19 + 20 + new_data: Dict[str, Dict[str, Dict[str, dict]]] = {} 21 + 22 + for url, info in data.items(): 23 + if url == "!version": 24 + continue 25 + ext, base = map(lambda x: x[::-1], url[::-1].split(".", 1)) 26 + if base.endswith(".tar"): 27 + base = base[:-4] 28 + ext = "tar." + ext 29 + # special logic for Maven repos 30 + if ext in ["jar", "pom", "module"]: 31 + comps = base.split("/") 32 + if "-" in comps[-1]: 33 + # convert base/name/ver/name-ver into base#name/ver 34 + 35 + filename = comps[-1] 36 + name = comps[-3] 37 + basever = comps[-2] 38 + ver = basever 39 + is_snapshot = ver.endswith("-SNAPSHOT") 40 + if is_snapshot: 41 + ver = ver.removesuffix("-SNAPSHOT") 42 + if filename.startswith(f"{name}-{ver}"): 43 + if is_snapshot: 44 + if filename.startswith(f"{name}-{ver}-SNAPSHOT"): 45 + ver += "-SNAPSHOT" 46 + else: 47 + ver += "-".join( 48 + filename.removeprefix(f"{name}-{ver}").split("-")[:3] 49 + ) 50 + comp_end = comps[-1].removeprefix(f"{name}-{ver}") 51 + else: 52 + ver, name, comp_end = None, None, None 53 + if name and ver and (not comp_end or comp_end.startswith("-")): 54 + base = "/".join(comps[:-1]) + "/" 55 + base = base.replace(f"/{name}/{basever}/", "#") 56 + base += f"{name}/{ver}" 57 + if is_snapshot: 58 + base += "/SNAPSHOT" 59 + if comp_end: 60 + base += "/" + comp_end[1:] 61 + scheme, rest = base.split("/", 1) 62 + if scheme not in new_data.keys(): 63 + new_data[scheme] = {} 64 + if rest not in new_data[scheme].keys(): 65 + new_data[scheme][rest] = {} 66 + if "hash" in info.keys(): 67 + new_data[scheme][rest][ext] = info["hash"] 68 + elif "text" in info.keys() and ext == "xml": 69 + # nix code in fetch-deps.nix will autogenerate metadata xml files groupId 70 + # is part of the URL, but it can be tricky to parse as we don't know the 71 + # exact repo base, so take it from the xml and pass it to nix 72 + xml = "".join(info["text"].split()) 73 + new_data[scheme][rest][ext] = { 74 + "groupId": xml.split("<groupId>")[1].split("</groupId>")[0], 75 + } 76 + if "<release>" in xml: 77 + new_data[scheme][rest][ext]["release"] = xml.split("<release>")[1].split( 78 + "</release>" 79 + )[0] 80 + if "<latest>" in xml: 81 + latest = xml.split("<latest>")[1].split("</latest>")[0] 82 + if latest != new_data[scheme][rest][ext].get("release"): 83 + new_data[scheme][rest][ext]["latest"] = latest 84 + if "<lastUpdated>" in xml: 85 + new_data[scheme][rest][ext]["lastUpdated"] = xml.split("<lastUpdated>")[ 86 + 1 87 + ].split("</lastUpdated>")[0] 88 + else: 89 + raise Exception("Unsupported key: " + repr(info)) 90 + 91 + # At this point, we have a map by part1 (initially the scheme), part2 (initially a 92 + # slash-separated string without the scheme and with potential # substitution as 93 + # seen above), extension. 94 + # Now, push some segments from "part2" into "part1" like this: 95 + # https # part1 96 + # domain1/b # part2 97 + # domain1/c 98 + # domain2/a 99 + # domain2/c 100 + # -> 101 + # https/domain1 # part1 102 + # b # part2 103 + # c 104 + # https/domain2 # part1 105 + # a # part2 106 + # c 107 + # This helps reduce the lockfile size because a Gradle project will usually use lots 108 + # of files from a single Maven repo 109 + 110 + data = new_data 111 + changed = True 112 + while changed: 113 + changed = False 114 + new_data = {} 115 + for part1, info1 in data.items(): 116 + starts: Set[str] = set() 117 + # by how many bytes the file size will be increased (roughly) 118 + lose = 0 119 + # by how many bytes the file size will be reduced (roughly) 120 + win = 0 121 + # how many different initial part2 segments there are 122 + count = 0 123 + for part2, info2 in info1.items(): 124 + if "/" not in part2: 125 + # can't push a segment from part2 into part1 126 + count = 0 127 + break 128 + st = part2.split("/", 1)[0] 129 + if st not in starts: 130 + lose += len(st) + 1 131 + count += 1 132 + starts.add(st) 133 + win += len(st) + 1 134 + if count == 0: 135 + new_data[part1] = info1 136 + continue 137 + # only allow pushing part2 segments into path1 if *either*: 138 + # - the domain isn't yet part of part1 139 + # - the initial part2 segment is always the same 140 + if count != 1 and "." in part1: 141 + new_data[part1] = info1 142 + continue 143 + # some heuristics that may or may not work well (originally this was 144 + # used when the above if wasn't here, but perhaps it's useless now) 145 + lose += (count - 1) * max(0, len(part1) - 4) 146 + if win > lose or ("." not in part1 and win >= lose): 147 + changed = True 148 + for part2, info2 in info1.items(): 149 + st, part3 = part2.split("/", 1) 150 + new_part1 = part1 + "/" + st 151 + if new_part1 not in new_data.keys(): 152 + new_data[new_part1] = {} 153 + new_data[new_part1][part3] = info2 154 + else: 155 + new_data[part1] = info1 156 + data = new_data 157 + 158 + new_data["!comment"] = "This is a nixpkgs Gradle dependency lockfile. For more details, refer to the Gradle section in the nixpkgs manual." # type: ignore 159 + new_data["!version"] = 1 # type: ignore 160 + 161 + with open(sys.argv[2], "wt") as f: 162 + json.dump(new_data, f, sort_keys=True, indent=1) 163 + f.write("\n")
+40
pkgs/development/tools/build-managers/gradle/default.nix
··· 130 ''; 131 }; 132 }; 133 134 meta = with lib; { 135 inherit platforms; ··· 179 hash = "sha256-PiQCKFON6fGHcqV06ZoLqVnoPW7zUQFDgazZYxeBOJo="; 180 defaultJava = jdk11; 181 }; 182 }
··· 130 ''; 131 }; 132 }; 133 + passthru.jdk = defaultJava; 134 135 meta = with lib; { 136 inherit platforms; ··· 180 hash = "sha256-PiQCKFON6fGHcqV06ZoLqVnoPW7zUQFDgazZYxeBOJo="; 181 defaultJava = jdk11; 182 }; 183 + 184 + wrapGradle = { 185 + lib, callPackage, mitm-cache, substituteAll, symlinkJoin, concatTextFile, makeSetupHook 186 + }: 187 + gradle-unwrapped: 188 + lib.makeOverridable (args: 189 + let 190 + gradle = gradle-unwrapped.override args; 191 + in symlinkJoin { 192 + name = "gradle-${gradle.version}"; 193 + 194 + paths = [ 195 + (makeSetupHook { name = "gradle-setup-hook"; } (concatTextFile { 196 + name = "setup-hook.sh"; 197 + files = [ 198 + (mitm-cache.setupHook) 199 + (substituteAll { 200 + src = ./setup-hook.sh; 201 + # jdk used for keytool 202 + inherit (gradle) jdk; 203 + init_script = ./init-build.gradle; 204 + }) 205 + ]; 206 + })) 207 + gradle 208 + mitm-cache 209 + ]; 210 + 211 + passthru = { 212 + fetchDeps = callPackage ./fetch-deps.nix { inherit mitm-cache; }; 213 + inherit (gradle) jdk; 214 + }; 215 + 216 + meta = gradle.meta // { 217 + # prefer normal gradle/mitm-cache over this wrapper, this wrapper only provides the setup hook 218 + # and passthru 219 + priority = (gradle.meta.priority or 0) + 1; 220 + }; 221 + }) { }; 222 }
+222
pkgs/development/tools/build-managers/gradle/fetch-deps.nix
···
··· 1 + { mitm-cache 2 + , lib 3 + , pkgs 4 + , stdenv 5 + , callPackage 6 + }: 7 + 8 + let 9 + getPkg = attrPath: 10 + lib.getAttrFromPath 11 + (lib.splitString "." (toString attrPath)) 12 + pkgs; 13 + in 14 + # the derivation to fetch/update deps for 15 + { pkg ? getPkg attrPath 16 + , pname ? null 17 + , attrPath ? pname 18 + # bwrap flags for the update script (this will be put in bash as-is) 19 + # this is relevant for downstream users 20 + , bwrapFlags ? "--ro-bind \"$PWD\" \"$PWD\"" 21 + # deps path (relative to the package directory, or absolute) 22 + , data 23 + # redirect stdout to stderr to allow the update script to be used with update script combinators 24 + , silent ? true 25 + , useBwrap ? stdenv.isLinux 26 + } @ attrs: 27 + 28 + let 29 + data' = builtins.removeAttrs 30 + (if builtins.isPath data then lib.importJSON data 31 + else if builtins.isString data then lib.importJSON "${dirOf pkg.meta.position}/${data}" 32 + else data) 33 + [ "!comment" "!version" ]; 34 + 35 + parseArtifactUrl = url: let 36 + extension = lib.last (lib.splitString "." url); 37 + splitUrl = lib.splitString "/" url; 38 + artifactId = builtins.elemAt splitUrl (builtins.length splitUrl - 3); 39 + baseVer = builtins.elemAt splitUrl (builtins.length splitUrl - 2); 40 + filename = builtins.elemAt splitUrl (builtins.length splitUrl - 1); 41 + filenameNoExt = lib.removeSuffix ".${extension}" filename; 42 + verCls = lib.removePrefix "${artifactId}-" filenameNoExt; 43 + in rec { 44 + inherit artifactId baseVer filename extension; 45 + isSnapshot = lib.hasSuffix "-SNAPSHOT" baseVer; 46 + version = 47 + if isSnapshot && !lib.hasPrefix "SNAPSHOT" verCls 48 + then builtins.concatStringsSep "-" (lib.take 3 (lib.splitString "-" verCls)) 49 + else baseVer; 50 + classifier = 51 + if verCls == version then null 52 + else lib.removePrefix "${version}-" verCls; 53 + # for snapshots 54 + timestamp = builtins.elemAt (lib.splitString "-" version) 1; 55 + buildNum = builtins.elemAt (lib.splitString "-" version) 2; 56 + }; 57 + 58 + parseMetadataUrl = url: let 59 + xmlBase = lib.removeSuffix "/maven-metadata.xml" url; 60 + vMeta = lib.hasSuffix "-SNAPSHOT" xmlBase; 61 + splitBase = lib.splitString "/" xmlBase; 62 + in 63 + if vMeta then { 64 + vMeta = true; 65 + baseVer = builtins.elemAt splitBase (builtins.length splitBase - 1); 66 + artifactId = builtins.elemAt splitBase (builtins.length splitBase - 2); 67 + } else { 68 + vMeta = false; 69 + baseVer = null; 70 + artifactId = builtins.elemAt splitBase (builtins.length splitBase - 1); 71 + }; 72 + 73 + extractHashArtifact = afterHash: let 74 + nameVer = builtins.match "([^/]*)/([^/]*)(/SNAPSHOT)?(/.*)?" afterHash; 75 + artifactId = builtins.elemAt nameVer 0; 76 + version = builtins.elemAt nameVer 1; 77 + isSnapshot = builtins.elemAt nameVer 2 != null; 78 + cls = builtins.elemAt nameVer 3; 79 + in rec { 80 + inherit artifactId version isSnapshot; 81 + baseVer = 82 + if !isSnapshot then version 83 + else builtins.head (builtins.match "(.*)-([^-]*)-([^-]*)" version) + "-SNAPSHOT"; 84 + classifier = 85 + if cls == null then null 86 + else lib.removePrefix "/" cls; 87 + clsSuf = 88 + if classifier == null then "" 89 + else "-${classifier}"; 90 + }; 91 + 92 + # replace base#name/ver with base/name/ver/name-ver 93 + decompressNameVer = prefix: let 94 + splitHash = lib.splitString "#" (builtins.concatStringsSep "/" prefix); 95 + inherit (extractHashArtifact (lib.last splitHash)) artifactId baseVer version clsSuf; 96 + in 97 + if builtins.length splitHash == 1 then builtins.head splitHash 98 + else builtins.concatStringsSep "/${artifactId}/${baseVer}/" (lib.init splitHash ++ [ "${artifactId}-${version}${clsSuf}" ]); 99 + 100 + # `visit` all elements in attrs and merge into a set 101 + # attrs will be passed as parent1, parent1 will be passed as parent2 102 + visitAttrs = parent1: prefix: attrs: 103 + builtins.foldl' 104 + (a: b: a // b) 105 + {} 106 + (lib.mapAttrsToList (visit parent1 attrs prefix) attrs); 107 + 108 + # convert a compressed deps.json into an uncompressed json used for mitm-cache.fetch 109 + visit = parent2: parent1: prefix: k: v: 110 + # groupId being present means this is a metadata xml "leaf" and we shouldn't descend further 111 + if builtins.isAttrs v && !v?groupId 112 + then visitAttrs parent1 (prefix ++ [k]) v 113 + else let 114 + url = "${decompressNameVer prefix}.${k}"; 115 + in { 116 + ${url} = 117 + if builtins.isString v then { hash = v; } 118 + else { 119 + text = let 120 + xmlBase = lib.removeSuffix "/maven-metadata.xml" url; 121 + meta = parseMetadataUrl url // v; 122 + inherit (meta) groupId vMeta artifactId baseVer; 123 + 124 + fileList = builtins.filter (x: lib.hasPrefix xmlBase x && x != url) (builtins.attrNames finalData); 125 + jarPomList = map parseArtifactUrl fileList; 126 + sortedJarPomList = 127 + lib.sort 128 + (a: b: lib.splitVersion a.version < lib.splitVersion b.version) 129 + jarPomList; 130 + 131 + uniqueVersionFiles = 132 + builtins.map ({ i, x }: x) 133 + (builtins.filter ({ i, x }: i == 0 || (builtins.elemAt sortedJarPomList (i - 1)).version != x.version) 134 + (lib.imap0 (i: x: { inherit i x; }) sortedJarPomList)); 135 + uniqueVersions' = map (x: x.version) uniqueVersionFiles; 136 + releaseVersions = map (x: x.version) (builtins.filter (x: !x.isSnapshot) uniqueVersionFiles); 137 + latestVer = v.latest or v.release or (lib.last uniqueVersions'); 138 + releaseVer = v.release or (lib.last releaseVersions); 139 + 140 + # The very latest version isn't necessarily used by Gradle, so it may not be present in the MITM data. 141 + # In order to generate better metadata xml, if the latest version is known but wasn't fetched by Gradle, 142 + # add it anyway. 143 + uniqueVersions = 144 + uniqueVersions' 145 + ++ lib.optional (!builtins.elem releaseVer uniqueVersions') releaseVer 146 + ++ lib.optional (!builtins.elem latestVer uniqueVersions' && releaseVer != latestVer) latestVer; 147 + 148 + lastUpdated = v.lastUpdated or 149 + (if vMeta then builtins.replaceStrings ["."] [""] snapshotTs 150 + else "20240101123456"); 151 + 152 + # the following are only used for snapshots 153 + snapshotTsAndNum = lib.splitString "-" latestVer; 154 + snapshotTs = builtins.elemAt snapshotTsAndNum 1; 155 + snapshotNum = lib.last snapshotTsAndNum; 156 + 157 + indent = x: s: builtins.concatStringsSep "\n" (map (s: x + s) (lib.splitString "\n" s)); 158 + containsSpecialXmlChars = s: builtins.match ''.*[<>"'&].*'' s != null; 159 + in 160 + # make sure all user-provided data is safe 161 + assert lib.hasInfix "${builtins.replaceStrings ["."] ["/"] groupId}/${artifactId}" url; 162 + assert !containsSpecialXmlChars groupId; 163 + assert !containsSpecialXmlChars lastUpdated; 164 + if vMeta then '' 165 + <?xml version="1.0" encoding="UTF-8"?> 166 + <metadata modelVersion="1.1.0"> 167 + <groupId>${groupId}</groupId> 168 + <artifactId>${artifactId}</artifactId> 169 + <version>${baseVer}</version> 170 + <versioning> 171 + <snapshot> 172 + <timestamp>${snapshotTs}</timestamp> 173 + <buildNumber>${snapshotNum}</buildNumber> 174 + </snapshot> 175 + <lastUpdated>${lastUpdated}</lastUpdated> 176 + <snapshotVersions> 177 + ${builtins.concatStringsSep "\n" (map (x: indent " " '' 178 + <snapshotVersion>${ 179 + lib.optionalString 180 + (x.classifier != null) 181 + "\n <classifier>${x.classifier}</classifier>" 182 + } 183 + <extension>${x.extension}</extension> 184 + <value>${x.version}</value> 185 + <updated>${builtins.replaceStrings ["."] [""] x.timestamp}</updated> 186 + </snapshotVersion>'') sortedJarPomList)} 187 + </snapshotVersions> 188 + </versioning> 189 + </metadata> 190 + '' 191 + else 192 + assert !containsSpecialXmlChars latestVer; 193 + assert !containsSpecialXmlChars releaseVer; 194 + '' 195 + <?xml version="1.0" encoding="UTF-8"?> 196 + <metadata modelVersion="1.1.0"> 197 + <groupId>${groupId}</groupId> 198 + <artifactId>${artifactId}</artifactId> 199 + <versioning> 200 + <latest>${latestVer}</latest> 201 + <release>${releaseVer}</release> 202 + <versions> 203 + ${builtins.concatStringsSep "\n" (map (x: " <version>${x}</version>") uniqueVersions)} 204 + </versions> 205 + <lastUpdated>${lastUpdated}</lastUpdated> 206 + </versioning> 207 + </metadata> 208 + ''; 209 + }; 210 + }; 211 + 212 + finalData = visitAttrs {} [] data'; 213 + in 214 + mitm-cache.fetch { 215 + name = "${pkg.pname or pkg.name}-deps"; 216 + data = finalData // { "!version" = 1; }; 217 + passthru = lib.optionalAttrs (!builtins.isAttrs data) { 218 + updateScript = callPackage ./update-deps.nix { } { 219 + inherit pkg pname attrPath bwrapFlags data silent useBwrap; 220 + }; 221 + }; 222 + }
+8
pkgs/development/tools/build-managers/gradle/init-build.gradle
···
··· 1 + gradle.projectsLoaded { 2 + rootProject.allprojects { 3 + tasks.withType(AbstractArchiveTask) { 4 + preserveFileTimestamps = false 5 + reproducibleFileOrder = true 6 + } 7 + } 8 + }
+10
pkgs/development/tools/build-managers/gradle/init-deps.gradle
···
··· 1 + gradle.projectsLoaded { 2 + rootProject.allprojects { 3 + task nixDownloadDeps { 4 + doLast { 5 + configurations.findAll{it.canBeResolved}.each{it.resolve()} 6 + buildscript.configurations.findAll{it.canBeResolved}.each{it.resolve()} 7 + } 8 + } 9 + } 10 + }
+70
pkgs/development/tools/build-managers/gradle/setup-hook.sh
···
··· 1 + gradleConfigureHook() { 2 + if [ -z "${GRADLE_USER_HOME-}" ]; then 3 + GRADLE_USER_HOME="$(mktemp -d)" 4 + fi 5 + export GRADLE_USER_HOME 6 + export TERM=dumb 7 + gradleFlagsArray+=(--no-daemon --console plain --init-script "${gradleInitScript:-@init_script@}") 8 + if [ -n "${MITM_CACHE_CA-}" ]; then 9 + if [ -z "${MITM_CACHE_KEYSTORE-}" ]; then 10 + MITM_CACHE_KEYSTORE="$MITM_CACHE_CERT_DIR/keystore" 11 + MITM_CACHE_KS_PWD="$(head -c10 /dev/random | base32)" 12 + echo y | @jdk@/bin/keytool -importcert -file "$MITM_CACHE_CA" -alias alias -keystore "$MITM_CACHE_KEYSTORE" -storepass "$MITM_CACHE_KS_PWD" 13 + fi 14 + gradleFlagsArray+=(-Dhttp.proxyHost="$MITM_CACHE_HOST" -Dhttp.proxyPort="$MITM_CACHE_PORT") 15 + gradleFlagsArray+=(-Dhttps.proxyHost="$MITM_CACHE_HOST" -Dhttps.proxyPort="$MITM_CACHE_PORT") 16 + gradleFlagsArray+=(-Djavax.net.ssl.trustStore="$MITM_CACHE_KEYSTORE" -Djavax.net.ssl.trustStorePassword="$MITM_CACHE_KS_PWD") 17 + else 18 + gradleFlagsArray+=(--offline) 19 + fi 20 + if ! [[ -v enableParallelBuilding ]]; then 21 + enableParallelBuilding=1 22 + fi 23 + if ! [[ -v enableParallelChecking ]]; then 24 + enableParallelChecking=1 25 + fi 26 + if ! [[ -v enableParallelUpdating ]]; then 27 + enableParallelUpdating=1 28 + fi 29 + } 30 + 31 + gradle() { 32 + command gradle $gradleFlags "${gradleFlagsArray[@]}" "$@" 33 + } 34 + 35 + gradleBuildPhase() { 36 + runHook preBuild 37 + 38 + gradle ${enableParallelBuilding:+--parallel} ${gradleBuildTask:-assemble} 39 + 40 + runHook postBuild 41 + } 42 + 43 + gradleCheckPhase() { 44 + runHook preCheck 45 + 46 + gradle ${enableParallelChecking:+--parallel} ${gradleCheckTask:-test} 47 + 48 + runHook postCheck 49 + } 50 + 51 + gradleUpdateScript() { 52 + runHook preBuild 53 + runHook preGradleUpdate 54 + 55 + gradle ${enableParallelUpdating:+--parallel} ${gradleUpdateTask:-nixDownloadDeps} 56 + 57 + runHook postGradleUpdate 58 + } 59 + 60 + if [ -z "${dontUseGradleConfigure-}" ]; then 61 + preConfigureHooks+=(gradleConfigureHook) 62 + fi 63 + 64 + if [ -z "${dontUseGradleBuild-}" ] && [ -z "${buildPhase-}" ]; then 65 + buildPhase=gradleBuildPhase 66 + fi 67 + 68 + if [ -z "${dontUseGradleCheck-}" ] && [ -z "${checkPhase-}" ]; then 69 + checkPhase=gradleCheckPhase 70 + fi
+122
pkgs/development/tools/build-managers/gradle/update-deps.nix
···
··· 1 + { lib 2 + , runtimeShell 3 + , srcOnly 4 + , writeTextFile 5 + , writeShellScript 6 + , path 7 + , bubblewrap 8 + , coreutils 9 + , curl 10 + , jq 11 + , mitm-cache 12 + , nix 13 + , openssl 14 + , procps 15 + , python3 16 + }: 17 + 18 + lib.makeOverridable 19 + ({ pkg, pname, attrPath, bwrapFlags, data, silent, useBwrap }: 20 + let 21 + keep = [ "MITM_CACHE_HOST" "MITM_CACHE_PORT" "MITM_CACHE_ADDRESS" "MITM_CACHE_CA" "MITM_CACHE_CERT_DIR" ]; 22 + gradleScript = writeShellScript "gradle-commands.sh" '' 23 + set -eo pipefail 24 + export http_proxy="$MITM_CACHE_ADDRESS" 25 + export https_proxy="$MITM_CACHE_ADDRESS" 26 + export SSL_CERT_FILE="$MITM_CACHE_CA" 27 + export NIX_SSL_CERT_FILE="$MITM_CACHE_CA" 28 + export GRADLE_USER_HOME="$(${coreutils}/bin/mktemp -d)" 29 + export IN_GRADLE_UPDATE_DEPS=1 30 + trap "${coreutils}/bin/rm -rf '$GRADLE_USER_HOME'" SIGINT SIGTERM ERR EXIT 31 + cd "$(${coreutils}/bin/mktemp -d)" 32 + ${coreutils}/bin/mkdir out 33 + export out="$PWD/out" 34 + trap "${coreutils}/bin/rm -rf '$PWD'" SIGINT SIGTERM ERR EXIT 35 + source "$stdenv/setup" 36 + phases="''${prePhases[*]:-} unpackPhase patchPhase ''${preConfigurePhases[*]:-} configurePhase gradleUpdateScript" genericBuild 37 + ''; 38 + source = srcOnly (pkg.overrideAttrs (old: { 39 + mitmCache = ""; 40 + gradleInitScript = ./init-deps.gradle; 41 + })); 42 + sourceDrvPath = builtins.unsafeDiscardOutputDependency source.drvPath; 43 + nixShellKeep = lib.concatMapStringsSep " " (x: "--keep ${x}") keep; 44 + in 45 + writeTextFile { 46 + name = "fetch-deps.sh"; 47 + executable = true; 48 + # see pkgs/common-updater/combinators.nix 49 + derivationArgs.passthru = 50 + { supportedFeatures = lib.optional silent "silent"; } 51 + // lib.optionalAttrs (attrPath != null) { inherit attrPath; }; 52 + text = '' 53 + #!${runtimeShell} 54 + set -eo pipefail 55 + export PATH="${lib.makeBinPath [ 56 + bubblewrap coreutils curl jq mitm-cache openssl 57 + procps python3.pkgs.ephemeral-port-reserve 58 + ]}:$PATH" 59 + outPath="${ 60 + # if this is an absolute path in nix store, use path relative to the store path 61 + if lib.hasPrefix "${builtins.storeDir}/" (toString data) 62 + then builtins.concatStringsSep "/" (lib.drop 1 (lib.splitString "/" (lib.removePrefix "${builtins.storeDir}/" (toString data)))) 63 + # if this is an absolute path anywhere else, just use that path 64 + else if lib.hasPrefix "/" (toString data) 65 + then toString data 66 + # otherwise, use a path relative to the package 67 + else "${dirOf pkg.meta.position}/${data}" 68 + }" 69 + 70 + pushd "$(mktemp -d)" >/dev/null 71 + MITM_CACHE_DIR="$PWD" 72 + trap "rm -rf '$MITM_CACHE_DIR'" SIGINT SIGTERM ERR EXIT 73 + openssl genrsa -out ca.key 2048 74 + openssl req -x509 -new -nodes -key ca.key -sha256 -days 1 -out ca.cer -subj "/C=AL/ST=a/L=a/O=a/OU=a/CN=example.org" 75 + export MITM_CACHE_HOST=127.0.0.1 76 + export MITM_CACHE_PORT="''${mitmCachePort:-$(ephemeral-port-reserve "$MITM_CACHE_HOST")}" 77 + export MITM_CACHE_ADDRESS="$MITM_CACHE_HOST:$MITM_CACHE_PORT" 78 + # forget all redirects - this makes the lockfiles predictable 79 + # not only does this strip CDN URLs, but it also improves security - since the redirects aren't 80 + # stored in the lockfile, a malicious actor can't change the redirect URL stored in the lockfile 81 + mitm-cache \ 82 + -l"$MITM_CACHE_ADDRESS" \ 83 + record \ 84 + --reject '\.(md5|sha(1|256|512:?):?)$' \ 85 + --forget-redirects-from '.*' \ 86 + --record-text '/maven-metadata\.xml$' >/dev/null 2>/dev/null & 87 + MITM_CACHE_PID="$!" 88 + # wait for mitm-cache to fully start 89 + for i in {0..20}; do 90 + ps -p "$MITM_CACHE_PID" >/dev/null || (echo "Failed to start mitm-cache" && exit 1) 91 + curl -so/dev/null "$MITM_CACHE_ADDRESS" && break 92 + [[ "$i" -eq 20 ]] && (echo "Failed to start mitm-cache" && exit 1) 93 + sleep 0.5 94 + done 95 + trap "kill '$MITM_CACHE_PID'" SIGINT SIGTERM ERR EXIT 96 + export MITM_CACHE_CERT_DIR="$PWD" 97 + export MITM_CACHE_CA="$MITM_CACHE_CERT_DIR/ca.cer" 98 + popd >/dev/null 99 + useBwrap="''${USE_BWRAP:-${toString useBwrap}}" 100 + if [ -n "$useBwrap" ]; then 101 + # bwrap isn't necessary, it's only used to prevent messy build scripts from touching ~ 102 + bwrap \ 103 + --unshare-all --share-net --clearenv --chdir / --setenv HOME /homeless-shelter \ 104 + --tmpfs /home --bind /tmp /tmp --ro-bind /nix /nix --ro-bind /run /run --proc /proc --dev /dev \ 105 + --ro-bind ${toString path} ${toString path} --bind "$MITM_CACHE_CERT_DIR" "$MITM_CACHE_CERT_DIR" \ 106 + ${builtins.concatStringsSep " " (map (x: "--setenv ${x} \"\$${x}\"") keep)} \ 107 + --setenv NIX_BUILD_SHELL bash ${bwrapFlags} ''${BWRAP_FLAGS:-} \ 108 + -- ${nix}/bin/nix-shell --pure --run ${gradleScript} ${nixShellKeep} ${sourceDrvPath} 109 + else 110 + NIX_BUILD_SHELL=bash nix-shell --pure --run ${gradleScript} ${nixShellKeep} ${sourceDrvPath} 111 + fi${lib.optionalString silent " >&2"} 112 + kill -s SIGINT "$MITM_CACHE_PID" 113 + for i in {0..20}; do 114 + # check for valid json 115 + if jq -e 1 "$MITM_CACHE_DIR/out.json" >/dev/null 2>/dev/null; then 116 + exec ${python3.interpreter} ${./compress-deps-json.py} "$MITM_CACHE_DIR/out.json" "$outPath" 117 + fi 118 + sleep 1 119 + done 120 + exit 1 121 + ''; 122 + })
+11 -4
pkgs/top-level/all-packages.nix
··· 18575 inherit jdk11 jdk17 jdk21; 18576 }; 18577 gradleGen = gradle-packages.gen; 18578 - gradle_6 = callPackage gradle-packages.gradle_6 { }; 18579 - gradle_7 = callPackage gradle-packages.gradle_7 { }; 18580 - gradle_8 = callPackage gradle-packages.gradle_8 { }; 18581 - gradle = gradle_8; 18582 18583 grcov = callPackage ../development/tools/misc/grcov { }; 18584
··· 18575 inherit jdk11 jdk17 jdk21; 18576 }; 18577 gradleGen = gradle-packages.gen; 18578 + wrapGradle = callPackage gradle-packages.wrapGradle { }; 18579 + 18580 + gradle_6-unwrapped = callPackage gradle-packages.gradle_6 { }; 18581 + gradle_7-unwrapped = callPackage gradle-packages.gradle_7 { }; 18582 + gradle_8-unwrapped = callPackage gradle-packages.gradle_8 { }; 18583 + gradle-unwrapped = gradle_8-unwrapped; 18584 + 18585 + gradle_6 = wrapGradle gradle_6-unwrapped; 18586 + gradle_7 = wrapGradle gradle_7-unwrapped; 18587 + gradle_8 = wrapGradle gradle_8-unwrapped; 18588 + gradle = wrapGradle gradle-unwrapped; 18589 18590 grcov = callPackage ../development/tools/misc/grcov { }; 18591