1{
2 jdk17,
3 jdk21,
4 jdk23,
5}:
6
7rec {
8 gen =
9
10 {
11 version,
12 hash,
13
14 # The default JDK/JRE that will be used for derived Gradle packages.
15 # A current LTS version of a JDK is a good choice.
16 defaultJava,
17
18 # The platforms supported by this Gradle package.
19 # Gradle Native-Platform ships some binaries that
20 # are compatible only with specific platforms.
21 # As of 2022-04 this affects platform compatibility
22 # of multiple Gradle releases, so this is used as default.
23 # See https://github.com/gradle/native-platform#supported-platforms
24 platforms ? [
25 "aarch64-darwin"
26 "aarch64-linux"
27 "i686-windows"
28 "x86_64-cygwin"
29 "x86_64-darwin"
30 "x86_64-linux"
31 "x86_64-windows"
32 ],
33
34 # Extra attributes to be merged into the resulting derivation's
35 # meta attribute.
36 meta ? { },
37 }:
38
39 {
40 lib,
41 stdenv,
42 fetchurl,
43 makeWrapper,
44 unzip,
45 ncurses5,
46 ncurses6,
47 udev,
48 testers,
49 runCommand,
50 writeText,
51 autoPatchelfHook,
52 buildPackages,
53
54 # The JDK/JRE used for running Gradle.
55 java ? defaultJava,
56
57 # Additional JDK/JREs to be registered as toolchains.
58 # See https://docs.gradle.org/current/userguide/toolchains.html
59 javaToolchains ? [ ],
60 }:
61
62 stdenv.mkDerivation (finalAttrs: {
63 pname = "gradle";
64 inherit version;
65
66 src = fetchurl {
67 inherit hash;
68 url = "https://services.gradle.org/distributions/gradle-${version}-bin.zip";
69 };
70
71 dontBuild = true;
72
73 nativeBuildInputs = [
74 makeWrapper
75 unzip
76 ]
77 ++ lib.optionals stdenv.hostPlatform.isLinux [
78 autoPatchelfHook
79 ];
80
81 buildInputs = [
82 stdenv.cc.cc
83 ncurses5
84 ncurses6
85 ];
86
87 # We only need to patchelf some libs embedded in JARs.
88 dontAutoPatchelf = true;
89
90 installPhase =
91 with builtins;
92 let
93 # set toolchains via installations.path property in gradle.properties.
94 # See https://docs.gradle.org/current/userguide/toolchains.html#sec:custom_loc
95 toolchainPaths = "org.gradle.java.installations.paths=${concatStringsSep "," javaToolchains}";
96 jnaLibraryPath = if stdenv.hostPlatform.isLinux then lib.makeLibraryPath [ udev ] else "";
97 jnaFlag =
98 if stdenv.hostPlatform.isLinux then "--add-flags \"-Djna.library.path=${jnaLibraryPath}\"" else "";
99 in
100 ''
101 mkdir -pv $out/lib/gradle/
102 cp -rv lib/ $out/lib/gradle/
103
104 gradle_launcher_jar=$(echo $out/lib/gradle/lib/gradle-launcher-*.jar)
105 test -f $gradle_launcher_jar
106 makeWrapper ${java}/bin/java $out/bin/gradle \
107 --set JAVA_HOME ${java} \
108 ${jnaFlag} \
109 --add-flags "-classpath $gradle_launcher_jar org.gradle.launcher.GradleMain"
110
111 echo "${toolchainPaths}" > $out/lib/gradle/gradle.properties
112 '';
113
114 dontFixup = !stdenv.hostPlatform.isLinux;
115
116 fixupPhase =
117 let
118 arch = if stdenv.hostPlatform.is64bit then "amd64" else "i386";
119 newFileEvents = toString (lib.versionAtLeast version "8.12");
120 in
121 ''
122 # get the correct jar executable for cross
123 export PATH="${buildPackages.jdk}/bin:$PATH"
124 . ${./patching.sh}
125
126 nativeVersion="$(extractVersion native-platform $out/lib/gradle/lib/native-platform-*.jar)"
127 for variant in "" "-ncurses5" "-ncurses6"; do
128 autoPatchelfInJar \
129 $out/lib/gradle/lib/native-platform-linux-${arch}$variant-''${nativeVersion}.jar \
130 "${lib.getLib stdenv.cc.cc}/lib64:${
131 lib.makeLibraryPath [
132 stdenv.cc.cc
133 ncurses5
134 ncurses6
135 ]
136 }"
137 done
138
139 # The file-events library _seems_ to follow the native-platform version, but
140 # we won’t assume that.
141 if [ -n "${newFileEvents}" ]; then
142 fileEventsVersion="$(extractVersion gradle-fileevents $out/lib/gradle/lib/gradle-fileevents-*.jar)"
143 autoPatchelfInJar \
144 $out/lib/gradle/lib/gradle-fileevents-''${fileEventsVersion}.jar \
145 "${lib.getLib stdenv.cc.cc}/lib64:${lib.makeLibraryPath [ stdenv.cc.cc ]}"
146 else
147 fileEventsVersion="$(extractVersion file-events $out/lib/gradle/lib/file-events-*.jar)"
148 autoPatchelfInJar \
149 $out/lib/gradle/lib/file-events-linux-${arch}-''${fileEventsVersion}.jar \
150 "${lib.getLib stdenv.cc.cc}/lib64:${lib.makeLibraryPath [ stdenv.cc.cc ]}"
151 fi
152
153 # The scanner doesn't pick up the runtime dependency in the jar.
154 # Manually add a reference where it will be found.
155 mkdir $out/nix-support
156 echo ${stdenv.cc.cc} > $out/nix-support/manual-runtime-dependencies
157 # Gradle will refuse to start without _both_ 5 and 6 versions of ncurses.
158 echo ${ncurses5} >> $out/nix-support/manual-runtime-dependencies
159 echo ${ncurses6} >> $out/nix-support/manual-runtime-dependencies
160 ${lib.optionalString stdenv.hostPlatform.isLinux "echo ${udev} >> $out/nix-support/manual-runtime-dependencies"}
161 '';
162
163 passthru.tests = {
164 version = testers.testVersion {
165 package = finalAttrs.finalPackage;
166 command = ''
167 env GRADLE_USER_HOME=$TMPDIR/gradle org.gradle.native.dir=$TMPDIR/native \
168 gradle --version
169 '';
170 };
171
172 java-application = testers.testEqualContents {
173 assertion = "can build and run a trivial Java application";
174 expected = writeText "expected" "hello\n";
175 actual =
176 runCommand "actual"
177 {
178 nativeBuildInputs = [ finalAttrs.finalPackage ];
179 src = ./tests/java-application;
180 }
181 ''
182 cp -a $src/* .
183 env GRADLE_USER_HOME=$TMPDIR/gradle org.gradle.native.dir=$TMPDIR/native \
184 gradle run --no-daemon --quiet --console plain > $out
185 '';
186 };
187 };
188 passthru.jdk = defaultJava;
189
190 meta =
191 with lib;
192 {
193 inherit platforms;
194 description = "Enterprise-grade build system";
195 longDescription = ''
196 Gradle is a build system which offers you ease, power and freedom.
197 You can choose the balance for yourself. It has powerful multi-project
198 build support. It has a layer on top of Ivy that provides a
199 build-by-convention integration for Ivy. It gives you always the choice
200 between the flexibility of Ant and the convenience of a
201 build-by-convention behavior.
202 '';
203 homepage = "https://www.gradle.org/";
204 changelog = "https://docs.gradle.org/${version}/release-notes.html";
205 downloadPage = "https://gradle.org/next-steps/?version=${version}";
206 sourceProvenance = with sourceTypes; [
207 binaryBytecode
208 binaryNativeCode
209 ];
210 license = licenses.asl20;
211 maintainers = with maintainers; [
212 britter
213 liff
214 lorenzleutgeb
215 ];
216 teams = [ lib.teams.java ];
217 mainProgram = "gradle";
218 }
219 // meta;
220 });
221
222 # NOTE: Default JDKs that are hardcoded below must be LTS versions
223 # and respect the compatibility matrix at
224 # https://docs.gradle.org/current/userguide/compatibility.html
225
226 gradle_8 = gen {
227 version = "8.14.3";
228 hash = "sha256-vXEQIhNJMGCVbsIp2Ua+7lcVjb2J0OYrkbyg+ixfNTE=";
229 defaultJava = jdk21;
230 };
231
232 gradle_7 = gen {
233 version = "7.6.6";
234 hash = "sha256-Zz2XdvMDvHBI/DMp0jLW6/EFGweJO9nRFhb62ahnO+A=";
235 defaultJava = jdk17;
236 };
237
238 wrapGradle =
239 {
240 lib,
241 callPackage,
242 mitm-cache,
243 replaceVars,
244 symlinkJoin,
245 concatTextFile,
246 makeSetupHook,
247 nix-update-script,
248 runCommand,
249 }:
250 gradle-unwrapped: updateAttrPath:
251 lib.makeOverridable (
252 args:
253 let
254 gradle = gradle-unwrapped.override args;
255 in
256 symlinkJoin {
257 pname = "gradle";
258 inherit (gradle) version;
259
260 paths = [
261 (makeSetupHook { name = "gradle-setup-hook"; } (concatTextFile {
262 name = "setup-hook.sh";
263 files = [
264 (mitm-cache.setupHook)
265 (replaceVars ./setup-hook.sh {
266 # jdk used for keytool
267 inherit (gradle) jdk;
268 init_script = "${./init-build.gradle}";
269 })
270 ];
271 }))
272 gradle
273 mitm-cache
274 ];
275
276 passthru = {
277 fetchDeps = callPackage ./fetch-deps.nix { inherit mitm-cache; };
278 inherit (gradle) jdk;
279 unwrapped = gradle;
280 tests = {
281 toolchains =
282 let
283 javaVersion = lib.getVersion jdk23;
284 javaMajorVersion = lib.versions.major javaVersion;
285 in
286 runCommand "detects-toolchains-from-nix-env"
287 {
288 # Use JDKs that are not the default for any of the gradle versions
289 nativeBuildInputs = [
290 (gradle.override {
291 javaToolchains = [
292 jdk23
293 ];
294 })
295 ];
296 src = ./tests/toolchains;
297 }
298 ''
299 cp -a $src/* .
300 substituteInPlace ./build.gradle --replace-fail '@JAVA_VERSION@' '${javaMajorVersion}'
301 env GRADLE_USER_HOME=$TMPDIR/gradle org.gradle.native.dir=$TMPDIR/native \
302 gradle run --no-daemon --quiet --console plain > $out
303 actual="$(<$out)"
304 if [[ "${javaVersion}" != "$actual"* ]]; then
305 echo "Error: Expected '${javaVersion}', to start with '$actual'" >&2
306 exit 1
307 fi
308 '';
309 }
310 // gradle.tests;
311 }
312 // lib.optionalAttrs (updateAttrPath != null) {
313 updateScript = nix-update-script {
314 attrPath = updateAttrPath;
315 extraArgs = [
316 "--url=https://github.com/gradle/gradle"
317 # Gradle’s .0 releases are tagged as `vX.Y.0`, but the actual
318 # release version omits the `.0`, so we’ll wanto to only capture
319 # the version up but not including the the trailing `.0`.
320 "--version-regex=^v(\\d+\\.\\d+(?:\\.[1-9]\\d?)?)(\\.0)?$"
321 ];
322 };
323 };
324
325 meta = gradle.meta // {
326 # prefer normal gradle/mitm-cache over this wrapper, this wrapper only provides the setup hook
327 # and passthru
328 priority = (gradle.meta.priority or lib.meta.defaultPriority) + 1;
329 };
330 }
331 ) { };
332}