Package: release.debian.org Severity: normal User: release.debian....@packages.debian.org Usertags: unblock
Hi, I'd like to request gradle/4.4.1-6 to be unblocked. When fixing the gradle package to work with OpenJDK 11 we mistakenly broke the compatibility with OpenJDK 8 (#925225). Even if Buster ships only with OpenJDK 11 we cared to preserve the compatibility of the Java build tools in Debian (Ant and Maven) with OpenJDK 8 since it's still the most popular JDK (a recent survey conducted by Jetbrains showed that 80% of the Java developers were still using Java 8, and only 20% have adopted Java 11). We can expect the users to be disappointed if the gradle package requires OpenJDK 11 or higher. Ubuntu fixed the OpenJDK 8 compatibility 3 months ago and forwarded the patch to Debian (the patch was backported from upstream). I've uploaded the fix to unstable as gradle/4.4.1-6 and I've verified that the main packages using Gradle are still building fine (openjfx, libspring-java, gradle itself and a few others). Thank you, Emmanuel Bourg unblock gradle/4.4.1-6
diff -Nru gradle-4.4.1/debian/changelog gradle-4.4.1/debian/changelog --- gradle-4.4.1/debian/changelog 2019-02-26 20:02:13.000000000 +0100 +++ gradle-4.4.1/debian/changelog 2019-06-22 00:52:47.000000000 +0200 @@ -1,3 +1,17 @@ +gradle (4.4.1-6) unstable; urgency=medium + + [ Tiago Stürmer Daitx ] + * Fix OpenJDK 8 compatibility: (Closes: #925225) + - debian/patches/java8-compatibility.patch: cast ByteBuffer to Buffer + in org.gradle.internal.hash.Hashing to prevent NoSuchMethodError + exception. + - debian/patches/java11-compatibility.patch: copy upstream commit for + "Use Lookup to invoke defineClass on Java 9+ (#4976)" instead using + the previous partial backport to enable both OpenJDK 8 and 11 support. + * debian/control: revert gradle Depends back to java 8 + + -- Emmanuel Bourg <ebo...@apache.org> Sat, 22 Jun 2019 00:52:47 +0200 + gradle (4.4.1-5) unstable; urgency=medium * Team upload. diff -Nru gradle-4.4.1/debian/control gradle-4.4.1/debian/control --- gradle-4.4.1/debian/control 2019-02-26 20:02:13.000000000 +0100 +++ gradle-4.4.1/debian/control 2019-06-22 00:49:27.000000000 +0200 @@ -76,7 +76,7 @@ Package: gradle Architecture: all -Depends: default-jre-headless (>= 2:1.9) | java9-runtime-headless, +Depends: default-jre-headless (>= 2:1.8) | java8-runtime-headless, libgradle-core-java (>= ${binary:Version}), libgradle-plugins-java (>= ${binary:Version}), ${misc:Depends} diff -Nru gradle-4.4.1/debian/patches/java11-compatibility.patch gradle-4.4.1/debian/patches/java11-compatibility.patch --- gradle-4.4.1/debian/patches/java11-compatibility.patch 2019-02-26 20:02:13.000000000 +0100 +++ gradle-4.4.1/debian/patches/java11-compatibility.patch 2019-06-22 00:50:07.000000000 +0200 @@ -4,51 +4,238 @@ https://github.com/gradle/gradle/commit/3db6e256987053171178aa96a0ef46caedc8d1a4 --- a/subprojects/base-services/src/main/java/org/gradle/internal/classloader/ClassLoaderUtils.java +++ b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/ClassLoaderUtils.java -@@ -24,6 +24,9 @@ +@@ -15,51 +15,41 @@ + */ + package org.gradle.internal.classloader; + ++import org.gradle.api.JavaVersion; + import org.gradle.internal.Cast; + import org.gradle.internal.UncheckedException; + import org.gradle.internal.concurrent.CompositeStoppable; + import org.gradle.internal.reflect.JavaMethod; +-import org.gradle.internal.reflect.JavaReflectionUtil; +-import sun.misc.Unsafe; import javax.annotation.Nullable; import java.io.IOException; +-import java.lang.reflect.Field; +-import java.net.MalformedURLException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; - import java.lang.reflect.Field; - import java.net.MalformedURLException; import java.net.URL; -@@ -31,16 +34,15 @@ - - public abstract class ClassLoaderUtils { + import java.net.URLConnection; +-public abstract class ClassLoaderUtils { +- - private static final Unsafe UNSAFE; -+ private static MethodHandle defineClassMethodHandle; - - static { - try { +- +- static { +- try { - Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafe.setAccessible(true); - UNSAFE = (Unsafe) theUnsafe.get(null); - } catch (NoSuchFieldException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { -+ MethodHandles.Lookup baseLookup = MethodHandles.lookup(); -+ MethodType defineClassMethodType = MethodType.methodType(Class.class, new Class[]{String.class, byte[].class, int.class, int.class}); -+ MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(ClassLoader.class, baseLookup); -+ defineClassMethodHandle = lookup.findVirtual(ClassLoader.class, "defineClass", defineClassMethodType); -+ } catch (Throwable e) { - throw new RuntimeException(e); +- throw new RuntimeException(e); +- } +- } ++import static org.gradle.internal.reflect.JavaReflectionUtil.method; ++import static org.gradle.internal.reflect.JavaReflectionUtil.staticMethod; + ++public abstract class ClassLoaderUtils { ++ private static final ClassDefiner CLASS_DEFINER; + private static final JavaMethod<ClassLoader, Package[]> GET_PACKAGES_METHOD; + private static final JavaMethod<ClassLoader, Package> GET_PACKAGE_METHOD; + + static { ++ CLASS_DEFINER = JavaVersion.current().isJava9Compatible() ? new LookupClassDefiner() : new ReflectionClassDefiner(); + GET_PACKAGES_METHOD = getMethodWithFallback(Package[].class, new Class[0], "getDefinedPackages", "getPackages"); +- GET_PACKAGE_METHOD = getMethodWithFallback(Package.class, new Class[] {String.class}, "getDefinedPackage", "getPackage"); ++ GET_PACKAGE_METHOD = getMethodWithFallback(Package.class, new Class[]{String.class}, "getDefinedPackage", "getPackage"); + } + + private static <T> JavaMethod<ClassLoader, T> getMethodWithFallback(Class<T> clazz, Class<?>[] params, String firstChoice, String fallback) { + JavaMethod<ClassLoader, T> method; + try { +- method = JavaReflectionUtil.method(ClassLoader.class, clazz, firstChoice, params); ++ method = method(ClassLoader.class, clazz, firstChoice, params); + } catch (Throwable e) { + // We must not be on Java 9 where the getDefinedPackages() method exists. Fall back to getPackages() +- method = JavaReflectionUtil.method(ClassLoader.class, clazz, fallback, params); ++ method = method(ClassLoader.class, clazz, fallback, params); } + return method; } -@@ -101,6 +103,10 @@ +@@ -85,8 +75,6 @@ public static void disableUrlConnectionCaching() { + URL url = new URL("jar:file://valid_jar_url_syntax.jar!/"); + URLConnection urlConnection = url.openConnection(); + urlConnection.setDefaultUseCaches(false); +- } catch (MalformedURLException e) { +- throw UncheckedException.throwAsUncheckedException(e); + } catch (IOException e) { + throw UncheckedException.throwAsUncheckedException(e); + } +@@ -101,6 +89,63 @@ public static void disableUrlConnectionCaching() { } public static <T> Class<T> define(ClassLoader targetClassLoader, String className, byte[] clazzBytes) { - return Cast.uncheckedCast(UNSAFE.defineClass(className, clazzBytes, 0, clazzBytes.length, targetClassLoader, null)); -+ try { -+ return (Class) defineClassMethodHandle.bindTo(targetClassLoader).invokeWithArguments(className, clazzBytes, 0, clazzBytes.length); -+ } catch (Throwable e) { -+ throw new RuntimeException(e); ++ return CLASS_DEFINER.defineClass(targetClassLoader, className, clazzBytes); ++ } ++ ++ private interface ClassDefiner { ++ <T> Class<T> defineClass(ClassLoader classLoader, String className, byte[] classBytes); ++ } ++ ++ private static class ReflectionClassDefiner implements ClassDefiner { ++ private final JavaMethod<ClassLoader, Class> defineClassMethod; ++ ++ private ReflectionClassDefiner() { ++ defineClassMethod = method(ClassLoader.class, Class.class, "defineClass", String.class, byte[].class, int.class, int.class); ++ } ++ ++ @Override ++ public <T> Class<T> defineClass(ClassLoader classLoader, String className, byte[] classBytes) { ++ return Cast.uncheckedCast(defineClassMethod.invoke(classLoader, className, classBytes, 0, classBytes.length)); ++ } ++ } ++ ++ private static class LookupClassDefiner implements ClassDefiner { ++ private final Class methodHandlesLookupClass; ++ private final JavaMethod methodHandlesLookup; ++ private final JavaMethod methodHandlesPrivateLookupIn; ++ private final JavaMethod lookupFindVirtual; ++ private final MethodType defineClassMethodType; ++ ++ private LookupClassDefiner() { ++ try { ++ methodHandlesLookupClass = Class.forName("java.lang.invoke.MethodHandles$Lookup"); ++ } catch (ClassNotFoundException e) { ++ throw new RuntimeException(e); ++ } ++ methodHandlesLookup = staticMethod(MethodHandles.class, methodHandlesLookupClass, "lookup"); ++ methodHandlesPrivateLookupIn = staticMethod(MethodHandles.class, methodHandlesLookupClass, "privateLookupIn", Class.class, methodHandlesLookupClass); ++ lookupFindVirtual = method(methodHandlesLookupClass, MethodHandle.class, "findVirtual", Class.class, String.class, MethodType.class); ++ defineClassMethodType = MethodType.methodType(Class.class, new Class[]{String.class, byte[].class, int.class, int.class}); ++ } ++ ++ /* ++ This method is equivalent to the following code but use reflection to compile on Java 7: ++ ++ MethodHandles.Lookup baseLookup = MethodHandles.lookup(); ++ MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(ClassLoader.class, baseLookup); ++ MethodHandle defineClassMethodHandle = lookup.findVirtual(ClassLoader.class, "defineClass", defineClassMethodType); ++ handle.bindTo(classLoader).invokeWithArguments(className, classBytes, 0, classBytes.length)); ++ */ ++ @Override ++ public <T> Class<T> defineClass(ClassLoader classLoader, String className, byte[] classBytes) { ++ Object baseLookup = methodHandlesLookup.invoke(null); ++ Object lookup = methodHandlesPrivateLookupIn.invoke(null, ClassLoader.class, baseLookup); ++ MethodHandle defineClassMethodHandle = (MethodHandle) lookupFindVirtual.invoke(lookup, ClassLoader.class, "defineClass", defineClassMethodType); ++ try { ++ return Cast.uncheckedCast(defineClassMethodHandle.bindTo(classLoader).invokeWithArguments(className, classBytes, 0, classBytes.length)); ++ } catch (Throwable throwable) { ++ throw new RuntimeException(throwable); ++ } + } } } +diff --git a/subprojects/core/src/main/java/org/gradle/process/internal/worker/child/WorkerProcessClassPathProvider.java b/subprojects/core/src/main/java/org/gradle/process/internal/worker/child/WorkerProcessClassPathProvider.java +index 9a7fd911c78f..c1324a04fef5 100644 +--- a/subprojects/core/src/main/java/org/gradle/process/internal/worker/child/WorkerProcessClassPathProvider.java ++++ b/subprojects/core/src/main/java/org/gradle/process/internal/worker/child/WorkerProcessClassPathProvider.java +@@ -18,6 +18,7 @@ + + import org.gradle.api.Action; + import org.gradle.api.GradleException; ++import org.gradle.api.JavaVersion; + import org.gradle.api.internal.ClassPathProvider; + import org.gradle.api.specs.Spec; + import org.gradle.cache.CacheRepository; +@@ -57,7 +58,9 @@ + import java.io.InputStream; + import java.net.URL; + import java.util.Arrays; ++import java.util.HashSet; + import java.util.List; ++import java.util.Set; + import java.util.zip.ZipEntry; + import java.util.zip.ZipOutputStream; + +@@ -116,32 +119,9 @@ public void execute(PersistentCache cache) { + try { + File jarFile = jarFile(cache); + LOGGER.debug("Generating worker process classes to {}.", jarFile); +- +- // TODO - calculate this list of classes dynamically +- List<Class<?>> classes = Arrays.asList( +- GradleWorkerMain.class, +- BootstrapSecurityManager.class, +- EncodedStream.EncodedInput.class, +- ClassLoaderUtils.class, +- FilteringClassLoader.class, +- FilteringClassLoader.Spec.class, +- ClassLoaderHierarchy.class, +- ClassLoaderVisitor.class, +- ClassLoaderSpec.class, +- SystemClassLoaderSpec.class, +- JavaReflectionUtil.class, +- JavaMethod.class, +- GradleException.class, +- NoSuchPropertyException.class, +- NoSuchMethodException.class, +- UncheckedException.class, +- PropertyAccessor.class, +- PropertyMutator.class, +- Factory.class, +- Spec.class); + ZipOutputStream outputStream = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(jarFile))); + try { +- for (Class<?> classToMap : classes) { ++ for (Class<?> classToMap : getClassesForWorkerJar()) { + remapClass(classToMap, outputStream); + } + } finally { +@@ -152,6 +132,37 @@ public void execute(PersistentCache cache) { + } + } + ++ private Set<Class<?>> getClassesForWorkerJar() { ++ // TODO - calculate this list of classes dynamically ++ List<Class<?>> classes = Arrays.asList( ++ GradleWorkerMain.class, ++ BootstrapSecurityManager.class, ++ EncodedStream.EncodedInput.class, ++ ClassLoaderUtils.class, ++ FilteringClassLoader.class, ++ ClassLoaderHierarchy.class, ++ ClassLoaderVisitor.class, ++ ClassLoaderSpec.class, ++ SystemClassLoaderSpec.class, ++ JavaReflectionUtil.class, ++ JavaMethod.class, ++ GradleException.class, ++ NoSuchPropertyException.class, ++ NoSuchMethodException.class, ++ UncheckedException.class, ++ PropertyAccessor.class, ++ PropertyMutator.class, ++ Factory.class, ++ Spec.class, ++ JavaVersion.class); ++ Set<Class<?>> result = new HashSet<Class<?>>(classes); ++ for (Class<?> klass : classes) { ++ result.addAll(Arrays.asList(klass.getDeclaredClasses())); ++ } ++ ++ return result; ++ } ++ + private void remapClass(Class<?> classToMap, ZipOutputStream jar) throws IOException { + String internalName = Type.getInternalName(classToMap); + String resourceName = internalName.concat(".class"); --- a/subprojects/base-services/src/main/java/org/gradle/api/JavaVersion.java +++ b/subprojects/base-services/src/main/java/org/gradle/api/JavaVersion.java @@ -17,25 +17,26 @@ diff -Nru gradle-4.4.1/debian/patches/java8-compatibility.patch gradle-4.4.1/debian/patches/java8-compatibility.patch --- gradle-4.4.1/debian/patches/java8-compatibility.patch 2019-02-26 20:02:13.000000000 +0100 +++ gradle-4.4.1/debian/patches/java8-compatibility.patch 2019-06-22 00:48:57.000000000 +0200 @@ -74,3 +74,14 @@ return charbuffer; } +--- a/subprojects/base-services/src/main/java/org/gradle/internal/hash/Hashing.java ++++ b/subprojects/base-services/src/main/java/org/gradle/internal/hash/Hashing.java +@@ -158,7 +158,7 @@ public class Hashing { + private void update(int length) { + checkNotDone(); + digest.update(buffer.array(), 0, length); +- buffer.clear(); ++ ((java.nio.Buffer) buffer).clear(); + } + + @Override