diff --git a/.idea/git_toolbox_prj.xml b/.idea/git_toolbox_prj.xml new file mode 100644 index 00000000..02b915b8 --- /dev/null +++ b/.idea/git_toolbox_prj.xml @@ -0,0 +1,15 @@ + + + + + + + \ No newline at end of file diff --git a/WORKSPACE b/WORKSPACE index 202256f0..ffdd6388 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -27,7 +27,7 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") git_repository( name = "grab_bazel_common", - commit = "120019c5290cceda3c795d9ddb2a3253b9b32b3e", + commit = "4a18937b1a30f11996742f34587d50a5aa1f04f4", remote = "https://github.com/grab/grab-bazel-common.git", ) @@ -71,42 +71,657 @@ load("@rules_jvm_external//:defs.bzl", "maven_install") load("@rules_jvm_external//:specs.bzl", "maven") maven_install( + name = "flavor1free_debug_android_test_maven", + artifacts = [ + "androidx.annotation:annotation-experimental:1.3.0", + "androidx.annotation:annotation-jvm:1.6.0", + "androidx.annotation:annotation:1.6.0", + "androidx.arch.core:core-common:2.2.0", + "androidx.arch.core:core-runtime:2.1.0", + "androidx.collection:collection:1.1.0", + "androidx.concurrent:concurrent-futures:1.1.0", + "androidx.core:core-ktx:1.2.0", + "androidx.core:core:1.10.1", + "androidx.lifecycle:lifecycle-common:2.6.1", + "androidx.lifecycle:lifecycle-livedata-core:2.6.1", + "androidx.lifecycle:lifecycle-livedata:2.6.1", + "androidx.lifecycle:lifecycle-process:2.6.1", + "androidx.lifecycle:lifecycle-runtime-ktx:2.6.1", + "androidx.lifecycle:lifecycle-runtime:2.6.1", + "androidx.lifecycle:lifecycle-service:2.6.1", + "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1", + "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.1", + "androidx.lifecycle:lifecycle-viewmodel:2.6.1", + "androidx.savedstate:savedstate-ktx:1.2.1", + "androidx.savedstate:savedstate:1.2.1", + "androidx.startup:startup-runtime:1.1.1", + "androidx.test.espresso:espresso-core:3.5.1", + "androidx.test.espresso:espresso-idling-resource:3.5.1", + "androidx.test.ext:junit:1.1.5", + "androidx.test.services:storage:1.4.2", + "androidx.test:annotation:1.0.1", + "androidx.test:core:1.5.0", + "androidx.test:monitor:1.6.1", + "androidx.test:runner:1.5.2", + "androidx.tracing:tracing:1.0.0", + "androidx.versionedparcelable:versionedparcelable:1.1.1", + "com.google.code.findbugs:jsr305:2.0.2", + "com.google.guava:listenablefuture:1.0", + "com.squareup:javawriter:2.1.1", + "javax.inject:javax.inject:1", + "junit:junit:4.13.2", + "org.hamcrest:hamcrest-core:1.3", + "org.hamcrest:hamcrest-integration:1.3", + "org.hamcrest:hamcrest-library:1.3", + "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4", + "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4", + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4", + "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4", + ], + excluded_artifacts = ["androidx.test.espresso:espresso-contrib"], + fail_on_missing_checksum = False, + jetify = True, + jetify_include_list = [ + "androidx.annotation:annotation", + "androidx.annotation:annotation-experimental", + "androidx.annotation:annotation-jvm", + "androidx.arch.core:core-common", + "androidx.arch.core:core-runtime", + "androidx.collection:collection", + "androidx.concurrent:concurrent-futures", + "androidx.core:core", + "androidx.core:core-ktx", + "androidx.lifecycle:lifecycle-common", + "androidx.lifecycle:lifecycle-livedata", + "androidx.lifecycle:lifecycle-livedata-core", + "androidx.lifecycle:lifecycle-process", + "androidx.lifecycle:lifecycle-runtime", + "androidx.lifecycle:lifecycle-runtime-ktx", + "androidx.lifecycle:lifecycle-service", + "androidx.lifecycle:lifecycle-viewmodel", + "androidx.lifecycle:lifecycle-viewmodel-ktx", + "androidx.lifecycle:lifecycle-viewmodel-savedstate", + "androidx.savedstate:savedstate", + "androidx.savedstate:savedstate-ktx", + "androidx.startup:startup-runtime", + "androidx.test.espresso:espresso-core", + "androidx.test.espresso:espresso-idling-resource", + "androidx.test.ext:junit", + "androidx.test.services:storage", + "androidx.test:annotation", + "androidx.test:core", + "androidx.test:monitor", + "androidx.test:runner", + "androidx.tracing:tracing", + "androidx.versionedparcelable:versionedparcelable", + "com.android.support:cardview-v7", + "com.google.code.findbugs:jsr305", + "com.google.guava:listenablefuture", + "com.squareup:javawriter", + "javax.inject:javax.inject", + "junit:junit", + "org.hamcrest:hamcrest-core", + "org.hamcrest:hamcrest-integration", + "org.hamcrest:hamcrest-library", + "org.jetbrains.kotlinx:kotlinx-coroutines-android", + "org.jetbrains.kotlinx:kotlinx-coroutines-bom", + "org.jetbrains.kotlinx:kotlinx-coroutines-core", + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm", + ], + override_targets = { + "androidx.annotation:annotation": "@maven//:androidx_annotation_annotation", + "androidx.annotation:annotation-experimental": "@maven//:androidx_annotation_annotation_experimental", + "androidx.arch.core:core-common": "@maven//:androidx_arch_core_core_common", + "androidx.arch.core:core-runtime": "@maven//:androidx_arch_core_core_runtime", + "androidx.collection:collection": "@maven//:androidx_collection_collection", + "androidx.core:core": "@maven//:androidx_core_core", + "androidx.core:core-ktx": "@maven//:androidx_core_core_ktx", + "androidx.lifecycle:lifecycle-common": "@maven//:androidx_lifecycle_lifecycle_common", + "androidx.lifecycle:lifecycle-livedata": "@maven//:androidx_lifecycle_lifecycle_livedata", + "androidx.lifecycle:lifecycle-livedata-core": "@maven//:androidx_lifecycle_lifecycle_livedata_core", + "androidx.lifecycle:lifecycle-process": "@maven//:androidx_lifecycle_lifecycle_process", + "androidx.lifecycle:lifecycle-runtime": "@maven//:androidx_lifecycle_lifecycle_runtime", + "androidx.lifecycle:lifecycle-runtime-ktx": "@maven//:androidx_lifecycle_lifecycle_runtime_ktx", + "androidx.lifecycle:lifecycle-service": "@maven//:androidx_lifecycle_lifecycle_service", + "androidx.lifecycle:lifecycle-viewmodel": "@maven//:androidx_lifecycle_lifecycle_viewmodel", + "androidx.lifecycle:lifecycle-viewmodel-ktx": "@maven//:androidx_lifecycle_lifecycle_viewmodel_ktx", + "androidx.lifecycle:lifecycle-viewmodel-savedstate": "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate", + "androidx.savedstate:savedstate": "@maven//:androidx_savedstate_savedstate", + "androidx.savedstate:savedstate-ktx": "@maven//:androidx_savedstate_savedstate_ktx", + "androidx.startup:startup-runtime": "@maven//:androidx_startup_startup_runtime", + "androidx.versionedparcelable:versionedparcelable": "@maven//:androidx_versionedparcelable_versionedparcelable", + "com.google.code.findbugs:jsr305": "@maven//:com_google_code_findbugs_jsr305", + "com.google.guava:listenablefuture": "@maven//:com_google_guava_listenablefuture", + "javax.inject:javax.inject": "@maven//:javax_inject_javax_inject", + "org.jetbrains.kotlinx:kotlinx-coroutines-android": "@maven//:org_jetbrains_kotlinx_kotlinx_coroutines_android", + "org.jetbrains.kotlinx:kotlinx-coroutines-bom": "@maven//:org_jetbrains_kotlinx_kotlinx_coroutines_bom", + "org.jetbrains.kotlinx:kotlinx-coroutines-core": "@maven//:org_jetbrains_kotlinx_kotlinx_coroutines_core", + }, + repositories = [ + "https://dl.google.com/dl/android/maven2/", + "https://repo.maven.apache.org/maven2/", + ], + resolve_timeout = 1000, + version_conflict_policy = "pinned", +) + +maven_install( + name = "flavor1paid_debug_android_test_maven", + artifacts = [ + "androidx.annotation:annotation-experimental:1.3.0", + "androidx.annotation:annotation-jvm:1.6.0", + "androidx.annotation:annotation:1.6.0", + "androidx.arch.core:core-common:2.2.0", + "androidx.arch.core:core-runtime:2.1.0", + "androidx.collection:collection:1.1.0", + "androidx.concurrent:concurrent-futures:1.1.0", + "androidx.core:core-ktx:1.2.0", + "androidx.core:core:1.10.1", + "androidx.lifecycle:lifecycle-common:2.6.1", + "androidx.lifecycle:lifecycle-livedata-core:2.6.1", + "androidx.lifecycle:lifecycle-livedata:2.6.1", + "androidx.lifecycle:lifecycle-process:2.6.1", + "androidx.lifecycle:lifecycle-runtime-ktx:2.6.1", + "androidx.lifecycle:lifecycle-runtime:2.6.1", + "androidx.lifecycle:lifecycle-service:2.6.1", + "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1", + "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.1", + "androidx.lifecycle:lifecycle-viewmodel:2.6.1", + "androidx.savedstate:savedstate-ktx:1.2.1", + "androidx.savedstate:savedstate:1.2.1", + "androidx.startup:startup-runtime:1.1.1", + "androidx.test.espresso:espresso-core:3.5.1", + "androidx.test.espresso:espresso-idling-resource:3.5.1", + "androidx.test.ext:junit:1.1.5", + "androidx.test.services:storage:1.4.2", + "androidx.test:annotation:1.0.1", + "androidx.test:core:1.5.0", + "androidx.test:monitor:1.6.1", + "androidx.test:runner:1.5.2", + "androidx.tracing:tracing:1.0.0", + "androidx.versionedparcelable:versionedparcelable:1.1.1", + "com.google.code.findbugs:jsr305:2.0.2", + "com.google.guava:listenablefuture:1.0", + "com.squareup:javawriter:2.1.1", + "javax.inject:javax.inject:1", + "junit:junit:4.13.2", + "org.hamcrest:hamcrest-core:1.3", + "org.hamcrest:hamcrest-integration:1.3", + "org.hamcrest:hamcrest-library:1.3", + "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4", + "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4", + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4", + "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4", + ], + excluded_artifacts = ["androidx.test.espresso:espresso-contrib"], + fail_on_missing_checksum = False, + jetify = True, + jetify_include_list = [ + "androidx.annotation:annotation", + "androidx.annotation:annotation-experimental", + "androidx.annotation:annotation-jvm", + "androidx.arch.core:core-common", + "androidx.arch.core:core-runtime", + "androidx.collection:collection", + "androidx.concurrent:concurrent-futures", + "androidx.core:core", + "androidx.core:core-ktx", + "androidx.lifecycle:lifecycle-common", + "androidx.lifecycle:lifecycle-livedata", + "androidx.lifecycle:lifecycle-livedata-core", + "androidx.lifecycle:lifecycle-process", + "androidx.lifecycle:lifecycle-runtime", + "androidx.lifecycle:lifecycle-runtime-ktx", + "androidx.lifecycle:lifecycle-service", + "androidx.lifecycle:lifecycle-viewmodel", + "androidx.lifecycle:lifecycle-viewmodel-ktx", + "androidx.lifecycle:lifecycle-viewmodel-savedstate", + "androidx.savedstate:savedstate", + "androidx.savedstate:savedstate-ktx", + "androidx.startup:startup-runtime", + "androidx.test.espresso:espresso-core", + "androidx.test.espresso:espresso-idling-resource", + "androidx.test.ext:junit", + "androidx.test.services:storage", + "androidx.test:annotation", + "androidx.test:core", + "androidx.test:monitor", + "androidx.test:runner", + "androidx.tracing:tracing", + "androidx.versionedparcelable:versionedparcelable", + "com.android.support:cardview-v7", + "com.google.code.findbugs:jsr305", + "com.google.guava:listenablefuture", + "com.squareup:javawriter", + "javax.inject:javax.inject", + "junit:junit", + "org.hamcrest:hamcrest-core", + "org.hamcrest:hamcrest-integration", + "org.hamcrest:hamcrest-library", + "org.jetbrains.kotlinx:kotlinx-coroutines-android", + "org.jetbrains.kotlinx:kotlinx-coroutines-bom", + "org.jetbrains.kotlinx:kotlinx-coroutines-core", + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm", + ], + override_targets = { + "androidx.annotation:annotation": "@maven//:androidx_annotation_annotation", + "androidx.annotation:annotation-experimental": "@maven//:androidx_annotation_annotation_experimental", + "androidx.arch.core:core-common": "@maven//:androidx_arch_core_core_common", + "androidx.arch.core:core-runtime": "@maven//:androidx_arch_core_core_runtime", + "androidx.collection:collection": "@maven//:androidx_collection_collection", + "androidx.core:core": "@maven//:androidx_core_core", + "androidx.core:core-ktx": "@maven//:androidx_core_core_ktx", + "androidx.lifecycle:lifecycle-common": "@maven//:androidx_lifecycle_lifecycle_common", + "androidx.lifecycle:lifecycle-livedata": "@maven//:androidx_lifecycle_lifecycle_livedata", + "androidx.lifecycle:lifecycle-livedata-core": "@maven//:androidx_lifecycle_lifecycle_livedata_core", + "androidx.lifecycle:lifecycle-process": "@maven//:androidx_lifecycle_lifecycle_process", + "androidx.lifecycle:lifecycle-runtime": "@maven//:androidx_lifecycle_lifecycle_runtime", + "androidx.lifecycle:lifecycle-runtime-ktx": "@maven//:androidx_lifecycle_lifecycle_runtime_ktx", + "androidx.lifecycle:lifecycle-service": "@maven//:androidx_lifecycle_lifecycle_service", + "androidx.lifecycle:lifecycle-viewmodel": "@maven//:androidx_lifecycle_lifecycle_viewmodel", + "androidx.lifecycle:lifecycle-viewmodel-ktx": "@maven//:androidx_lifecycle_lifecycle_viewmodel_ktx", + "androidx.lifecycle:lifecycle-viewmodel-savedstate": "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate", + "androidx.savedstate:savedstate": "@maven//:androidx_savedstate_savedstate", + "androidx.savedstate:savedstate-ktx": "@maven//:androidx_savedstate_savedstate_ktx", + "androidx.startup:startup-runtime": "@maven//:androidx_startup_startup_runtime", + "androidx.versionedparcelable:versionedparcelable": "@maven//:androidx_versionedparcelable_versionedparcelable", + "com.google.code.findbugs:jsr305": "@maven//:com_google_code_findbugs_jsr305", + "com.google.guava:listenablefuture": "@maven//:com_google_guava_listenablefuture", + "javax.inject:javax.inject": "@maven//:javax_inject_javax_inject", + "org.jetbrains.kotlinx:kotlinx-coroutines-android": "@maven//:org_jetbrains_kotlinx_kotlinx_coroutines_android", + "org.jetbrains.kotlinx:kotlinx-coroutines-bom": "@maven//:org_jetbrains_kotlinx_kotlinx_coroutines_bom", + "org.jetbrains.kotlinx:kotlinx-coroutines-core": "@maven//:org_jetbrains_kotlinx_kotlinx_coroutines_core", + }, + repositories = [ + "https://dl.google.com/dl/android/maven2/", + "https://repo.maven.apache.org/maven2/", + ], + resolve_timeout = 1000, + version_conflict_policy = "pinned", +) + +maven_install( + name = "flavor2free_debug_android_test_maven", + artifacts = [ + "androidx.annotation:annotation-experimental:1.3.0", + "androidx.annotation:annotation-jvm:1.6.0", + "androidx.annotation:annotation:1.6.0", + "androidx.arch.core:core-common:2.2.0", + "androidx.arch.core:core-runtime:2.1.0", + "androidx.collection:collection:1.1.0", + "androidx.concurrent:concurrent-futures:1.1.0", + "androidx.core:core-ktx:1.2.0", + "androidx.core:core:1.10.1", + "androidx.lifecycle:lifecycle-common:2.6.1", + "androidx.lifecycle:lifecycle-livedata-core:2.6.1", + "androidx.lifecycle:lifecycle-livedata:2.6.1", + "androidx.lifecycle:lifecycle-process:2.6.1", + "androidx.lifecycle:lifecycle-runtime-ktx:2.6.1", + "androidx.lifecycle:lifecycle-runtime:2.6.1", + "androidx.lifecycle:lifecycle-service:2.6.1", + "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1", + "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.1", + "androidx.lifecycle:lifecycle-viewmodel:2.6.1", + "androidx.savedstate:savedstate-ktx:1.2.1", + "androidx.savedstate:savedstate:1.2.1", + "androidx.startup:startup-runtime:1.1.1", + "androidx.test.espresso:espresso-core:3.5.1", + "androidx.test.espresso:espresso-idling-resource:3.5.1", + "androidx.test.ext:junit:1.1.5", + "androidx.test.services:storage:1.4.2", + "androidx.test:annotation:1.0.1", + "androidx.test:core:1.5.0", + "androidx.test:monitor:1.6.1", + "androidx.test:runner:1.5.2", + "androidx.tracing:tracing:1.0.0", + "androidx.versionedparcelable:versionedparcelable:1.1.1", + "com.google.code.findbugs:jsr305:2.0.2", + "com.google.guava:listenablefuture:1.0", + "com.squareup:javawriter:2.1.1", + "javax.inject:javax.inject:1", + "junit:junit:4.13.2", + "org.hamcrest:hamcrest-core:1.3", + "org.hamcrest:hamcrest-integration:1.3", + "org.hamcrest:hamcrest-library:1.3", + "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4", + "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4", + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4", + "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4", + ], + excluded_artifacts = ["androidx.test.espresso:espresso-contrib"], + fail_on_missing_checksum = False, + jetify = True, + jetify_include_list = [ + "androidx.annotation:annotation", + "androidx.annotation:annotation-experimental", + "androidx.annotation:annotation-jvm", + "androidx.arch.core:core-common", + "androidx.arch.core:core-runtime", + "androidx.collection:collection", + "androidx.concurrent:concurrent-futures", + "androidx.core:core", + "androidx.core:core-ktx", + "androidx.lifecycle:lifecycle-common", + "androidx.lifecycle:lifecycle-livedata", + "androidx.lifecycle:lifecycle-livedata-core", + "androidx.lifecycle:lifecycle-process", + "androidx.lifecycle:lifecycle-runtime", + "androidx.lifecycle:lifecycle-runtime-ktx", + "androidx.lifecycle:lifecycle-service", + "androidx.lifecycle:lifecycle-viewmodel", + "androidx.lifecycle:lifecycle-viewmodel-ktx", + "androidx.lifecycle:lifecycle-viewmodel-savedstate", + "androidx.savedstate:savedstate", + "androidx.savedstate:savedstate-ktx", + "androidx.startup:startup-runtime", + "androidx.test.espresso:espresso-core", + "androidx.test.espresso:espresso-idling-resource", + "androidx.test.ext:junit", + "androidx.test.services:storage", + "androidx.test:annotation", + "androidx.test:core", + "androidx.test:monitor", + "androidx.test:runner", + "androidx.tracing:tracing", + "androidx.versionedparcelable:versionedparcelable", + "com.android.support:cardview-v7", + "com.google.code.findbugs:jsr305", + "com.google.guava:listenablefuture", + "com.squareup:javawriter", + "javax.inject:javax.inject", + "junit:junit", + "org.hamcrest:hamcrest-core", + "org.hamcrest:hamcrest-integration", + "org.hamcrest:hamcrest-library", + "org.jetbrains.kotlinx:kotlinx-coroutines-android", + "org.jetbrains.kotlinx:kotlinx-coroutines-bom", + "org.jetbrains.kotlinx:kotlinx-coroutines-core", + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm", + ], + override_targets = { + "androidx.annotation:annotation": "@maven//:androidx_annotation_annotation", + "androidx.annotation:annotation-experimental": "@maven//:androidx_annotation_annotation_experimental", + "androidx.arch.core:core-common": "@maven//:androidx_arch_core_core_common", + "androidx.arch.core:core-runtime": "@maven//:androidx_arch_core_core_runtime", + "androidx.collection:collection": "@maven//:androidx_collection_collection", + "androidx.core:core": "@maven//:androidx_core_core", + "androidx.core:core-ktx": "@maven//:androidx_core_core_ktx", + "androidx.lifecycle:lifecycle-common": "@maven//:androidx_lifecycle_lifecycle_common", + "androidx.lifecycle:lifecycle-livedata": "@maven//:androidx_lifecycle_lifecycle_livedata", + "androidx.lifecycle:lifecycle-livedata-core": "@maven//:androidx_lifecycle_lifecycle_livedata_core", + "androidx.lifecycle:lifecycle-process": "@maven//:androidx_lifecycle_lifecycle_process", + "androidx.lifecycle:lifecycle-runtime": "@maven//:androidx_lifecycle_lifecycle_runtime", + "androidx.lifecycle:lifecycle-runtime-ktx": "@maven//:androidx_lifecycle_lifecycle_runtime_ktx", + "androidx.lifecycle:lifecycle-service": "@maven//:androidx_lifecycle_lifecycle_service", + "androidx.lifecycle:lifecycle-viewmodel": "@maven//:androidx_lifecycle_lifecycle_viewmodel", + "androidx.lifecycle:lifecycle-viewmodel-ktx": "@maven//:androidx_lifecycle_lifecycle_viewmodel_ktx", + "androidx.lifecycle:lifecycle-viewmodel-savedstate": "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate", + "androidx.savedstate:savedstate": "@maven//:androidx_savedstate_savedstate", + "androidx.savedstate:savedstate-ktx": "@maven//:androidx_savedstate_savedstate_ktx", + "androidx.startup:startup-runtime": "@maven//:androidx_startup_startup_runtime", + "androidx.versionedparcelable:versionedparcelable": "@maven//:androidx_versionedparcelable_versionedparcelable", + "com.google.code.findbugs:jsr305": "@maven//:com_google_code_findbugs_jsr305", + "com.google.guava:listenablefuture": "@maven//:com_google_guava_listenablefuture", + "javax.inject:javax.inject": "@maven//:javax_inject_javax_inject", + "org.jetbrains.kotlinx:kotlinx-coroutines-android": "@maven//:org_jetbrains_kotlinx_kotlinx_coroutines_android", + "org.jetbrains.kotlinx:kotlinx-coroutines-bom": "@maven//:org_jetbrains_kotlinx_kotlinx_coroutines_bom", + "org.jetbrains.kotlinx:kotlinx-coroutines-core": "@maven//:org_jetbrains_kotlinx_kotlinx_coroutines_core", + }, + repositories = [ + "https://dl.google.com/dl/android/maven2/", + "https://repo.maven.apache.org/maven2/", + ], + resolve_timeout = 1000, + version_conflict_policy = "pinned", +) + +maven_install( + name = "flavor2paid_debug_android_test_maven", + artifacts = [ + "androidx.annotation:annotation-experimental:1.3.0", + "androidx.annotation:annotation-jvm:1.6.0", + "androidx.annotation:annotation:1.6.0", + "androidx.arch.core:core-common:2.2.0", + "androidx.arch.core:core-runtime:2.1.0", + "androidx.collection:collection:1.1.0", + "androidx.concurrent:concurrent-futures:1.1.0", + "androidx.core:core-ktx:1.2.0", + "androidx.core:core:1.10.1", + "androidx.lifecycle:lifecycle-common:2.6.1", + "androidx.lifecycle:lifecycle-livedata-core:2.6.1", + "androidx.lifecycle:lifecycle-livedata:2.6.1", + "androidx.lifecycle:lifecycle-process:2.6.1", + "androidx.lifecycle:lifecycle-runtime-ktx:2.6.1", + "androidx.lifecycle:lifecycle-runtime:2.6.1", + "androidx.lifecycle:lifecycle-service:2.6.1", + "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1", + "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.1", + "androidx.lifecycle:lifecycle-viewmodel:2.6.1", + "androidx.savedstate:savedstate-ktx:1.2.1", + "androidx.savedstate:savedstate:1.2.1", + "androidx.startup:startup-runtime:1.1.1", + "androidx.test.espresso:espresso-core:3.5.1", + "androidx.test.espresso:espresso-idling-resource:3.5.1", + "androidx.test.ext:junit:1.1.5", + "androidx.test.services:storage:1.4.2", + "androidx.test:annotation:1.0.1", + "androidx.test:core:1.5.0", + "androidx.test:monitor:1.6.1", + "androidx.test:runner:1.5.2", + "androidx.tracing:tracing:1.0.0", + "androidx.versionedparcelable:versionedparcelable:1.1.1", + "com.google.code.findbugs:jsr305:2.0.2", + "com.google.guava:listenablefuture:1.0", + "com.squareup:javawriter:2.1.1", + "javax.inject:javax.inject:1", + "junit:junit:4.13.2", + "org.hamcrest:hamcrest-core:1.3", + "org.hamcrest:hamcrest-integration:1.3", + "org.hamcrest:hamcrest-library:1.3", + "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4", + "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4", + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4", + "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4", + ], + excluded_artifacts = ["androidx.test.espresso:espresso-contrib"], + fail_on_missing_checksum = False, + jetify = True, + jetify_include_list = [ + "androidx.annotation:annotation", + "androidx.annotation:annotation-experimental", + "androidx.annotation:annotation-jvm", + "androidx.arch.core:core-common", + "androidx.arch.core:core-runtime", + "androidx.collection:collection", + "androidx.concurrent:concurrent-futures", + "androidx.core:core", + "androidx.core:core-ktx", + "androidx.lifecycle:lifecycle-common", + "androidx.lifecycle:lifecycle-livedata", + "androidx.lifecycle:lifecycle-livedata-core", + "androidx.lifecycle:lifecycle-process", + "androidx.lifecycle:lifecycle-runtime", + "androidx.lifecycle:lifecycle-runtime-ktx", + "androidx.lifecycle:lifecycle-service", + "androidx.lifecycle:lifecycle-viewmodel", + "androidx.lifecycle:lifecycle-viewmodel-ktx", + "androidx.lifecycle:lifecycle-viewmodel-savedstate", + "androidx.savedstate:savedstate", + "androidx.savedstate:savedstate-ktx", + "androidx.startup:startup-runtime", + "androidx.test.espresso:espresso-core", + "androidx.test.espresso:espresso-idling-resource", + "androidx.test.ext:junit", + "androidx.test.services:storage", + "androidx.test:annotation", + "androidx.test:core", + "androidx.test:monitor", + "androidx.test:runner", + "androidx.tracing:tracing", + "androidx.versionedparcelable:versionedparcelable", + "com.android.support:cardview-v7", + "com.google.code.findbugs:jsr305", + "com.google.guava:listenablefuture", + "com.squareup:javawriter", + "javax.inject:javax.inject", + "junit:junit", + "org.hamcrest:hamcrest-core", + "org.hamcrest:hamcrest-integration", + "org.hamcrest:hamcrest-library", + "org.jetbrains.kotlinx:kotlinx-coroutines-android", + "org.jetbrains.kotlinx:kotlinx-coroutines-bom", + "org.jetbrains.kotlinx:kotlinx-coroutines-core", + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm", + ], + override_targets = { + "androidx.annotation:annotation": "@maven//:androidx_annotation_annotation", + "androidx.annotation:annotation-experimental": "@maven//:androidx_annotation_annotation_experimental", + "androidx.arch.core:core-common": "@maven//:androidx_arch_core_core_common", + "androidx.arch.core:core-runtime": "@maven//:androidx_arch_core_core_runtime", + "androidx.collection:collection": "@maven//:androidx_collection_collection", + "androidx.core:core": "@maven//:androidx_core_core", + "androidx.core:core-ktx": "@maven//:androidx_core_core_ktx", + "androidx.lifecycle:lifecycle-common": "@maven//:androidx_lifecycle_lifecycle_common", + "androidx.lifecycle:lifecycle-livedata": "@maven//:androidx_lifecycle_lifecycle_livedata", + "androidx.lifecycle:lifecycle-livedata-core": "@maven//:androidx_lifecycle_lifecycle_livedata_core", + "androidx.lifecycle:lifecycle-process": "@maven//:androidx_lifecycle_lifecycle_process", + "androidx.lifecycle:lifecycle-runtime": "@maven//:androidx_lifecycle_lifecycle_runtime", + "androidx.lifecycle:lifecycle-runtime-ktx": "@maven//:androidx_lifecycle_lifecycle_runtime_ktx", + "androidx.lifecycle:lifecycle-service": "@maven//:androidx_lifecycle_lifecycle_service", + "androidx.lifecycle:lifecycle-viewmodel": "@maven//:androidx_lifecycle_lifecycle_viewmodel", + "androidx.lifecycle:lifecycle-viewmodel-ktx": "@maven//:androidx_lifecycle_lifecycle_viewmodel_ktx", + "androidx.lifecycle:lifecycle-viewmodel-savedstate": "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate", + "androidx.savedstate:savedstate": "@maven//:androidx_savedstate_savedstate", + "androidx.savedstate:savedstate-ktx": "@maven//:androidx_savedstate_savedstate_ktx", + "androidx.startup:startup-runtime": "@maven//:androidx_startup_startup_runtime", + "androidx.versionedparcelable:versionedparcelable": "@maven//:androidx_versionedparcelable_versionedparcelable", + "com.google.code.findbugs:jsr305": "@maven//:com_google_code_findbugs_jsr305", + "com.google.guava:listenablefuture": "@maven//:com_google_guava_listenablefuture", + "javax.inject:javax.inject": "@maven//:javax_inject_javax_inject", + "org.jetbrains.kotlinx:kotlinx-coroutines-android": "@maven//:org_jetbrains_kotlinx_kotlinx_coroutines_android", + "org.jetbrains.kotlinx:kotlinx-coroutines-bom": "@maven//:org_jetbrains_kotlinx_kotlinx_coroutines_bom", + "org.jetbrains.kotlinx:kotlinx-coroutines-core": "@maven//:org_jetbrains_kotlinx_kotlinx_coroutines_core", + }, + repositories = [ + "https://dl.google.com/dl/android/maven2/", + "https://repo.maven.apache.org/maven2/", + ], + resolve_timeout = 1000, + version_conflict_policy = "pinned", +) + +maven_install( + name = "maven", artifacts = DAGGER_ARTIFACTS + GRAB_BAZEL_COMMON_ARTIFACTS + [ "androidx.activity:activity-compose:1.7.2", - "androidx.activity:activity:1.6.0", - "androidx.annotation:annotation:1.1.0", + "androidx.activity:activity-ktx:1.7.2", + "androidx.activity:activity:1.7.2", + "androidx.annotation:annotation-experimental:1.3.0", + "androidx.annotation:annotation:1.6.0", + "androidx.appcompat:appcompat-resources:1.6.1", "androidx.appcompat:appcompat:1.6.1", - "androidx.compose.compiler:compiler:1.4.3", + "androidx.arch.core:core-common:2.2.0", + "androidx.arch.core:core-runtime:2.1.0", + "androidx.collection:collection:1.1.0", + "androidx.compose.animation:animation-core:1.2.1", + "androidx.compose.animation:animation:1.2.1", "androidx.compose.foundation:foundation-layout:1.4.3", "androidx.compose.foundation:foundation:1.4.3", + "androidx.compose.material:material-icons-core:1.4.3", + "androidx.compose.material:material-ripple:1.4.3", "androidx.compose.material:material:1.4.3", + "androidx.compose.runtime:runtime-saveable:1.4.3", + "androidx.compose.runtime:runtime:1.4.3", + "androidx.compose.ui:ui-geometry:1.4.3", + "androidx.compose.ui:ui-graphics:1.4.3", + "androidx.compose.ui:ui-text:1.4.3", + "androidx.compose.ui:ui-tooling-data:1.4.3", + "androidx.compose.ui:ui-tooling-preview:1.4.3", "androidx.compose.ui:ui-tooling:1.4.3", + "androidx.compose.ui:ui-unit:1.4.3", "androidx.compose.ui:ui:1.4.3", "androidx.constraintlayout:constraintlayout-core:1.0.4", maven.artifact( artifact = "constraintlayout", exclusions = [ "androidx.appcompat:appcompat", + "androidx.constraintlayout:constraintlayout-core", "androidx.core:core", ], group = "androidx.constraintlayout", version = "2.1.4", ), + "androidx.core:core-ktx:1.2.0", "androidx.core:core:1.10.1", + "androidx.cursoradapter:cursoradapter:1.0.0", + "androidx.customview:customview:1.0.0", "androidx.databinding:databinding-adapters:7.2.2", "androidx.databinding:databinding-common:7.2.2", + "androidx.databinding:databinding-compiler-common:7.2.2", "androidx.databinding:databinding-compiler:7.2.2", + "androidx.databinding:databinding-ktx:7.2.2", "androidx.databinding:databinding-runtime:7.2.2", "androidx.databinding:viewbinding:7.2.2", + "androidx.drawerlayout:drawerlayout:1.0.0", "androidx.emoji2:emoji2:1.3.0", + "androidx.fragment:fragment:1.3.6", + "androidx.interpolator:interpolator:1.0.0", "androidx.lifecycle:lifecycle-common:2.6.1", - "androidx.lifecycle:lifecycle-runtime:2.5.1", - "androidx.lifecycle:lifecycle-viewmodel:2.5.1", - "androidx.test.espresso:espresso-core:3.5.1", - "androidx.test.ext:junit:1.1.5", - "androidx.test:monitor:1.6.1", - "junit:junit:4.13.2", - "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.31", + "androidx.lifecycle:lifecycle-livedata-core:2.6.1", + "androidx.lifecycle:lifecycle-livedata:2.6.1", + "androidx.lifecycle:lifecycle-process:2.6.1", + "androidx.lifecycle:lifecycle-runtime-ktx:2.6.1", + "androidx.lifecycle:lifecycle-runtime:2.6.1", + "androidx.lifecycle:lifecycle-service:2.6.1", + "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1", + "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.1", + "androidx.lifecycle:lifecycle-viewmodel:2.6.1", + "androidx.loader:loader:1.0.0", + "androidx.savedstate:savedstate-ktx:1.2.1", + "androidx.savedstate:savedstate:1.2.1", + "androidx.startup:startup-runtime:1.1.1", + "androidx.vectordrawable:vectordrawable-animated:1.1.0", + "androidx.vectordrawable:vectordrawable:1.1.0", + "androidx.versionedparcelable:versionedparcelable:1.1.1", + "androidx.viewpager:viewpager:1.0.0", + "com.android.databinding:baseLibrary:7.2.2", + "com.android.tools.build.jetifier:jetifier-core:1.0.0-beta09", + "com.android.tools:annotations:30.2.2", + "com.google.auto:auto-common:0.10", + "com.google.code.findbugs:jsr305:3.0.2", + "com.google.code.gson:gson:2.8.6", + "com.google.dagger:dagger-compiler:2.29", + "com.google.dagger:dagger-producers:2.29", + "com.google.dagger:dagger-spi:2.29", + "com.google.dagger:dagger:2.29", + "com.google.errorprone:error_prone_annotations:2.3.4", + "com.google.errorprone:javac-shaded:9-dev-r4023-3", + "com.google.googlejavaformat:google-java-format:1.5", + "com.google.guava:failureaccess:1.0.1", + "com.google.guava:guava:30.1-jre", + "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava", + "com.google.j2objc:j2objc-annotations:1.3", + "com.googlecode.juniversalchardet:juniversalchardet:1.0.3", + "com.squareup:javapoet:1.13.0", + "com.sun.istack:istack-commons-runtime:3.0.8", + "com.sun.xml.fastinfoset:FastInfoset:1.2.16", + "commons-codec:commons-codec:1.11", + "commons-io:commons-io:2.4", + "jakarta.activation:jakarta.activation-api:1.2.1", + "jakarta.xml.bind:jakarta.xml.bind-api:2.3.2", + "javax.annotation:jsr250-api:1.0", + "javax.inject:javax.inject:1", + "net.ltgt.gradle.incap:incap:0.2", + "org.checkerframework:checker-compat-qual:2.5.3", + "org.checkerframework:checker-qual:3.5.0", + "org.glassfish.jaxb:jaxb-runtime:2.3.2", + "org.glassfish.jaxb:txw2:2.3.2", + "org.jetbrains.kotlinx:atomicfu:0.17.3", + "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4", + "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4", + "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4", + "org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.1.0", + "org.jvnet.staxex:stax-ex:1.8.1", ], excluded_artifacts = ["androidx.test.espresso:espresso-contrib"], fail_on_missing_checksum = False, @@ -114,36 +729,105 @@ maven_install( jetify_include_list = [ "androidx.activity:activity", "androidx.activity:activity-compose", + "androidx.activity:activity-ktx", "androidx.annotation:annotation", - "androidx.compose.compiler:compiler", + "androidx.annotation:annotation-experimental", + "androidx.appcompat:appcompat-resources", + "androidx.arch.core:core-common", + "androidx.arch.core:core-runtime", + "androidx.collection:collection", + "androidx.compose.animation:animation", + "androidx.compose.animation:animation-core", "androidx.compose.foundation:foundation", "androidx.compose.foundation:foundation-layout", "androidx.compose.material:material", + "androidx.compose.material:material-icons-core", + "androidx.compose.material:material-ripple", + "androidx.compose.runtime:runtime", + "androidx.compose.runtime:runtime-saveable", "androidx.compose.ui:ui", + "androidx.compose.ui:ui-geometry", + "androidx.compose.ui:ui-graphics", + "androidx.compose.ui:ui-text", "androidx.compose.ui:ui-tooling", + "androidx.compose.ui:ui-tooling-data", + "androidx.compose.ui:ui-tooling-preview", + "androidx.compose.ui:ui-unit", "androidx.constraintlayout:constraintlayout", "androidx.constraintlayout:constraintlayout-core", "androidx.core:core", + "androidx.core:core-ktx", + "androidx.cursoradapter:cursoradapter", + "androidx.customview:customview", "androidx.databinding:databinding-adapters", "androidx.databinding:databinding-common", "androidx.databinding:databinding-compiler", + "androidx.databinding:databinding-compiler-common", + "androidx.databinding:databinding-ktx", "androidx.databinding:databinding-runtime", "androidx.databinding:viewbinding", + "androidx.drawerlayout:drawerlayout", "androidx.emoji2:emoji2", + "androidx.fragment:fragment", + "androidx.interpolator:interpolator", "androidx.lifecycle:lifecycle-common", + "androidx.lifecycle:lifecycle-livedata", + "androidx.lifecycle:lifecycle-livedata-core", + "androidx.lifecycle:lifecycle-process", "androidx.lifecycle:lifecycle-runtime", + "androidx.lifecycle:lifecycle-runtime-ktx", + "androidx.lifecycle:lifecycle-service", "androidx.lifecycle:lifecycle-viewmodel", - "androidx.test.espresso:espresso-core", - "androidx.test.ext:junit", - "androidx.test:monitor", + "androidx.lifecycle:lifecycle-viewmodel-ktx", + "androidx.lifecycle:lifecycle-viewmodel-savedstate", + "androidx.loader:loader", + "androidx.savedstate:savedstate", + "androidx.savedstate:savedstate-ktx", + "androidx.startup:startup-runtime", + "androidx.vectordrawable:vectordrawable", + "androidx.vectordrawable:vectordrawable-animated", + "androidx.versionedparcelable:versionedparcelable", + "androidx.viewpager:viewpager", + "com.android.databinding:baseLibrary", "com.android.support:cardview-v7", - "junit:junit", - "org.jetbrains.kotlin:kotlin-stdlib-jdk8", + "com.android.tools.build.jetifier:jetifier-core", + "com.android.tools:annotations", + "com.google.auto:auto-common", + "com.google.code.findbugs:jsr305", + "com.google.code.gson:gson", + "com.google.dagger:dagger", + "com.google.dagger:dagger-compiler", + "com.google.dagger:dagger-producers", + "com.google.dagger:dagger-spi", + "com.google.errorprone:error_prone_annotations", + "com.google.errorprone:javac-shaded", + "com.google.googlejavaformat:google-java-format", + "com.google.guava:failureaccess", + "com.google.guava:guava", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "com.googlecode.juniversalchardet:juniversalchardet", + "com.squareup:javapoet", + "com.sun.istack:istack-commons-runtime", + "com.sun.xml.fastinfoset:FastInfoset", + "commons-codec:commons-codec", + "commons-io:commons-io", + "jakarta.activation:jakarta.activation-api", + "jakarta.xml.bind:jakarta.xml.bind-api", + "javax.annotation:jsr250-api", + "javax.inject:javax.inject", + "net.ltgt.gradle.incap:incap", + "org.checkerframework:checker-compat-qual", + "org.checkerframework:checker-qual", + "org.glassfish.jaxb:jaxb-runtime", + "org.glassfish.jaxb:txw2", + "org.jetbrains.kotlinx:atomicfu", + "org.jetbrains.kotlinx:kotlinx-coroutines-android", + "org.jetbrains.kotlinx:kotlinx-coroutines-bom", + "org.jetbrains.kotlinx:kotlinx-coroutines-core", + "org.jetbrains.kotlinx:kotlinx-metadata-jvm", + "org.jvnet.staxex:stax-ex", ], - maven_install_json = "//:maven_install.json", - override_targets = { - "androidx.appcompat:appcompat": "@//third_party:androidx_appcompat_appcompat", - }, repositories = [ "https://dl.google.com/dl/android/maven2/", "https://repo.maven.apache.org/maven2/", @@ -152,9 +836,26 @@ maven_install( version_conflict_policy = "pinned", ) -load("@maven//:defs.bzl", "pinned_maven_install") - -pinned_maven_install() +maven_install( + name = "test_maven", + artifacts = [ + "junit:junit:4.13.2", + "org.hamcrest:hamcrest-core:1.3", + ], + excluded_artifacts = ["androidx.test.espresso:espresso-contrib"], + fail_on_missing_checksum = False, + jetify = True, + jetify_include_list = [ + "com.android.support:cardview-v7", + "junit:junit", + "org.hamcrest:hamcrest-core", + ], + repositories = [ + "https://repo.maven.apache.org/maven2/", + ], + resolve_timeout = 1000, + version_conflict_policy = "pinned", +) android_sdk_repository( name = "androidsdk", diff --git a/build.gradle b/build.gradle index 9e7ebf6c..d89cdb2b 100644 --- a/build.gradle +++ b/build.gradle @@ -135,11 +135,11 @@ grazel { jetifyIncludeList.add("com.android.support:cardview-v7") jetifyExcludeList.add("androidx.appcompat:appcompat") artifactPinning { - enabled.set(true) + enabled.set(false) } - overrideTargetLabels.putAll( - ["androidx.appcompat:appcompat": "@//third_party:androidx_appcompat_appcompat"] - ) +// overrideTargetLabels.putAll( +// ["androidx.appcompat:appcompat": "@//third_party:androidx_appcompat_appcompat"] +// ) versionConflictPolicy = "pinned" } kotlin { diff --git a/constants.gradle b/constants.gradle index eb0e7734..35590514 100644 --- a/constants.gradle +++ b/constants.gradle @@ -15,7 +15,7 @@ */ ext { groupId = "com.grab.grazel" - versionName = project.hasProperty("versionName") ? versionName : "0.4.1-alpha.13" + versionName = project.hasProperty("versionName") ? versionName : "0.4.1-alpha.13-rd" website = "https://grab.github.io/Grazel/" } diff --git a/flavor-libs/sample-library-flavor1/BUILD.bazel b/flavor-libs/sample-library-flavor1/BUILD.bazel index d226594b..8741e324 100644 --- a/flavor-libs/sample-library-flavor1/BUILD.bazel +++ b/flavor-libs/sample-library-flavor1/BUILD.bazel @@ -23,6 +23,6 @@ grab_kt_jvm_test( "//visibility:public", ], deps = [ - "@maven//:junit_junit", + "@test_maven//:junit_junit", ], ) diff --git a/flavor-libs/sample-library-flavor2/BUILD.bazel b/flavor-libs/sample-library-flavor2/BUILD.bazel index 052ff9e3..de25a256 100644 --- a/flavor-libs/sample-library-flavor2/BUILD.bazel +++ b/flavor-libs/sample-library-flavor2/BUILD.bazel @@ -23,6 +23,6 @@ grab_kt_jvm_test( "//visibility:public", ], deps = [ - "@maven//:junit_junit", + "@test_maven//:junit_junit", ], ) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 85e16ca7..fbd00886 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -23,9 +23,14 @@ javapoet = "1.13.0" junit = "4.13.2" kotlin = "1.8.10" kotlin-dsl = "2.3.3" +kotlin-serialization = "1.5.1" +kotlinx-coroutines-android = "1.3.9" +kotlinx-coroutines-core-jvm = "1.7.2" +leakcanary-android = "2.12" lifecycle-viewmodel = "2.6.1" mockito-kotlin = "1.6.0" nexus = "1.1.0" +paging-runtime = "3.1.1" picnic = "0.4.0" truth = "1.1.3" @@ -38,6 +43,7 @@ androidx-constraintlayout-core = { module = "androidx.constraintlayout:constrain androidx-core = { module = "androidx.core:core", version.ref = "androidx-core" } androidx-emoji2 = { module = "androidx.emoji2:emoji2", version.ref = "emoji2" } androidx-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel", version.ref = "lifecycle-viewmodel" } +androidx-paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "paging-runtime" } androidx-test-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-espresso" } androidx-test-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-junit" } androidx-lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime", version.ref = "androidx-lifecycle-runtime" } @@ -70,10 +76,15 @@ javapoet = { module = "com.squareup:javapoet", version.ref = "javapoet" } junit = { module = "junit:junit", version.ref = "junit" } kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } +kotlin-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlin-serialization" } +kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines-android" } +kotlinx-coroutines-core-jvm = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm", version.ref = "kotlinx-coroutines-core-jvm" } +leakcanary-android = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakcanary-android" } mockito-kotlin = { module = "com.nhaarman:mockito-kotlin", version.ref = "mockito-kotlin" } nexus-gradle-publish-plugin = { module = "io.github.gradle-nexus:publish-plugin", version.ref = "nexus" } picnic = { module = "com.jakewharton.picnic:picnic", version.ref = "picnic" } [plugins] kotlin-dsl = { id = "org.gradle.kotlin.kotlin-dsl", version.ref = "kotlin-dsl" } -gradle-publish = { id = "com.gradle.plugin-publish", version.ref = "gradle-plugin-publish" } \ No newline at end of file +gradle-publish = { id = "com.gradle.plugin-publish", version.ref = "gradle-plugin-publish" } +kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } \ No newline at end of file diff --git a/grazel-gradle-plugin/build.gradle b/grazel-gradle-plugin/build.gradle index d78a20f4..3b9a8f7c 100644 --- a/grazel-gradle-plugin/build.gradle +++ b/grazel-gradle-plugin/build.gradle @@ -31,6 +31,7 @@ plugins { id "java-gradle-plugin" alias(libs.plugins.kotlin.dsl) alias(libs.plugins.gradle.publish) + alias(libs.plugins.kotlin.serialization) id "maven-publish" id "idea" } @@ -88,6 +89,9 @@ dependencies { implementation libs.google.dagger kapt libs.google.dagger.compiler + implementation libs.kotlin.serialization + implementation libs.kotlinx.coroutines.core.jvm + testImplementation "org.jetbrains.kotlin:kotlin-test" testImplementation "org.jetbrains.kotlin:kotlin-test-junit" testImplementation libs.google.truth diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/bazel/rules/MavenRules.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/bazel/rules/MavenRules.kt index 83e17af7..c892e00e 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/bazel/rules/MavenRules.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/bazel/rules/MavenRules.kt @@ -20,7 +20,6 @@ import com.grab.grazel.bazel.starlark.AssigneeBuilder import com.grab.grazel.bazel.starlark.StarlarkType import com.grab.grazel.bazel.starlark.StatementsBuilder import com.grab.grazel.bazel.starlark.StringStatement -import com.grab.grazel.bazel.starlark.add import com.grab.grazel.bazel.starlark.array import com.grab.grazel.bazel.starlark.asString import com.grab.grazel.bazel.starlark.assigneeBuilder @@ -29,7 +28,6 @@ import com.grab.grazel.bazel.starlark.obj import com.grab.grazel.bazel.starlark.quote sealed class MavenRepository : AssigneeBuilder { - data class DefaultMavenRepository( val url: String, val username: String? = null, @@ -54,7 +52,7 @@ sealed class MavenRepository : AssigneeBuilder { * @param appendExternal Indicates whether to append or prepend the External variables */ private fun combineExternalVariablesAndArray( - externalVariables: List, + externalVariables: Set, arrayValues: List, appendExternal: Boolean = false, ) = assigneeBuilder { @@ -73,17 +71,18 @@ fun StatementsBuilder.mavenInstall( name: String? = null, rulesJvmExternalName: String, artifacts: Set = emptySet(), - mavenRepositories: List = emptyList(), - externalArtifacts: List = emptyList(), - externalRepositories: List = emptyList(), + mavenRepositories: Set = emptySet(), + externalArtifacts: Set = emptySet(), + externalRepositories: Set = emptySet(), jetify: Boolean = false, mavenInstallJson: String? = null, jetifyIncludeList: List = emptyList(), failOnMissingChecksum: Boolean = true, resolveTimeout: Int = 600, - excludeArtifacts: List = emptyList(), + excludeArtifacts: Set = emptySet(), overrideTargets: Map = emptyMap(), versionConflictPolicy: String? = null, + artifactPinning: Boolean, ) { load("@$rulesJvmExternalName//:defs.bzl", "maven_install") load("@$rulesJvmExternalName//:specs.bzl", "maven") @@ -98,7 +97,7 @@ fun StatementsBuilder.mavenInstall( "repositories" `=` combineExternalVariablesAndArray( externalRepositories, - mavenRepositories.map { it.build().asString() }, + mavenRepositories.map { it.build().asString() }.sorted(), true, ) @@ -134,6 +133,13 @@ fun StatementsBuilder.mavenInstall( "version_conflict_policy" `=` it.quote } } + + if (artifactPinning) { + load("@$name//:defs.bzl") { + "${name}_pinned_maven_install" `=` "pinned_maven_install".quote + } + add("${name}_pinned_maven_install()") + } } /** @@ -196,46 +202,4 @@ sealed class MavenInstallArtifact : StarlarkType { } } } -} - -fun StatementsBuilder.jvmRules( - rulesJvmExternalRule: BazelRepositoryRule, - resolveTimeout: Int = 600, - artifacts: Set = emptySet(), - artifactPinning: Boolean, - mavenInstallJson: String? = null, - mavenRepositories: List = emptyList(), - externalArtifacts: List = emptyList(), - externalRepositories: List = emptyList(), - excludeArtifacts: List = emptyList(), - overrideTargets: Map = emptyMap(), - jetify: Boolean = false, - jetifyIncludeList: List = emptyList(), - failOnMissingChecksum: Boolean = true, - versionConflictPolicy: String? = null, -) { - add(rulesJvmExternalRule) - - newLine() - - mavenInstall( - rulesJvmExternalName = rulesJvmExternalRule.name, - artifacts = artifacts, - mavenRepositories = mavenRepositories, - externalArtifacts = externalArtifacts, - externalRepositories = externalRepositories, - overrideTargets = overrideTargets, - mavenInstallJson = mavenInstallJson, - jetify = jetify, - jetifyIncludeList = jetifyIncludeList, - failOnMissingChecksum = failOnMissingChecksum, - resolveTimeout = resolveTimeout, - excludeArtifacts = excludeArtifacts, - versionConflictPolicy = versionConflictPolicy, - ) - - if (artifactPinning) { - load("@maven//:defs.bzl", "pinned_maven_install") - add("pinned_maven_install()") - } } \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/bazel/starlark/BazelDependency.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/bazel/starlark/BazelDependency.kt index aebc5c3f..fb941704 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/bazel/starlark/BazelDependency.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/bazel/starlark/BazelDependency.kt @@ -17,7 +17,6 @@ package com.grab.grazel.bazel.starlark import org.gradle.api.Project -import org.gradle.api.artifacts.Dependency sealed class BazelDependency { data class ProjectDependency( @@ -49,16 +48,20 @@ sealed class BazelDependency { override fun toString() = dep } - data class MavenDependency(val dependency: Dependency) : BazelDependency() { + data class MavenDependency( + val repo: String = "maven", + val group: String, + val name: String + ) : BazelDependency() { private fun String.toBazelPath(): String { return replace(".", "_").replace("-", "_") } override fun toString(): String { - val group = dependency.group?.toBazelPath() ?: "" - val name = dependency.name.toBazelPath() - return "@maven//:${group}_$name" + val group = group.toBazelPath() + val name = name.toBazelPath() + return "@$repo//:${group}_$name" } } } \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/bazel/starlark/Functions.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/bazel/starlark/Functions.kt index 1302c640..13946916 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/bazel/starlark/Functions.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/bazel/starlark/Functions.kt @@ -22,7 +22,7 @@ import java.io.PrintWriter data class FunctionStatement( val name: String, - private val params: List, + private val params: List, private val multilineParams: Boolean = false ) : Assignee { override fun write(level: Int, writer: PrintWriter) { @@ -75,6 +75,25 @@ fun StatementsBuilder.load(bzlFile: String, vararg symbols: String) { loadStrategy.load(this, bzlFile, *symbols) } +/** + * Load statement with option to alias imported symbol via `assignmentBuilder` + * + * Eg: + * ``` + * load("@maven//:defs.bzl", default_pinned_maven_install = "pinned_maven_install") + * ``` + */ +fun StatementsBuilder.load(path: String, assignmentBuilder: AssignmentBuilder.() -> Unit = {}) { + val symbolImports = Assignments(assignmentBuilder = assignmentBuilder) + add( + FunctionStatement( + name = "load", + params = listOf(path.quote.toStatement()) + symbolImports + ) + ) +} + + @Suppress("unused") fun StatementsBuilder.glob(include: ArrayStatement): FunctionStatement { val multilineParams = include.elements.size > 2 diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/di/GrazelComponent.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/di/GrazelComponent.kt index e4bcc295..17f97218 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/di/GrazelComponent.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/di/GrazelComponent.kt @@ -30,7 +30,6 @@ import com.grab.grazel.gradle.dependencies.DependenciesDataSource import com.grab.grazel.gradle.dependencies.DependenciesGraphsBuilder import com.grab.grazel.gradle.dependencies.DependenciesModule import com.grab.grazel.gradle.dependencies.DependencyGraphs -import com.grab.grazel.gradle.dependencies.MavenInstallArtifactsCalculator import com.grab.grazel.gradle.variant.AndroidVariantDataSource import com.grab.grazel.gradle.variant.VariantBuilder import com.grab.grazel.gradle.variant.VariantMatcher @@ -43,6 +42,7 @@ import com.grab.grazel.migrate.android.AndroidLibraryDataExtractor import com.grab.grazel.migrate.android.ManifestValuesBuilder import com.grab.grazel.migrate.dependencies.ArtifactsPinner import com.grab.grazel.migrate.dependencies.DefaultArtifactsPinner +import com.grab.grazel.migrate.dependencies.MavenInstallArtifactsCalculator import com.grab.grazel.migrate.internal.ProjectBazelFileBuilder import com.grab.grazel.migrate.internal.RootBazelFileBuilder import com.grab.grazel.migrate.internal.WorkspaceBuilder diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/Dependencies.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/Dependencies.kt index a8f1991c..5615d7b7 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/Dependencies.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/Dependencies.kt @@ -17,14 +17,27 @@ package com.grab.grazel.gradle.dependencies import com.grab.grazel.GrazelExtension -import com.grab.grazel.bazel.rules.MavenInstallArtifact -import com.grab.grazel.bazel.rules.MavenInstallArtifact.Exclusion.SimpleExclusion +import com.grab.grazel.bazel.rules.ANDROIDX_GROUP +import com.grab.grazel.bazel.rules.ANNOTATION_ARTIFACT +import com.grab.grazel.bazel.rules.DAGGER_GROUP +import com.grab.grazel.bazel.rules.DATABINDING_GROUP +import com.grab.grazel.bazel.starlark.BazelDependency +import com.grab.grazel.bazel.starlark.BazelDependency.StringDependency import com.grab.grazel.di.qualifiers.RootProject import com.grab.grazel.gradle.ConfigurationDataSource import com.grab.grazel.gradle.ConfigurationScope +import com.grab.grazel.gradle.ConfigurationScope.TEST import com.grab.grazel.gradle.RepositoryDataSource import com.grab.grazel.gradle.configurationScopes +import com.grab.grazel.gradle.hasDatabinding import com.grab.grazel.gradle.variant.AndroidVariantsExtractor +import com.grab.grazel.gradle.variant.DEFAULT_VARIANT +import com.grab.grazel.gradle.variant.TEST_VARIANT +import com.grab.grazel.gradle.variant.Variant +import com.grab.grazel.gradle.variant.VariantBuilder +import com.grab.grazel.gradle.variant.isConfigScope +import com.grab.grazel.gradle.variant.migratableConfigurations +import com.grab.grazel.migrate.dependencies.MavenInstallStore import com.grab.grazel.util.GradleProvider import org.gradle.api.Project import org.gradle.api.artifacts.Configuration @@ -46,17 +59,6 @@ internal val IGNORED_ARTIFACT_GROUPS = listOf( "org.jetbrains.kotlin" ) -@Deprecated( - "No longer used, use com.grab.grazel.gradle.dependencies.ExcludeRule instead.", - replaceWith = ReplaceWith("ExcludeRule", "com.grab.grazel.gradle.dependencies.ExcludeRule") -) -data class ExcludeRuleOld( - val group: String, - val artifact: String -) { - override fun toString(): String = "$group:$artifact" -} - /** * Simple data holder for a Maven artifact containing its group, name and version. */ @@ -64,7 +66,6 @@ internal data class MavenArtifact( val group: String?, val name: String?, val version: String? = null, - val excludeRuleOlds: Set = emptySet() ) { val id get() = "$group:$name" override fun toString() = "$group:$name:$version" @@ -93,20 +94,6 @@ internal interface DependenciesDataSource { vararg scopes: ConfigurationScope ): Sequence> - /** - * Returns the resolved artifacts dependencies for the given projects in the fully qualified Maven format. - * - * @param projects The list of projects for which the artifacts need to be resolved - * @param overrideArtifactVersions List of fully qualified maven coordinates with versions that used for calculation - * instead of the one calculated automatically. - * - * @return List of artifacts in fully qualified Maven format - */ - fun resolvedArtifactsFor( - projects: List, - overrideArtifactVersions: List = emptyList() - ): List - /** * @return true if the project has any private dependencies in any configuration */ @@ -132,6 +119,14 @@ internal interface DependenciesDataSource { rootProject: Project, fileExtension: String? = null ): Map + + /** + * Non project dependencies for the given [buildGraphType] + */ + fun externalDependencies( + project: Project, + buildGraphType: BuildGraphType + ): List } @Singleton @@ -142,38 +137,13 @@ internal class DefaultDependenciesDataSource @Inject constructor( private val artifactsConfig: ArtifactsConfig, private val repositoryDataSource: RepositoryDataSource, private val dependencyResolutionService: GradleProvider, - private val androidVariantsExtractor: AndroidVariantsExtractor + private val androidVariantsExtractor: AndroidVariantsExtractor, + private val variantBuilder: VariantBuilder, + private val mavenInstallStore: MavenInstallStore ) : DependenciesDataSource { private val configurationScopes by lazy { grazelExtension.configurationScopes() } - private val excludeArtifactsDenyList by lazy { grazelExtension.rules.mavenInstall.excludeArtifactsDenyList.get() } - - private val resolvedVersions: Map by lazy { - rootProject - .subprojects - .asSequence() - .flatMap { it.firstLevelModuleDependencies() } - .flatMap { (listOf(it) + it.children).asSequence() } - .map { it.moduleGroup + ":" + it.moduleName to it.moduleVersion } - .toMap() - } - - // TODO Can be moved else where for clarity - private val resolvedRepositories: Map by lazy { - rootProject - .subprojects - .asSequence() - .flatMap { it.externalResolvedDependencies().asSequence() } - .filter { it.repositoryName != null && it.moduleVersion != null } - .map { componentResult -> - val moduleVersion = componentResult.moduleVersion - val id = moduleVersion!!.group + ":" + moduleVersion.name - id to componentResult.repositoryName!! - }.distinct() - .toMap() - } - private fun Project.buildGraphTypes() = configurationScopes.flatMap { configurationScope -> androidVariantsExtractor.getVariants(this).map { variant -> @@ -181,28 +151,6 @@ internal class DefaultDependenciesDataSource @Inject constructor( } } - /** - * Given a group, name and version will update version with following properties - * * Overridden version by user - * * Resolved version by Gradle - * * Declared version in buildscript. - */ - private fun correctArtifactVersion( - mavenArtifact: MavenArtifact, - overrideArtifactVersions: Map = emptyMap() - ): MavenArtifact { - // To correctly calculate the actual used version, we map the version from resolvedVersions since - // resolvedVersions would contain the actual resolved dependency version (respecting resolution strategy) - // instead of the ones declared in a project's build file - // Additionally we also check if user needs to override the version via overrideArtifactVersions and use - // that if found - val id = "${mavenArtifact.group}:${mavenArtifact.name}" - val newVersion = overrideArtifactVersions[id] - ?: resolvedVersions[id] - ?: mavenArtifact.version - return mavenArtifact.copy(version = newVersion) - } - /** * @return `true` when the `MavenArtifact` is present is ignored by user. */ @@ -213,79 +161,6 @@ internal class DefaultDependenciesDataSource @Inject constructor( */ private val MavenArtifact.isExcluded get() = artifactsConfig.excludedList.contains(id) - override fun resolvedArtifactsFor( - projects: List, - overrideArtifactVersions: List - ): List { - // Prepare override versions map - val overrideArtifactVersionMap = overrideArtifactVersions.associate { mavenCoordinate -> - try { - val chunks = mavenCoordinate.split(":") - chunks.first() + ":" + chunks[1] to chunks[2] - } catch (e: IndexOutOfBoundsException) { - error("$mavenCoordinate is not a proper maven coordinate, please ensure version is correctly specified") - } - } - - // Filter out configurations we are interested in. - val configurations = projects - .asSequence() - .flatMap { project -> - configurationDataSource.configurations( - project, - *configurationScopes - ) - } - .toList() - - // Calculate all the external artifacts - val externalArtifacts = configurations.asSequence() - .flatMap { it.dependencies.asSequence() } - .filter { it.group != null } - .filterIsInstance() - .map { dependency -> dependency.toMavenArtifact() } - - - // Collect all forced versions - // (Perf fix) - collecting all projects' forced modules is costly, hence take the first sub project - // TODO Provide option to consider all forced versions backed by a flag. - val forcedVersions = sequenceOf(rootProject.subprojects.first()) - .flatMap { project -> - configurationDataSource.configurations( - project, - *configurationScopes - ) - } - .let(::collectForcedVersions) - - return (externalArtifacts + forcedVersions) - .groupBy { it.id } - .map { (_, mavenArtifacts) -> - // Merge all exclude rules so that we have a cumulative set - mavenArtifacts - .first() - .copy( - excludeRuleOlds = mavenArtifacts - .flatMap(MavenArtifact::excludeRuleOlds) - .toSet() - ) - }.asSequence() - .filter { mavenArtifact -> - // Only allow dependencies from supported repositories - mavenArtifact.isFromSupportedRepository(repositoryDataSource) - }.filter { mavenArtifact -> - // Don't include artifacts that are excluded or included - !mavenArtifact.isIgnored && !mavenArtifact.isExcluded - }.map { mavenArtifact -> - // Fix the artifact version as per resolvedVersions or overrideVersions - correctArtifactVersion( - mavenArtifact = mavenArtifact, - overrideArtifactVersions = overrideArtifactVersionMap - ) - }.map(MavenArtifact::toMavenInstallArtifact) - .toList() - } - override fun hasDepsFromUnsupportedRepositories(project: Project): Boolean { return project .externalResolvedDependencies() @@ -296,32 +171,34 @@ internal class DefaultDependenciesDataSource @Inject constructor( override fun hasIgnoredArtifacts(project: Project): Boolean { return project.firstLevelModuleDependencies() .flatMap { (listOf(it) + it.children).asSequence() } - .filter { !IGNORED_ARTIFACT_GROUPS.contains(it.moduleGroup) } + .filter { it.moduleGroup !in IGNORED_ARTIFACT_GROUPS } .any { MavenArtifact(it.moduleGroup, it.moduleName).isIgnored } } override fun mavenDependencies( project: Project, vararg buildGraphTypes: BuildGraphType - ): Sequence = - declaredDependencies(project, *buildGraphTypes.map { it.configurationScope }.toTypedArray()) - .filter { (configuration, _) -> - if (buildGraphTypes.isEmpty()) { - true - } else { - configurationDataSource.isThisConfigurationBelongsToThisVariants( - project, - *buildGraphTypes.map { it.variant }.toTypedArray(), - configuration = configuration - ) - } + ): Sequence { + return declaredDependencies( + project, + *buildGraphTypes.map { it.configurationScope }.toTypedArray() + ).filter { (configuration, _) -> + if (buildGraphTypes.isEmpty()) { + true + } else { + configurationDataSource.isThisConfigurationBelongsToThisVariants( + project, + *buildGraphTypes.map { it.variant }.toTypedArray(), + configuration = configuration + ) } - .map { it.second } - .filter { it.group != null && !IGNORED_ARTIFACT_GROUPS.contains(it.group) } + }.map { it.second } + .filter { it.group != null && it.group !in IGNORED_ARTIFACT_GROUPS } .filter { val artifact = MavenArtifact(it.group, it.name) !artifact.isExcluded && !artifact.isIgnored }.filter { it !is ProjectDependency } + } override fun projectDependencies( project: Project, vararg scopes: ConfigurationScope @@ -359,6 +236,57 @@ internal class DefaultDependenciesDataSource @Inject constructor( return results } + override fun externalDependencies( + project: Project, + buildGraphType: BuildGraphType + ): List { + val variants = variantBuilder.build(project).groupBy(Variant<*>::name) + val inputVariant = buildGraphType.variant + // From input variant map to Grazel variant + val grazelVariant: Variant<*> = when { + inputVariant != null -> variants[inputVariant.name]!!.first { + it.variantType.isConfigScope(project, buildGraphType.configurationScope) + } + // Input variant is null, probably legacy code path assumes non android project has no + // variants. To compensate, map it to `default` or `test` based on + // BuildGraphType.configurationScope + // This will no longer needed after migrating legacy code path to use variants + else -> when (buildGraphType.configurationScope) { + TEST -> variants[TEST_VARIANT]!!.first() + else -> variants[DEFAULT_VARIANT]!!.first() + } + } + + return grazelVariant.migratableConfigurations + .asSequence() + .flatMap { it.allDependencies.filterIsInstance() } + .filter { it.group !in IGNORED_ARTIFACT_GROUPS } + .filter { + val artifact = MavenArtifact(it.group, it.name) + !artifact.isExcluded && !artifact.isIgnored + }.filter { + if (project.hasDatabinding) { + it.group != DATABINDING_GROUP && (it.group != ANDROIDX_GROUP && it.name != ANNOTATION_ARTIFACT) + } else true + }.map { dependency -> + val variantHierarchy = buildSet { + add(grazelVariant.name) + addAll(grazelVariant.extendsFrom.reversed()) + } + when (dependency.group) { + DAGGER_GROUP -> StringDependency("//:dagger") + else -> mavenInstallStore.get( + variants = variantHierarchy, + group = dependency.group!!, + name = dependency.name + ) ?: run { + error("$dependency cant be found for migrating ${project.name}") + } + } + }.distinct() + .toList() + } + /** * Resolves all the external dependencies for the given project. By resolving all the dependencies, we get accurate * dependency information that respects resolution strategy, substitution and any other modification by Gradle apart @@ -373,15 +301,6 @@ internal class DefaultDependenciesDataSource @Inject constructor( ) ) - /** - * @return true if the [MavenArtifact] was fetched from a supported repository - */ - private fun MavenArtifact.isFromSupportedRepository( - repositoryDataSource: RepositoryDataSource - ) = resolvedRepositories.containsKey(id) && !repositoryDataSource - .unsupportedRepositoryNames - .contains(resolvedRepositories.getValue(id)) - /** * Collects first level module dependencies from their resolved configuration. Additionally, excludes any artifacts * that are not meant to be used in Bazel as defined by [IGNORED_ARTIFACT_GROUPS] @@ -401,7 +320,7 @@ internal class DefaultDependenciesDataSource @Inject constructor( sequenceOf() } }.filterIsInstance() - .filter { !IGNORED_ARTIFACT_GROUPS.contains(it.moduleGroup) } + .filter { it.moduleGroup !in IGNORED_ARTIFACT_GROUPS } } internal fun firstLevelModuleDependencies(project: Project) = @@ -427,57 +346,9 @@ internal class DefaultDependenciesDataSource @Inject constructor( } } - /** - * Collects any custom resolution strategy (particularly forced modules) defined in the given `configurations` - * - * @return Gradle's forced modules artifacts parsed to `MavenArtifact`. - */ - private fun collectForcedVersions( - configurations: Sequence - ): Sequence = mutableMapOf().apply { - configurations - .flatMap { it.resolutionStrategy.forcedModules.asSequence() } - .forEach { mvSelector -> - val key = MavenArtifact(mvSelector.group, mvSelector.name, mvSelector.version) - put(key, key.id) - } - }.keys.asSequence() - - /** - * Map [ExternalDependency] to [MavenArtifact] with relevant details like exclude rules. - */ - private fun ExternalDependency.toMavenArtifact(): MavenArtifact { - return MavenArtifact( - group = group, - name = name, - version = version, - excludeRuleOlds = excludeRules - .asSequence() - .map { - @Suppress("USELESS_ELVIS") // Gradle lying, module can be null - ExcludeRuleOld(it.group, it.module ?: "") - } - .filterNot { it.artifact.isNullOrBlank() } - .filterNot { excludeArtifactsDenyList.contains(it.toString()) } - .toSet() - ) - } - private fun DefaultResolvedDependency.toMavenArtifact() = MavenArtifact( group = moduleGroup, name = moduleName, version = moduleVersion, ) -} - -internal fun MavenArtifact.toMavenInstallArtifact(): MavenInstallArtifact { - return when { - excludeRuleOlds.isEmpty() -> MavenInstallArtifact.SimpleArtifact(toString()) - else -> MavenInstallArtifact.DetailedArtifact( - group = group!!, - artifact = name!!, - version = version!!, - exclusions = excludeRuleOlds.map { SimpleExclusion("${it.group}:${it.artifact}") } - ) - } } \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/MavenInstallArtifactsCalculator.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/MavenInstallArtifactsCalculator.kt deleted file mode 100644 index 46766694..00000000 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/MavenInstallArtifactsCalculator.kt +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright 2022 Grabtaxi Holdings PTE LTD (GRAB) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.grab.grazel.gradle.dependencies - -import com.grab.grazel.GrazelExtension -import com.grab.grazel.di.qualifiers.RootProject -import com.grab.grazel.gradle.ConfigurationDataSource -import com.grab.grazel.gradle.RepositoryDataSource -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.ExternalDependency -import org.gradle.api.artifacts.result.ResolutionResult -import org.gradle.api.internal.artifacts.repositories.DefaultMavenArtifactRepository -import org.gradle.api.internal.artifacts.result.DefaultResolvedComponentResult -import org.gradle.api.internal.artifacts.result.DefaultResolvedDependencyResult -import javax.inject.Inject - -data class Repository( - val name: String, - val repository: DefaultMavenArtifactRepository -) - -data class ExcludeRule( - val group: String, - val artifact: String -) { - override fun toString(): String = "$group:$artifact" -} - -class MavenExternalArtifact( - val group: String, - val name: String, - val version: String, - val repository: Repository, - val componentResult: DefaultResolvedComponentResult, - val excludeRules: List -) { - val id get() = "$group:$name:$version resolved from ${repository.name}" - override fun toString() = id -} - - -@Deprecated("Use task based dependency resolution instead") -internal class MavenInstallArtifactsCalculator -@Inject -constructor( - @param:RootProject private val rootProject: Project, - private val configurationDataSource: ConfigurationDataSource, - private val repositoryDataSource: RepositoryDataSource, - private val grazelExtension: GrazelExtension, -) { - - private val excludeArtifactsDenyList by lazy { - grazelExtension.rules.mavenInstall.excludeArtifactsDenyList.get() - } - - fun calculate(): Map> { - return emptyMap() - } - - /** - * Takes a list of variants and the list of configurations in them to produce a `MavenExternalArtifact` - * The data required for `MavenExternalArtifact` comes from different places and this method merges - * from all of them to produce `Map` of variants and `MavenExternalArtifact`s. - * - * * Repository is calculated from merging all repositories in the project. - * @see [RepositoryDataSource.allRepositoriesByName] - * * Exclude rules are calculated from `ExternalDependency` provided from `configuration.dependencies` - * * [ResolutionResult] is used to calculate dependency versions to ensure final version after - * dependency resolution is used - * - */ - private fun resolveVariantDependencies( - variantConfigs: Map> - ): Map> { - val repositories = repositoryDataSource.allRepositoriesByName - return variantConfigs.mapValues { (_, configurations) -> - val excludeRules = calculateExcludeRules(configurations) - configurations - .asSequence() - .filter { it.isCanBeResolved } - .map { it.incoming } - .flatMap { resolvableDependencies -> - try { - resolvableDependencies - .resolutionResult - .root - .dependencies - .asSequence() - .filterIsInstance() - .map { it.selected } - .filter { !it.toString().startsWith("project :") } - .filterIsInstance() - .map { componentResult -> - val version = componentResult.moduleVersion!! - MavenExternalArtifact( - group = version.group, - version = version.version, - name = version.name, - repository = Repository( - name = componentResult.repositoryName!!, - repository = repositories[componentResult.repositoryName!!]!! - ), - componentResult = componentResult, - excludeRules = excludeRules.getOrDefault( - version.toString(), - emptyList() - ) - ) - } - } catch (e: Exception) { - e.printStackTrace() - emptySequence() - } - }.toSortedSet(compareBy(MavenExternalArtifact::id)) - .toList() - } - } - - /** - * Calculate and merge exclude rules from all dependency declarations. - * - * @param configurations Configurations to merge exclude rules from - * @return Map of maven id and its merged exclude rules. - */ - private fun calculateExcludeRules( - configurations: List - ): Map> { - return configurations - .asSequence() - .flatMap { it.dependencies } - .filter { it.group != null } - .filterIsInstance() - .groupBy { dep -> "${dep.group}:${dep.name}:${dep.version}" } - .mapValues { (_, artifacts) -> - artifacts.flatMap { it.extractExcludeRules() }.distinct() - }.filterValues { it.isNotEmpty() } - } - - private fun ExternalDependency.extractExcludeRules(): Set { - return excludeRules - .asSequence() - .map { - @Suppress("USELESS_ELVIS") // Gradle lying, module can be null - ExcludeRule(it.group, it.module ?: "") - } - .filterNot { it.artifact.isNullOrBlank() } - .filterNot { excludeArtifactsDenyList.contains(it.toString()) } - .toSet() - } -} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/ResolvedComponentsVisitor.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/ResolvedComponentsVisitor.kt new file mode 100644 index 00000000..c119ea74 --- /dev/null +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/ResolvedComponentsVisitor.kt @@ -0,0 +1,107 @@ +/* + * Copyright 2023 Grabtaxi Holdings PTE LTD (GRAB) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.grab.grazel.gradle.dependencies + +import com.grab.grazel.gradle.dependencies.ResolvedComponentsVisitor.Companion.IGNORED_ARTIFACTS +import com.grab.grazel.util.ansiCyan +import com.grab.grazel.util.ansiGreen +import com.grab.grazel.util.ansiYellow +import org.gradle.api.artifacts.result.ResolutionResult +import org.gradle.api.artifacts.result.ResolvedComponentResult +import org.gradle.api.artifacts.result.ResolvedDependencyResult +import org.gradle.api.internal.artifacts.result.DefaultResolvedComponentResult +import java.util.TreeSet + +private typealias Node = ResolvedComponentResult + +/** + * Visitor to flatten all components (including transitives) from a root [ResolvedComponentResult]. + * Ignore few artifacts specified by [IGNORED_ARTIFACTS] + */ +internal class ResolvedComponentsVisitor { + + private fun printIndented(level: Int, message: String, logger: (message: String) -> Unit) { + val prefix = if (level == 0) "─" else " └" + val indent = (0..level * 2).joinToString(separator = "") { "─" } + val msg = message.let { + when (level) { + 0 -> it.ansiCyan + 1 -> it.ansiGreen + else -> it.ansiYellow + } + } + logger("$prefix$indent $msg") + } + + private val Node.isProject get() = toString().startsWith("project :") + private val Node.repository + get() = (this as DefaultResolvedComponentResult).repositoryName ?: "" + + /** + * Visit all external dependency nodes in the graph and map them to [T] using the [transform] + * function. Both current component and its transitive dependencies are provided in the callback + * + * @param root The root component usually [ResolutionResult.getRoot] + * @param transform The callback used to convert to [T] + */ + fun > visit( + root: Node, + logger: (message: String) -> Unit = { }, + transform: (component: Node, repository: String, dependencies: Set) -> T? + ): Set { + val transitiveClosureMap = mutableMapOf>() + val visited = mutableSetOf() + val result = TreeSet(compareBy { it }) + + fun dfs(node: Node, level: Int = 0) { + if (node in visited) return + visited.add(node) + printIndented(level, node.toString(), logger) + + val transitiveClosure = TreeSet(compareBy(Node::toString)) + node.dependencies + .asSequence() + .filterIsInstance() + .map { it.selected } + .filter { !it.isProject } + .filter { dep -> IGNORED_ARTIFACTS.none { dep.toString().startsWith(it) } } + .forEach { directDependency -> + dfs(directDependency, level + 1) + + transitiveClosure.add(directDependency) + transitiveClosure.addAll(transitiveClosureMap[directDependency] ?: emptySet()) + } + transitiveClosureMap[node] = transitiveClosure + // TODO(arun) Memoize the transform + if (!node.isProject) { + transform( + node, + node.repository, + transitiveClosure.map { it.toString() + ":" + it.repository }.toSet() + )?.let(result::add) + } + } + dfs(root) + return result + } + + companion object { + private val IGNORED_ARTIFACTS = listOf( + "org.jetbrains.kotlin:kotlin-parcelize-runtime" + ) + } +} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/model/ExcludeRule.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/model/ExcludeRule.kt similarity index 74% rename from grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/model/ExcludeRule.kt rename to grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/model/ExcludeRule.kt index 745c1e1e..e754f52b 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/model/ExcludeRule.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/model/ExcludeRule.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 Grabtaxi Holdings PTE LTD (GRAB) + * Copyright 2023 Grabtaxi Holdings PTE LTD (GRAB) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,9 +14,12 @@ * limitations under the License. */ -package com.grab.grazel.migrate.dependencies.model +package com.grab.grazel.gradle.dependencies.model -internal data class ExcludeRule( +import java.io.Serializable + +@kotlinx.serialization.Serializable +data class ExcludeRule( val group: String, val artifact: String -) \ No newline at end of file +) : Serializable diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/model/ResolveDependenciesResult.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/model/ResolveDependenciesResult.kt new file mode 100644 index 00000000..fdbb00f2 --- /dev/null +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/model/ResolveDependenciesResult.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2023 Grabtaxi Holdings PTE LTD (GRAB) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.grab.grazel.gradle.dependencies.model + +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.decodeFromStream +import org.gradle.api.file.RegularFile +import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.Versioned +import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.DefaultVersionComparator +import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.Version +import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionParser +import java.io.File + +@Serializable +data class ResolveDependenciesResult( + val variantName: String, + val dependencies: Map> = HashMap() +) { + companion object { + fun fromJson(json: RegularFile) = fromJson(json.asFile) + fun fromJson(json: File) = json + .inputStream() + .buffered().use { stream -> Json.decodeFromStream(stream) } + } +} + +@Serializable +data class ResolvedDependency( + val id: String, + val version: String, + val shortId: String, + val direct: Boolean, + val dependencies: Set, + val excludeRules: Set, + val repository: String +) : Comparable { + override fun compareTo(other: ResolvedDependency) = id.compareTo(other.id) + + companion object { + fun from(dependencyNotation: String): ResolvedDependency { + val (group, name, version, repository) = dependencyNotation.split(":") + val shortId = "$group:$name" + return ResolvedDependency( + id = "$group:$name:$version", + version = version, + shortId = shortId, + direct = false, + dependencies = emptySet(), + excludeRules = emptySet(), + repository = repository + ) + } + } +} + +/** + * Unwrap [ResolvedDependency] such that it contains all its dependencies in the form of + * [ResolvedDependency] + */ +val ResolvedDependency.allDependencies: Set + get() = buildSet { + add(this@allDependencies.copy(dependencies = emptySet())) + addAll(dependencies.map { dependency -> ResolvedDependency.from(dependency) }) + } + + +class VersionInfo(val version: String) : Versioned, Comparable { + private val parsedVersion = VersionParser().transform(version) + override fun getVersion(): Version = parsedVersion + private val comparator = DefaultVersionComparator() + override fun compareTo(other: VersionInfo) = comparator.compare(this, other) +} + +val ResolvedDependency.versionInfo get() = VersionInfo(version = version) \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/VariantBuilder.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/VariantBuilder.kt index 10e0d347..b2261319 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/VariantBuilder.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/VariantBuilder.kt @@ -14,7 +14,7 @@ import javax.inject.Singleton /** * [VariantBuilder] is used to construct unified [Set] of [Variant] types for Android/Jvm [Project] * - * [VariantBuilder.build] caches constructed Variants and can be called multiple times for a project. + * [VariantBuilder.onVariants] caches constructed Variants and can be called multiple times for a project. * * For lazy construction and safe to call during configuration phase use [VariantBuilder.onVariants] * @@ -144,20 +144,41 @@ constructor( action(AndroidVariant(project, variant)) } - // Special case, if this module does not have flavors declared then variants - // will be just buildTypes. Since we already would have passed buildType variants - // above we don't need to pass it again here. + if (flavors.isNotEmpty()) { - buildTypes.flatMap { buildType -> - VariantType.values().filter { it != JvmBuild }.map { variantType -> - AndroidBuildType( - project = project, - backingVariant = buildType, - variantType = variantType, - flavors = flavorNames - ) - } - }.distinctBy { it.name + it.variantType }.forEach { variant -> action(variant) } + // Special case, if this module does not have flavors declared then variants + // will be just buildTypes. Since we already would have passed buildType variants + // above we don't need to pass it again here. + buildTypes + .asSequence() + .flatMap { buildType -> + VariantType.values() + .filter { it != JvmBuild } + .map { variantType -> + AndroidBuildType( + project = project, + backingVariant = buildType, + variantType = variantType, + flavors = flavorNames + ) + } + }.distinctBy { it.name + it.variantType } + .forEach(action) + + VariantType + .values() + .asSequence() + .filter { it != JvmBuild } + .flatMap { variantType -> + flavors.map { flavor -> + AndroidFlavor( + project, + flavor, + variantType, + buildTypeNames + ) + } + }.forEach(action) } } else if (project.isJvm) { action(JvmVariant(project = project, variantType = JvmBuild)) diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/MigrationModule.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/MigrationModule.kt index 794fe267..2ed721c4 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/MigrationModule.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/MigrationModule.kt @@ -16,5 +16,12 @@ package com.grab.grazel.migrate +import com.grab.grazel.migrate.dependencies.DefaultMavenInstallStore +import com.grab.grazel.migrate.dependencies.MavenInstallStore +import dagger.Binds + @dagger.Module -internal interface MigrationModule \ No newline at end of file +internal interface MigrationModule { + @Binds + fun DefaultMavenInstallStore.binds(): MavenInstallStore +} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidExtractor.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidExtractor.kt index 9f5d1ceb..54b1157f 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidExtractor.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidExtractor.kt @@ -34,7 +34,7 @@ import com.grab.grazel.gradle.variant.AndroidVariantDataSource import com.grab.grazel.gradle.variant.MatchedVariant import com.grab.grazel.gradle.variant.getMigratableBuildVariants import com.grab.grazel.gradle.variant.nameSuffix -import com.grab.grazel.migrate.android.PathResolveMode.* +import com.grab.grazel.migrate.android.PathResolveMode.DIRECTORY import com.grab.grazel.migrate.android.SourceSetType.ASSETS import com.grab.grazel.migrate.android.SourceSetType.JAVA_KOTLIN import com.grab.grazel.migrate.android.SourceSetType.RESOURCES @@ -88,7 +88,7 @@ constructor( dependent, matchedVariant ) - } + dependenciesDataSource.collectMavenDeps( + } + dependenciesDataSource.externalDependencies( project, BuildGraphType(BUILD, matchedVariant.variant) ) + project.kotlinParcelizeDeps() diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidInstrumentationBinaryDataExtractor.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidInstrumentationBinaryDataExtractor.kt index e8965e14..eabe729c 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidInstrumentationBinaryDataExtractor.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidInstrumentationBinaryDataExtractor.kt @@ -69,7 +69,7 @@ internal class DefaultAndroidInstrumentationBinaryDataExtractor ).map { dependency -> gradleDependencyToBazelDependency.map(project, dependency, matchedVariant) } + - dependenciesDataSource.collectMavenDeps( + dependenciesDataSource.externalDependencies( project, BuildGraphType(ConfigurationScope.ANDROID_TEST, matchedVariant.variant) ) + diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidLibraryDataExtractor.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidLibraryDataExtractor.kt deleted file mode 100644 index 8c75de2b..00000000 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidLibraryDataExtractor.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2022 Grabtaxi Holdings PTE LTD (GRAB) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.grab.grazel.migrate.android - -import com.grab.grazel.bazel.rules.ANDROIDX_GROUP -import com.grab.grazel.bazel.rules.ANNOTATION_ARTIFACT -import com.grab.grazel.bazel.rules.DAGGER_GROUP -import com.grab.grazel.bazel.rules.DATABINDING_GROUP -import com.grab.grazel.bazel.starlark.BazelDependency -import com.grab.grazel.gradle.dependencies.BuildGraphType -import com.grab.grazel.gradle.dependencies.DependenciesDataSource -import com.grab.grazel.gradle.hasDatabinding -import org.gradle.api.Project - -internal fun DependenciesDataSource.collectMavenDeps( - project: Project, vararg buildGraphTypes: BuildGraphType -): List = mavenDependencies(project, *buildGraphTypes) - .filter { - if (project.hasDatabinding) { - it.group != DATABINDING_GROUP && (it.group != ANDROIDX_GROUP && it.name != ANNOTATION_ARTIFACT) - } else true - }.map { - if (it.group == DAGGER_GROUP) { - BazelDependency.StringDependency("//:dagger") - } else { - BazelDependency.MavenDependency(it) - } - }.distinct() - .toList() diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidUnitTestDataExtractor.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidUnitTestDataExtractor.kt index b4fb1d26..c660c36d 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidUnitTestDataExtractor.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidUnitTestDataExtractor.kt @@ -79,7 +79,7 @@ internal class DefaultAndroidUnitTestDataExtractor @Inject constructor( ).map { dependent -> gradleDependencyToBazelDependency.map(project, dependent, matchedVariant) } + - dependenciesDataSource.collectMavenDeps( + dependenciesDataSource.externalDependencies( project, BuildGraphType(ConfigurationScope.TEST, matchedVariant.variant) ) + diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/Crashlytics.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/Crashlytics.kt index 123b54f0..1a6cb9b5 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/Crashlytics.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/Crashlytics.kt @@ -17,6 +17,8 @@ package com.grab.grazel.migrate.android import com.grab.grazel.GrazelExtension +import com.grab.grazel.gradle.variant.AndroidVariantDataSource +import com.grab.grazel.gradle.variant.getMigratableBuildVariants import com.grab.grazel.gradle.variant.MatchedVariant import com.grab.grazel.gradle.variant.nameSuffix import org.gradle.api.Project diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/MavenInstallArtifactsCalculator.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/MavenInstallArtifactsCalculator.kt new file mode 100644 index 00000000..15b0ccaf --- /dev/null +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/MavenInstallArtifactsCalculator.kt @@ -0,0 +1,427 @@ +/* + * Copyright 2022 Grabtaxi Holdings PTE LTD (GRAB) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.grab.grazel.migrate.dependencies + +import com.grab.grazel.GrazelExtension +import com.grab.grazel.bazel.rules.MavenInstallArtifact +import com.grab.grazel.bazel.rules.MavenInstallArtifact.* +import com.grab.grazel.bazel.rules.MavenInstallArtifact.Exclusion.* +import com.grab.grazel.bazel.rules.MavenRepository.* +import com.grab.grazel.di.qualifiers.RootProject +import com.grab.grazel.gradle.RepositoryDataSource +import com.grab.grazel.gradle.dependencies.IGNORED_ARTIFACT_GROUPS +import com.grab.grazel.gradle.dependencies.model.ExcludeRule +import com.grab.grazel.gradle.variant.DEFAULT_VARIANT +import com.grab.grazel.gradle.variant.Variant +import com.grab.grazel.gradle.variant.VariantBuilder +import com.grab.grazel.gradle.variant.migratableConfigurations +import com.grab.grazel.migrate.android.JetifierDataExtractor +import com.grab.grazel.migrate.dependencies.model.MavenExternalArtifact +import com.grab.grazel.migrate.dependencies.model.Repository +import com.grab.grazel.migrate.dependencies.model.mergeWith +import com.grab.grazel.migrate.dependencies.model.toMavenArtifact +import com.grab.grazel.util.ansiCyan +import com.grab.grazel.util.ansiYellow +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.ExternalDependency +import org.gradle.api.artifacts.repositories.PasswordCredentials +import org.gradle.api.artifacts.result.ResolutionResult +import org.gradle.api.internal.artifacts.repositories.DefaultMavenArtifactRepository +import org.gradle.api.internal.artifacts.result.DefaultResolvedComponentResult +import org.gradle.api.internal.artifacts.result.DefaultResolvedDependencyResult +import java.util.* +import javax.inject.Inject + +internal class MavenInstallArtifactsCalculator +@Inject +constructor( + @param:RootProject private val rootProject: Project, + private val repositoryDataSource: RepositoryDataSource, + private val grazelExtension: GrazelExtension, + private val variantBuilder: VariantBuilder, + private val mavenInstallStore: MavenInstallStore +) { + private val excludeArtifactsDenyList by lazy { + grazelExtension.rules.mavenInstall.excludeArtifactsDenyList.get() + } + private val mavenInstallExtension get() = grazelExtension.rules.mavenInstall + + /** + * Calculates `MavenInstallData` with exclude rules, dependency resolution, and repositories for + * the given `projectsToMigrate`. + */ + fun get( + projectsToMigrate: List, + externalArtifacts: Set, + externalRepositories: Set + ): Set { + val artifactsMap = calculateArtifactsMap(projectsToMigrate) + return artifactsMap.mapNotNull { (variantName, artifacts) -> + val name = variantName.toMavenRepoName() + val mavenInstallArtifacts = artifacts.map { artifact -> + when { + artifact.excludeRules.isEmpty() -> SimpleArtifact(artifact.id) + else -> DetailedArtifact( + group = artifact.group, + artifact = artifact.name, + version = artifact.version, + exclusions = artifact.excludeRules.map { + SimpleExclusion("${it.group}:${it.artifact}") + } + ) + } + }.sortedBy { it.id }.toSet().also { if (it.isEmpty()) return@mapNotNull null } + + val mavenRepositories = artifacts + .asSequence() + .map { it.repository.repository } + .distinct() + .map { repo -> repo.toMavenRepository() } + .toSet() + + val overridesFromExtension = mavenInstallExtension.overrideTargetLabels.get().toList() + val overridesFromArtifacts = artifacts + .mapNotNull(MavenExternalArtifact::overrideTarget) + .map { it.artifactShortId to it.label.toString() } + .toList() + val overrideTargets = (overridesFromArtifacts + overridesFromExtension) + .sortedWith( + compareBy(Pair::second).thenBy(Pair::first) + ).toMap() + MavenInstallData( + name = name, + artifacts = mavenInstallArtifacts, + externalArtifacts = if (variantName == DEFAULT_VARIANT) externalArtifacts else emptySet(), + repositories = mavenRepositories, + externalRepositories = if (variantName == DEFAULT_VARIANT) externalRepositories else emptySet(), + jetifierData = JetifierDataExtractor().extract( + rootProject = rootProject, + includeList = mavenInstallExtension.jetifyIncludeList.get(), + excludeList = mavenInstallExtension.jetifyExcludeList.get(), + allArtifacts = mavenInstallArtifacts.map(MavenInstallArtifact::id) + ), + failOnMissingChecksum = false, + excludeArtifacts = mavenInstallExtension.excludeArtifacts.get().toSet(), + overrideTargets = overrideTargets, + resolveTimeout = mavenInstallExtension.resolveTimeout, + artifactPinning = mavenInstallExtension.artifactPinning.enabled.get(), + versionConflictPolicy = mavenInstallExtension.versionConflictPolicy + ) + }.sortedBy { it.name }.toSet() + } + + + /** + * Calculate a map of `variantName` and their [MavenExternalArtifact] instances from each project's + * configurations. + */ + private fun calculateArtifactsMap( + projectsToMigrate: List + ): Map> { + val allVariants = projectsToMigrate.flatMap { variantBuilder.build(it) } + + // Group variants and their configurations + val variantConfigs = allVariants + .groupBy(Variant<*>::name, Variant<*>::migratableConfigurations) + .mapValues { it.value.flatten().asSequence() } + + // With the variant specific configurations, map to their direct dependencies. + val variantDependencies = resolveVariantDirectDependencies(variantConfigs) + + // Reduce variant dependencies to only contain dependencies unique to them. + val reducedDependencies = reduceDependencies( + variantDependencies = variantDependencies, + variantsExtendsMap = allVariants + .groupBy(Variant<*>::name, Variant<*>::extendsFrom) + .mapValues { it.value.flatten().toSet() } + ).onEach { (variantName, dependencies) -> + // Cache the computed buckets into MavenInstallStore + val variantRepoName = variantName.toMavenRepoName() + dependencies.forEach { artifact -> + mavenInstallStore[variantRepoName, artifact.group] = artifact.name + } + } + + // Filtered dependencies would not have accurate transitive closure in each bucket since + // dependencies might have been filtered in above step. To fix, we compute a new transitive + // graph using MavenInstallArtifact.componentResult + return computeFlatTransitiveGraph(reducedDependencies) + } + + /** + * Takes a list of variant name and the list of [Configuration] in them to produce + * a `MavenExternalArtifact` of direct dependencies. + * + * The data required for `MavenExternalArtifact` comes from different places and this method merges + * from all of them to produce `Map` of variants and `MavenExternalArtifact`s. The is derived as + * stated below. + * + * * Repository is calculated from merging all repositories in the project. + * * Exclude rules are calculated from `ExternalDependency` provided from `configuration.dependencies` + * * [org.gradle.api.artifacts.ResolvableDependencies.getDependencies] is used to calculate the direct + * dependencies of a configuration. + * * [ResolutionResult] is used to calculate dependency versions after resolving the correct version + * and on which repository it was resolved from. + * Only [ResolutionResult] contains transitive dependencies' information hence it is retained in + * [MavenExternalArtifact.componentResult] for further processing + */ + private fun resolveVariantDirectDependencies( + variantConfigs: Map> + ): Map> { + val repositories = repositoryDataSource.allRepositoriesByName + return variantConfigs.mapValues { (_, configurations) -> + val visitedComponents = mutableMapOf() + val excludeRules = calculateExcludeRules(configurations) + configurations + .filter { it.isCanBeResolved } + .flatMap { config -> + val resolvableDependencies = config.incoming + val directDependencies = resolvableDependencies + .dependencies + .asSequence() + .filterIsInstance() + .groupBy { "${it.group}:${it.name}" } + resolvableDependencies + .resolutionResult + .allComponents + .asSequence() + .filter { !it.toString().startsWith("project :") } + .filter { it.toString() !in visitedComponents } + .filter { it.moduleVersion!!.group !in IGNORED_ARTIFACT_GROUPS } + .filterIsInstance() + .filter { component -> + component.moduleVersion + ?.let { id -> "${id.group}:${id.name}" } + ?.let { it in directDependencies } ?: false + }.map { component -> + val version = component.moduleVersion!! + MavenExternalArtifact( + group = version.group, + version = version.version, + name = version.name, + repository = Repository( + name = component.repositoryName!!, + repository = repositories[component.repositoryName!!]!! + ), + excludeRules = excludeRules.getOrDefault( + version.toString(), + emptyList() + ) + ).apply { componentResult = component }.also { + visitedComponents[it.id] = "" + } + } + }.pickMaxVersion() + } + } + + /** + * From the [Sequence] of [MavenExternalArtifact] potentially containing duplicates, picks the + * max version of a dependency. + */ + private fun Sequence.pickMaxVersion() = groupBy { + it.shortId + }.mapValues { (_, artifacts) -> + artifacts.maxOf { it }.let { maxVersionArtifact -> + when { + artifacts.size > 1 -> { + val others = artifacts.filter { it != maxVersionArtifact } + maxVersionArtifact.mergeWith(others = others) + } + + else -> maxVersionArtifact + } + } + }.asSequence().map { it.value } + + /** + * Calculate and merge exclude rules from all dependency declarations. + * + * @param configurations Configurations to merge exclude rules from + * @return Map of maven id and its merged exclude rules. + */ + private fun calculateExcludeRules( + configurations: Sequence + ): Map> { + return configurations + .flatMap { config -> config.hierarchy.flatMap { it.dependencies } } + .filter { it.group != null } + .filterIsInstance() + .groupBy { dep -> "${dep.group}:${dep.name}:${dep.version}" } + .mapValues { (_, artifacts) -> + artifacts.flatMap { it.extractExcludeRules() } + .distinct() + .sortedBy { it.toString() } + }.filterValues { it.isNotEmpty() } + } + + + /** + * Variant to dependencies map can contain duplicates i.e dependencies in one variant can be + * also present in another. This method tries to ensure the base variants (default, test) contains + * all the common dependencies from their descendants by utilising [Variant.extendsFrom] property. + * + * For example: + * + * // and "default" extendsFrom "flavor" + * "default" = ["artifact1", "artifact2", "artifact3"] + * "flavor" = ["artifact1", "artifact3"] + * result = ["artifact3"] + */ + private fun reduceDependencies( + variantDependencies: Map>, + variantsExtendsMap: Map> + ): Map> { + val results = variantsExtendsMap.mapValues { (currentVariant, extendsFrom) -> + if (currentVariant == DEFAULT_VARIANT || extendsFrom.isEmpty()) { + variantDependencies.getOrDefault(currentVariant, emptySequence()) + } else { + val baseVariantDeps = extendsFrom + .asSequence() + .map { extends -> + variantDependencies.getOrDefault( + extends, + emptySequence() + ) + }.flatten() + .distinctBy { it.id } + reduce( + currentDeps = variantDependencies.getOrDefault(currentVariant, emptySequence()), + baseVariantDeps = baseVariantDeps + ) + } + } + return results + } + + private fun reduce( + currentDeps: Sequence, + baseVariantDeps: Sequence + ): Sequence { + val baseVariantDepsMap = baseVariantDeps.groupBy { it.id } + // Filter all dependencies that are not in baseDeps + return currentDeps + .filter { !baseVariantDepsMap.contains(it.id) } + .sortedBy { it.id } + } + + + private fun computeFlatTransitiveGraph( + reducedDeps: Map> + ): Map> { + val repositories = repositoryDataSource.allRepositoriesByName + val results = mutableMapOf>() + + // Compute the default classpath first + val defaultClasspath = flatten(reducedDeps[DEFAULT_VARIANT]!!, repositories) + .also { dependencies -> results[DEFAULT_VARIANT] = dependencies } + + val defaultClasspathMap = defaultClasspath.associateBy(MavenExternalArtifact::shortId) + + // With default classpath as base, compute the transitive graph of other classpaths + reducedDeps.filter { it.key != DEFAULT_VARIANT } + .mapValues { (_, dependencies) -> + flatten( + dependencies = dependencies, + repositories = repositories, + computeOverrides = true, + defaultClasspath = defaultClasspathMap + ) + }.forEach { (variant, artifacts) -> results[variant] = artifacts } + return results + } + + private fun flatten( + dependencies: Sequence, + repositories: Map, + computeOverrides: Boolean = false, + defaultClasspath: Map = emptyMap() + ): Sequence { + // Flatten transitive graph of each ComponentResult by visiting all dependencies + val visited = mutableMapOf() + return dependencies.flatMap { mavenArtifact -> + val result = mutableSetOf() + + fun visit(componentResult: DefaultResolvedComponentResult, level: Int = 0) { + printIndented(level, componentResult.toString()) + visited.getOrPut( + componentResult.moduleVersion.toString() + ) { componentResult } + if (componentResult.moduleVersion!!.group in IGNORED_ARTIFACT_GROUPS) + return + + componentResult.dependencies + .asSequence() + .filterIsInstance() + .map { it.selected } + .filterIsInstance() + .map { selected -> + selected to selected.toMavenArtifact( + repositories = repositories, + defaultClasspath = if (computeOverrides) defaultClasspath else emptyMap() + ) + }.filter { (_, artifact) -> artifact.group !in IGNORED_ARTIFACT_GROUPS } + .forEach { (selected, artifact: MavenExternalArtifact) -> + result += artifact + if (!visited.contains(selected.moduleVersion!!.toString())) { + visit(selected, level + 1) + } + } + } + result += mavenArtifact + visit(mavenArtifact.componentResult) + result + }.pickMaxVersion() + } + + private fun DefaultMavenArtifactRepository.toMavenRepository(): DefaultMavenRepository { + val passwordCredentials = try { + getCredentials(PasswordCredentials::class.java) + } catch (e: Exception) { + // We only support basic auth now + null + } + return DefaultMavenRepository( + url.toString(), + passwordCredentials?.username, + passwordCredentials?.password + ) + } + + private fun ExternalDependency.extractExcludeRules(): Set { + return excludeRules + .asSequence() + .map { + @Suppress("USELESS_ELVIS") // Gradle lying, module can be null + (ExcludeRule( + it.group, + it.module ?: "" + )) + } + .filterNot { it.artifact.isNullOrBlank() } + .filterNot { it.toString() in excludeArtifactsDenyList } + .toSet() + } + + private fun printIndented(level: Int, message: String) { + val prefix = if (level == 0) "─" else " └" + val indent = (0..level * 2).joinToString(separator = "") { "─" } + val msg = message.let { if (level == 0) it.ansiCyan else it.ansiYellow } + rootProject.logger.info("$prefix$indent $msg") + } +} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/MavenInstallStore.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/MavenInstallStore.kt new file mode 100644 index 00000000..f2c257ba --- /dev/null +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/MavenInstallStore.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2023 Grabtaxi Holdings PTE LTD (GRAB) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.grab.grazel.migrate.dependencies + +import com.grab.grazel.bazel.starlark.BazelDependency.MavenDependency +import java.util.concurrent.ConcurrentHashMap +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Data structure to hold information about generated maven repositories in `WORKSPACE` + */ +internal interface MavenInstallStore { + /** + * For a given variant hierarchy and `group` and `name`, the function will try to look + * for the dependency in each of the variant hierarchy and return the first one found. + * + * For example, if `androidx.activity:activity` is given and it was categorized + * under `@maven` repository then will return `@maven//:androidx_activity_activity` + * in form of [MavenDependency] + */ + operator fun get(variants: Set, group: String, name: String): MavenDependency? + + operator fun set(variantRepoName: String, group: String, name: String) + + val size: Int +} + +@Singleton +class DefaultMavenInstallStore +@Inject +constructor() : MavenInstallStore { + + private data class ArtifactKey( + val variant: String, + val group: String, + val name: String, + ) + + private val map = ConcurrentHashMap() + + override val size: Int get() = map.size + + override fun get(variants: Set, group: String, name: String): MavenDependency { + fun get(repo: String): MavenDependency? = + if (map.containsKey(ArtifactKey(repo, group, name))) { + MavenDependency(repo, group, name) + } else null + + return variants.asSequence().mapNotNull { variant -> + val repoName = variant.toMavenRepoName() + get(repoName) + }.firstOrNull() ?: get("maven") ?: run { + // When no dependency is found in the index, assume @maven. This could be incorrect + // but makes for easier testing + MavenDependency(group = group, name = name) + } + } + + override fun set(variantRepoName: String, group: String, name: String) { + map[ArtifactKey(variantRepoName, group, name)] = variantRepoName + } +} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/model/MavenExternalArtifact.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/model/MavenExternalArtifact.kt index 86fa2687..baa8f1ca 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/model/MavenExternalArtifact.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/dependencies/model/MavenExternalArtifact.kt @@ -17,10 +17,12 @@ package com.grab.grazel.migrate.dependencies.model import com.grab.grazel.bazel.starlark.BazelDependency.MavenDependency +import com.grab.grazel.gradle.dependencies.model.ExcludeRule import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.Versioned import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.DefaultVersionComparator import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.Version import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionParser +import org.gradle.api.internal.artifacts.repositories.DefaultMavenArtifactRepository import org.gradle.api.internal.artifacts.result.DefaultResolvedComponentResult internal data class MavenExternalArtifact( @@ -62,4 +64,34 @@ internal fun MavenExternalArtifact.mergeWith(others: List ).apply { componentResult = current.componentResult } +} + +internal fun DefaultResolvedComponentResult.toMavenArtifact( + repositories: Map, + excludeRules: List = emptyList(), + defaultClasspath: Map = emptyMap() +): MavenExternalArtifact { + val version = moduleVersion!! + val shortId = version.group + ":" + version.name + val overrideTarget = if (defaultClasspath.isEmpty()) null else { + when (shortId) { + in defaultClasspath -> OverrideTarget( + artifactShortId = shortId, + label = MavenDependency(group = version.group, name = version.name) + ) + + else -> null + } + } + return MavenExternalArtifact( + group = version.group, + version = version.version, + name = version.name, + repository = Repository( + name = repositoryName!!, + repository = repositories[repositoryName!!]!! + ), + excludeRules = excludeRules, + overrideTarget = overrideTarget + ).also { it.componentResult = this } } \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/internal/WorkspaceBuilder.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/internal/WorkspaceBuilder.kt index 69d78592..cdbce11d 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/internal/WorkspaceBuilder.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/internal/WorkspaceBuilder.kt @@ -19,40 +19,32 @@ package com.grab.grazel.migrate.internal import com.android.build.gradle.BaseExtension import com.grab.grazel.GrazelExtension import com.grab.grazel.bazel.rules.DAGGER_ARTIFACTS -import com.grab.grazel.bazel.rules.DAGGER_GROUP import com.grab.grazel.bazel.rules.DAGGER_REPOSITORIES -import com.grab.grazel.bazel.rules.DATABINDING_ARTIFACTS -import com.grab.grazel.bazel.rules.DATABINDING_GROUP import com.grab.grazel.bazel.rules.GRAB_BAZEL_COMMON_ARTIFACTS -import com.grab.grazel.bazel.rules.MavenRepository.DefaultMavenRepository import com.grab.grazel.bazel.rules.androidNdkRepository import com.grab.grazel.bazel.rules.androidSdkRepository import com.grab.grazel.bazel.rules.bazelCommonRepository import com.grab.grazel.bazel.rules.daggerWorkspaceRules -import com.grab.grazel.bazel.rules.jvmRules import com.grab.grazel.bazel.rules.kotlinCompiler import com.grab.grazel.bazel.rules.kotlinRepository import com.grab.grazel.bazel.rules.loadBazelCommonArtifacts import com.grab.grazel.bazel.rules.loadDaggerArtifactsAndRepositories +import com.grab.grazel.bazel.rules.mavenInstall import com.grab.grazel.bazel.rules.registerKotlinToolchain import com.grab.grazel.bazel.rules.toolAndroidRepository import com.grab.grazel.bazel.rules.workspace import com.grab.grazel.bazel.starlark.LoadStrategy import com.grab.grazel.bazel.starlark.StatementsBuilder +import com.grab.grazel.bazel.starlark.add import com.grab.grazel.bazel.starlark.statements import com.grab.grazel.di.qualifiers.RootProject import com.grab.grazel.gradle.GradleProjectInfo -import com.grab.grazel.gradle.RepositoryDataSource -import com.grab.grazel.gradle.dependencies.DependenciesDataSource -import com.grab.grazel.gradle.dependencies.MavenArtifact -import com.grab.grazel.gradle.dependencies.toMavenInstallArtifact import com.grab.grazel.gradle.isAndroidApplication import com.grab.grazel.migrate.BazelFileBuilder -import com.grab.grazel.migrate.android.JetifierDataExtractor import com.grab.grazel.migrate.android.parseCompileSdkVersion import com.grab.grazel.migrate.dependencies.ArtifactsPinner +import com.grab.grazel.migrate.dependencies.MavenInstallArtifactsCalculator import org.gradle.api.Project -import org.gradle.api.artifacts.repositories.PasswordCredentials import org.gradle.kotlin.dsl.the import javax.inject.Inject import javax.inject.Singleton @@ -62,18 +54,16 @@ internal class WorkspaceBuilder( private val projectsToMigrate: List, private val grazelExtension: GrazelExtension, private val gradleProjectInfo: GradleProjectInfo, - private val dependenciesDataSource: DependenciesDataSource, - private val repositoryDataSource: RepositoryDataSource, - private val artifactsPinner: ArtifactsPinner + private val artifactsPinner: ArtifactsPinner, + private val mavenInstallArtifactsCalculator: MavenInstallArtifactsCalculator ) : BazelFileBuilder { @Singleton class Factory @Inject constructor( @param:RootProject private val rootProject: Project, private val grazelExtension: GrazelExtension, private val gradleProjectInfo: GradleProjectInfo, - private val dependenciesDataSource: DependenciesDataSource, - private val repositoryDataSource: RepositoryDataSource, private val artifactsPinner: ArtifactsPinner, + private val mavenInstallArtifactsCalculator: MavenInstallArtifactsCalculator ) { fun create( projectsToMigrate: List @@ -82,16 +72,11 @@ internal class WorkspaceBuilder( projectsToMigrate, grazelExtension, gradleProjectInfo, - dependenciesDataSource, - repositoryDataSource, - artifactsPinner + artifactsPinner, + mavenInstallArtifactsCalculator ) } - private val dependenciesExtension get() = grazelExtension.dependencies - private val mavenInstall get() = grazelExtension.rules.mavenInstall - private val hasDatabinding = gradleProjectInfo.hasDatabinding - override fun build() = statements(loadStrategy = LoadStrategy.Inline()) { workspace(name = rootProject.name) @@ -108,7 +93,6 @@ internal class WorkspaceBuilder( private fun StatementsBuilder.buildJvmRules() { val hasDagger = gradleProjectInfo.hasDagger - val externalArtifacts = mutableListOf() val externalRepositories = mutableListOf() @@ -123,63 +107,31 @@ internal class WorkspaceBuilder( loadBazelCommonArtifacts(grazelExtension.rules.bazelCommon.repository.name) externalArtifacts += GRAB_BAZEL_COMMON_ARTIFACTS - val mavenArtifacts = dependenciesDataSource - .resolvedArtifactsFor( - projects = projectsToMigrate, - overrideArtifactVersions = dependenciesExtension.overrideArtifactVersions.get() - ).asSequence() - .filter { - val dagger = if (hasDagger) !it.id.contains(DAGGER_GROUP) else true - val db = if (hasDatabinding) !it.id.contains(DATABINDING_GROUP) else true - dagger && db - } - - val databindingArtifacts = if (!hasDatabinding) emptySequence() else { - DATABINDING_ARTIFACTS.map(MavenArtifact::toMavenInstallArtifact).asSequence() + val mavenInstall = grazelExtension.rules.mavenInstall.apply { + add(repository) + } + mavenInstallArtifactsCalculator.get( + projectsToMigrate, + externalArtifacts.toSortedSet(), + externalRepositories.toSortedSet() + ).forEach { mavenInstallData -> + mavenInstall( + name = mavenInstallData.name, + rulesJvmExternalName = mavenInstall.repository.name, + artifacts = mavenInstallData.artifacts, + externalArtifacts = mavenInstallData.externalArtifacts, + mavenRepositories = mavenInstallData.repositories, + externalRepositories = mavenInstallData.externalRepositories, + jetify = mavenInstallData.jetifierData.isEnabled, + jetifyIncludeList = mavenInstallData.jetifierData.includeList, + failOnMissingChecksum = false, + resolveTimeout = mavenInstallData.resolveTimeout, + excludeArtifacts = mavenInstallData.excludeArtifacts, + overrideTargets = mavenInstallData.overrideTargets, + versionConflictPolicy = mavenInstallData.versionConflictPolicy, + artifactPinning = artifactsPinner.isEnabled + ) } - - val repositories = repositoryDataSource.supportedRepositories - .map { repo -> - val passwordCredentials = if (grazelExtension.rules.mavenInstall.includeCredentials) { - try { - repo.getCredentials(PasswordCredentials::class.java) - } catch (e: Exception) { - // We only support basic auth now - null - } - } else null - DefaultMavenRepository( - repo.url.toString(), - passwordCredentials?.username, - passwordCredentials?.password - ) - } - - val allArtifacts = (mavenArtifacts + databindingArtifacts).sortedBy { it.id }.toSet() - - val jetifierData = JetifierDataExtractor().extract( - rootProject = rootProject, - includeList = mavenInstall.jetifyIncludeList.get(), - excludeList = mavenInstall.jetifyExcludeList.get(), - allArtifacts = allArtifacts.map { it.id } - ) - - jvmRules( - rulesJvmExternalRule = mavenInstall.repository, - artifacts = allArtifacts, - mavenRepositories = (repositories).distinct().toList(), - externalArtifacts = externalArtifacts.toList(), - externalRepositories = externalRepositories.toList(), - jetify = jetifierData.isEnabled, - jetifyIncludeList = jetifierData.includeList, - failOnMissingChecksum = false, - artifactPinning = artifactsPinner.isEnabled, - mavenInstallJson = artifactsPinner.mavenInstallJson(), - resolveTimeout = mavenInstall.resolveTimeout, - excludeArtifacts = mavenInstall.excludeArtifacts.get(), - overrideTargets = mavenInstall.overrideTargetLabels.get(), - versionConflictPolicy = mavenInstall.versionConflictPolicy, - ) } @@ -201,7 +153,7 @@ internal class WorkspaceBuilder( } } - internal fun addAndroidSdkRepositories(statementsBuilder: StatementsBuilder): Unit = + internal fun addAndroidSdkRepositories(statementsBuilder: StatementsBuilder) { statementsBuilder.run { // Find the android application module and extract compileSdk and buildToolsVersion rootProject @@ -222,6 +174,7 @@ internal class WorkspaceBuilder( ndkApiLevel = grazelExtension.android.ndkApiLevel ) } + } private fun validateNdkApiLevel() { val ndkApiLevel = grazelExtension.android.ndkApiLevel ?: return diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/kotlin/KotlinProjectDataExtractor.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/kotlin/KotlinProjectDataExtractor.kt index 832ac0f1..103445e2 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/kotlin/KotlinProjectDataExtractor.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/kotlin/KotlinProjectDataExtractor.kt @@ -27,7 +27,6 @@ import com.grab.grazel.gradle.dependencies.DependencyGraphs import com.grab.grazel.gradle.dependencies.GradleDependencyToBazelDependency import com.grab.grazel.gradle.hasKotlinAndroidExtensions import com.grab.grazel.migrate.android.SourceSetType -import com.grab.grazel.migrate.android.collectMavenDeps import com.grab.grazel.migrate.android.filterSourceSetPaths import com.grab.grazel.migrate.dependencies.calculateDirectDependencyTags import dagger.Lazy @@ -71,7 +70,10 @@ constructor( ).map { dependent -> gradleDependencyToBazelDependency.map(project, dependent, null) } + - dependenciesDataSource.collectMavenDeps(project) + + dependenciesDataSource.externalDependencies( + project, + BuildGraphType(ConfigurationScope.BUILD) + ) + project.androidJarDeps() + project.kotlinParcelizeDeps() diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/kotlin/KotlinUnitTestDataExtractor.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/kotlin/KotlinUnitTestDataExtractor.kt index 6cf9b329..90ae2525 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/kotlin/KotlinUnitTestDataExtractor.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/kotlin/KotlinUnitTestDataExtractor.kt @@ -26,7 +26,6 @@ import com.grab.grazel.gradle.dependencies.DependencyGraphs import com.grab.grazel.gradle.dependencies.GradleDependencyToBazelDependency import com.grab.grazel.migrate.android.FORMAT_UNIT_TEST_NAME import com.grab.grazel.migrate.android.SourceSetType -import com.grab.grazel.migrate.android.collectMavenDeps import com.grab.grazel.migrate.android.filterNonDefaultSourceSetDirs import com.grab.grazel.migrate.android.filterSourceSetPaths import com.grab.grazel.migrate.common.calculateTestAssociate @@ -77,7 +76,7 @@ internal class DefaultKotlinUnitTestDataExtractor @Inject constructor( } ) addAll( - dependenciesDataSource.collectMavenDeps( + dependenciesDataSource.externalDependencies( project, BuildGraphType(ConfigurationScope.TEST) ) diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/tasks/internal/ComputeWorkspaceDependenciesTask.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/tasks/internal/ComputeWorkspaceDependenciesTask.kt new file mode 100644 index 00000000..d7a51220 --- /dev/null +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/tasks/internal/ComputeWorkspaceDependenciesTask.kt @@ -0,0 +1,146 @@ +/* + * Copyright 2023 Grabtaxi Holdings PTE LTD (GRAB) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.grab.grazel.tasks.internal + +import com.grab.grazel.gradle.dependencies.model.ResolveDependenciesResult +import com.grab.grazel.gradle.dependencies.model.ResolvedDependency +import com.grab.grazel.gradle.dependencies.model.allDependencies +import com.grab.grazel.gradle.dependencies.model.versionInfo +import com.grab.grazel.gradle.variant.DEFAULT_VARIANT +import com.grab.grazel.gradle.variant.VariantBuilder +import dagger.Lazy +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import org.gradle.api.DefaultTask +import org.gradle.api.Project +import org.gradle.api.file.RegularFile +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.TaskAction +import org.gradle.kotlin.dsl.register +import java.io.File +import java.util.stream.Collectors +import java.util.stream.Collectors.flatMapping +import java.util.stream.Collectors.groupingByConcurrent +import kotlin.streams.toList + +@CacheableTask +abstract class ComputeWorkspaceDependenciesTask : DefaultTask() { + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val compileDependenciesJsons: ListProperty + + @get:OutputFile + abstract val mergedDependencies: RegularFileProperty + + init { + group = GRAZEL_TASK_GROUP + description = "Computes external maven dependencies for bazel" + } + + @TaskAction + fun action() { + val flattenClasspath = compileDependenciesJsons.get() + .parallelStream() + .map(ResolveDependenciesResult::fromJson) + .collect( + // Group variantName to dependencies + groupingByConcurrent( + ResolveDependenciesResult::variantName, + // Extract compile classpath and flatten it by including the transitive closure + flatMapping( + { resolvedDependency -> + resolvedDependency + .dependencies + .getValue("compile") + .stream() + .flatMap { it.allDependencies.stream() } + .parallel() + }, + // To find the max version, need to group by their shortID + groupingByConcurrent( + ResolvedDependency::shortId, + // Once grouped, reduce it and only pick the highest version + Collectors.reducing(null) { old, new -> + when { + old == null -> new + new == null -> old + else -> if (old.versionInfo > new.versionInfo) old else new + } + } + ) + ) + ) + ) + + val defaultClasspath = flattenClasspath.getValue(DEFAULT_VARIANT) + + val finalClasspath = flattenClasspath + .entries + .parallelStream() + .filter { it.key != DEFAULT_VARIANT } + .collect( + Collectors.toConcurrentMap( + { it.key }, + { (variantName, dependencies) -> + dependencies + .values + .stream() + .map { dependency -> + if (dependency!!.shortId in defaultClasspath) { + // TODO(arun) Add override target and check for all parent classpaths + println(dependency.shortId) + } + dependency + }.toList() + } + ) + ).apply { + put(DEFAULT_VARIANT, defaultClasspath.values.toList()) + }.filterValues { it.isNotEmpty() } + + mergedDependencies.asFile.get().writeText(Json.encodeToString(finalClasspath.toMap())) + } + + companion object { + private const val TASK_NAME = "computeWorkspaceDependencies" + internal fun register(rootProject: Project, variantBuilderProvider: Lazy) { + val computeTask = rootProject.tasks + .register(TASK_NAME) { + mergedDependencies.set( + File( + rootProject.buildDir, + "grazel/mergedDependencies.json" + ) + ) + } + ResolveVariantDependenciesTask.register( + rootProject, + variantBuilderProvider + ) { taskProvider -> + computeTask.configure { + compileDependenciesJsons.add(taskProvider.flatMap { it.resolvedDependencies }) + } + } + } + } +} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/tasks/internal/ResolveVariantDependenciesTask.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/tasks/internal/ResolveVariantDependenciesTask.kt new file mode 100644 index 00000000..7c78b0cc --- /dev/null +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/tasks/internal/ResolveVariantDependenciesTask.kt @@ -0,0 +1,278 @@ +/* + * Copyright 2023 Grabtaxi Holdings PTE LTD (GRAB) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.grab.grazel.tasks.internal + +import com.grab.grazel.gradle.dependencies.ResolvedComponentsVisitor +import com.grab.grazel.gradle.dependencies.model.ExcludeRule +import com.grab.grazel.gradle.dependencies.model.ResolveDependenciesResult +import com.grab.grazel.gradle.dependencies.model.ResolvedDependency +import com.grab.grazel.gradle.variant.Variant +import com.grab.grazel.gradle.variant.VariantBuilder +import com.grab.grazel.gradle.variant.isBase +import com.grab.grazel.util.dependsOn +import dagger.Lazy +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import org.gradle.api.DefaultTask +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.artifacts.ExternalDependency +import org.gradle.api.artifacts.result.ResolvedComponentResult +import org.gradle.api.file.RegularFile +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.MapProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.TaskProvider +import org.gradle.kotlin.dsl.named +import org.gradle.kotlin.dsl.register +import java.io.File +import kotlin.streams.asSequence + +@CacheableTask +abstract class ResolveVariantDependenciesTask : DefaultTask() { + + @get:Input + abstract val variantName: Property + + @get:Input + abstract val base: Property + + @get:Input + abstract val compileConfiguration: ListProperty + + @get:Input + abstract val compileDirectDependencies: MapProperty + + @get:Input + abstract val compileExcludeRules: MapProperty> + + @get:Input + abstract val annotationProcessorConfiguration: ListProperty + + @get:Input + abstract val kotlinCompilerPluginConfiguration: ListProperty + + @get:OutputFile + abstract val resolvedDependencies: RegularFileProperty + + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val baseDependenciesJsons: ListProperty + + init { + group = GRAZEL_TASK_GROUP + description = "Resolves configurations and serialized them to be read on later" + } + + private fun ListProperty.toResolvedDependencies( + directDependenciesMap: Map = emptyMap(), + baseDependenciesMap: Map = emptyMap(), + excludeRulesMap: Map> = emptyMap(), + removeTransitives: Boolean = false + ): Set { + return get().asSequence().flatMap { root -> + ResolvedComponentsVisitor() + .visit(root, logger::info) { component, repository, dependencies -> + val version = component.moduleVersion!! + val shortId = version.group + ":" + version.name + val isDirect = shortId in directDependenciesMap + if (shortId !in baseDependenciesMap) + ResolvedDependency( + id = component.toString(), + shortId = shortId, + direct = isDirect, + version = version.version, + dependencies = dependencies, + repository = repository, + excludeRules = excludeRulesMap.getOrDefault(shortId, emptySet()) + ) + else null + }.asSequence() + }.filter { if (removeTransitives) it.direct else true }.toSet() + } + + @TaskAction + fun action() { + val baseDependenciesMap = buildMap { + if (!base.get()) { + // For non baseVariant tasks, every dependency that appears in the base task's json output + // is consider direct dependencies, hence parse it add to [directDependenciesMap] + baseDependenciesJsons.get() + .stream() + .parallel() + .map(ResolveDependenciesResult.Companion::fromJson) + .sequential() + .asSequence() + .flatMap { it.dependencies.getValue("compile") } // Make this configurable + .groupBy(ResolvedDependency::shortId, ResolvedDependency::direct) + .mapValues { entry -> entry.value.any { it } } + .forEach { (shortId, direct) -> + if (direct) put(shortId, shortId) + } + } + } + + val resolvedDependenciesResult = ResolveDependenciesResult( + variantName = variantName.get(), + dependencies = buildMap { + put( + "compile", + compileConfiguration.toResolvedDependencies( + directDependenciesMap = compileDirectDependencies.get(), + baseDependenciesMap = baseDependenciesMap, + excludeRulesMap = compileExcludeRules.get(), + removeTransitives = !base.get() + ) + ) + /*put( + "annotationProcessing", + annotationProcessorConfiguration.toResolvedDependencies() + ) + put("kotlinExtension", kotlinCompilerPluginConfiguration.toResolvedDependencies())*/ + } + ) + resolvedDependencies.get() + .asFile + .writeText(Json.encodeToString(resolvedDependenciesResult)) + } + + companion object { + internal fun register( + rootProject: Project, + variantBuilderProvider: Lazy, + subprojectTaskConfigure: (TaskProvider) -> Unit + ) { + // Register a lifecycle to aggregate all subproject tasks + val rootResolveDependenciesTask = rootProject.tasks.register("resolveDependencies") {} + rootProject.afterEvaluate { + val variantBuilder = variantBuilderProvider.get() + subprojects.forEach { project -> + // First pass to create all tasks + variantBuilder.onVariants(project) { variant -> + processVariant(project, variant, rootResolveDependenciesTask) + } + // Second pass to establish inter dependencies based on extendsFrom property + variantBuilder.onVariants(project) { variant -> + configureVariantTaskDependencies(project, variant, subprojectTaskConfigure) + } + } + } + } + + private fun ExternalDependency.extractExcludeRules(): Set { + return excludeRules + .map { + @Suppress("USELESS_ELVIS") // Gradle lying, module can be null + (ExcludeRule( + it.group, + it.module ?: "" + )) + } + .filterNot { it.artifact.isNullOrBlank() } + // TODO(arun) Respect excludeArtifactsDenyList + //.filterNot { it.toString() in excludeArtifactsDenyList } + .toSet() + } + + private fun processVariant( + project: Project, + variant: Variant<*>, + rootResolveDependenciesTask: TaskProvider + ) { + val resolveVariantDependenciesTask = project.tasks + .register(variant.name + "ResolveDependencies") { + variantName.set(variant.name) + base.set(variant.isBase) + compileConfiguration.addAll(project.provider { + variant.compileConfiguration + .map { it.incoming.resolutionResult.root } + .toList() + }) + compileDirectDependencies.set(project.provider { + variant.compileConfiguration + .asSequence() + .flatMap { it.incoming.dependencies } + .filterIsInstance() + .associate { "${it.group}:${it.name}" to "${it.group}:${it.name}" } + }) + compileExcludeRules.set(project.provider { + variant.compileConfiguration + .asSequence() + .flatMap { it.incoming.dependencies } + .filterIsInstance() + .groupBy { dep -> "${dep.group}:${dep.name}" } + .mapValues { (_, artifacts) -> + artifacts.flatMap { it.extractExcludeRules() }.toSet() + }.filterValues { it.isNotEmpty() } + + }) + /* runtimeConfiguration.addAll(project.provider { + variant.runtimeConfiguration + .map { it.incoming.resolutionResult.root } + .toImmutableList() + }) + annotationProcessorConfiguration.addAll(project.provider { + variant.annotationProcessorConfiguration.map { it.incoming.resolutionResult.root } + }) + kotlinCompilerPluginConfiguration.addAll(project.provider { + variant.kotlinCompilerPluginConfiguration + .map { it.incoming.resolutionResult.root } + .toList() + })*/ + resolvedDependencies.set( + File( + project.buildDir, + "grazel/${variant.name}/dependencies.json" + ) + ) + } + rootResolveDependenciesTask.dependsOn(resolveVariantDependenciesTask) + } + + + private fun configureVariantTaskDependencies( + project: Project, + variant: Variant<*>, + subprojectTaskConfigure: (TaskProvider) -> Unit + ) { + val tasks = project.tasks + val taskName = variant.name + "ResolveDependencies" + val resolveTask = tasks.named(taskName) + resolveTask.configure { + val variantTask = this + variant.extendsFrom.forEach { extends -> + try { + val taskName = extends + "ResolveDependencies" + val extendsTask = tasks.named(taskName) + variantTask.baseDependenciesJsons.add(extendsTask.flatMap { it.resolvedDependencies }) + } catch (e: Exception) { + // TODO(arun) Handle gracefully + } + } + } + subprojectTaskConfigure(resolveTask) + } + } +} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/tasks/internal/TasksManager.kt b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/tasks/internal/TasksManager.kt index ed375189..57123fc6 100644 --- a/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/tasks/internal/TasksManager.kt +++ b/grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/tasks/internal/TasksManager.kt @@ -63,6 +63,7 @@ constructor( * See [Task Graph](https://grab.github.io/Grazel/gradle_tasks/#task-graph) */ fun configTasks() { + ComputeWorkspaceDependenciesTask.register(rootProject, grazelComponent.variantBuilder()) // Root bazel file generation task that should run at the start of migration val rootGenerateBazelScriptsTasks = GenerateRootBazelScriptsTask.register( rootProject, diff --git a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/DefaultDependenciesDataSourceTest.kt b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/DefaultDependenciesDataSourceTest.kt index d92b110f..163b9609 100644 --- a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/DefaultDependenciesDataSourceTest.kt +++ b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/DefaultDependenciesDataSourceTest.kt @@ -25,7 +25,10 @@ import com.grab.grazel.fake.FLAVOR2 import com.grab.grazel.fake.FakeAndroidVariantDataSource import com.grab.grazel.gradle.dependencies.* import com.grab.grazel.gradle.variant.AndroidVariantsExtractor +import com.grab.grazel.gradle.variant.DefaultAndroidVariantDataSource import com.grab.grazel.gradle.variant.DefaultAndroidVariantsExtractor +import com.grab.grazel.gradle.variant.DefaultVariantBuilder +import com.grab.grazel.migrate.dependencies.DefaultMavenInstallStore import org.gradle.api.Project import org.gradle.kotlin.dsl.add import org.gradle.kotlin.dsl.configure @@ -91,6 +94,12 @@ class DefaultDependenciesDataSourceTest : GrazelPluginTest() { dependencyResolutionService = DefaultDependencyResolutionService.register(rootProject), grazelExtension = GrazelExtension(rootProject), androidVariantsExtractor = androidVariantsExtractor, + variantBuilder = DefaultVariantBuilder( + DefaultAndroidVariantDataSource( + androidVariantsExtractor + ) + ), + mavenInstallStore = DefaultMavenInstallStore() ) } @@ -154,6 +163,12 @@ class DefaultDependenciesDataSourceTest : GrazelPluginTest() { dependencyResolutionService = DefaultDependencyResolutionService.register(rootProject), grazelExtension = GrazelExtension(rootProject), androidVariantsExtractor = androidVariantsExtractor, + variantBuilder = DefaultVariantBuilder( + DefaultAndroidVariantDataSource( + androidVariantsExtractor + ) + ), + mavenInstallStore = DefaultMavenInstallStore() ) assertTrue( "hasIgnoredArtifacts returns true when project contains any ignored artifacts", @@ -181,6 +196,12 @@ class DefaultDependenciesDataSourceTest : GrazelPluginTest() { dependencyResolutionService = DefaultDependencyResolutionService.register(rootProject), grazelExtension = GrazelExtension(rootProject), androidVariantsExtractor = DefaultAndroidVariantsExtractor(), + variantBuilder = DefaultVariantBuilder( + DefaultAndroidVariantDataSource( + androidVariantsExtractor + ) + ), + mavenInstallStore = DefaultMavenInstallStore() ) val dependencyArtifactMap = dependenciesDataSource.dependencyArtifactMap( rootProject, diff --git a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/dependencies/MavenInstallArtifactsCalculatorTest.kt b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/dependencies/MavenInstallArtifactsCalculatorTest.kt deleted file mode 100644 index 31e1115f..00000000 --- a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/gradle/dependencies/MavenInstallArtifactsCalculatorTest.kt +++ /dev/null @@ -1,75 +0,0 @@ -package com.grab.grazel.gradle.dependencies - -import com.android.build.gradle.AppExtension -import com.grab.grazel.buildProject -import com.grab.grazel.fake.FLAVOR1 -import com.grab.grazel.fake.FLAVOR2 -import com.grab.grazel.gradle.ANDROID_APPLICATION_PLUGIN -import com.grab.grazel.util.addGrazelExtension -import com.grab.grazel.util.createGrazelComponent -import com.grab.grazel.util.doEvaluate -import org.gradle.api.Project -import org.gradle.kotlin.dsl.configure -import org.gradle.kotlin.dsl.dependencies -import org.gradle.kotlin.dsl.repositories -import org.junit.After -import org.junit.Before -import org.junit.Test - -class MavenInstallArtifactsCalculatorTest { - private lateinit var rootProject: Project - private lateinit var androidBinary: Project - private lateinit var mavenInstallArtifactsCalculator: MavenInstallArtifactsCalculator - - @Before - fun setUp() { - rootProject = buildProject("root") - rootProject.addGrazelExtension() - val grazelComponent = rootProject.createGrazelComponent() - mavenInstallArtifactsCalculator = grazelComponent.mavenInstallArtifactsCalculator().get() - androidBinary = buildProject("app-binary", parent = rootProject) - configureAndroidBinary() - } - - private fun configureAndroidBinary() { - androidBinary.run { - plugins.apply { - apply(ANDROID_APPLICATION_PLUGIN) - } - repositories { - mavenCentral() - google() - } - extensions.configure { - defaultConfig { - compileSdkVersion(29) - buildToolsVersion("29.0.3") - } - flavorDimensions("service") - productFlavors { - create(FLAVOR1) { - dimension = "service" - } - create(FLAVOR2) { - dimension = "service" - } - } - } - dependencies { - add( - "implementation", - "androidx.appcompat:appcompat:1.3.0" - ) - } - doEvaluate() - } - } - - @After - fun tearDown() { - } - - @Test - fun `test`() { - } -} \ No newline at end of file diff --git a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/android/DefaultAndroidUnitTestDataExtractorTest.kt b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/android/DefaultAndroidUnitTestDataExtractorTest.kt index f8236728..b08de53a 100644 --- a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/android/DefaultAndroidUnitTestDataExtractorTest.kt +++ b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/android/DefaultAndroidUnitTestDataExtractorTest.kt @@ -33,7 +33,9 @@ import com.grab.grazel.gradle.dependencies.GradleDependencyToBazelDependency import com.grab.grazel.gradle.variant.AndroidVariantsExtractor import com.grab.grazel.gradle.variant.DefaultAndroidVariantDataSource import com.grab.grazel.gradle.variant.DefaultAndroidVariantsExtractor +import com.grab.grazel.gradle.variant.DefaultVariantBuilder import com.grab.grazel.gradle.variant.MatchedVariant +import com.grab.grazel.migrate.dependencies.DefaultMavenInstallStore import com.grab.grazel.util.doEvaluate import org.gradle.api.Project import org.gradle.kotlin.dsl.configure @@ -104,6 +106,12 @@ class DefaultAndroidUnitTestDataExtractorTest : GrazelPluginTest() { dependencyResolutionService = DefaultDependencyResolutionService.register(rootProject), grazelExtension = GrazelExtension(rootProject), androidVariantsExtractor = androidVariantsExtractor, + variantBuilder = DefaultVariantBuilder( + DefaultAndroidVariantDataSource( + androidVariantsExtractor + ) + ), + mavenInstallStore = DefaultMavenInstallStore() ) val dependencyGraphs = FakeDependencyGraphs() diff --git a/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/dependencies/MavenInstallArtifactsCalculatorTest.kt b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/dependencies/MavenInstallArtifactsCalculatorTest.kt new file mode 100644 index 00000000..7b09ec01 --- /dev/null +++ b/grazel-gradle-plugin/src/test/kotlin/com/grab/grazel/migrate/dependencies/MavenInstallArtifactsCalculatorTest.kt @@ -0,0 +1,39 @@ +package com.grab.grazel.migrate.dependencies + +import com.grab.grazel.buildProject +import com.grab.grazel.gradle.variant.setupAndroidVariantProject +import com.grab.grazel.gradle.variant.setupJvmVariantProject +import com.grab.grazel.util.addGrazelExtension +import com.grab.grazel.util.createGrazelComponent +import com.grab.grazel.util.doEvaluate +import org.gradle.api.Project +import org.junit.Before +import org.junit.Test + +class MavenInstallArtifactsCalculatorTest { + private lateinit var rootProject: Project + private lateinit var androidProject: Project + private lateinit var jvmProject: Project + private lateinit var mavenInstallArtifactsCalculator: MavenInstallArtifactsCalculator + private val projectsToMigrate by lazy { listOf(rootProject, androidProject, jvmProject) } + + @Before + fun setUp() { + rootProject = buildProject("root").also { + it.addGrazelExtension() + } + androidProject = buildProject("android", rootProject).also { + setupAndroidVariantProject(it) + } + jvmProject = buildProject("java", rootProject).also { + setupJvmVariantProject(it) + } + projectsToMigrate.forEach { it.doEvaluate() } + val grazelComponent = rootProject.createGrazelComponent() + mavenInstallArtifactsCalculator = grazelComponent.mavenInstallArtifactsCalculator().get() + } + + @Test + fun `test`() { + } +} \ No newline at end of file diff --git a/sample-android-library/BUILD.bazel b/sample-android-library/BUILD.bazel index 4a5f017d..a2215ded 100644 --- a/sample-android-library/BUILD.bazel +++ b/sample-android-library/BUILD.bazel @@ -83,7 +83,7 @@ grab_android_local_test( deps = [ "//:parcelize", "//sample-android-library:sample-android-library-flavor1-free-debug", - "@maven//:junit_junit", + "@test_maven//:junit_junit", ], ) @@ -105,7 +105,7 @@ grab_android_local_test( deps = [ "//:parcelize", "//sample-android-library:sample-android-library-flavor1-paid-debug", - "@maven//:junit_junit", + "@test_maven//:junit_junit", ], ) @@ -127,7 +127,7 @@ grab_android_local_test( deps = [ "//:parcelize", "//sample-android-library:sample-android-library-flavor2-free-debug", - "@maven//:junit_junit", + "@test_maven//:junit_junit", ], ) @@ -149,6 +149,6 @@ grab_android_local_test( deps = [ "//:parcelize", "//sample-android-library:sample-android-library-flavor2-paid-debug", - "@maven//:junit_junit", + "@test_maven//:junit_junit", ], ) diff --git a/sample-android/BUILD.bazel b/sample-android/BUILD.bazel index 19f3cdd7..b8d1567b 100644 --- a/sample-android/BUILD.bazel +++ b/sample-android/BUILD.bazel @@ -32,7 +32,6 @@ android_binary( crunch_png = False, custom_package = "com.grab.grazel.android.sample", debug_key = "//buildsystem:debug-keystore", - enable_compose = True, enable_data_binding = True, incremental_dexing = True, manifest = "src/debug/AndroidManifest.xml", @@ -74,7 +73,6 @@ android_binary( "@maven//:androidx_activity_activity", "@maven//:androidx_activity_activity_compose", "@maven//:androidx_appcompat_appcompat", - "@maven//:androidx_compose_compiler_compiler", "@maven//:androidx_compose_foundation_foundation", "@maven//:androidx_compose_foundation_foundation_layout", "@maven//:androidx_compose_material_material", @@ -128,7 +126,6 @@ android_binary( crunch_png = False, custom_package = "com.grab.grazel.android.sample", debug_key = "//buildsystem:debug-keystore", - enable_compose = True, enable_data_binding = True, incremental_dexing = True, manifest = "src/debug/AndroidManifest.xml", @@ -170,7 +167,6 @@ android_binary( "@maven//:androidx_activity_activity", "@maven//:androidx_activity_activity_compose", "@maven//:androidx_appcompat_appcompat", - "@maven//:androidx_compose_compiler_compiler", "@maven//:androidx_compose_foundation_foundation", "@maven//:androidx_compose_foundation_foundation_layout", "@maven//:androidx_compose_material_material", @@ -224,7 +220,6 @@ android_binary( crunch_png = False, custom_package = "com.grab.grazel.android.sample", debug_key = "//buildsystem:debug-keystore", - enable_compose = True, enable_data_binding = True, incremental_dexing = True, manifest = "src/debug/AndroidManifest.xml", @@ -266,7 +261,6 @@ android_binary( "@maven//:androidx_activity_activity", "@maven//:androidx_activity_activity_compose", "@maven//:androidx_appcompat_appcompat", - "@maven//:androidx_compose_compiler_compiler", "@maven//:androidx_compose_foundation_foundation", "@maven//:androidx_compose_foundation_foundation_layout", "@maven//:androidx_compose_material_material", @@ -320,7 +314,6 @@ android_binary( crunch_png = False, custom_package = "com.grab.grazel.android.sample", debug_key = "//buildsystem:debug-keystore", - enable_compose = True, enable_data_binding = True, incremental_dexing = True, manifest = "src/debug/AndroidManifest.xml", @@ -362,7 +355,6 @@ android_binary( "@maven//:androidx_activity_activity", "@maven//:androidx_activity_activity_compose", "@maven//:androidx_appcompat_appcompat", - "@maven//:androidx_compose_compiler_compiler", "@maven//:androidx_compose_foundation_foundation", "@maven//:androidx_compose_foundation_foundation_layout", "@maven//:androidx_compose_material_material", @@ -420,10 +412,12 @@ android_instrumentation_binary( "//sample-android-flavor:sample-android-flavor-flavor1-free-debug", "//sample-android-library:sample-android-library-flavor1-free-debug", "//sample-kotlin-library", + "@flavor1free_debug_android_test_maven//:androidx_test_espresso_espresso_core", + "@flavor1free_debug_android_test_maven//:androidx_test_ext_junit", + "@flavor1free_debug_android_test_maven//:androidx_test_monitor", "@maven//:androidx_activity_activity", "@maven//:androidx_activity_activity_compose", "@maven//:androidx_appcompat_appcompat", - "@maven//:androidx_compose_compiler_compiler", "@maven//:androidx_compose_foundation_foundation", "@maven//:androidx_compose_foundation_foundation_layout", "@maven//:androidx_compose_material_material", @@ -435,9 +429,6 @@ android_instrumentation_binary( "@maven//:androidx_lifecycle_lifecycle_common", "@maven//:androidx_lifecycle_lifecycle_runtime", "@maven//:androidx_lifecycle_lifecycle_viewmodel", - "@maven//:androidx_test_espresso_espresso_core", - "@maven//:androidx_test_ext_junit", - "@maven//:androidx_test_monitor", ], ) @@ -474,10 +465,12 @@ android_instrumentation_binary( "//sample-android-flavor:sample-android-flavor-flavor1-paid-debug", "//sample-android-library:sample-android-library-flavor1-paid-debug", "//sample-kotlin-library", + "@flavor1paid_debug_android_test_maven//:androidx_test_espresso_espresso_core", + "@flavor1paid_debug_android_test_maven//:androidx_test_ext_junit", + "@flavor1paid_debug_android_test_maven//:androidx_test_monitor", "@maven//:androidx_activity_activity", "@maven//:androidx_activity_activity_compose", "@maven//:androidx_appcompat_appcompat", - "@maven//:androidx_compose_compiler_compiler", "@maven//:androidx_compose_foundation_foundation", "@maven//:androidx_compose_foundation_foundation_layout", "@maven//:androidx_compose_material_material", @@ -489,9 +482,6 @@ android_instrumentation_binary( "@maven//:androidx_lifecycle_lifecycle_common", "@maven//:androidx_lifecycle_lifecycle_runtime", "@maven//:androidx_lifecycle_lifecycle_viewmodel", - "@maven//:androidx_test_espresso_espresso_core", - "@maven//:androidx_test_ext_junit", - "@maven//:androidx_test_monitor", ], ) @@ -528,10 +518,12 @@ android_instrumentation_binary( "//sample-android-flavor:sample-android-flavor-flavor2-free-debug", "//sample-android-library:sample-android-library-flavor2-free-debug", "//sample-kotlin-library", + "@flavor2free_debug_android_test_maven//:androidx_test_espresso_espresso_core", + "@flavor2free_debug_android_test_maven//:androidx_test_ext_junit", + "@flavor2free_debug_android_test_maven//:androidx_test_monitor", "@maven//:androidx_activity_activity", "@maven//:androidx_activity_activity_compose", "@maven//:androidx_appcompat_appcompat", - "@maven//:androidx_compose_compiler_compiler", "@maven//:androidx_compose_foundation_foundation", "@maven//:androidx_compose_foundation_foundation_layout", "@maven//:androidx_compose_material_material", @@ -543,9 +535,6 @@ android_instrumentation_binary( "@maven//:androidx_lifecycle_lifecycle_common", "@maven//:androidx_lifecycle_lifecycle_runtime", "@maven//:androidx_lifecycle_lifecycle_viewmodel", - "@maven//:androidx_test_espresso_espresso_core", - "@maven//:androidx_test_ext_junit", - "@maven//:androidx_test_monitor", ], ) @@ -582,10 +571,12 @@ android_instrumentation_binary( "//sample-android-flavor:sample-android-flavor-flavor2-paid-debug", "//sample-android-library:sample-android-library-flavor2-paid-debug", "//sample-kotlin-library", + "@flavor2paid_debug_android_test_maven//:androidx_test_espresso_espresso_core", + "@flavor2paid_debug_android_test_maven//:androidx_test_ext_junit", + "@flavor2paid_debug_android_test_maven//:androidx_test_monitor", "@maven//:androidx_activity_activity", "@maven//:androidx_activity_activity_compose", "@maven//:androidx_appcompat_appcompat", - "@maven//:androidx_compose_compiler_compiler", "@maven//:androidx_compose_foundation_foundation", "@maven//:androidx_compose_foundation_foundation_layout", "@maven//:androidx_compose_material_material", @@ -597,8 +588,5 @@ android_instrumentation_binary( "@maven//:androidx_lifecycle_lifecycle_common", "@maven//:androidx_lifecycle_lifecycle_runtime", "@maven//:androidx_lifecycle_lifecycle_viewmodel", - "@maven//:androidx_test_espresso_espresso_core", - "@maven//:androidx_test_ext_junit", - "@maven//:androidx_test_monitor", ], ) diff --git a/sample-android/build.gradle b/sample-android/build.gradle index 0a9cfbbf..1b9a34a5 100644 --- a/sample-android/build.gradle +++ b/sample-android/build.gradle @@ -123,6 +123,10 @@ android { excludes += '/META-INF/{AL2.0,LGPL2.1}' } } + + buildFeatures { + compose = false + } } dependencies { @@ -142,6 +146,10 @@ dependencies { exclude group: "androidx.appcompat", module: "appcompat" } + // Test for variant classpath + debugImplementation libs.androidx.paging.runtime + debugImplementation libs.leakcanary.android + implementation libs.androidx.compose.ui implementation libs.androidx.compose.ui.tooling implementation libs.androidx.compose.material diff --git a/sample-kotlin-library/BUILD.bazel b/sample-kotlin-library/BUILD.bazel index c8382d98..151db88f 100644 --- a/sample-kotlin-library/BUILD.bazel +++ b/sample-kotlin-library/BUILD.bazel @@ -23,6 +23,6 @@ grab_kt_jvm_test( "//visibility:public", ], deps = [ - "@maven//:junit_junit", + "@test_maven//:junit_junit", ], )