Revert "Revert "Support for testing different dependency versions""

This reverts commit 4e0409161cc998c8ac8004ff78b6ee404fb37cc2.

Reason for revert: Now that we are pushing the new updated AGP, this OutOfMemory error has a high chance of disappearing, so we are going to test one more time.

Test: ./gradlew bOS

Change-Id: I01b69ca02683a1bfff1dafabb960c344bf73c17b
diff --git a/buildSrc/init.gradle b/buildSrc/init.gradle
index 410952c..7674773 100644
--- a/buildSrc/init.gradle
+++ b/buildSrc/init.gradle
@@ -90,8 +90,10 @@
         // Copy instrumentation test APKs and app APKs into the dist dir
         // For test apks, they are uploaded only if we have java test sources.
         // For regular app apks, they are uploaded only if they have java sources.
+        // Copy only the minDepVersions debug version since that is the publish
+        // flavor.
         project.tasks.whenTaskAdded { task ->
-            if (task.name.startsWith("packageDebug")) {
+            if (task.name.startsWith("packageMinDepVersionsDebug")) {
                 // run this task only if we should run it
                 def testApk = task.name.contains("AndroidTest")
                 if (testApk) {
@@ -122,7 +124,10 @@
                                 // Exclude media-compat-test-* modules from existing support library
                                 // presubmit tests.
                                 if (fileName.contains("media-compat-test")) {
-                                    fileName.replace("-debug-androidTest", "")
+                                    fileName
+                                            .replace(fileName,
+                                            "minDepVersions-${fileName}")
+                                            .replace("-minDepVersions-debug-androidTest", "")
                                 } else {
                                     // multiple modules may have the same name so prefix the name with
                                     // the module's path to ensure it is unique.
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
index 948eb00..b1b6f61 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
@@ -36,11 +36,13 @@
 import org.gradle.api.JavaVersion.VERSION_1_8
 import org.gradle.api.Plugin
 import org.gradle.api.Project
+import org.gradle.api.artifacts.ConfigurationContainer
 import org.gradle.api.plugins.JavaLibraryPlugin
 import org.gradle.api.plugins.JavaPlugin
 import org.gradle.api.plugins.JavaPluginConvention
 import org.gradle.api.tasks.bundling.Jar
 import org.gradle.api.tasks.compile.JavaCompile
+import org.gradle.kotlin.dsl.get
 import org.gradle.kotlin.dsl.getPlugin
 import org.gradle.kotlin.dsl.withType
 
@@ -157,7 +159,6 @@
     private fun Project.configureAndroidCommonOptions(extension: BaseExtension) {
         extension.compileSdkVersion(CURRENT_SDK_VERSION)
         extension.buildToolsVersion = BUILD_TOOLS_VERSION
-
         // Expose the compilation SDK for use as the target SDK in test manifests.
         extension.defaultConfig.addManifestPlaceholders(
                 mapOf("target-sdk-version" to CURRENT_SDK_VERSION))
@@ -177,6 +178,35 @@
                 project.dependencies.add("annotationProcessor",
                         project.project(":customerrorprone"))
             }
+            project.configurations.all { configuration ->
+                configuration.resolutionStrategy.eachDependency { dep ->
+                    val target = dep.target
+                    // Enforce the ban on declaring dependencies with version ranges.
+                    if (isDependencyRange(target.version)) {
+                        throw IllegalArgumentException(
+                                "Dependency ${dep.target} declares its version as " +
+                                        "version range ${dep.target.version} however the use of " +
+                                        "version ranges is not allowed, please update the " +
+                                        "dependency to list a fixed version.")
+                    }
+                }
+            }
+        }
+        if (project.name != "docs-fake") {
+            // Add another "version" flavor dimension which would have two flavors minDepVersions
+            // and maxDepVersions. Flavor minDepVersions builds the libraries against the specified
+            // versions of their dependencies while maxDepVersions builds the libraries against
+            // the local versions of their dependencies (so for example if library A specifies
+            // androidx.collection:collection:1.2.0 as its dependency then minDepVersions would
+            // build using exactly that version while maxDepVersions would build against
+            // project(":collection") instead.)
+            extension.flavorDimensions("version")
+            extension.productFlavors {
+                it.create("minDepVersions")
+                it.get("minDepVersions").dimension = "version"
+                it.create("maxDepVersions")
+                it.get("maxDepVersions").dimension = "version"
+            }
         }
 
         // Use a local debug keystore to avoid build server issues.
@@ -194,6 +224,16 @@
         extension.buildTypes.getByName("debug").isTestCoverageEnabled =
                 !hasProperty("android.injected.invoked.from.ide") &&
                 !isBenchmark()
+
+        extension.variants.all { variant ->
+            if (variant.flavorName.toLowerCase().contains(
+                            "maxdepversions")) {
+                useMaxiumumDependencyVersions(project.configurations)
+            }
+        }
+        // Set the officially published version to be the release version with minimum dependency
+        // versions.
+        extension.defaultPublishConfig(Release.DEFAULT_PUBLISH_CONFIG)
     }
 
     private fun Project.configureAndroidLibraryOptions(extension: LibraryExtension) {
@@ -253,3 +293,50 @@
     // benchmark convention is to end name with "-benchmark"
     return name.endsWith("-benchmark")
 }
+
+/**
+ * Goes through all the dependencies in the passed in configurations and finds each dependency
+ * depending on an androidx library, then each of these dependencies is substituted with its
+ * equivalent in the current development project (e.g module("androidx.collection:collection:x.y.z")
+ * becomes project(":collection".) Throws an error if there is a major release conflict between
+ * the two dependency versions.
+ */
+private fun Project.useMaxiumumDependencyVersions(configurations: ConfigurationContainer) {
+    configurations.all { configuration ->
+        configuration.resolutionStrategy.eachDependency { dep ->
+            if (artifactSupportsSemVer(dep.target.group)) {
+                // TODO: support projects having two ':' chars in the name
+                val localDependencyProject = findProject(":${dep.target.name}")
+                if (localDependencyProject != null &&
+                        localDependencyProject.version.toString() != "unspecified") {
+                    if (localDependencyProject.version().major ==
+                            Version(dep.target.version!!).major) {
+                        configuration.resolutionStrategy.dependencySubstitution.apply {
+                            substitute(module("${dep.target}"))
+                                    .with(project(":${localDependencyProject.name}"))
+                        }
+                    } else {
+                        throw IllegalArgumentException("The local version for dependency" +
+                                " ${localDependencyProject.name} is in major release" +
+                                " ${localDependencyProject.version().major}, but the " +
+                                "specified dependency's major release is " +
+                                "${Version(dep.target.version!!).major}" +
+                                ", please update the dependency's version to match " +
+                                "that major release as it is required that all libraries at HEAD " +
+                                "are compatible with each other at all times")
+                    }
+                }
+            }
+        }
+    }
+}
+
+private fun isDependencyRange(version: String?): Boolean {
+    return ((version!!.startsWith("[") || version.startsWith("(")) &&
+            (version.endsWith("]") || version.endsWith(")")) ||
+            version.endsWith("+"))
+}
+
+private fun artifactSupportsSemVer(artifactGroup: String): Boolean {
+    return artifactGroup.startsWith("androidx.")
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/androidx/build/DiffAndDocs.kt b/buildSrc/src/main/kotlin/androidx/build/DiffAndDocs.kt
index d2af2b9..560c87c 100644
--- a/buildSrc/src/main/kotlin/androidx/build/DiffAndDocs.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/DiffAndDocs.kt
@@ -291,9 +291,8 @@
     ) {
 
         registerPrebuilts(extension)
-
         library.libraryVariants.all { variant ->
-            if (variant.name == "release") {
+            if (variant.name == Release.DEFAULT_PUBLISH_CONFIG) {
                 // include R.file generated for prebuilts
                 rules.filter { it.resolve(extension)?.strategy is Prebuilts }.forEach { rule ->
                     docsTasks[rule.name]?.include { fileTreeElement ->
diff --git a/buildSrc/src/main/kotlin/androidx/build/Release.kt b/buildSrc/src/main/kotlin/androidx/build/Release.kt
index 7bdf64e..0161039 100644
--- a/buildSrc/src/main/kotlin/androidx/build/Release.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/Release.kt
@@ -174,6 +174,7 @@
     @Suppress("MemberVisibilityCanBePrivate")
     const val DIFF_TASK_PREFIX = "createDiffArchive"
     const val FULL_ARCHIVE_TASK_NAME = "createArchive"
+    const val DEFAULT_PUBLISH_CONFIG = "minDepVersionsRelease"
     // lazily created config action params so that we don't keep re-creating them
     private var configActionParams: GMavenZipTask.ConfigAction.Params? = null
 
diff --git a/core/build.gradle b/core/build.gradle
index 5a76b23..6f57be3 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -8,7 +8,7 @@
 
 dependencies {
     api(project(":annotation"))
-    api(project(":collection"))
+    api("androidx.collection:collection:1.0.0")
     api(ARCH_LIFECYCLE_RUNTIME, libs.exclude_annotations_transitive)
     api project(':versionedparcelable')
 
diff --git a/docs-fake/build.gradle b/docs-fake/build.gradle
index aa268d1..e73ba9b 100644
--- a/docs-fake/build.gradle
+++ b/docs-fake/build.gradle
@@ -44,5 +44,5 @@
             manifest.srcFile "AndroidManifest.xml"
         }
     }
-    flavorDimensions("library-group")
+    flavorDimensions "library-group"
 }
diff --git a/navigation/common/build.gradle b/navigation/common/build.gradle
index e55c040..24fd3eb 100644
--- a/navigation/common/build.gradle
+++ b/navigation/common/build.gradle
@@ -49,7 +49,7 @@
 
 //used by testCompile safe-args-generator
 android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
+    def name = variant.name
     def suffix = name.capitalize()
     project.tasks.create(name: "jar${suffix}", type: Jar){
         dependsOn variant.javaCompile
diff --git a/navigation/safe-args-generator/build.gradle b/navigation/safe-args-generator/build.gradle
index b2eba0b..2c086b7 100644
--- a/navigation/safe-args-generator/build.gradle
+++ b/navigation/safe-args-generator/build.gradle
@@ -54,7 +54,7 @@
     it.classpath = files(classpath.minus(androidJar).plus(androidJar))
 }
 
-tasks.findByName("compileKotlin").dependsOn(":navigation:navigation-common:jarDebug")
+tasks.findByName("compileKotlin").dependsOn(":navigation:navigation-common:jarMinDepVersionsDebug")
 
 supportLibrary {
     name = 'Android Navigation TypeSafe Arguments Generator'
diff --git a/persistence/db/build.gradle b/persistence/db/build.gradle
index f8730cb..299502b 100644
--- a/persistence/db/build.gradle
+++ b/persistence/db/build.gradle
@@ -31,7 +31,7 @@
 
 // Used by testCompile in room-compiler
 android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
+    def name = variant.name
     def suffix = name.capitalize()
     def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
         dependsOn variant.javaCompile
diff --git a/room/compiler/build.gradle b/room/compiler/build.gradle
index 76efe9a..6a35f0a 100644
--- a/room/compiler/build.gradle
+++ b/room/compiler/build.gradle
@@ -79,8 +79,8 @@
 }
 
 tasks.findByName("compileKotlin").dependsOn(generateAntlrTask)
-tasks.findByName("compileKotlin").dependsOn(":room:room-runtime:jarDebug")
-tasks.findByName("compileKotlin").dependsOn(":sqlite:sqlite:jarDebug")
+tasks.findByName("compileKotlin").dependsOn(":room:room-runtime:jarMinDepVersionsDebug")
+tasks.findByName("compileKotlin").dependsOn(":sqlite:sqlite:jarMinDepVersionsDebug")
 
 supportLibrary {
     name = "Android Room Compiler"
diff --git a/room/runtime/build.gradle b/room/runtime/build.gradle
index eb262ed..7e8100d 100644
--- a/room/runtime/build.gradle
+++ b/room/runtime/build.gradle
@@ -52,7 +52,7 @@
 
 // Used by testCompile in room-compiler
 android.libraryVariants.all { variant ->
-    def name = variant.buildType.name
+    def name = variant.name
     def suffix = name.capitalize()
     project.tasks.create(name: "jar${suffix}", type: Jar){
         dependsOn variant.javaCompile