You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SBT packageBin平台依赖排除问题:多平台JAR致包体积过大

问题描述

我们在基于Java 11的Play Framework项目中使用SBT 1.5.5构建,直接依赖org.deeplearning4j:deeplearning4j-nlp:1.0.0-M2。该依赖的传递依赖(例如org.bytedeco:ffmpeg:5.0-1.5.7)属于平台相关库,通过SBT的classifier区分不同平台(如linux-x86、linux-x86_64、windows-x86_64等)。

执行sbt universal:packageBin等打包命令时,所有平台版本的这类大体积JAR都会被打包进最终的“胖JAR”,导致产物体积是实际所需的8倍。其他原生打包器变体(如windows:packageBin或debian:packageBin)也存在同样问题,会包含所有平台版本的JAR。

我们尝试逐个排除不需要的依赖(如下代码),但最终产物中仍会保留对应JAR,也不知道如何全局配置排除所有无关平台的classifier依赖:

excludeDependencies  += "org.bytedeco" % "ffmpeg" % "5.0-1.5.7" classifier "macosx-x86_64"

精简后的build.sbt代码如下:

import com.typesafe.sbt.packager.SettingsHelper._

name := """my-name"""

lazy val isSubmodule = !file("./root.txt").exists

// common properties ///////////////////////////////////////////////////////////////

lazy val commonPropsPath = if (isSubmodule) { "../../common_conf/config.properties" }
  else {"./common_conf/config.properties"}

lazy val commonProps = settingKey[java.util.Properties]("The application properties")

commonProps := {
  val prop = new java.util.Properties()
  IO.load(prop, new File(commonPropsPath))
  prop
}

organization := commonProps.value.getProperty("organization")
version := commonProps.value.getProperty("version")
scalaVersion := commonProps.value.getProperty("scalaVersion")

// ######### javacOptions #########
javacOptions ++= Seq("-source", "1.10", "-target", "1.10")
javacOptions ++= Seq("-Xlint:unchecked", "-Xlint:cast")
javacOptions ++= Seq("-Werror")
javacOptions ++= Seq("-Xlint:-options")

// ######### scalacOptions #########
scalacOptions ++=  Seq("-target:jvm-1.8")
scalacOptions in(Compile, doc) += "-no-java-comments"

// common properties ///////////////////////////////////////////////////////////////


lazy val matching = (project in file("."))
  .enablePlugins(PlayJava, PlayEbean, GitVersioning, BuildInfoPlugin, JavaAppPackaging, DebianPlugin, SbtWeb)
  .settings(
    ...... )


Compile / playEbeanModels := Seq("my.code.package.**")
playEbeanDebugLevel := 4

libraryDependencies ++= Seq(
  javaJdbc,
  filters,
  javaWs,
  guice,

  "mysql" % "mysql-connector-java" % "8.0.29",
  "org.liquibase" % "liquibase-core" % "4.11.0",

  "org.deeplearning4j" % "deeplearning4j-nlp" % "1.0.0-M2" ,  
  "org.nd4j" % "nd4j-native-platform" % "1.0.0-M2",
  "org.nd4j" % "nd4j-native" % "1.0.0-M2",
  "org.apache.lucene" % "lucene-snowball" % "3.0.3",
  "org.jsoup" % "jsoup" % "1.15.+", 

  "org.projectlombok" % "lombok" % "1.18.24", 
  "io.ebean" % "ebean-externalmapping-xml" % "12.6.1", 

  "commons-io" % "commons-io" % "2.11.+",
  "commons-codec" % "commons-codec" % "1.15",
  "org.apache.commons" % "commons-lang3" % "3.12.+",


  "javax.xml.bind" % "jaxb-api" % "2.3.1",
  "com.sun.xml.bind" % "jaxb-core" % "2.3.0.1",
  "com.sun.xml.bind" % "jaxb-impl" % "2.3.1"

)

// test here to exclude each unwanted dependency individually
excludeDependencies  += "org.bytedeco" % "ffmpeg" % "5.0-1.5.7" classifier "macosx-x86_64"
excludeDependencies  += "org.bytedeco" % "ffmpeg" % "5.0-1.5.7" classifier "windows-x86_64"
excludeDependencies  += "org.bytedeco" % "ffmpeg" % "5.0-1.5.7" classifier "windows-x86"


resolvers ++= Seq(
  Resolver.mavenLocal,
  Resolver.sonatypeRepo("snapshots")
)

Test / fork := false

updateOptions := updateOptions.value.withCachedResolution(true)

if (isSubmodule) {
  jcheckStyleConfig := "../../conf/checkstyle-config.xml"
}
else {
  jcheckStyleConfig := "./conf/checkstyle-config.xml"
}

Test / jacocoExcludes := Seq(
  "**.*"
)

// Debian package description
Linux / maintainer := "My values here"
Linux / packageSummary := "My values here"
packageDescription := "My values here"

publishTo := {
 ........
}

credentials += Credentials(....... )

publish in Debian := (publish in Debian).dependsOn(publish).value

Compile / packageBin / publishArtifact := true
Compile / packageDoc / publishArtifact := false
Compile / packageSrc / publishArtifact := false
Test / publishArtifact := false

makeDeploymentSettings(Debian, packageBin in Debian, "deb")
解决方案

1. 利用ByTeco官方平台配置(推荐)

ByTeco系依赖(包括DL4J用到的ffmpeg、nd4j等)支持通过配置指定目标平台,自动只拉取对应平台的依赖。在build.sbt中添加以下内容:

// 指定目标平台,根据部署环境修改,比如linux-x86_64、windows-x86_64
lazy val targetPlatform = "linux-x86_64"

// 替换nd4j-native-platform为指定平台的nd4j-native,避免拉取全平台包
libraryDependencies ++= Seq(
  "org.nd4j" % "nd4j-native" % "1.0.0-M2" classifier targetPlatform,
  "org.bytedeco" % "ffmpeg" % "5.0-1.5.7" classifier targetPlatform
)

// 排除默认的platform包,防止拉取所有平台依赖
excludeDependencies ++= Seq(
  "org.nd4j" % "nd4j-native-platform",
  "org.bytedeco" % "ffmpeg-platform"
)

2. 全局过滤无关classifier依赖

如果需要通用的全局过滤规则,可通过SBT的updateOptions配置自定义过滤器,只保留目标平台和无classifier的通用依赖:

lazy val allowedClassifiers = Set("linux-x86_64", "") // 空字符串对应无classifier的通用JAR

updateOptions := updateOptions.value.withFilter(new DependencyFilter {
  override def apply(module: ModuleID, artifact: Artifact, metadata: ModuleMetadata): Boolean = {
    artifact.classifier.forall(c => allowedClassifiers.contains(c))
  }
})

3. 针对SBT打包器的兜底配置

如果上述配置后打包仍包含多余JAR,可直接修改打包器的映射规则,过滤掉无关平台的文件:

// 对universal打包器生效
Universal / mappings := {
  val original = (Universal / mappings).value
  original.filter { case (_, path) =>
    !path.contains("macosx-x86_64") && !path.contains("windows-x86_64") && !path.contains("windows-x86")
  }
}

// 对Debian打包器生效
Debian / mappings := {
  val original = (Debian / mappings).value
  original.filter { case (_, path) =>
    !path.contains("macosx-x86_64") && !path.contains("windows-x86_64") && !path.contains("windows-x86")
  }
}

4. 验证依赖排除结果

执行以下命令查看依赖树,确认无关平台的依赖已被排除:

sbt dependencyTree

内容的提问来源于stack exchange,提问作者jdo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 15:27:23