工件

描述

工件是用于发布子项目特定版本的单个文件。该概念源自 Apache MavenIvy

在 JVM 生态中,常见工件为 Java 归档(JAR 文件)。压缩包格式通常更受青睐,因其更易管理、下载和存储。

举例来说,以下是库的工件列表,在 ivy.xml 文件中枚举:

  <publications>
    <artifact name="core_3" type="jar" ext="jar" conf="compile"/>
    <artifact e:classifier="sources" name="core_3" type="src" ext="jar" conf="sources"/>
    <artifact e:classifier="javadoc" name="core_3" type="doc" ext="jar" conf="docs"/>
    <artifact name="core_3" type="pom" ext="pom" conf="pom"/>
  </publications>

这表明工件具有名称、类型和扩展名,以及可选的 classifier。

  • name。与子项目的模块名相同。
  • type。工件的功能类别,如 jarsrcdoc
  • extension。文件扩展名,如 jarwarzipxml 等。
  • classifier。在 Maven 中,classifier 是可为替代或次要工件追加的任意字符串。

选择默认工件

默认发布的工件为:

  1. 主二进制 JAR
  2. 包含主源码和资源的 JAR
  3. 包含 API 文档的 JAR

您可添加测试类、源码或 API 的工件,也可禁用部分主工件。

要添加所有 Test 工件:

lazy val app = (project in file("app"))
  .settings(
    Test / publishArtifact := true,
  )

要单独添加:

lazy val app = (project in file("app"))
  .settings(
    // enable publishing the jar produced by `Test/package`
    Test / packageBin / publishArtifact := true,

    // enable publishing the test API jar
    Test / packageDoc / publishArtifact := true,

    // enable publishing the test sources jar
    Test / packageSrc / publishArtifact := true,
  )

要单独禁用主工件:

lazy val app = (project in file("app"))
  .settings(
    // disable publishing the main jar produced by `package`
    Compile / packageBin / publishArtifact := false,

    // disable publishing the main API jar
    Compile / packageDoc / publishArtifact := false,

    // disable publishing the main sources jar
    Compile / packageSrc / publishArtifact := false,
  )

修改默认工件

publishArtifact 外,每个内置工件还有若干可配置设置。基本包括 artifact(类型为 SettingKey[Artifact])、mappings(类型为 TaskKey[(File, String)])和 artifactPath(类型为 SettingKey[File])。它们按上一节所述以 (Config / <task>) 限定作用域。

例如,要修改主工件的类型:

Compile / packageBin / artifact := {
  val prev: Artifact = (Compile / packageBin / artifact).value
  prev.withType("bundle")
}

生成的工件名由 artifactName 设置决定。该设置类型为 (ScalaVersion, ModuleID, Artifact) => String。ScalaVersion 参数提供完整 Scala 版本字符串及版本字符串的二进制兼容部分。返回的 String 为要生成的文件名。默认实现为 Artifact.artifactName _。可修改该函数以生成不同的本地工件名,而不影响发布名(发布名由 artifact 定义与仓库模式共同决定)。

例如,要生成不含 classifier 或交叉路径的最小名称:

artifactName := { (sv: ScalaVersion, module: ModuleID, artifact: Artifact) =>
  artifact.name + "-" + module.revision + "." + artifact.extension
}

(注意:实践中很少会去掉 classifier。)

最后,您可通过映射 packagedArtifact 任务获取工件的 (Artifact, File) 对。若不需要 Artifact,可从打包任务(packagepackageDocpackageSrc)直接获取 File。两种情况下,映射任务以获取文件均可确保先生成工件,从而保证文件为最新。

例如:

val myTask = taskKey[Unit]("My task.")

myTask :=  {
  val (art, file) = (Compile / packageBin / packagedArtifact).value
  println("Artifact definition: " + art)
  println("Packaged file: " + file.getAbsolutePath)
}

定义自定义工件

除配置内置工件外,您还可声明要发布的其他工件。使用 Ivy 元数据时允许多个工件,但 Maven POM 文件仅支持基于 classifier 区分工件,且这些信息不会记录在 POM 中。

基本 Artifact 构造如下:

Artifact("name", "type", "extension")
Artifact("name", "classifier")
Artifact("name", url: URL)
Artifact("name", Map("extra1" -> "value1", "extra2" -> "value2"))

例如:

Artifact("myproject", "zip", "zip")
Artifact("myproject", "image", "jpg")
Artifact("myproject", "jdk15")

有关工件的更多详情,请参阅 Ivy 文档。有关组合上述参数并指定 [Configurations] 和额外属性,请参阅 Artifact API

要将这些工件声明为发布,请将它们映射到生成工件的任务:

val myImageTask = taskKey[File](...)

myImageTask := {
  val artifact: File = makeArtifact(...)
  artifact
}

addArtifact(Artifact("myproject", "image", "jpg"), myImageTask)

addArtifact 返回一系列设置(包装在 SettingsDefinition 中)。在完整构建配置中的用法如下:

lazy val app = (project in file("app"))
  .settings(
    addArtifact(...)
  )

发布 .war 文件

Web 应用的常见做法是发布 .war 文件而非 .jar 文件。

lazy val app = (project in file("app"))
  .settings(
    // disable .jar publishing
    Compile / packageBin / publishArtifact := false,

    // create an Artifact for publishing the .war file
    Compile / packageWar / artifact := {
      val prev: Artifact = (Compile / packageWar / artifact).value
      prev.withType("war").withExtension("war")
    },

    // add the .war file to what gets published
    addArtifact(Compile / packageWar / artifact, packageWar),
  )

使用带工件的依赖

要从具有自定义或多个工件的依赖中指定要使用的工件,请在依赖上使用 artifacts 方法。例如:

libraryDependencies += ("org" % "name" % "rev").artifacts(Artifact("name", "type", "ext"))

fromclassifer 方法(在 sbt update 页中描述)实际上是转换为 artifacts 的便捷方法:

def from(url: String) = artifacts(Artifact(name, new URL(url)))
def classifier(c: String) = artifacts(Artifact(name, c))

即,以下两种依赖声明等价:

libraryDependencies += ("org.testng" % "testng" % "5.7").classifier("jdk15")

libraryDependencies += ("org.testng" % "testng" % "5.7").artifacts(Artifact("testng", "jdk15"))