diff options
author | Aaron Hill <aa1ronham@gmail.com> | 2019-04-25 16:44:20 -0400 |
---|---|---|
committer | Aaron Hill <aa1ronham@gmail.com> | 2019-04-25 21:47:24 -0400 |
commit | 747c582804bdcd0409417fdd92f7a3042d80b6b5 (patch) | |
tree | 945e46af6d7359a987d31a30c162bc167e741407 | |
parent | 37d713bdb7459cdd44707b350fd12fd59517b415 (diff) | |
download | Artifactural-747c582804bdcd0409417fdd92f7a3042d80b6b5.tar.gz Artifactural-747c582804bdcd0409417fdd92f7a3042d80b6b5.tar.bz2 Artifactural-747c582804bdcd0409417fdd92f7a3042d80b6b5.zip |
Add compatibility with Gradle 4.10 and above
This commits adds compatibility for Gradle 4.10 onward (tested on 4.10
and 5,4), while retainting compatibiliy with 4.9
Due to the ABI-incompatible changes in some internal gradle classes, we
need to use bytecode manipulation to ensure that our built class files
are compatible with both Gradle 4.9 and Gradle >= 4.10
Bytecode manipulation is performed at built time. A custom Gradle task
is used to read in the compiled class files from disk, and write out a
modified version to the final jar artifact.
In order to make as few bytecode modifications as possible, this commit
bumps the Gradle wrapper version to 4.10. This means that we're
compiling against Gradle 4.10, and using bytecode manipulation to retain
compatibility with 4.9. Doing the reverse (compiling against 4.9) would
be significantly more difficult, as we would need to statically
reference classes that exist in Gradle 4.10 but not 4.9 (specifically,
RepositoryDescriptor)
We perform two different bytecode transformations:
1. We modify the call to 'super()' in GradleRepositoryAdapter. In Gradle
4.10, the suepr constructor takes one argument, but in 4.9, it takes
zero arguments. In order to allow GradleRepositoryAdapter to compile
normally, we write a 'fake' call to 'super(null)', and replace with a
call to 'super()'.
2. We delete the method 'getDescriptor' from GradleRepositoryAdapter. In
Gradle 4.9, its return type does not exist, and will cause a
NoClassDefFoundError when Gradle attempts to classload it via
Class#getDeclaredMethods. However, it's necessary to include
'getDescriptor' so that GradleRepositoryAdapter (we need to override the
abstract method in a parent class).
-rw-r--r-- | build.gradle | 158 | ||||
-rw-r--r-- | gradle.properties | 1 | ||||
-rw-r--r-- | gradle/wrapper/gradle-wrapper.jar | bin | 52271 -> 54413 bytes | |||
-rw-r--r-- | gradle/wrapper/gradle-wrapper.properties | 4 | ||||
-rwxr-xr-x | gradlew | 78 | ||||
-rw-r--r-- | gradlew.bat | 174 | ||||
-rw-r--r-- | src/gradlecomp/java/com/amadornes/artifactural/gradle/Gradle410RepositoryAdapter.java | 42 | ||||
-rw-r--r-- | src/gradlecomp/java/com/amadornes/artifactural/gradle/GradleRepositoryAdapter.java | 54 |
8 files changed, 381 insertions, 130 deletions
diff --git a/build.gradle b/build.gradle index 635950d..d6cbd04 100644 --- a/build.gradle +++ b/build.gradle @@ -1,9 +1,30 @@ +buildscript { + repositories { + mavenCentral() + } + dependencies { + // Needed for bytecode manipulation + classpath 'org.ow2.asm:asm-all:5.2' + } +} + plugins { id 'net.minecrell.licenser' version '0.3' id 'org.ajoberstar.grgit' version '2.3.0' //id 'com.github.johnrengelman.shadow' version '2.0.4' } +import java.nio.file.Paths +import org.objectweb.asm.ClassReader +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.Opcodes +import org.objectweb.asm.tree.ClassNode +import org.objectweb.asm.tree.MethodInsnNode +import org.objectweb.asm.tree.VarInsnNode +import org.objectweb.asm.tree.InsnNode + +import java.util.jar.JarFile + apply plugin: 'java' apply plugin: 'maven-publish' @@ -15,7 +36,7 @@ def gitVersion() { def hash = desc.remove(desc.size() - 1) def offset = desc.remove(desc.size() - 1) def tag = desc.join('-') - def branch = grgit.branch.current().name + def branch = grgit.branch.current().name return "${tag}.${offset}" //${t -> if (branch != 'master') t << '-' + branch}" } @@ -52,10 +73,143 @@ dependencies { compile sourceSets.gradlecomp.output } + + +def gradleRepositoryAdapterPath = Paths.get("com", "amadornes", "artifactural", "gradle", "GradleRepositoryAdapter.class") +def classesDirs = sourceSets.gradlecomp.output.classesDirs.getFiles().first().toPath() + + +def _constructorName = "<init>" +def _constructorDesc = "(Lcom/amadornes/artifactural/api/repository/Repository;Lorg/gradle/api/internal/artifacts/repositories/DefaultMavenLocalArtifactRepository;)V" +def _modifiedSuperDesc = "()V" + +// This task patches 'GradleRepositoryAdapter' to make it compatibile with both Gradle 4.9 and 4.10 +// In Gradle 4.9, the constructor of AbstractArtifactRepository changes has zero arguments +// (instead of the one-argument constructor in 4.10, which we compile against) +// It's not possible to write a normal Java class that calls a constructor which doesn't exist at compile time. +// Therefore, we need to patch the bytecode of the class to properly call the non-arg super() constrcutor +class PatchGradleRepositoryAdapter extends DefaultTask { + + @Input String constructorName + @Input String constructorDesc + @Input String modifiedSuperDesc + @Input File gradleRepositoryAdapter + @Input File classesDir + @OutputDirectory java.nio.file.Path outputDir + + + @TaskAction + void patchClass() { + def originalGradleRepositoryAdapter = Paths.get(classesDir.toString(), gradleRepositoryAdapter.toString()) + + ClassNode node = new ClassNode() + ClassReader reader = new ClassReader(new FileInputStream(originalGradleRepositoryAdapter.toFile())) + reader.accept(node, 0) + + + def constructor = node.methods.find { + it.name == constructorName && it.desc == constructorDesc + } + + def getDescriptor = node.methods.find { + it.name == "getDescriptor" + } + + if (constructor == null) { + throw new RuntimeException("Failed to find target constructor!") + } + + if (getDescriptor == null) { + throw new RuntimeException("Failed to find getDescriptor()") + } + + // Strip out this method - it only exists + // so that GradleRepositoryAdapter will compile + node.methods.remove(getDescriptor) + + def superInvoc = constructor.instructions.find { + it instanceof MethodInsnNode && it.owner == "org/gradle/api/internal/artifacts/repositories/AbstractArtifactRepository" + } as MethodInsnNode + + if (superInvoc == null) { + throw new RuntimeException("Failed to find target super() invocation!") + } + + superInvoc.desc = modifiedSuperDesc + + def aconstNull = superInvoc.previous + if (!(aconstNull instanceof InsnNode) || aconstNull.type != 0) { + throw new RuntimeException("Unexpected instruction: " + aconstNull) + } + + constructor.instructions.remove(aconstNull) + + // Push first parameter of constructor (the ObjectFactory) onto the stack. + // This has the effect of calling super(objectFactory) + //constructor.instructions.insertBefore(superInvoc, new VarInsnNode(Opcodes.ALOAD, 1)) + + ClassWriter writer = new ClassWriter(0) + node.accept(writer) + + def outputFile = outputDir.resolve(gradleRepositoryAdapter.toPath()).toFile() + + outputFile.parentFile.mkdirs() + FileOutputStream fs = new FileOutputStream(outputFile) + fs.write(writer.toByteArray()) + } +} + + + +task patchConstructor(type: PatchGradleRepositoryAdapter) { + constructorName = _constructorName + constructorDesc = _constructorDesc + modifiedSuperDesc = _modifiedSuperDesc + classesDir = classesDirs.toFile() + outputDir = Paths.get(buildDir.toString(), "modifiedclasses") + gradleRepositoryAdapter = gradleRepositoryAdapterPath.toFile() +} + jar { from sourceSets.api.output from sourceSets.shared.output - from sourceSets.gradlecomp.output + from(sourceSets.gradlecomp.output) { + exclude gradleRepositoryAdapterPath.toString() + } + + from patchConstructor.outputs +} + +jar.doLast { + def jarPath = it.outputs.files.getFiles().first() + def jarFile = new JarFile(jarPath) + def entry = jarFile.getEntry(gradleRepositoryAdapterPath.toString()) + def stream = jarFile.getInputStream(entry) + + ClassReader reader = new ClassReader(stream) + ClassNode node = new ClassNode() + reader.accept(node, 0) + + def constructor = node.methods.find { + it.name == _constructorName && it.desc == _constructorDesc + } + + def superInvoc = constructor.instructions.find { + it instanceof MethodInsnNode && it.owner == "org/gradle/api/internal/artifacts/repositories/AbstractArtifactRepository" + } as MethodInsnNode + + if (superInvoc.desc == _modifiedSuperDesc) { + println("Successfully modified super() call!") + } else { + throw new RuntimeException("Failed to modify super() invocation - got desc of " + superInvoc.desc) + } + + def getDescriptor = node.methods.find { it.name == "getDescriptor"} + if (getDescriptor != null) { + throw new RuntimeException("Failed to remove getDescriptor with signature: " + getDescriptor.signature) + } else { + println("Successfully stripped getDescriptor method!") + } } task sourcesJar(type: Jar, dependsOn: classes) { diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar Binary files differindex 30d399d..0d4a951 100644 --- a/gradle/wrapper/gradle-wrapper.jar +++ b/gradle/wrapper/gradle-wrapper.jar diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 5aa013a..2e7105d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Jun 09 16:26:43 CEST 2018 +#Thu Apr 25 19:18:54 EDT 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-all.zip @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -6,20 +6,38 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,31 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -90,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -114,6 +113,7 @@ fi if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` @@ -154,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 8a0b282..e95643d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,90 +1,84 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/src/gradlecomp/java/com/amadornes/artifactural/gradle/Gradle410RepositoryAdapter.java b/src/gradlecomp/java/com/amadornes/artifactural/gradle/Gradle410RepositoryAdapter.java new file mode 100644 index 0000000..ceaf989 --- /dev/null +++ b/src/gradlecomp/java/com/amadornes/artifactural/gradle/Gradle410RepositoryAdapter.java @@ -0,0 +1,42 @@ +/* + * Artifactural + * Copyright (c) 2018. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package com.amadornes.artifactural.gradle; + +import com.amadornes.artifactural.api.repository.Repository; +import org.gradle.api.internal.artifacts.repositories.DefaultMavenLocalArtifactRepository; +import org.gradle.api.internal.artifacts.repositories.descriptor.FlatDirRepositoryDescriptor; +import org.gradle.api.internal.artifacts.repositories.descriptor.RepositoryDescriptor; +import org.gradle.api.model.ObjectFactory; + +import java.util.ArrayList; + +public class Gradle410RepositoryAdapter extends GradleRepositoryAdapter { + + Gradle410RepositoryAdapter(ObjectFactory objectFactory, Repository repository, DefaultMavenLocalArtifactRepository local) { + super(objectFactory, repository, local); + } + + + @Override + public RepositoryDescriptor getDescriptor() { + return new FlatDirRepositoryDescriptor("ArtifacturalRepository", new ArrayList<>()); + } + +} diff --git a/src/gradlecomp/java/com/amadornes/artifactural/gradle/GradleRepositoryAdapter.java b/src/gradlecomp/java/com/amadornes/artifactural/gradle/GradleRepositoryAdapter.java index 64d6652..669ad4c 100644 --- a/src/gradlecomp/java/com/amadornes/artifactural/gradle/GradleRepositoryAdapter.java +++ b/src/gradlecomp/java/com/amadornes/artifactural/gradle/GradleRepositoryAdapter.java @@ -37,11 +37,14 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.Resol import org.gradle.api.internal.artifacts.repositories.AbstractArtifactRepository; import org.gradle.api.internal.artifacts.repositories.DefaultMavenLocalArtifactRepository; import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository; +import org.gradle.api.internal.artifacts.repositories.descriptor.FlatDirRepositoryDescriptor; +import org.gradle.api.internal.artifacts.repositories.descriptor.RepositoryDescriptor; import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceArtifactResolver; import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver; import org.gradle.api.internal.artifacts.repositories.resolver.MavenResolver; import org.gradle.api.internal.artifacts.repositories.resolver.MetadataFetchingCost; import org.gradle.api.internal.component.ArtifactType; +import org.gradle.api.model.ObjectFactory; import org.gradle.internal.action.InstantiatingAction; import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata; import org.gradle.internal.component.external.model.ModuleDependencyMetadata; @@ -65,10 +68,12 @@ import org.gradle.internal.resource.local.LocalFileStandInExternalResource; import org.gradle.internal.resource.local.LocallyAvailableExternalResource; import org.gradle.internal.resource.metadata.ExternalResourceMetaData; import org.gradle.internal.resource.transfer.DefaultCacheAwareExternalResourceAccessor; +import org.gradle.util.GradleVersion; import java.io.File; import java.io.IOException; import java.net.URI; +import java.util.ArrayList; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -85,7 +90,18 @@ public class GradleRepositoryAdapter extends AbstractArtifactRepository implemen maven.setUrl(local); maven.setName(name); - GradleRepositoryAdapter repo = new GradleRepositoryAdapter(repository, maven); + GradleRepositoryAdapter repo; + + // On Gradle 4.10 above, we need to use the constructor with the 'ObjectFactory' parameter + // (which can be safely passed as null - see BaseMavenInstaller). + // We use Gradle410RepositoryAdapter, which actually overrides 'getDescriptor' + if (GradleVersion.current().compareTo(GradleVersion.version("4.10")) >= 0) { + repo = new Gradle410RepositoryAdapter(null, repository, maven); + } else { + // On versions of gradle older than 4.10, we use the no-arg super constructor + repo = new GradleRepositoryAdapter(repository, maven); + } + repo.setName(name); handler.add(repo); return repo; @@ -96,7 +112,25 @@ public class GradleRepositoryAdapter extends AbstractArtifactRepository implemen private final String root; private final LocatedArtifactCache cache; + + // This constructor is modified via bytecode manipulation in 'build.gradle' + // DO NOT change this without modifying 'build.gradle' + // This contructor is used on Gradle 4.9 and below private GradleRepositoryAdapter(Repository repository, DefaultMavenLocalArtifactRepository local) { + // This is replaced with a call to 'super()', with no arguments + super(null); + this.repository = repository; + this.local = local; + this.root = cleanRoot(local.getUrl()); + this.cache = new LocatedArtifactCache(new File(root)); + } + + + // This constructor is used on Gradle 4.10 and above + GradleRepositoryAdapter(ObjectFactory objectFactory, Repository repository, DefaultMavenLocalArtifactRepository local) { + super(objectFactory); + // This duplication from the above two-argument constructor is unfortunate, + // but unavoidable this.repository = repository; this.local = local; this.root = cleanRoot(local.getUrl()); @@ -182,6 +216,24 @@ public class GradleRepositoryAdapter extends AbstractArtifactRepository implemen }; } + // This method will be deleted entirely in build.gradle + // In order for this class to compile, this method needs to exist + // at compile-time. However, the class 'RepositoryDescriptor' doesn't + // exist in Gradle 4.9. If we try to classload a class + // that contains RepositoryDescriptor as a method return type, + // the JVM will try to classload RepositoryDescriptor, leading + // to a NoClassDefFoundError + + // To fix this, we strip out this method at build time. + // At runtime, we instantiate Gradle410RepositoryAdapter + // when we're running on Gradle 4.10 on above. + // This ensures that 'getDescriptor' exists on Gradle 4.10, + // and doesn't existon Gradle 4.9 + public RepositoryDescriptor getDescriptor() { + throw new Error("This method should be been stripped at build time!"); + } + + private static String cleanRoot(URI uri) { String ret = uri.normalize().getPath().replace('\\', '/'); if (!ret.endsWith("/")) ret += '/'; |