diff --git a/server/src/main/java/com/defold/extender/Extender.java b/server/src/main/java/com/defold/extender/Extender.java index 46e9eade..61b47aad 100644 --- a/server/src/main/java/com/defold/extender/Extender.java +++ b/server/src/main/java/com/defold/extender/Extender.java @@ -1989,9 +1989,54 @@ private List getAllJars(Map extensionJarMap) thr Map context = createContext(mergedAppContext); List allJars = ExtenderUtil.pruneItems( (List)context.get("engineJars"), includeJars, excludeJars); allJars.addAll( ExtenderUtil.pruneItems( extensionJars, includeJars, excludeJars) ); + allJars = pruneConflictingAndroidMultidexJars(allJars); return allJars; } + private static boolean hasGradleDependency(List jars, String artifactPrefix) { + String escapedPrefix = Pattern.quote(artifactPrefix); + Pattern aarClassesPattern = Pattern.compile(String.format(".*/%s-[^/]+\\.aar/classes\\.jar$", escapedPrefix)); + Pattern jarPattern = Pattern.compile(String.format(".*/%s-[^/]+\\.jar$", escapedPrefix)); + + for (String jarPath : jars) { + String normalizedPath = jarPath.replace('\\', '/'); + if (aarClassesPattern.matcher(normalizedPath).matches() || jarPattern.matcher(normalizedPath).matches()) { + return true; + } + } + return false; + } + + private static boolean isEngineShareJavaJar(String jarPath, String engineJarName) { + return jarPath.replace('\\', '/').endsWith("/share/java/" + engineJarName); + } + + static List pruneConflictingAndroidMultidexJars(List jars) { + List prunedJars = new ArrayList<>(jars); + + if (hasGradleDependency(prunedJars, "androidx.multidex-multidex")) { + List removed = prunedJars.stream() + .filter(jarPath -> isEngineShareJavaJar(jarPath, "androidx-multidex.jar")) + .collect(Collectors.toList()); + if (!removed.isEmpty()) { + LOGGER.info("Removing engine androidx multidex jar(s) due to Gradle dependency: {}", removed); + prunedJars.removeAll(removed); + } + } + + if (hasGradleDependency(prunedJars, "com.android.support-multidex")) { + List removed = prunedJars.stream() + .filter(jarPath -> isEngineShareJavaJar(jarPath, "android-support-multidex.jar")) + .collect(Collectors.toList()); + if (!removed.isEmpty()) { + LOGGER.info("Removing engine support multidex jar(s) due to Gradle dependency: {}", removed); + prunedJars.removeAll(removed); + } + } + + return prunedJars; + } + // arguments: // jars - the list of all available jar files gathered from the build // extensionJarMap - a mapping from a jar file to a list of its corresponding proGuard contexts @@ -2136,6 +2181,13 @@ private File buildMainDexList(List jars) throws ExtenderException { // Find the engine libraries (**/share/java/*.jar) List mainListJars = ExtenderUtil.filterStrings(jars, ExtenderConst.ENGINE_JAR_RE); + // Keep multidex classes in the main dex even when engine multidex jars are replaced + // by Gradle dependencies. + mainListJars.addAll(ExtenderUtil.filterStrings(jars, "(?:.*)/androidx\\.multidex-multidex-[^/]+\\.aar/classes\\.jar$")); + mainListJars.addAll(ExtenderUtil.filterStrings(jars, "(?:.*)/androidx\\.multidex-multidex-[^/]+\\.jar$")); + mainListJars.addAll(ExtenderUtil.filterStrings(jars, "(?:.*)/com\\.android\\.support-multidex-[^/]+\\.aar/classes\\.jar$")); + mainListJars.addAll(ExtenderUtil.filterStrings(jars, "(?:.*)/com\\.android\\.support-multidex-[^/]+\\.jar$")); + mainListJars = ExtenderUtil.makeUnique(mainListJars); if (mainListJars.isEmpty()) { throw new ExtenderException("Regex failed to find any engine jars: " + ExtenderConst.ENGINE_JAR_RE); diff --git a/server/src/test/java/com/defold/extender/ExtenderTest.java b/server/src/test/java/com/defold/extender/ExtenderTest.java index 799673c9..90735cc3 100644 --- a/server/src/test/java/com/defold/extender/ExtenderTest.java +++ b/server/src/test/java/com/defold/extender/ExtenderTest.java @@ -336,6 +336,48 @@ public void testExcludeItems() throws IOException, InterruptedException, Extende } } + @Test + public void testPruneConflictingAndroidMultidexJarsAndroidX() { + List jars = new ArrayList<>(); + jars.add("/var/extender/sdk/hash/defoldsdk/ext/share/java/androidx-multidex.jar"); + jars.add("/var/extender/sdk/hash/defoldsdk/share/java/glfw_android.jar"); + jars.add("/tmp/.gradle/unpacked/androidx.multidex-multidex-2.0.1.aar/classes.jar"); + + List result = Extender.pruneConflictingAndroidMultidexJars(jars); + + assertFalse(result.contains("/var/extender/sdk/hash/defoldsdk/ext/share/java/androidx-multidex.jar")); + assertTrue(result.contains("/var/extender/sdk/hash/defoldsdk/share/java/glfw_android.jar")); + assertTrue(result.contains("/tmp/.gradle/unpacked/androidx.multidex-multidex-2.0.1.aar/classes.jar")); + } + + @Test + public void testPruneConflictingAndroidMultidexJarsSupportLib() { + List jars = new ArrayList<>(); + jars.add("/var/extender/sdk/hash/defoldsdk/ext/share/java/android-support-multidex.jar"); + jars.add("/var/extender/sdk/hash/defoldsdk/share/java/glfw_android.jar"); + jars.add("/tmp/.gradle/unpacked/com.android.support-multidex-1.0.3.aar/classes.jar"); + + List result = Extender.pruneConflictingAndroidMultidexJars(jars); + + assertFalse(result.contains("/var/extender/sdk/hash/defoldsdk/ext/share/java/android-support-multidex.jar")); + assertTrue(result.contains("/var/extender/sdk/hash/defoldsdk/share/java/glfw_android.jar")); + assertTrue(result.contains("/tmp/.gradle/unpacked/com.android.support-multidex-1.0.3.aar/classes.jar")); + } + + @Test + public void testPruneConflictingAndroidMultidexJarsNoGradleMultidex() { + List jars = new ArrayList<>(); + jars.add("/var/extender/sdk/hash/defoldsdk/ext/share/java/androidx-multidex.jar"); + jars.add("/var/extender/sdk/hash/defoldsdk/share/java/glfw_android.jar"); + jars.add("/tmp/.gradle/unpacked/com.google.android.gms-play-services-ads-24.0.0.aar/classes.jar"); + + List result = Extender.pruneConflictingAndroidMultidexJars(jars); + + assertTrue(result.contains("/var/extender/sdk/hash/defoldsdk/ext/share/java/androidx-multidex.jar")); + assertTrue(result.contains("/var/extender/sdk/hash/defoldsdk/share/java/glfw_android.jar")); + assertTrue(result.contains("/tmp/.gradle/unpacked/com.google.android.gms-play-services-ads-24.0.0.aar/classes.jar")); + } + @Test public void testAppManifestContext() throws IOException, ExtenderException {