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
相关产品推荐
相关产品推荐

