Skip to content

Commit eebc4d1

Browse files
fix: improve sorting of various build outputs to ensure determinism.
1 parent 4d32bef commit eebc4d1

File tree

2 files changed

+67
-62
lines changed

2 files changed

+67
-62
lines changed

src/main/kotlin/com/autonomousapps/model/internal/Source.kt

Lines changed: 64 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -4,68 +4,16 @@ package com.autonomousapps.model.internal
44

55
import com.autonomousapps.internal.parse.AndroidResParser
66
import com.autonomousapps.internal.utils.LexicographicIterableComparator
7+
import com.autonomousapps.internal.utils.efficient
8+
import com.autonomousapps.model.internal.AndroidResSource.AttrRef.Companion.type
79
import com.squareup.moshi.JsonClass
810
import dev.zacsweers.moshix.sealed.annotations.TypeLabel
911

1012
@JsonClass(generateAdapter = false, generator = "sealed:type")
1113
internal sealed class Source(
1214
/** Source file path relative to project dir (e.g. `src/main/com/foo/Bar.kt`). */
1315
open val relativePath: String,
14-
) : Comparable<Source> {
15-
16-
override fun compareTo(other: Source): Int = when (this) {
17-
is AndroidAssetSource -> {
18-
when (other) {
19-
is AndroidAssetSource -> compareAndroidAssetSource(other)
20-
is AndroidResSource -> 1
21-
is CodeSource -> 1
22-
}
23-
}
24-
25-
is AndroidResSource -> {
26-
when (other) {
27-
is AndroidAssetSource -> -1
28-
is AndroidResSource -> compareAndroidResSource(other)
29-
is CodeSource -> 1
30-
}
31-
}
32-
33-
is CodeSource -> {
34-
when (other) {
35-
is AndroidAssetSource -> -1
36-
is AndroidResSource -> -1
37-
is CodeSource -> compareCodeSource(other)
38-
}
39-
}
40-
}
41-
42-
private fun compareAndroidAssetSource(other: AndroidAssetSource): Int {
43-
return compareBy(AndroidAssetSource::relativePath)
44-
.compare(this as AndroidAssetSource, other)
45-
}
46-
47-
private fun compareAndroidResSource(other: AndroidResSource): Int {
48-
return compareBy(AndroidResSource::relativePath)
49-
.thenBy(LexicographicIterableComparator()) { it.styleParentRefs }
50-
.thenBy(LexicographicIterableComparator()) { it.attrRefs }
51-
.thenBy(LexicographicIterableComparator()) { it.usedClasses }
52-
.compare(this as AndroidResSource, other)
53-
}
54-
55-
private fun compareCodeSource(other: CodeSource): Int {
56-
return compareBy(CodeSource::relativePath)
57-
.thenComparing(CodeSource::kind)
58-
.thenComparing(CodeSource::className)
59-
.thenComparing(compareBy<CodeSource, String?>(nullsFirst()) { it.superClass })
60-
.thenBy(LexicographicIterableComparator()) { it.interfaces }
61-
.thenBy(LexicographicIterableComparator()) { it.usedNonAnnotationClasses }
62-
.thenBy(LexicographicIterableComparator()) { it.usedAnnotationClasses }
63-
.thenBy(LexicographicIterableComparator()) { it.usedInvisibleAnnotationClasses }
64-
.thenBy(LexicographicIterableComparator()) { it.exposedClasses }
65-
.thenBy(LexicographicIterableComparator()) { it.imports }
66-
.compare(this as CodeSource, other)
67-
}
68-
}
16+
) : Comparable<Source>
6917

7018
/** A single `.class` file in this project. */
7119
@TypeLabel("code")
@@ -103,6 +51,24 @@ internal data class CodeSource(
10351
// val binaryClassAccesses: Map<String, Set<MemberAccess>>,
10452
) : Source(relativePath) {
10553

54+
override fun compareTo(other: Source): Int {
55+
return if (other is CodeSource) {
56+
compareBy(CodeSource::relativePath)
57+
.thenComparing(CodeSource::kind)
58+
.thenComparing(CodeSource::className)
59+
.thenComparing(compareBy<CodeSource, String?>(nullsFirst()) { it.superClass })
60+
.thenBy(LexicographicIterableComparator()) { it.interfaces }
61+
.thenBy(LexicographicIterableComparator()) { it.usedNonAnnotationClasses }
62+
.thenBy(LexicographicIterableComparator()) { it.usedAnnotationClasses }
63+
.thenBy(LexicographicIterableComparator()) { it.usedInvisibleAnnotationClasses }
64+
.thenBy(LexicographicIterableComparator()) { it.exposedClasses }
65+
.thenBy(LexicographicIterableComparator()) { it.imports }
66+
.compare(this, other)
67+
} else {
68+
1
69+
}
70+
}
71+
10672
enum class Kind {
10773
JAVA,
10874
KOTLIN,
@@ -125,6 +91,35 @@ internal data class AndroidResSource(
12591
val usedClasses: Set<String>,
12692
) : Source(relativePath) {
12793

94+
companion object {
95+
fun newInstance(
96+
relativePath: String,
97+
styleParentRefs: Set<StyleParentRef>,
98+
attrRefs: Set<AttrRef>,
99+
usedClasses: Set<String>,
100+
): AndroidResSource {
101+
return AndroidResSource(
102+
relativePath,
103+
styleParentRefs.toSortedSet().efficient(),
104+
attrRefs.toSortedSet().efficient(),
105+
usedClasses.toSortedSet().efficient(),
106+
)
107+
}
108+
}
109+
110+
override fun compareTo(other: Source): Int {
111+
return when (other) {
112+
is AndroidResSource -> compareBy(AndroidResSource::relativePath)
113+
.thenBy(LexicographicIterableComparator()) { it.styleParentRefs }
114+
.thenBy(LexicographicIterableComparator()) { it.attrRefs }
115+
.thenBy(LexicographicIterableComparator()) { it.usedClasses }
116+
.compare(this, other)
117+
118+
is AndroidAssetSource -> 1
119+
is CodeSource -> -1
120+
}
121+
}
122+
128123
@JsonClass(generateAdapter = false)
129124
/** The parent of a style resource, e.g. "Theme.AppCompat.Light.DarkActionBar". */
130125
data class StyleParentRef(val styleParent: String) : Comparable<StyleParentRef> {
@@ -151,10 +146,11 @@ internal data class AndroidResSource(
151146
assertNoDots("id", id)
152147
}
153148

154-
override fun compareTo(other: AttrRef): Int = compareBy<AttrRef>(
155-
{ it.type },
156-
{ it.id }
157-
).compare(this, other)
149+
override fun compareTo(other: AttrRef): Int {
150+
return compareBy(AttrRef::type)
151+
.thenComparing(AttrRef::id)
152+
.compare(this, other)
153+
}
158154

159155
companion object {
160156

@@ -288,7 +284,15 @@ internal data class AndroidResSource(
288284
@JsonClass(generateAdapter = false)
289285
internal data class AndroidAssetSource(
290286
override val relativePath: String,
291-
) : Source(relativePath)
287+
) : Source(relativePath) {
288+
override fun compareTo(other: Source): Int {
289+
return if (other is AndroidAssetSource) {
290+
compareBy(AndroidAssetSource::relativePath).compare(this, other)
291+
} else {
292+
-1
293+
}
294+
}
295+
}
292296

293297
private fun String.toCanonicalResString(): String = replace('.', '_')
294298

src/main/kotlin/com/autonomousapps/tasks/XmlSourceExploderTask.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import com.autonomousapps.internal.parse.AndroidResBuilder
88
import com.autonomousapps.internal.parse.AndroidResParser
99
import com.autonomousapps.internal.utils.bufferWriteJsonSet
1010
import com.autonomousapps.internal.utils.getAndDelete
11+
import com.autonomousapps.model.internal.AndroidResSource
1112
import org.gradle.api.DefaultTask
1213
import org.gradle.api.file.ConfigurableFileCollection
1314
import org.gradle.api.file.DirectoryProperty
@@ -93,6 +94,7 @@ abstract class XmlSourceExploderTask @Inject constructor(
9394
val output = parameters.output.getAndDelete()
9495

9596
val projectDir = parameters.projectDir.get().asFile
97+
9698
val explodedLayouts = AndroidLayoutParser(
9799
layouts = parameters.layouts.files,
98100
projectDir = projectDir
@@ -101,7 +103,6 @@ abstract class XmlSourceExploderTask @Inject constructor(
101103
resources = parameters.androidRes,
102104
projectDir = projectDir
103105
).androidResSource
104-
105106
val explodedManifests = AndroidManifestParser(
106107
manifests = parameters.manifests,
107108
projectDir = projectDir,
@@ -140,7 +141,7 @@ abstract class XmlSourceExploderTask @Inject constructor(
140141
)
141142
}
142143

143-
val androidResSource = builders.values.asSequence()
144+
val androidResSource: Set<AndroidResSource> = builders.values.asSequence()
144145
.map { it.build() }
145146
.toSortedSet()
146147

0 commit comments

Comments
 (0)