Skip to content

Commit c920db8

Browse files
committed
support multiple android base packages
fixes #372 fixes #402
1 parent 2204ba3 commit c920db8

File tree

17 files changed

+373
-126
lines changed

17 files changed

+373
-126
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright (C) 2021-2022 Rick Busarow
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
package modulecheck.api.context
17+
18+
import modulecheck.parsing.gradle.SourceSetName
19+
import modulecheck.project.AndroidMcProject
20+
import modulecheck.project.McProject
21+
import modulecheck.project.ProjectContext
22+
import modulecheck.utils.SafeCache
23+
24+
data class AndroidBasePackages(
25+
private val delegate: SafeCache<SourceSetName, String?>,
26+
private val project: McProject
27+
) : ProjectContext.Element {
28+
29+
override val key: ProjectContext.Key<AndroidBasePackages>
30+
get() = Key
31+
32+
suspend fun get(sourceSetName: SourceSetName): String? {
33+
if (project !is AndroidMcProject) return null
34+
35+
return delegate.getOrPut(sourceSetName) {
36+
37+
// Note that this isn't just looking for a manifest file. It's looking for a manifest which
38+
// has a defined base package. It's possible for a manifest to exist, but just add an
39+
// Activity or something, if the package is already defined in an withUpstream source set.
40+
sourceSetName
41+
.withUpstream(project)
42+
.firstNotNullOfOrNull { sourceSetOrUpstream ->
43+
44+
project.manifestFileForSourceSetName(sourceSetOrUpstream)?.basePackage
45+
}
46+
}
47+
}
48+
49+
companion object Key : ProjectContext.Key<AndroidBasePackages> {
50+
override suspend operator fun invoke(project: McProject): AndroidBasePackages {
51+
52+
return AndroidBasePackages(SafeCache(), project)
53+
}
54+
}
55+
}
56+
57+
suspend fun ProjectContext.androidBasePackages(): AndroidBasePackages =
58+
get(AndroidBasePackages)
59+
60+
suspend fun ProjectContext.androidBasePackagesForSourceSetName(
61+
sourceSetName: SourceSetName
62+
): String? = androidBasePackages().get(sourceSetName)

modulecheck-api/src/main/kotlin/modulecheck/api/context/AndroidDataBindingDeclarations.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,11 @@ data class AndroidDataBindingDeclarations(
4141

4242
if (project !is AndroidMcProject) return emptyLazySet()
4343

44-
val basePackage = project.androidPackageOrNull ?: return emptyLazySet()
45-
4644
return delegate.getOrPut(sourceSetName) {
4745

46+
val basePackage = project.androidBasePackagesForSourceSetName(sourceSetName)
47+
?: return@getOrPut emptyLazySet()
48+
4849
lazySet(
4950
lazyDataSource {
5051
project.layoutFiles()
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright (C) 2021-2022 Rick Busarow
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
package modulecheck.api.context
17+
18+
import modulecheck.parsing.gradle.SourceSetName
19+
import modulecheck.project.McProject
20+
import modulecheck.project.ProjectContext
21+
import modulecheck.utils.SafeCache
22+
23+
data class AndroidRFqNames(
24+
private val delegate: SafeCache<SourceSetName, String?>,
25+
private val project: McProject
26+
) : ProjectContext.Element {
27+
28+
override val key: ProjectContext.Key<AndroidRFqNames>
29+
get() = Key
30+
31+
suspend fun get(sourceSetName: SourceSetName): String? {
32+
33+
return delegate.getOrPut(sourceSetName) {
34+
35+
project.androidBasePackagesForSourceSetName(sourceSetName)?.let { "$it.R" }
36+
}
37+
}
38+
39+
companion object Key : ProjectContext.Key<AndroidRFqNames> {
40+
override suspend operator fun invoke(project: McProject): AndroidRFqNames {
41+
42+
return AndroidRFqNames(SafeCache(), project)
43+
}
44+
}
45+
}
46+
47+
suspend fun ProjectContext.androidRFqNames(): AndroidRFqNames =
48+
get(AndroidRFqNames)
49+
50+
suspend fun ProjectContext.androidRFqNameForSourceSetName(
51+
sourceSetName: SourceSetName
52+
): String? = androidRFqNames().get(sourceSetName)

modulecheck-api/src/main/kotlin/modulecheck/api/context/AndroidResourceDeclarations.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ data class AndroidResourceDeclarations(
4343
val android = project as? AndroidMcProject
4444
?: return@getOrPut emptyLazySet()
4545

46-
val rName = android.androidRFqNameOrNull ?: return@getOrPut emptyLazySet()
46+
val rName = android.androidRFqNameForSourceSetName(sourceSetName)
47+
?: return@getOrPut emptyLazySet()
4748

4849
val resourceParser = AndroidResourceParser()
4950

modulecheck-api/src/main/kotlin/modulecheck/api/context/AndroidResourceReferences.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,14 @@ data class AndroidResourceReferences(
4545

4646
private suspend fun fetchNewReferences(sourceSetName: SourceSetName): LazySet<Reference> {
4747

48-
val androidRFqNameOrNull = (project as? AndroidMcProject)?.androidRFqNameOrNull
48+
val androidRFqNameOrNull = (project as? AndroidMcProject)
49+
?.androidRFqNameForSourceSetName(sourceSetName)
50+
?: return emptyLazySet()
51+
4952
val packagePrefix = (project as? AndroidMcProject)
50-
?.androidPackageOrNull
53+
?.androidBasePackagesForSourceSetName(sourceSetName)
5154
?.let { "$it." }
52-
53-
if (androidRFqNameOrNull == null || packagePrefix == null) {
54-
return emptyLazySet()
55-
}
55+
?: return emptyLazySet()
5656

5757
val jvm = project.jvmFilesForSourceSetName(sourceSetName)
5858
.map { jvmFile ->

modulecheck-api/src/main/kotlin/modulecheck/api/context/Declarations.kt

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ import kotlinx.coroutines.flow.toList
1919
import modulecheck.parsing.gradle.SourceSetName
2020
import modulecheck.parsing.source.DeclarationName
2121
import modulecheck.parsing.source.asDeclarationName
22-
import modulecheck.project.AndroidMcProject
2322
import modulecheck.project.ConfiguredProjectDependency
2423
import modulecheck.project.McProject
2524
import modulecheck.project.ProjectContext
2625
import modulecheck.project.isAndroid
2726
import modulecheck.utils.LazySet
27+
import modulecheck.utils.LazySet.DataSource
2828
import modulecheck.utils.LazySet.DataSource.Priority.HIGH
2929
import modulecheck.utils.SafeCache
3030
import modulecheck.utils.dataSource
@@ -41,32 +41,30 @@ data class Declarations(
4141
suspend fun get(sourceSetName: SourceSetName): LazySet<DeclarationName> {
4242
return delegate.getOrPut(sourceSetName) {
4343

44-
val inheritedSourceSetsNames = sourceSetName.inheritedSourceSetNames(
45-
project,
46-
includeSelf = true
47-
)
44+
val sets = mutableListOf<LazySet<DeclarationName>>()
45+
val sources = mutableListOf<DataSource<DeclarationName>>()
4846

49-
val rNameOrNull = (project as? AndroidMcProject)?.androidRFqNameOrNull
47+
sourceSetName
48+
.withUpstream(project)
49+
.forEach { sourceSetOrUpstream ->
5050

51-
val sets = mutableListOf<LazySet<DeclarationName>>()
51+
val rNameOrNull = project.androidRFqNameForSourceSetName(sourceSetOrUpstream)
5252

53-
val sources = inheritedSourceSetsNames
54-
.flatMap { inherited ->
55-
project.jvmFilesForSourceSetName(inherited)
53+
project.jvmFilesForSourceSetName(sourceSetOrUpstream)
5654
.toList()
5755
.map { dataSource(HIGH) { it.declarations } }
58-
}
59-
.toMutableList()
56+
.let { sources.addAll(it) }
6057

61-
if (rNameOrNull != null) {
62-
sources.add(dataSource { setOf(rNameOrNull.asDeclarationName()) })
63-
}
58+
if (rNameOrNull != null) {
59+
sources.add(dataSource { setOf(rNameOrNull.asDeclarationName()) })
60+
}
6461

65-
if (project.isAndroid()) {
66-
sets.add(project.androidResourceDeclarationsForSourceSetName(sourceSetName))
62+
if (project.isAndroid()) {
63+
sets.add(project.androidResourceDeclarationsForSourceSetName(sourceSetOrUpstream))
6764

68-
sets.add(project.androidDataBindingDeclarationsForSourceSetName(sourceSetName))
69-
}
65+
sets.add(project.androidDataBindingDeclarationsForSourceSetName(sourceSetOrUpstream))
66+
}
67+
}
7068

7169
lazySet(sets, sources)
7270
}

modulecheck-api/src/main/kotlin/modulecheck/api/context/ManifestFiles.kt

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,13 @@ data class ManifestFiles(
3535
if (project !is AndroidMcProject) return null
3636

3737
return delegate.getOrPut(sourceSetName) {
38-
val file = project.manifests[sourceSetName]
39-
?.existsOrNull()
40-
?: return@getOrPut null
41-
42-
XmlFile.ManifestFile(file)
38+
sourceSetName
39+
.withUpstream(project)
40+
.firstNotNullOfOrNull { sourceSetOrUpstream ->
41+
project.manifests[sourceSetOrUpstream]
42+
?.existsOrNull()
43+
}
44+
?.let { file -> XmlFile.ManifestFile(file) }
4345
}
4446
}
4547

@@ -55,8 +57,4 @@ suspend fun ProjectContext.manifestFiles(): ManifestFiles = get(ManifestFiles)
5557

5658
suspend fun ProjectContext.manifestFileForSourceSetName(
5759
sourceSetName: SourceSetName
58-
): XmlFile.ManifestFile? = manifestFiles()
59-
.get(sourceSetName)
60-
?.takeIf { it.file.exists() }
61-
?: manifestFiles().get(SourceSetName.MAIN)
62-
?.takeIf { it.file.exists() }
60+
): XmlFile.ManifestFile? = manifestFiles().get(sourceSetName)

modulecheck-core/src/main/kotlin/modulecheck/core/rule/DisableViewBindingRule.kt

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,20 @@
1515

1616
package modulecheck.core.rule
1717

18+
import modulecheck.api.context.androidBasePackagesForSourceSetName
1819
import modulecheck.api.context.androidResourceReferencesForSourceSetName
1920
import modulecheck.api.context.dependents
2021
import modulecheck.api.context.importsForSourceSetName
21-
import modulecheck.api.context.layoutFiles
22+
import modulecheck.api.context.layoutFilesForSourceSetName
2223
import modulecheck.api.rule.ModuleCheckRule
2324
import modulecheck.api.settings.ChecksSettings
2425
import modulecheck.core.rule.android.DisableViewBindingGenerationFinding
2526
import modulecheck.parsing.gradle.SourceSetName
26-
import modulecheck.parsing.gradle.all
2727
import modulecheck.parsing.source.asExplicitReference
2828
import modulecheck.project.AndroidMcProject
2929
import modulecheck.project.McProject
3030
import modulecheck.utils.capitalize
31+
import modulecheck.utils.existsOrNull
3132

3233
class DisableViewBindingRule : ModuleCheckRule<DisableViewBindingGenerationFinding> {
3334

@@ -43,56 +44,58 @@ class DisableViewBindingRule : ModuleCheckRule<DisableViewBindingGenerationFindi
4344
@Suppress("UnstableApiUsage")
4445
if (!androidProject.viewBindingEnabled) return emptyList()
4546

46-
val layouts = androidProject
47-
.layoutFiles()
48-
.all()
49-
.all()
50-
5147
val dependents = project.dependents()
5248

53-
val basePackage = project.androidPackageOrNull
54-
?: return listOf(
55-
DisableViewBindingGenerationFinding(
56-
dependentProject = project, dependentPath = project.path, buildFile = project.buildFile
57-
)
58-
)
49+
project.sourceSets.keys
50+
.forEach { sourceSetName ->
5951

60-
val usedLayouts = layouts
61-
.filter { it.file.exists() }
62-
.filter { layoutFile ->
52+
val basePackage = project.androidBasePackagesForSourceSetName(sourceSetName)
53+
?: return@forEach
6354

64-
val generated = layoutFile.file
65-
.nameWithoutExtension
66-
.split("_")
67-
.joinToString("") { fragment -> fragment.capitalize() } + "Binding"
55+
val generatedBindings = project.layoutFilesForSourceSetName(sourceSetName)
56+
.mapNotNull { it.file.existsOrNull() }
57+
.map { layoutFile ->
58+
val simpleBindingName = layoutFile.nameWithoutExtension
59+
.split("_")
60+
.joinToString("") { fragment -> fragment.capitalize() } + "Binding"
6861

69-
val reference = "$basePackage.databinding.$generated".asExplicitReference()
62+
// fully qualified
63+
"$basePackage.databinding.$simpleBindingName".asExplicitReference()
64+
}
7065

71-
val usedInProject = project
72-
.importsForSourceSetName(SourceSetName.MAIN)
73-
.contains(reference)
66+
val usedInProject = sourceSetName.withDownStream(project)
67+
.any { sourceSetNameOrDownstream ->
7468

75-
usedInProject || dependents
69+
generatedBindings.any { generated ->
70+
71+
project.importsForSourceSetName(sourceSetNameOrDownstream)
72+
.contains(generated)
73+
}
74+
}
75+
76+
if (usedInProject) return emptyList()
77+
78+
// TODO -- this needs to be changed to respect the source sets of the downstream project
79+
val usedInDependent = dependents
7680
.any { dep ->
7781

78-
dep
79-
.importsForSourceSetName(SourceSetName.MAIN)
80-
.contains(reference) || dep
81-
.androidResourceReferencesForSourceSetName(SourceSetName.MAIN)
82-
.contains(reference)
82+
generatedBindings.any { generated ->
83+
dep
84+
.importsForSourceSetName(SourceSetName.MAIN)
85+
.contains(generated) || dep
86+
.androidResourceReferencesForSourceSetName(SourceSetName.MAIN)
87+
.contains(generated)
88+
}
8389
}
90+
91+
if (usedInDependent) return emptyList()
8492
}
85-
.toList()
86-
87-
return if (usedLayouts.isNotEmpty()) {
88-
emptyList()
89-
} else {
90-
listOf(
91-
DisableViewBindingGenerationFinding(
92-
dependentProject = project, project.path, buildFile = project.buildFile
93-
)
93+
94+
return listOf(
95+
DisableViewBindingGenerationFinding(
96+
dependentProject = project, dependentPath = project.path, buildFile = project.buildFile
9497
)
95-
}
98+
)
9699
}
97100

98101
override fun shouldApply(checksSettings: ChecksSettings): Boolean {

modulecheck-core/src/main/kotlin/modulecheck/core/rule/InheritedDependencyRule.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class InheritedDependencyRule : ModuleCheckRule<InheritedDependencyFinding> {
5858
return configurationName.toSourceSetName()
5959
// Check the receiver's configuration first, but if the dependency isn't used there, also
6060
// check the upstream configurations.
61-
.inheritedSourceSetNames(project, includeSelf = true)
61+
.withUpstream(project)
6262
.any { sourceSet ->
6363
dependencyPathsForSourceSet(sourceSet)
6464
.contains(this.project.path to isTestFixture)

0 commit comments

Comments
 (0)