Skip to content

Commit 818cb74

Browse files
committed
Refactor DependencyGraphs to Gradle BuildService pattern (#161)
Fixes #161
1 parent edbbc82 commit 818cb74

25 files changed

+291
-49
lines changed

WORKSPACE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,7 @@ maven_install(
440440
"android.arch.lifecycle:livedata-core",
441441
"android.arch.lifecycle:runtime",
442442
"android.arch.lifecycle:viewmodel",
443+
"androidx.fragment:fragment",
443444
"com.android.databinding:baseLibrary",
444445
"com.android.databinding:library",
445446
"com.android.support.test.espresso:espresso-core",
@@ -459,7 +460,6 @@ maven_install(
459460
"com.android.support:support-compat",
460461
"com.android.support:support-core-ui",
461462
"com.android.support:support-core-utils",
462-
"com.android.support:support-fragment",
463463
"com.android.support:support-vector-drawable",
464464
"com.android.support:versionedparcelable",
465465
"com.android.support:viewpager",

grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/di/GrazelComponent.kt

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,10 @@ import com.grab.grazel.gradle.GradleProjectInfo
2626
import com.grab.grazel.gradle.MigrationChecker
2727
import com.grab.grazel.gradle.MigrationCriteriaModule
2828
import com.grab.grazel.gradle.RepositoryDataSource
29+
import com.grab.grazel.gradle.dependencies.DefaultDependencyGraphsService
2930
import com.grab.grazel.gradle.dependencies.DefaultDependencyResolutionService
3031
import com.grab.grazel.gradle.dependencies.DependenciesDataSource
31-
import com.grab.grazel.gradle.dependencies.DependenciesGraphsBuilder
3232
import com.grab.grazel.gradle.dependencies.DependenciesModule
33-
import com.grab.grazel.gradle.dependencies.DependencyGraphs
3433
import com.grab.grazel.gradle.variant.AndroidVariantDataSource
3534
import com.grab.grazel.gradle.variant.VariantBuilder
3635
import com.grab.grazel.gradle.variant.VariantMatcher
@@ -96,6 +95,8 @@ internal interface GrazelComponent {
9695
fun manifestValuesBuilder(): ManifestValuesBuilder
9796

9897
fun dependencyResolutionService(): GradleProvider<DefaultDependencyResolutionService>
98+
fun dependencyGraphsService(): GradleProvider<DefaultDependencyGraphsService>
99+
fun configurationDataSource(): Lazy<ConfigurationDataSource>
99100
fun repositoryDataSource(): Lazy<RepositoryDataSource>
100101
}
101102

@@ -124,10 +125,6 @@ internal interface GrazelModule {
124125
@Provides
125126
fun @receiver:RootProject Project.provideGrazelExtension(): GrazelExtension = the()
126127

127-
@Provides
128-
@Singleton
129-
fun DependenciesGraphsBuilder.provideDependencyGraphs(): DependencyGraphs = build()
130-
131128
@Provides
132129
@Singleton
133130
fun GrazelExtension.provideKotlinExtension() = rules.kotlin

grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/GradleProjectInfo.kt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ package com.grab.grazel.gradle
1818

1919
import com.grab.grazel.GrazelExtension
2020
import com.grab.grazel.di.qualifiers.RootProject
21+
import com.grab.grazel.gradle.dependencies.DefaultDependencyGraphsService
2122
import com.grab.grazel.gradle.dependencies.DependencyGraphs
2223
import com.grab.grazel.gradle.dependencies.model.WorkspaceDependencies
23-
import dagger.Lazy
24+
import com.grab.grazel.util.GradleProvider
2425
import org.gradle.api.Project
2526
import org.gradle.kotlin.dsl.provideDelegate
2627
import java.io.File
@@ -43,7 +44,7 @@ interface GradleProjectInfo {
4344
internal class DefaultGradleProjectInfo(
4445
override val rootProject: Project,
4546
override val grazelExtension: GrazelExtension,
46-
private val dependencyGraphsProvider: Lazy<DependencyGraphs>,
47+
private val dependencyGraphsService: GradleProvider<DefaultDependencyGraphsService>,
4748
private val workspaceDependencies: WorkspaceDependencies
4849
) : GradleProjectInfo {
4950

@@ -53,19 +54,19 @@ internal class DefaultGradleProjectInfo(
5354
@param:RootProject
5455
private val rootProject: Project,
5556
private val grazelExtension: GrazelExtension,
56-
private val dependencyGraphsProvider: Lazy<DependencyGraphs>,
57+
private val dependencyGraphsService: GradleProvider<DefaultDependencyGraphsService>,
5758
) {
5859
fun create(
5960
workspaceDependencies: WorkspaceDependencies
6061
): GradleProjectInfo = DefaultGradleProjectInfo(
6162
rootProject,
6263
grazelExtension,
63-
dependencyGraphsProvider,
64+
dependencyGraphsService,
6465
workspaceDependencies
6566
)
6667
}
6768

68-
private val projectGraph: DependencyGraphs get() = dependencyGraphsProvider.get()
69+
private val projectGraph: DependencyGraphs get() = dependencyGraphsService.get().get()
6970

7071
override val hasDagger: Boolean by lazy {
7172
workspaceDependencies

grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/MigrationCriteria.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616

1717
package com.grab.grazel.gradle
1818

19+
import com.grab.grazel.gradle.dependencies.DefaultDependencyGraphsService
1920
import com.grab.grazel.gradle.dependencies.DependenciesDataSource
20-
import com.grab.grazel.gradle.dependencies.DependencyGraphs
21-
import dagger.Lazy
21+
import com.grab.grazel.util.GradleProvider
2222
import dagger.Module
2323
import dagger.Provides
2424
import dagger.multibindings.ElementsIntoSet
@@ -44,7 +44,7 @@ internal object MigrationCriteriaModule {
4444

4545
@Singleton
4646
internal class MigrationChecker @Inject constructor(
47-
private val dependencyGraphsProvider: Lazy<DependencyGraphs>,
47+
private val dependencyGraphsService: GradleProvider<DefaultDependencyGraphsService>,
4848
private val migrationCriteria: Set<@JvmSuppressWildcards MigrationCriteria>
4949
) : MigrationCriteria {
5050
/**
@@ -63,7 +63,8 @@ internal class MigrationChecker @Inject constructor(
6363
return when {
6464
canMigrateInternal(project) -> true
6565
else -> {
66-
dependencyGraphsProvider
66+
dependencyGraphsService
67+
.get()
6768
.get()
6869
.dependenciesSubGraphByVariant(project)
6970
.all(::canMigrateInternal)

grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/dependencies/DependenciesModule.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ internal interface DependenciesModule {
4242
@RootProject rootProject: Project
4343
): GradleProvider<@JvmSuppressWildcards DefaultDependencyResolutionService> =
4444
DefaultDependencyResolutionService.register(rootProject)
45+
46+
@Singleton
47+
@Provides
48+
fun dependencyGraphsService(
49+
@RootProject rootProject: Project
50+
): GradleProvider<@JvmSuppressWildcards DefaultDependencyGraphsService> =
51+
DefaultDependencyGraphsService.register(rootProject)
4552
}
4653
}
4754

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
* Copyright 2022 Grabtaxi Holdings PTE LTD (GRAB)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.grab.grazel.gradle.dependencies
18+
19+
import com.grab.grazel.di.qualifiers.RootProject
20+
import com.grab.grazel.gradle.ConfigurationDataSource
21+
import com.grab.grazel.gradle.dependencies.DependencyGraphsService.Companion.SERVICE_NAME
22+
import com.grab.grazel.gradle.variant.AndroidVariantDataSource
23+
import kotlinx.coroutines.runBlocking
24+
import kotlinx.coroutines.sync.Mutex
25+
import kotlinx.coroutines.sync.withLock
26+
import org.gradle.api.Project
27+
import org.gradle.api.services.BuildService
28+
import org.gradle.api.services.BuildServiceParameters
29+
30+
/**
31+
* A [BuildService] to lazily build and cache dependency graphs during task execution phase.
32+
*
33+
* This ensures that Gradle configuration resolution happens during execution phase rather than
34+
* configuration phase, following Gradle best practices.
35+
*/
36+
internal interface DependencyGraphsService : BuildService<DependencyGraphsService.Params>,
37+
AutoCloseable {
38+
39+
/**
40+
* Get the dependency graphs. Lazily builds the graphs on first access using dependencies
41+
* provided via [configure].
42+
*
43+
* @throws IllegalStateException if [configure] has not been called
44+
*/
45+
fun get(): DependencyGraphs
46+
47+
/**
48+
* Configure the service with dependencies needed to build graphs. This should be called during
49+
* task configuration phase. The actual graph building is deferred until [get] is called.
50+
*
51+
* @param rootProject The root Gradle project
52+
* @param dependenciesDataSource Source for project dependencies
53+
* @param configurationDataSource Source for configuration data
54+
* @param androidVariantDataSource Source for Android variant data
55+
*/
56+
fun configure(
57+
rootProject: Project,
58+
dependenciesDataSource: DependenciesDataSource,
59+
configurationDataSource: ConfigurationDataSource,
60+
androidVariantDataSource: AndroidVariantDataSource
61+
)
62+
63+
companion object {
64+
internal const val SERVICE_NAME = "DependencyGraphsService"
65+
}
66+
67+
interface Params : BuildServiceParameters
68+
}
69+
70+
internal abstract class DefaultDependencyGraphsService : DependencyGraphsService {
71+
private var dependencyGraphs: DependencyGraphs? = null
72+
private val buildMutex = Mutex()
73+
74+
private var rootProject: Project? = null
75+
private var dependenciesDataSource: DependenciesDataSource? = null
76+
private var configurationDataSource: ConfigurationDataSource? = null
77+
private var androidVariantDataSource: AndroidVariantDataSource? = null
78+
79+
override fun configure(
80+
rootProject: Project,
81+
dependenciesDataSource: DependenciesDataSource,
82+
configurationDataSource: ConfigurationDataSource,
83+
androidVariantDataSource: AndroidVariantDataSource
84+
) {
85+
this.rootProject = rootProject
86+
this.dependenciesDataSource = dependenciesDataSource
87+
this.configurationDataSource = configurationDataSource
88+
this.androidVariantDataSource = androidVariantDataSource
89+
}
90+
91+
override fun get(): DependencyGraphs {
92+
// Lazily build on first access
93+
if (dependencyGraphs == null) {
94+
val root = rootProject
95+
?: error("DependencyGraphsService not configured. Call configure() first during task configuration.")
96+
val deps = dependenciesDataSource
97+
?: error("DependencyGraphsService not configured. Call configure() first during task configuration.")
98+
val config = configurationDataSource
99+
?: error("DependencyGraphsService not configured. Call configure() first during task configuration.")
100+
val variants = androidVariantDataSource
101+
?: error("DependencyGraphsService not configured. Call configure() first during task configuration.")
102+
103+
runBlocking {
104+
buildMutex.withLock {
105+
if (dependencyGraphs == null) {
106+
dependencyGraphs = DependenciesGraphsBuilder(
107+
root,
108+
deps,
109+
config,
110+
variants
111+
).build()
112+
}
113+
}
114+
}
115+
}
116+
return dependencyGraphs!!
117+
}
118+
119+
override fun close() {
120+
dependencyGraphs = null
121+
rootProject = null
122+
dependenciesDataSource = null
123+
configurationDataSource = null
124+
androidVariantDataSource = null
125+
}
126+
127+
companion object {
128+
internal fun register(@RootProject rootProject: Project) = rootProject
129+
.gradle
130+
.sharedServices
131+
.registerIfAbsent(SERVICE_NAME, DefaultDependencyGraphsService::class.java) {}
132+
}
133+
}

grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/VariantDataSource.kt

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,21 @@ import org.gradle.api.Project
2828

2929
internal interface AndroidVariantDataSource {
3030
/**
31-
* Variant filter instance to filter out unsupported variants
31+
* Returns the variant filter instance to filter out unsupported variants.
32+
*
33+
* Note: This function may be computed on each call by reading from [GrazelExtension.android.variantFilter].
34+
* This ensures the filter is evaluated after user configuration runs.
3235
*/
33-
val variantFilter: Action<VariantFilter>?
36+
fun getVariantFilter(): Action<VariantFilter>?
3437

3538
/**
3639
* This method will return the flavors which are ignored after evaluate the ignore variants
37-
* determined by [variantFilter]
40+
* determined by [getVariantFilter]
3841
*/
3942
fun getIgnoredFlavors(project: Project): List<ProductFlavor>
4043

4144
/**
42-
* This method will return the variants which are ignored by the configuration determined by [variantFilter]
45+
* This method will return the variants which are ignored by the configuration determined by [getVariantFilter]
4346
*/
4447
fun getIgnoredVariants(project: Project): List<BaseVariant>
4548

@@ -92,9 +95,11 @@ internal class DefaultVariantFilter(variant: BaseVariant) : VariantFilter {
9295

9396
internal class DefaultAndroidVariantDataSource(
9497
private val androidVariantsExtractor: AndroidVariantsExtractor,
95-
override val variantFilter: Action<VariantFilter>? = null,
98+
private val variantFilterProvider: () -> Action<VariantFilter>? = { null },
9699
) : AndroidVariantDataSource {
97100

101+
override fun getVariantFilter(): Action<VariantFilter>? = variantFilterProvider()
102+
98103
private fun Project.androidVariants() =
99104
androidVariantsExtractor.getVariants(this) +
100105
androidVariantsExtractor.getUnitTestVariants(this) +
@@ -168,7 +173,7 @@ internal class DefaultAndroidVariantDataSource(
168173
private fun ignoredVariantFilter(
169174
variant: BaseVariant
170175
): Boolean = DefaultVariantFilter(variant)
171-
.apply { variantFilter?.execute(this) }
176+
.apply { getVariantFilter()?.execute(this) }
172177
.ignored
173178
}
174179

grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/gradle/variant/VariantModule.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ internal interface VariantModule {
2626
androidVariantsExtractor: DefaultAndroidVariantsExtractor,
2727
@RootProject rootProject: Project
2828
): AndroidVariantDataSource = DefaultAndroidVariantDataSource(
29-
variantFilter = android.variantFilter,
29+
variantFilterProvider = { android.variantFilter },
3030
androidVariantsExtractor = androidVariantsExtractor
3131
)
3232
}

grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidExtractor.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import com.android.build.gradle.api.AndroidSourceSet
2121
import com.grab.grazel.GrazelExtension
2222
import com.grab.grazel.bazel.rules.Multidex
2323
import com.grab.grazel.bazel.starlark.BazelDependency
24+
import com.grab.grazel.gradle.dependencies.DefaultDependencyGraphsService
2425
import com.grab.grazel.gradle.dependencies.DependenciesDataSource
2526
import com.grab.grazel.gradle.variant.VariantGraphKey
2627
import com.grab.grazel.gradle.variant.VariantType
@@ -37,7 +38,7 @@ import com.grab.grazel.gradle.variant.nameSuffix
3738
import com.grab.grazel.migrate.android.SourceSetType.JAVA_KOTLIN
3839
import com.grab.grazel.migrate.dependencies.calculateDirectDependencyTags
3940
import com.grab.grazel.migrate.kotlin.kotlinParcelizeDeps
40-
import dagger.Lazy
41+
import com.grab.grazel.util.GradleProvider
4142
import org.gradle.api.Project
4243
import org.gradle.kotlin.dsl.getByType
4344
import javax.inject.Inject
@@ -59,11 +60,11 @@ constructor(
5960
private val androidManifestParser: AndroidManifestParser,
6061
private val grazelExtension: GrazelExtension,
6162
private val dependenciesDataSource: DependenciesDataSource,
62-
private val dependencyGraphsProvider: Lazy<DependencyGraphs>,
63+
private val dependencyGraphsService: GradleProvider<DefaultDependencyGraphsService>,
6364
private val gradleDependencyToBazelDependency: GradleDependencyToBazelDependency
6465
) : AndroidLibraryDataExtractor {
6566

66-
private val projectDependencyGraphs get() = dependencyGraphsProvider.get()
67+
private val projectDependencyGraphs: DependencyGraphs get() = dependencyGraphsService.get().get()
6768

6869
override fun extract(
6970
project: Project,

grazel-gradle-plugin/src/main/kotlin/com/grab/grazel/migrate/android/AndroidInstrumentationBinaryDataExtractor.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import com.android.build.gradle.BaseExtension
2020
import com.android.build.gradle.api.AndroidSourceSet
2121
import com.grab.grazel.GrazelExtension
2222
import com.grab.grazel.bazel.starlark.BazelDependency
23+
import com.grab.grazel.gradle.dependencies.DefaultDependencyGraphsService
2324
import com.grab.grazel.gradle.dependencies.DependenciesDataSource
2425
import com.grab.grazel.gradle.dependencies.DependencyGraphs
2526
import com.grab.grazel.gradle.dependencies.GradleDependencyToBazelDependency
@@ -30,7 +31,7 @@ import com.grab.grazel.gradle.variant.AndroidVariantDataSource
3031
import com.grab.grazel.gradle.variant.MatchedVariant
3132
import com.grab.grazel.gradle.variant.getMigratableBuildVariants
3233
import com.grab.grazel.gradle.variant.nameSuffix
33-
import dagger.Lazy
34+
import com.grab.grazel.util.GradleProvider
3435
import org.gradle.api.Project
3536
import org.gradle.kotlin.dsl.getByType
3637
import java.io.File
@@ -50,14 +51,14 @@ internal class DefaultAndroidInstrumentationBinaryDataExtractor
5051
@Inject constructor(
5152
private val variantDataSource: AndroidVariantDataSource,
5253
private val dependenciesDataSource: DependenciesDataSource,
53-
private val dependencyGraphsProvider: Lazy<DependencyGraphs>,
54+
private val dependencyGraphsService: GradleProvider<DefaultDependencyGraphsService>,
5455
private val gradleDependencyToBazelDependency: GradleDependencyToBazelDependency,
5556
private val androidManifestParser: AndroidManifestParser,
5657
private val manifestValuesBuilder: ManifestValuesBuilder,
5758
private val keyStoreExtractor: KeyStoreExtractor,
5859
private val grazelExtension: GrazelExtension,
5960
) : AndroidInstrumentationBinaryDataExtractor {
60-
private val projectDependencyGraphs get() = dependencyGraphsProvider.get()
61+
private val projectDependencyGraphs: DependencyGraphs get() = dependencyGraphsService.get().get()
6162

6263
override fun extract(
6364
project: Project,

0 commit comments

Comments
 (0)