in multiversion/src/main/scala/multiversion/commands/ExportCommand.scala [291:404]
def generateBzlFile(
index: ResolutionIndex,
cache: FileCache[Task]
): Result[Path] = {
val resolvedArtifacts = index.unevictedArtifacts
val outputIndex: mutable.Map[String, ArtifactOutput] =
collection.concurrent.TrieMap.empty[String, ArtifactOutput]
val progressBar =
new DownloadProgressRenderer(resolvedArtifacts.length, app.env.clock)
val files: List[Task[List[Either[Throwable, ArtifactOutput]]]] =
resolvedArtifacts.map { r =>
val logger = progressBar.loggers.newCacheLogger(r.dependency)
type Fetch[T] = Task[Either[ArtifactError, T]]
def tryFetch(artifact: Artifact, policy: CachePolicy): Fetch[File] =
cache
.withCachePolicies(List(policy))
.withLogger(logger)
.file(artifact)
.run
def foldShas(attempts: List[Fetch[File]]): Fetch[File] =
if (attempts.isEmpty) Task.point(Left(new ArtifactError.NotFound("")))
else
attempts.tail.foldLeft(attempts.head) { case (task, nextAttempt) =>
task.flatMap {
case Left(_) =>
// Fetch failed, try next (Url, CachePolicy) combination
nextAttempt
case success => Task.point(success)
}
}
val shas = foldShas(for {
// Attempt 1: Fetch "*.jar.sha256" URL locally
// Attempt 2: Fetch "*.jar" URL locally
// Attempt 3: Fetch "*.jar.sha256" URL remotely
// Attempt 4: Fetch "*.jar" URL remotely
url <- List(
r.artifact.checksumUrls.get("SHA-256"),
Some(r.artifact.url)
).flatten
policy <- List(CachePolicy.LocalOnly, CachePolicy.Update)
} yield tryFetch(r.artifact.withUrl(url), policy))
val sourcesShas = foldShas(for {
url <- List(
r.sourcesArtifact.flatMap(_.checksumUrls.get("SHA-256")),
r.sourcesArtifact.map(_.url),
).flatten
policy <- List(CachePolicy.LocalOnly, CachePolicy.Update)
} yield tryFetch(r.artifact.withUrl(url), policy))
shas.flatMap {
case Right(file) =>
(sourcesShas
.map {
case Right(sourceFile) => Some(sourceFile)
case Left(_) => None
})
.map { sourceSha =>
List(Try {
val output = ArtifactOutput(
dependency = r.dependency,
artifact = r.artifact,
artifactSha256 = Sha256.compute(file),
sourcesArtifact = r.sourcesArtifact,
sourcesArtifactSha256 = sourceSha.map(Sha256.compute)
)
outputIndex.put(r.dependency.bazelLabel, output)
output
}.toEither)
}
case Left(value) =>
// Ignore download failures. It's common that some dependencies have
// pom files but no jar files. For example,
// https://repo1.maven.org/maven2/io/monix/monix_2.12/2.3.2/ There
// exists `Artifact.optional` and `Dependency.optional`, which seem
// helpful to distinguish these kinds of dependencies but they are
// true by default so I'm not sure if they're intended to be used
// for that purpose.
Task.point(Nil)
}
}
val all = runParallelTasks(files, progressBar, cache.ec).flatten
val errors = all.collect { case Left(error) => Diagnostic.exception(error) }
Diagnostic.fromDiagnostics(errors.toList) match {
case Some(error) =>
ErrorResult(error)
case None =>
val artifacts: Seq[ArtifactOutput] = all
.collect({ case Right(a) => a })
.toList
.distinctBy(_.label)
if (artifacts.isEmpty) {
ErrorResult(
Diagnostic.error(
"no resolved artifacts." +
"To fix this problem, make sure your configuration declares a non-empty list of 'dependencies'."
)
)
} else {
val rendered =
DepsOutput(
artifacts.sortBy(_.dependency.repr),
index,
outputIndex
).render
val out =
if (outputPath.isAbsolute()) outputPath
else app.env.workingDirectory.resolve(outputPath)
if (!Files.exists(out.getParent())) {
Files.createDirectories(out.getParent())
}
Files.write(out, rendered.getBytes(StandardCharsets.UTF_8))
ValueResult(out)
}
}
}