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