【问题标题】:Find repository where sbt finds a plugin or JAR查找 sbt 找到插件或 JAR 的存储库
【发布时间】:2019-05-24 23:10:05
【问题描述】:

尝试对sbt 构建进行故障排除,想知道sbt.repositories 中的哪个模式与给定的三元组匹配。

我有一个插件和两个 sbt 构建。一个构建找到插件,而另一个没有。两种构建都使用sbt.repositories 文件来覆盖外部存储库和内部站点(在公司防火墙后面)。

sbt.repositories 文件在两个版本之间是不同的,所以我怀疑其中一个缺少与插件匹配的模式。 sbt.repositories 文件足够大,我不希望手动或反复试验。

我想使用sbt 自己的工具来打印消息,例如“使用模式(sbt.repositories 中的一些模式)我在(某个 URL)找到了插件(org、name、ver)”。

sbt.repositories 的模式具有以下格式。 sbt 必须循环遍历这些模式,计算所有组合并测试结果是否为有效 URL。我想自己访问那个类,所以我可以找出sbt.repositories 中的哪个模式允许一个构建找到插件(或JAR)。然后我会将该模式添加到另一个构建的sbt.repositories

有谁知道此功能是否可通过构建 DSL(即来自 build.sbt)或为 sbt 执行此操作的解析器类的名称提供?

sbt.repositories
repo-name = http://some.internal.website.com:1234/artifacts/releases,[organisation]/[module](/scala_[scalaVersion])(/sbt_[sbtVersion])/[revision]/[artifact](-[classifier])(-[revision]).[ext]

我试过sbt -Dsbt.log.noformat=true --debug clean update compile > someLogFile.txt,它告诉我找到了哪些插件,包括我感兴趣的插件,但不是在哪里找到它们。

【问题讨论】:

  • 我不知道如何通过 sbt 找到它,但是这个文件可能会提示您添加哪种模式:<userprofile>\.ivy2\cache\<pathToDependency>\ivydata-<version>.properties
  • 我可以尝试通过将日志语句添加到 scala-sbt.org/0.13/api/index.html#sbt.Resolver$$url$ 来编写自己的 Resolver

标签: scala plugins dependencies sbt dependency-management


【解决方案1】:

写了一些又快又脏的东西,但它对我有用。我很高兴看到使用 Ivy 解析器的解决方案,它必须提供相同的功能,而且更可靠。

根据我的经验,因为人们很难理解像 [organisation]/[module](/scala_[scalaVersion])(/sbt_[sbtVersion])/[revision]/[artifact](-[classifier])(-[revision]).[ext] 这样的模式是否匹配他们需要的模式,或者是否复制了 sbt.repositories 中已经存在的另一种模式,人们只需将模式从一个 repo 文件复制粘贴到另一个文件中直到构建工作。随着时间的推移,这会造成混乱,我们需要工具来处理诸如“哪个模式与给定的依赖项匹配”或“哪两个模式相同”或“哪个模式与任何依赖项不匹配”等问题。

import java.io.File
import java.net.URL
import java.util.regex.Pattern

import scala.collection.JavaConversions._
import scala.collection.mutable
import scala.io.{Codec, Source}
import scala.util.control.NonFatal

object FindDependency {

  def main(args: Array[String]): Unit = {
    val root = new File(".")
    val sbtRepos = new File(root, "./build/sbt.repositories")

    implicit val dep = Dep(organisation = "com.foo.bar.sbt", module = "baz", artifact = "baz", revision = "1.2.3-SNAPSHOT", ext = "jar"
      , scalaVersion = "2.10", sbtVersion = "0.13", artType = "jar", classifier = "")

    println(sbtRepos.getCanonicalPath + ": exists?" + sbtRepos.exists)
    processSbtRepos(sbtRepos)
  }

  def processSbtRepos(repoFilePath: File)(implicit dep: Dep): Unit = {
    val source = Source.fromFile(repoFilePath)(Codec.ISO8859)
    source.getLines.foreach { line =>
      if (!ignore(line)) {
        println(s"\n$line")
        val namePattern = line.split(":") map (_.trim)
        if (namePattern.length >= 3) artifactUrl(namePattern)
      }
    }
    source.close
  }

  def artifactUrl(words: Array[String])(implicit dep: Dep): Unit = {
    val artBase = repoUrl(words)
    val artifactPattern = if (artBase.indexOf('[') > 0) artBase else artBase + ",[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"
    val artUrls = dep.potentialUrls(artifactPattern)
    artUrls.foreach { url =>
      if (isValidUrl(url)) {
        println(s"$artifactPattern -> $url")
      }
    }
  }

  def isValidUrl(url: String): Boolean = {
    try {
      val u = new URL(url)
      val contentLength = u.openConnection.getContentLength
      val openStream = u.openStream
      val binaryData = new Array[Byte](contentLength)
      openStream.read(binaryData)
      openStream.close
      true
    } catch {
      case NonFatal(_) => false
    }
  }

  def repoUrl(words: Array[String]): String = words.drop(1).mkString(":")

  def ignore(line: String): Boolean = {
    line.indexOf("http://") < 0
  }

}

/**
  * Represents a dependency
  *
  * @param artType artifact type, e.g. "sources", "javadoc", ""
  */
case class Dep(organisation: String, module: String, artifact: String, revision: String
               , ext: String, scalaVersion: String, sbtVersion: String, artType: String
               , classifier: String) {
  /**
    * Given a pattern and the known parameters for a dependency, replace the details of the dependency in the pattern.
    *
    * @return a concrete URL (with optional portions) where the dependency may be located
    */
  def replace(pattern: String): String = {
    pattern.replaceAll(Pattern.quote("[artifact]"), artifact)
      .replaceAll(Pattern.quote("[ext]"), ext)
      .replaceAll(Pattern.quote("[module]"), module)
      .replaceAll(Pattern.quote("[organisation]"), organisation)
      .replaceAll(Pattern.quote("[organization]"), organisation)
      .replaceAll(Pattern.quote("[revision]"), revision)
      .replaceAll(Pattern.quote("[scalaVersion]"), scalaVersion)
      .replaceAll(Pattern.quote("[sbtVersion]"), sbtVersion)
      .replaceAll(Pattern.quote("[type]"), artType)
      .replaceAll(Pattern.quote("[classifier]"), classifier)
  }

  def potentialUrls(pattern: String): Seq[String] = {
    val urlWithOpts = replace(pattern)
      .replaceAll(" ", "")
      .replaceAll(",", "/")
      .replaceAll("\\(", "(?") // mark the optional elements with the prefix "?"
    val words = urlWithOpts.split("\\(|\\)").map(_.trim).filterNot(_.isEmpty).filterNot(_ == "?-")
    val urls = mutable.ArrayBuffer.empty[String]
    val urlsCrt = mutable.ArrayBuffer.empty[String] // a temp container for urls
    urls.add(words(0)) // the first "word" is the repo URL, which is not optional
    words.drop(1).foreach { word =>
      // transfer the current list of URLs to the temp container
      urlsCrt.clear
      urlsCrt ++= urls
      urls.clear
      if (word.startsWith("?")) {
        // append all URLs that were there before, unchanged (because this word is optional)
        urls ++= urlsCrt
        // as well as all URLs that were there before, each concatenated with this word
        urls ++= urlsCrt.map(_ + word.substring(1))
      } else {
        // to each URL that was there before, concatenate the new word
        urls ++= urlsCrt.map(_ + word)
      }
    }
    urls
  }
}

【讨论】:

    猜你喜欢
    • 2016-04-17
    • 1970-01-01
    • 2016-06-23
    • 2018-05-10
    • 2014-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多