Skip to content
This repository has been archived by the owner on Jun 4, 2024. It is now read-only.

Commit

Permalink
Make Zinc APIs serialization reproducible by sorting it
Browse files Browse the repository at this point in the history
  • Loading branch information
jroylance committed Aug 6, 2020
1 parent 179001a commit 5794919
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 10 deletions.
2 changes: 1 addition & 1 deletion rules/private/phases/phase_zinc_compile.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def phase_zinc_compile(ctx, g):
g.classpaths.plugin,
g.classpaths.compile,
g.classpaths.compiler,
],
] + [zinc.deps_files for zinc in zincs],
)

outputs = [g.classpaths.jar, mains_file, apis, infos, relations, setup, stamps, used, tmp]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import java.nio.file.attribute.FileTime
import java.util.zip.{GZIPInputStream, GZIPOutputStream}
import java.util.Optional
import sbt.internal.inc.binary.converters.{ProtobufReaders, ProtobufWriters}
import sbt.internal.inc.schema.Type.Structure
import sbt.internal.inc.{APIs, Analysis, Relations, SourceInfos, Stamper, Stamps, schema, Stamp => StampImpl}
import sbt.internal.inc.schema.{ClassDependencies, UsedName, UsedNames, Values}
import sbt.internal.inc.schema.{AnalyzedClass, Annotation, ClassDependencies, ClassLike, Companions, NameHash, TypeParameter, UsedName, UsedNames, Values}
import sbt.io.IO
import scala.math.Ordering
import scala.collection.mutable.StringBuilder
import xsbti.api.AnalyzedClass
import scala.collection.immutable.TreeMap
import xsbti.compile.analysis.{GenericMapper, ReadMapper, ReadWriteMappers, Stamp, WriteMapper}
import xsbti.compile.{AnalysisContents, AnalysisStore, MiniSetup}
Expand Down Expand Up @@ -110,16 +110,63 @@ class AnxAnalyses(format: AnxAnalysisStore.Format) {
private[this] val reader = new ProtobufReaders(mappers.getReadMapper, schema.Version.V1)
private[this] val writer = new ProtobufWriters(mappers.getWriteMapper)

def sortAnnotation(annotation: Annotation): Annotation = {
annotation.copy(
arguments = annotation.arguments.sortWith(_.hashCode() > _.hashCode())
)
}

def sortTypeParameter(typeParameter: TypeParameter): TypeParameter = {
typeParameter.copy(
annotations = typeParameter.annotations.map(annotation => sortAnnotation(annotation)).sortWith(_.hashCode() > _.hashCode()),
typeParameters = typeParameter.typeParameters.map(_typeParameter => sortTypeParameter(_typeParameter)),
)
}

def sortStructure(structure: Structure): Structure = {
structure.copy(
parents = structure.parents.sortWith(_.hashCode() < _.hashCode()),
declared = structure.declared.sortWith(_.hashCode() < _.hashCode()),
inherited = structure.inherited.sortWith(_.hashCode() < _.hashCode()),
)
}

def sortClassLike(classLike: ClassLike): ClassLike = {
classLike.copy(
structure = classLike.structure.flatMap(structure => Option[Structure](sortStructure(structure))),
savedAnnotations = classLike.savedAnnotations.sorted,
childrenOfSealedClass = classLike.childrenOfSealedClass.sortWith(_.hashCode() > _.hashCode()),
typeParameters = classLike.typeParameters.map(typeParameter => sortTypeParameter(typeParameter)).sortWith(_.hashCode() > _.hashCode()),
)
}

def sortCompanions(companions: Companions): Companions = {
Companions(
classApi = companions.classApi.flatMap(classLike => Option[ClassLike](sortClassLike(classLike))),
objectApi = companions.objectApi.flatMap(classLike => Option[ClassLike](sortClassLike(classLike))),
)
}

def sortAnalyzedClassMap(analyzedClassMap: Map[String, AnalyzedClass]): TreeMap[String, AnalyzedClass] = {
TreeMap[String, AnalyzedClass]() ++ analyzedClassMap.mapValues { analyzedClass =>
analyzedClass.copy(
api = analyzedClass.api.flatMap(companions => Option[Companions](sortCompanions(companions))),
nameHashes = analyzedClass.nameHashes.sortWith(_.hashCode() > _.hashCode()),
)
}
}

def sortApis(apis: schema.APIs): schema.APIs = {
apis.copy(
internal = sortAnalyzedClassMap(apis.internal),
external = sortAnalyzedClassMap(apis.external),
)
}

def apis = new Store[APIs](
stream => reader.fromApis(shouldStoreApis = true)(format.read(schema.APIs, stream)),
(stream, value) => format.write(
writer.toApis(
APIs(
TreeMap[String, AnalyzedClass]() ++ value.internal,
TreeMap[String, AnalyzedClass]() ++ value.external
),
shouldStoreApis = true
).update(apiFileWrite),
sortApis(writer.toApis(value, shouldStoreApis = true).update(apiFileWrite)),
stream
)
)
Expand Down

0 comments on commit 5794919

Please sign in to comment.