From ef6968e20ce374e7cc94dd61bf8510e80af824a9 Mon Sep 17 00:00:00 2001 From: bitloi Date: Sun, 15 Feb 2026 05:21:54 +0100 Subject: [PATCH] Path normalization for Windows + scalafmt - CacheUrlConversion: normalize paths for comparison (forward slashes) and strip leading slash on Windows file URIs so cache matching works. - Apply scalafmt to touched files. --- .../CoursierDependencyResolution.scala | 3 ++- .../internal/CacheUrlConversion.scala | 21 +++++++++++++++++-- .../DependencyLockManager.scala | 6 +++++- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/lm-coursier/src/main/scala/lmcoursier/CoursierDependencyResolution.scala b/lm-coursier/src/main/scala/lmcoursier/CoursierDependencyResolution.scala index ea5901324..13d32316e 100644 --- a/lm-coursier/src/main/scala/lmcoursier/CoursierDependencyResolution.scala +++ b/lm-coursier/src/main/scala/lmcoursier/CoursierDependencyResolution.scala @@ -382,7 +382,8 @@ class CoursierDependencyResolution( .groupBy(_._1) .view .mapValues(_.map { case (_, pub, art, _) => - val originalUrl = lmcoursier.internal.CacheUrlConversion.cacheFileToOriginalUrl(art.url, cache) + val originalUrl = + lmcoursier.internal.CacheUrlConversion.cacheFileToOriginalUrl(art.url, cache) (originalUrl, pub.classifier.value, pub.ext.value) }) .toMap diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/CacheUrlConversion.scala b/lm-coursier/src/main/scala/lmcoursier/internal/CacheUrlConversion.scala index f53ed5811..62345709b 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/CacheUrlConversion.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/CacheUrlConversion.scala @@ -15,13 +15,30 @@ object CacheUrlConversion { final val FileUrlPrefix = "file:" final val UnconvertiblePrefix = "${CSR_CACHE}" + private def normalizePathForComparison(path: String): String = + path.replace('\\', '/') + + private def normalizedFilePath(fileUrl: String): String = { + val afterPrefix = fileUrl.stripPrefix(FileUrlPrefix).replaceFirst("^/+", "/") + val withForwardSlash = normalizePathForComparison(afterPrefix) + if ( + withForwardSlash.length >= 3 && withForwardSlash + .charAt(0) == '/' && withForwardSlash.charAt(2) == ':' + ) + withForwardSlash.substring(1) + else + withForwardSlash + } + def cacheFileToOriginalUrl(fileUrl: String, cacheDir: File): String = { if (!fileUrl.startsWith(FileUrlPrefix)) return fileUrl - val filePath = fileUrl.stripPrefix(FileUrlPrefix).replaceFirst("^/+", "/") + val filePath = normalizedFilePath(fileUrl) val cachePaths = Seq( cacheDir.getAbsolutePath, cacheDir.getCanonicalPath - ).distinct.map(p => if (p.endsWith("/")) p else p + "/") + ).distinct.map(p => + normalizePathForComparison(if (p.endsWith("/") || p.endsWith("\\")) p else p + "/") + ) def extractHttpUrl(relativePath: String): Option[String] = { val protocolSepIndex = relativePath.indexOf('/') diff --git a/main/src/main/scala/sbt/internal/librarymanagement/DependencyLockManager.scala b/main/src/main/scala/sbt/internal/librarymanagement/DependencyLockManager.scala index 75a23e459..a6c574e58 100644 --- a/main/src/main/scala/sbt/internal/librarymanagement/DependencyLockManager.scala +++ b/main/src/main/scala/sbt/internal/librarymanagement/DependencyLockManager.scala @@ -44,7 +44,11 @@ object DependencyLockManager: isValid } - private def artifactUrlForLock(rawUrl: String, cacheDir: Option[File], moduleDesc: String): String = + private def artifactUrlForLock( + rawUrl: String, + cacheDir: Option[File], + moduleDesc: String + ): String = if !rawUrl.startsWith(CacheUrlConversion.FileUrlPrefix) then rawUrl else cacheDir match