diff --git a/README.md b/README.md index 59236418..89d738b9 100644 --- a/README.md +++ b/README.md @@ -48,13 +48,13 @@ ThisBuild / scalaVersion := "2.13.6" lazy val app = (project in file("app")) .settings( - Compile / assembly / mainClass := Some("com.example.Main"), + assembly / mainClass := Some("com.example.Main"), // more settings here ... ) lazy val utils = (project in file("utils")) .settings( - Compile / assembly / assemblyJarName := "utils.jar", + assembly / assemblyJarName := "utils.jar", // more settings here ... ) ``` @@ -72,18 +72,25 @@ single JAR file: `target/scala_X.X.X/projectname-assembly-X.X.X.jar`. If you specify a `assembly / mainClass` in build.sbt (or just let it autodetect one) then you'll end up with a fully executable JAR, ready to rock. -Here is the list of the keys you can rewire scoped to `Compile / assembly` task: +Here is the list of the keys you can rewire that are scoped to current subproject's `assembly` task: assemblyJarName test mainClass - assemblyOutputPath assemblyMergeStrategy assemblyOption - assemblyExcludedJars assembledMappings + assemblyOutputPath assemblyOption assembledMappings + assembledMappings + +And here is the list of the keys you can rewite that are scoped globally: + + assemblyAppendContentHash assemblyCacheOutput assemblyCacheUnzip + assemblyExcludedJars assemblyMergeStrategy assemblyShadeRules + +Keys scoped to the subproject should be placed in `.settings(...)` whereas the globally scoped keys can either be placed inside of `.settings(...)` or scoped using `ThisBuild / ` to be shared across multiple subprojects. For example the name of the jar can be set as follows in build.sbt: ```scala lazy val app = (project in file("app")) .settings( - Compile / assembly / assemblyJarName := "something.jar", + assembly / assemblyJarName := "something.jar", // more settings here ... ) ``` @@ -93,7 +100,7 @@ To set an explicit main class, ```scala lazy val app = (project in file("app")) .settings( - Compile / assembly / mainClass := Some("com.example.Main"), + assembly / mainClass := Some("com.example.Main"), // more settings here ... ) ``` @@ -103,7 +110,7 @@ To run the test during assembly, ```scala lazy val app = (project in file("app")) .settings( - Compile / assembly / test := (Test / test).value, + assembly / test := (Test / test).value, // more settings here ... ) ``` @@ -113,7 +120,7 @@ Excluding an explicit main class from your assembly requires something a little ``` lazy val app = (project in file("app")) .settings( - Compile / assembly / packageOptions ~= { pos => + assembly / packageOptions ~= { pos => pos.filterNot { po => po.isInstanceOf[Package.MainClass] } @@ -143,20 +150,20 @@ The mapping of path names to merge strategies is done via the setting `assemblyMergeStrategy` which can be augmented as follows: ```scala -Compile / assembly / assemblyMergeStrategy := { +ThisBuild / assemblyMergeStrategy := { case PathList("javax", "servlet", xs @ _*) => MergeStrategy.first case PathList(ps @ _*) if ps.last endsWith ".html" => MergeStrategy.first case "application.conf" => MergeStrategy.concat case "unwanted.txt" => MergeStrategy.discard case x => - val oldStrategy = (assembly / assemblyMergeStrategy).value + val oldStrategy = (ThisBuild / assemblyMergeStrategy).value oldStrategy(x) } ``` **NOTE**: -- `Compile / assembly / assemblyMergeStrategy` expects a function. You can't do `Compile / assembly / assemblyMergeStrategy := MergeStrategy.first`! -- Some files must be discarded or renamed otherwise to avoid breaking the zip (due to duplicate file name) or the legal license. Delegate default handling to `(Compile / assembly / assemblyMergeStrategy)` as the above pattern matching example. +- `ThisBuild / assemblyMergeStrategy` expects a function. You can't do `ThisBuild / assemblyMergeStrategy := MergeStrategy.first`! +- Some files must be discarded or renamed otherwise to avoid breaking the zip (due to duplicate file name) or the legal license. Delegate default handling to `(ThisBuild / assemblyMergeStrategy)` as the above pattern matching example. By the way, the first case pattern in the above using `PathList(...)` is how you can pick `javax/servlet/*` from the first jar. If the default `MergeStrategy.deduplicate` is not working for you, that likely means you have multiple versions of some library pulled by your dependency graph. The real solution is to fix that dependency graph. You can work around it by `MergeStrategy.first` but don't be surprised when you see `ClassNotFoundException`. @@ -205,9 +212,9 @@ sbt-assembly can shade classes from your projects or from the library dependenci Backed by [Jar Jar Links](https://code.google.com/archive/p/jarjar/wikis/CommandLineDocs.wiki), bytecode transformation (via ASM) is used to change references to the renamed classes. ```scala - Compile / assembly / assemblyShadeRules := Seq( - ShadeRule.rename("org.apache.commons.io.**" -> "shadeio.@1").inAll - ) +ThisBuild / assemblyShadeRules := Seq( + ShadeRule.rename("org.apache.commons.io.**" -> "shadeio.@1").inAll +) ``` Here are the shade rules: @@ -225,9 +232,9 @@ The `rename` rules takes a vararg of String pairs in ` -> ` for Instead of `.inAll`, call `.inProject` to match your project source, or call `.inLibrary("commons-io" % "commons-io" % "2.4", ...)` to match specific library dependencies. `inProject` and `inLibrary(...)` can be chained. ```scala - Compile / assembly/ assemblyShadeRules := Seq( - ShadeRule.rename("org.apache.commons.io.**" -> "shadeio.@1").inLibrary("commons-io" % "commons-io" % "2.4", ...).inProject - ) +ThisBuild / assemblyShadeRules := Seq( + ShadeRule.rename("org.apache.commons.io.**" -> "shadeio.@1").inLibrary("commons-io" % "commons-io" % "2.4", ...).inProject +) ``` The `ShadeRule.zap` rule causes any matched class to be removed from the resulting jar file. All zap rules are processed before renaming rules. @@ -237,7 +244,11 @@ The `ShadeRule.keep` rule marks all matched classes as "roots". If any keep rule To see the verbose output for shading: ```scala - Compile / assembly / logLevel := Level.Debug +lazy val app = (project in file("app")) + .settings( + assembly / logLevel := Level.Debug + // more settings here ... + ) ``` #### Scala libraries @@ -315,10 +326,10 @@ libraryDependencies ~= { _ map { To exclude specific files, customize merge strategy: ```scala -Compile / assembly / assemblyMergeStrategy := { +ThisBuild / assemblyMergeStrategy := { case PathList("about.html") => MergeStrategy.rename case x => - val oldStrategy = (assembly / assemblyMergeStrategy).value + val oldStrategy = (ThisBuild / assemblyMergeStrategy).value oldStrategy(x) } ``` @@ -332,7 +343,19 @@ To make a JAR file containing only the external dependencies, type This is intended to be used with a JAR that only contains your project ```scala -Compile / assembly / assemblyOption := (Compile / assembly / assemblyOption).value.copy(includeScala = false, includeDependency = false) +lazy val app = (project in file("app")) + .settings( + assemblyPackageScala / assembleArtifact := false, + assemblyPackageDependency / assembleArtifact := false, + + // or as follows + assembly / assemblyOption ~= { + _.withIncludeScala(false) + .withIncludeDependency(false) + }, + + // more settings here ... + ) ``` NOTE: If you use [`-jar` option for `java`](http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/java.html#jar), it will ignore `-cp`, so if you have multiple JAR files you have to use `-cp` and pass the main class: `java -cp "jar1.jar:jar2.jar" Main` @@ -342,7 +365,17 @@ NOTE: If you use [`-jar` option for `java`](http://docs.oracle.com/javase/7/docs To exclude Scala library (JARs that start with `scala-` and are included in the binary Scala distribution) to run with `scala` command, ```scala -Compile / assembly / assemblyOption := (Compile / assembly / assemblyOption).value.copy(includeScala = false) +lazy val app = (project in file("app")) + .settings( + assemblyPackageScala / assembleArtifact := false, + + // or as follows + assembly / assemblyOption ~= { + _.withIncludeScala(false) + }, + + // more settings here ... + ) ``` ### assemblyExcludedJars @@ -350,10 +383,15 @@ Compile / assembly / assemblyOption := (Compile / assembly / assemblyOption).val If all efforts fail, here's a way to exclude JAR files: ```scala -Compile / assembly / assemblyExcludedJars := { - val cp = (assembly / fullClasspath).value - cp filter {_.data.getName == "compile-0.1.0.jar"} -} +lazy val app = (project in file("app")) + .settings( + assembly / assemblyExcludedJars := { + val cp = (assembly / fullClasspath).value + cp filter {_.data.getName == "compile-0.1.0.jar"} + }, + + // more settings here ... + ) ``` Other Things @@ -364,7 +402,13 @@ Other Things You can also append SHA-1 fingerprint to the assembly file name, this may help you to determine whether it has changed and, for example, if it's necessary to deploy the dependencies, ```scala -Compile / assembly / assemblyOption := (assembly / assemblyOption).withAppendContentHash(true) +ThisBuild / assemblyAppendContentHash := true + +// or +lazy val app = (project in file("app")) + .settings( + assembly / assemblyOption ~= { _.withAppendContentHash(true) } + ) ``` ### Caching @@ -372,13 +416,25 @@ Compile / assembly / assemblyOption := (assembly / assemblyOption).withAppendCon By default for performance reasons, the result of unzipping any dependency JAR files to disk is cached from run-to-run. This feature can be disabled by setting: ```scala -Compile / assembly / assemblyOption := (assembly / assemblyOption).withCacheUnzip(false) +ThisBuild / assemblyCacheUnzip := false + +// or +lazy val app = (project in file("app")) + .settings( + assembly / assemblyOption ~= { _.withCacheUnzip(false) } + ) ``` In addition the fat JAR is cached so its timestamp changes only when the input changes. This feature requires checking the SHA-1 hash of all *.class files, and the hash of all dependency *.jar files. If there are a large number of class files, this could take a long time, although with hashing of jar files, rather than their contents, the speed has recently been [improved](https://github.com/sbt/sbt-assembly/issues/68). This feature can be disabled by setting: ```scala -Compile / assembly / assemblyOption := (assembly / assemblyOption).withCacheOutput(false) +ThisBuild / assemblyCacheOutput := false + +// or +lazy val app = (project in file("app")) + .settings( + assembly / assemblyOption ~= { _.withCacheOutput(false) } + ) ``` ### Prepending a launch script @@ -388,9 +444,12 @@ Your can prepend a launch script to the fat jar. This script will be a valid she ```scala import sbtassembly.AssemblyPlugin.defaultUniversalScript -assembly / assemblyOption := (assembly / assemblyOption).value.copy(prependShellScript = Some(defaultUniversalScript(shebang = false))) +ThisBuild / assemblyPrependShellScript := = Some(defaultUniversalScript(shebang = false))) -assembly / assemblyJarName := s"${name.value}-${version.value}" +lazy val app = (project in file("app")) + .settings( + assembly / assemblyJarName := s"${name.value}-${version.value}" + ) ``` This will prepend the following shell script to the jar. @@ -413,9 +472,12 @@ You can also choose to prepend just the shell script to the fat jar as follows: ```scala import sbtassembly.AssemblyPlugin.defaultShellScript -Compile / assembly / assemblyOption := (assembly / assemblyOption).value.copy(prependShellScript = Some(defaultShellScript)) +ThisBuild / assemblyPrependShellScript := Some(defaultShellScript) -Compile / assembly / assemblyJarName := s"${name.value}-${version.value}" +lazy val app = (project in file("app")) + .settings( + assembly / assemblyJarName := s"${name.value}-${version.value}" + ) ``` ### Publishing (Not Recommended) @@ -424,12 +486,12 @@ Publishing fat JARs out to the world is discouraged because non-modular JARs cau and all of the other artifacts, add an `assembly` classifier (or other): ```scala -Compile / assembly / artifact := { - val art = (Compile / assembly / artifact).value +assembly / artifact := { + val art = (assembly / artifact).value art.withClassifier(Some("assembly")) } -addArtifact(Compile / assembly / artifact, assembly) +addArtifact(assembly / artifact, assembly) ``` ### Q: Despite the concerned friends, I still want publish fat JARs. What advice do you have? @@ -449,13 +511,19 @@ lazy val cosmetic = project .settings( name := "shaded-something", // I am sober. no dependencies. - Compile / packageBin := (fatJar / Compile / assembly).value + Compile / packageBin := (fatJar / assembly).value ) ``` License ------- -Copyright (c) 2010-2014 e.e d3si9n, Coda Hale - Published under The MIT License, see LICENSE + +Copyright e.e d3si9n, LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/notes/0.7.0.markdown b/notes/0.7.0.markdown index 3f99daed..af310f9c 100644 --- a/notes/0.7.0.markdown +++ b/notes/0.7.0.markdown @@ -13,7 +13,7 @@ Scala identifiers for the keys remain the same (e.g. `jarName`), but key names a jarName in assembly := "foo.jar" -### use `assembleArtifact` instead of `publishArtifact` +### use `assembleArtifact` instead of `publishArtifact` To exclude Scala library, assembleArtifact in packageScala := false diff --git a/src/main/scala/sbtassembly/AssemblyKeys.scala b/src/main/scala/sbtassembly/AssemblyKeys.scala index 1a4e1baa..626c9a50 100644 --- a/src/main/scala/sbtassembly/AssemblyKeys.scala +++ b/src/main/scala/sbtassembly/AssemblyKeys.scala @@ -5,20 +5,26 @@ import Keys._ import com.eed3si9n.jarjarabrams trait AssemblyKeys { - lazy val assembly = taskKey[File]("Builds a deployable fat jar.") - lazy val assembleArtifact = settingKey[Boolean]("Enables (true) or disables (false) assembling an artifact.") - lazy val assemblyOption = taskKey[AssemblyOption]("Configuration for making a deployable fat jar.") - lazy val assembledMappings = taskKey[Seq[MappingSet]]("Keeps track of jar origins for each source.") + lazy val assembly = taskKey[File]("Builds a deployable fat JAR") + lazy val assembleArtifact = settingKey[Boolean]("Enables (true) or disables (false) assembling an artifact") + lazy val assemblyOption = taskKey[AssemblyOption]("Configuration for making a deployable fat JAR") + lazy val assembledMappings = taskKey[Seq[MappingSet]]("Keeps track of jar origins for each source") - lazy val assemblyPackageScala = taskKey[File]("Produces the scala artifact.") - lazy val assemblyPackageDependency = taskKey[File]("Produces the dependency artifact.") + lazy val assemblyPackageScala = taskKey[File]("Produces the Scala artifact") + lazy val assemblyPackageDependency = taskKey[File]("Produces the dependency artifact") lazy val assemblyJarName = taskKey[String]("name of the fat jar") lazy val assemblyDefaultJarName = taskKey[String]("default name of the fat jar") lazy val assemblyOutputPath = taskKey[File]("output path of the fat jar") lazy val assemblyExcludedJars = taskKey[Classpath]("list of excluded jars") lazy val assemblyMergeStrategy = settingKey[String => MergeStrategy]("mapping from archive member path to merge strategy") lazy val assemblyShadeRules = settingKey[Seq[jarjarabrams.ShadeRule]]("shading rules backed by jarjar") + lazy val assemblyAppendContentHash = settingKey[Boolean]("Appends SHA-1 fingerprint to the assembly file name") + lazy val assemblyMaxHashLength = settingKey[Int]("Length of SHA-1 fingerprint used for the assembly file name") + lazy val assemblyCacheUnzip = settingKey[Boolean]("Caches the unzipped products of the dependency JAR files") + lazy val assemblyCacheOutput = settingKey[Boolean]("Caches the output if the content has not changed") + lazy val assemblyPrependShellScript = settingKey[Option[Seq[String]]]("A launch script to prepend to the fat JAR") } + object AssemblyKeys extends AssemblyKeys // Keep track of the source package of mappings that come from a jar, so we can diff --git a/src/main/scala/sbtassembly/AssemblyPlugin.scala b/src/main/scala/sbtassembly/AssemblyPlugin.scala index fa4593ae..d52922c4 100644 --- a/src/main/scala/sbtassembly/AssemblyPlugin.scala +++ b/src/main/scala/sbtassembly/AssemblyPlugin.scala @@ -22,46 +22,35 @@ object AssemblyPlugin extends sbt.AutoPlugin { } import autoImport.{ Assembly => _, baseAssemblySettings => _, _ } - val defaultShellScript: Seq[String] = defaultShellScript() - - def defaultShellScript(javaOpts: Seq[String] = Seq.empty): Seq[String] = { - val javaOptsString = javaOpts.map(_ + " ").mkString - Seq("#!/usr/bin/env sh", s"""exec java -jar $javaOptsString$$JAVA_OPTS "$$0" "$$@"""", "") - } - - private def universalScript(shellCommands: String, - cmdCommands: String, - shebang: Boolean): String = { - Seq( - if (shebang) "#!/usr/bin/env sh" else "", - "@ 2>/dev/null # 2>nul & echo off & goto BOF\r", - ":", - shellCommands.replaceAll("\r\n|\n", "\n"), - "exit", - Seq( - "", - ":BOF", - cmdCommands.replaceAll("\r\n|\n", "\r\n"), - "exit /B %errorlevel%", - "" - ).mkString("\r\n") - ).filterNot(_.isEmpty).mkString("\n") - } - - def defaultUniversalScript(javaOpts: Seq[String] = Seq.empty, shebang: Boolean = true): Seq[String] = { - val javaOptsString = javaOpts.map(_ + " ").mkString - Seq(universalScript( - shellCommands = s"""exec java -jar $javaOptsString$$JAVA_OPTS "$$0" "$$@"""", - cmdCommands = s"""java -jar $javaOptsString%JAVA_OPTS% "%~dpnx0" %*""", - shebang = shebang - )) - } + override lazy val globalSettings: Seq[Def.Setting[_]] = Seq( + assemblyMergeStrategy := MergeStrategy.defaultMergeStrategy, + assemblyShadeRules := Nil, + assemblyExcludedJars := Nil, + assembleArtifact in packageBin := true, + assembleArtifact in assemblyPackageScala := true, + assembleArtifact in assemblyPackageDependency := true, + assemblyAppendContentHash := false, + assemblyCacheUnzip := true, + assemblyCacheOutput := true, + assemblyPrependShellScript := None + ) override lazy val projectSettings: Seq[Def.Setting[_]] = assemblySettings - lazy val baseAssemblySettings: Seq[sbt.Def.Setting[_]] = Seq( + // Compile-specific defaults + lazy val assemblySettings: Seq[sbt.Def.Setting[_]] = baseAssemblySettings ++ Seq( + packageOptions in assembly := { + val os = (packageOptions in (Compile, packageBin)).value + (mainClass in assembly).value map { s => + Package.MainClass(s) +: (os filterNot {_.isInstanceOf[Package.MainClass]}) + } getOrElse {os} + }, + packageOptions in assemblyPackageScala := (packageOptions in (Compile, packageBin)).value, + packageOptions in assemblyPackageDependency := (packageOptions in (Compile, packageBin)).value + ) + + lazy val baseAssemblySettings: Seq[sbt.Def.Setting[_]] = (Seq( assembly := Assembly.assemblyTask(assembly).value, - logLevel in assembly := Level.Info, assembledMappings in assembly := Assembly.assembledMappingsTask(assembly).value, assemblyPackageScala := Assembly.assemblyTask(assemblyPackageScala).value, assembledMappings in assemblyPackageScala := Assembly.assembledMappingsTask(assemblyPackageScala).value, @@ -73,57 +62,15 @@ object AssemblyPlugin extends sbt.AutoPlugin { test in assemblyPackageScala := (test in assembly).value, test in assemblyPackageDependency := (test in assembly).value, - // assemblyOption - assembleArtifact in packageBin := true, - assembleArtifact in assemblyPackageScala := true, - assembleArtifact in assemblyPackageDependency := true, - assemblyMergeStrategy in assembly := MergeStrategy.defaultMergeStrategy, - assemblyShadeRules in assembly := Seq(), - assemblyExcludedJars in assembly := Nil, - assemblyOption in assembly := { - val s = streams.value - AssemblyOption( - assemblyDirectory = Some(s.cacheDirectory / "assembly"), - includeBin = (assembleArtifact in packageBin).value, - includeScala = (assembleArtifact in assemblyPackageScala).value, - includeDependency = (assembleArtifact in assemblyPackageDependency).value, - mergeStrategy = (assemblyMergeStrategy in assembly).value, - excludedJars = (assemblyExcludedJars in assembly).value, - excludedFiles = Assembly.defaultExcludedFiles, - cacheOutput = true, - cacheUnzip = true, - appendContentHash = false, - prependShellScript = None, - maxHashLength = None, - shadeRules = (assemblyShadeRules in assembly).value.toVector, - scalaVersion = scalaVersion.value, - level = (logLevel in assembly).value) - }, - - assemblyOption in assemblyPackageScala := { - val ao = (assemblyOption in assembly).value - ao.withIncludeBin(false) - .withIncludeScala(true) - .withIncludeDependency(false) - }, - - assemblyOption in assemblyPackageDependency := { - val ao = (assemblyOption in assembly).value - ao.withIncludeBin(false) - .withIncludeScala(true) - .withIncludeDependency(true) - }, - - // packageOptions + // packageOptions not specific to Compile scope. see also assemblySettings packageOptions in assembly := { - val os = (packageOptions in (Compile, packageBin)).value + val os = (packageOptions in packageBin).value (mainClass in assembly).value map { s => Package.MainClass(s) +: (os filterNot {_.isInstanceOf[Package.MainClass]}) } getOrElse {os} }, - - packageOptions in assemblyPackageScala := (packageOptions in (Compile, packageBin)).value, - packageOptions in assemblyPackageDependency := (packageOptions in (Compile, packageBin)).value, + packageOptions in assemblyPackageScala := (packageOptions in packageBin).value, + packageOptions in assemblyPackageDependency := (packageOptions in packageBin).value, // outputPath assemblyOutputPath in assembly := { (target in assembly).value / (assemblyJarName in assembly).value }, @@ -144,7 +91,76 @@ object AssemblyPlugin extends sbt.AutoPlugin { fullClasspath in assembly := (fullClasspath or (fullClasspath in Runtime)).value, externalDependencyClasspath in assembly := (externalDependencyClasspath or (externalDependencyClasspath in Runtime)).value + ) ++ inTask(assembly)(assemblyOptionSettings) + ++ inTask(assemblyPackageScala)(assemblyOptionSettings) + ++ inTask(assemblyPackageDependency)(assemblyOptionSettings) + ++ Seq( + assemblyOption in assemblyPackageScala ~= { + _.withIncludeBin(false) + .withIncludeScala(true) + .withIncludeDependency(false) + }, + assemblyOption in assemblyPackageDependency ~= { + _.withIncludeBin(false) + .withIncludeScala(true) + .withIncludeDependency(true) + } + )) + + def assemblyOptionSettings: Seq[Setting[_]] = Seq( + assemblyOption := { + val s = streams.value + AssemblyOption() + .withAssemblyDirectory(s.cacheDirectory / "assembly") + .withIncludeBin((assembleArtifact in packageBin).value) + .withIncludeScala((assembleArtifact in assemblyPackageScala).value) + .withIncludeDependency((assembleArtifact in assemblyPackageDependency).value) + .withMergeStrategy(assemblyMergeStrategy.value) + .withExcludedJars(assemblyExcludedJars.value) + .withExcludedFiles(Assembly.defaultExcludedFiles) + .withCacheOutput(assemblyCacheOutput.value) + .withCacheUnzip(assemblyCacheUnzip.value) + .withAppendContentHash(assemblyAppendContentHash.value) + .withPrependShellScript(assemblyPrependShellScript.value) + .withMaxHashLength(assemblyMaxHashLength.?.value) + .withShadeRules(assemblyShadeRules.value) + .withScalaVersion(scalaVersion.value) + .withLevel(logLevel.?.value.getOrElse(Level.Info)) + } ) - lazy val assemblySettings: Seq[sbt.Def.Setting[_]] = baseAssemblySettings + lazy val defaultShellScript: Seq[String] = defaultShellScript() + + def defaultShellScript(javaOpts: Seq[String] = Seq.empty): Seq[String] = { + val javaOptsString = javaOpts.map(_ + " ").mkString + Seq("#!/usr/bin/env sh", s"""exec java -jar $javaOptsString$$JAVA_OPTS "$$0" "$$@"""", "") + } + + private def universalScript(shellCommands: String, + cmdCommands: String, + shebang: Boolean): String = { + Seq( + if (shebang) "#!/usr/bin/env sh" else "", + "@ 2>/dev/null # 2>nul & echo off & goto BOF\r", + ":", + shellCommands.replaceAll("\r\n|\n", "\n"), + "exit", + Seq( + "", + ":BOF", + cmdCommands.replaceAll("\r\n|\n", "\r\n"), + "exit /B %errorlevel%", + "" + ).mkString("\r\n") + ).filterNot(_.isEmpty).mkString("\n") + } + + def defaultUniversalScript(javaOpts: Seq[String] = Seq.empty, shebang: Boolean = true): Seq[String] = { + val javaOptsString = javaOpts.map(_ + " ").mkString + Seq(universalScript( + shellCommands = s"""exec java -jar $javaOptsString$$JAVA_OPTS "$$0" "$$@"""", + cmdCommands = s"""java -jar $javaOptsString%JAVA_OPTS% "%~dpnx0" %*""", + shebang = shebang + )) + } } diff --git a/src/sbt-test/merging/merging/build.sbt b/src/sbt-test/merging/merging/build.sbt index 6bdcc3c4..3e7563e2 100644 --- a/src/sbt-test/merging/merging/build.sbt +++ b/src/sbt-test/merging/merging/build.sbt @@ -1,20 +1,21 @@ -lazy val testmerge = (project in file(".")). - settings( - version := "0.1", +version in ThisBuild := "0.1" +scalaVersion in ThisBuild := "2.11.12" +assemblyMergeStrategy in ThisBuild := { + case "a" => MergeStrategy.concat + case "b" => MergeStrategy.first + case "c" => MergeStrategy.last + case "d" => MergeStrategy.filterDistinctLines + case "e" => MergeStrategy.deduplicate + case "f" => MergeStrategy.discard + case PathList("x", "y") => MergeStrategy.discard + case x => + val oldStrategy = (assemblyMergeStrategy in ThisBuild).value + oldStrategy(x) +} + +lazy val testmerge = (project in file(".")) + .settings( assemblyJarName in assembly := "foo.jar", - scalaVersion := "2.11.12", - assemblyMergeStrategy in assembly := { - case "a" => MergeStrategy.concat - case "b" => MergeStrategy.first - case "c" => MergeStrategy.last - case "d" => MergeStrategy.filterDistinctLines - case "e" => MergeStrategy.deduplicate - case "f" => MergeStrategy.discard - case PathList("x", "y") => MergeStrategy.discard - case x => - val oldStrategy = (assemblyMergeStrategy in assembly).value - oldStrategy(x) - }, TaskKey[Unit]("check") := { IO.withTemporaryDirectory { dir ⇒ IO.unzip(crossTarget.value / "foo.jar", dir) diff --git a/src/sbt-test/sbt-assembly/appendhash/build.sbt b/src/sbt-test/sbt-assembly/appendhash/build.sbt index 190e56db..62ed79e6 100644 --- a/src/sbt-test/sbt-assembly/appendhash/build.sbt +++ b/src/sbt-test/sbt-assembly/appendhash/build.sbt @@ -1,14 +1,19 @@ -lazy val root = (project in file(".")). - settings( +version in ThisBuild := "0.1" +scalaVersion in ThisBuild := "2.12.8" +assemblyAppendContentHash in ThisBuild := true + +lazy val root = (project in file(".")) + .settings( name := "foo", - version := "0.1", - scalaVersion := "2.10.7", libraryDependencies ++= Seq( - "net.databinder.dispatch" %% "dispatch-core" % "0.11.0" + "com.eed3si9n" %% "gigahorse-okhttp" % "0.5.0" ), - assemblyOption in assembly ~= { _.copy(includeScala = false, includeDependency = false) }, - assemblyOption in assembly ~= { _.copy(appendContentHash = true) }, - assemblyOption in assemblyPackageDependency ~= { _.copy(appendContentHash = true) }, + + (assemblyOption in assembly) ~= { + _.withIncludeScala(false) + .withIncludeDependency(false) + }, + InputKey[Unit]("checkFile") := { val args = sbt.complete.Parsers.spaceDelimited("").parsed val expectFileNameRegex = args.head.r @@ -16,6 +21,7 @@ lazy val root = (project in file(".")). expectFileNameRegex.findFirstIn(jar.getName).isDefined }) }, + TaskKey[Unit]("checkPrevious") := { import sbinary.DefaultProtocol._ import sbtassembly.PluginCompat._ diff --git a/src/sbt-test/sbt-assembly/appendhash/test b/src/sbt-test/sbt-assembly/appendhash/test index 92889f9f..beefe0a1 100644 --- a/src/sbt-test/sbt-assembly/appendhash/test +++ b/src/sbt-test/sbt-assembly/appendhash/test @@ -10,7 +10,7 @@ > assemblyPackageDependency > checkFile "foo-assembly-0.1-deps-[0-9abcdef]{40}.jar" -> set assemblyOption in assembly ~= { _.copy(appendContentHash = true, maxHashLength = Some(5)) } +> set assemblyMaxHashLength in ThisBuild := 5 > clean > assembly > checkFile "foo-assembly-0.1-[0-9abcdef]{5}.jar" diff --git a/src/sbt-test/sbt-assembly/config/build.sbt b/src/sbt-test/sbt-assembly/config/build.sbt index 6cc71a4c..7d1b3cd0 100644 --- a/src/sbt-test/sbt-assembly/config/build.sbt +++ b/src/sbt-test/sbt-assembly/config/build.sbt @@ -1,10 +1,9 @@ -lazy val root = (project in file(".")). - settings( - version := "0.1", - scalaVersion := "2.11.12" - ). - settings(inConfig(Test)(baseAssemblySettings): _*). - settings( +version in ThisBuild := "0.1" +scalaVersion in ThisBuild := "2.11.12" + +lazy val root = (project in file(".")) + .settings(inConfig(Test)(baseAssemblySettings)) + .settings( assemblyJarName in (Test, assembly) := "foo.jar", TaskKey[Unit]("check") := { val process = sys.process.Process("java", Seq("-jar", (crossTarget.value / "foo.jar").toString)) diff --git a/src/sbt-test/sbt-assembly/deps/build.sbt b/src/sbt-test/sbt-assembly/deps/build.sbt index ed9e5c32..1a0a2d70 100644 --- a/src/sbt-test/sbt-assembly/deps/build.sbt +++ b/src/sbt-test/sbt-assembly/deps/build.sbt @@ -1,7 +1,8 @@ -lazy val root = (project in file(".")). - settings( - version := "0.1", - scalaVersion := "2.11.12", +version in ThisBuild := "0.1" +scalaVersion in ThisBuild := "2.11.12" + +lazy val root = (project in file(".")) + .settings( libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.1" % "test", libraryDependencies += "ch.qos.logback" % "logback-classic" % "0.9.29" % "runtime", unmanagedJars in Compile ++= { @@ -13,7 +14,7 @@ lazy val root = (project in file(".")). unmanagedJars in Test ++= { (baseDirectory.value / "lib" / "test" ** "*.jar").classpath }, - assemblyExcludedJars in assembly := { + assemblyExcludedJars := { (fullClasspath in assembly).value filter {_.data.getName == "compile-0.1.0.jar"} }, assemblyJarName in assembly := "foo.jar", diff --git a/src/sbt-test/sbt-assembly/empty/build.sbt b/src/sbt-test/sbt-assembly/empty/build.sbt index 1aaf6df8..612483ca 100644 --- a/src/sbt-test/sbt-assembly/empty/build.sbt +++ b/src/sbt-test/sbt-assembly/empty/build.sbt @@ -1,2 +1,2 @@ -version := "0.1" -scalaVersion := "2.10.7" +version in ThisBuild := "0.1" +scalaVersion in ThisBuild := "2.12.8" diff --git a/src/sbt-test/sbt-assembly/piecemeal/build.sbt b/src/sbt-test/sbt-assembly/piecemeal/build.sbt index 42dce1e5..71696065 100644 --- a/src/sbt-test/sbt-assembly/piecemeal/build.sbt +++ b/src/sbt-test/sbt-assembly/piecemeal/build.sbt @@ -1,12 +1,28 @@ -lazy val root = (project in file(".")). - settings( +version in ThisBuild := "0.1" +scalaVersion in ThisBuild := "2.10.7" + +assembleArtifact in (ThisBuild, assemblyPackageScala) := false +assembleArtifact in (ThisBuild, assemblyPackageDependency) := false + +lazy val root = (project in file(".")) + .settings( name := "foo", - version := "0.1", - scalaVersion := "2.10.7", - assembleArtifact in assemblyPackageScala := false, - assembleArtifact in assemblyPackageDependency := false, - TaskKey[Unit]("check") := { - val process = sys.process.Process("java", Seq("-cp", + + // assembly / assemblyOption ~= { + // _.withIncludeScala(false) + // .withIncludeDependency(false) + // }, + + TaskKey[Unit]("check1") := { + val process = sys.process.Process("java", Seq("-cp", + (crossTarget.value / "foo-assembly-0.1.jar").toString, + "Main")) + val out = (process!!) + if (out.trim != "hello") sys.error("unexpected output: " + out) + () + }, + TaskKey[Unit]("check2") := { + val process = sys.process.Process("java", Seq("-cp", (crossTarget.value / "scala-library-2.10.7-assembly.jar").toString + ":" + (crossTarget.value / "foo-assembly-0.1.jar").toString, "Main")) diff --git a/src/sbt-test/sbt-assembly/piecemeal/test b/src/sbt-test/sbt-assembly/piecemeal/test index 1df42025..cc80d076 100644 --- a/src/sbt-test/sbt-assembly/piecemeal/test +++ b/src/sbt-test/sbt-assembly/piecemeal/test @@ -2,6 +2,9 @@ > assembly $ exists target/scala-2.10/foo-assembly-0.1.jar +# try running this JAR. this should fail +-> check1 + # check if the file gets created > assemblyPackageDependency $ exists target/scala-2.10/foo-assembly-0.1-deps.jar @@ -11,4 +14,4 @@ $ exists target/scala-2.10/foo-assembly-0.1-deps.jar $ exists target/scala-2.10/scala-library-2.10.7-assembly.jar # check if it says hello -> check +> check2 diff --git a/src/sbt-test/sbt-assembly/simple/build.sbt b/src/sbt-test/sbt-assembly/simple/build.sbt index 3dc33b8e..b4edac62 100644 --- a/src/sbt-test/sbt-assembly/simple/build.sbt +++ b/src/sbt-test/sbt-assembly/simple/build.sbt @@ -1,10 +1,9 @@ -lazy val root = (project in file(".")). - settings( - version := "0.1", - scalaVersion := "2.10.7", - assemblyOption in assembly ~= { _.copy(cacheOutput = true) }, - assemblyOption in assembly ~= { _.copy(cacheUnzip = true) }, - assemblyJarName in assembly := "foo.jar", +version in ThisBuild := "0.1" +scalaVersion in ThisBuild := "2.12.8" + +lazy val root = (project in file(".")) + .settings( + assemblyJarName := "foo.jar", TaskKey[Unit]("check") := { val process = sys.process.Process("java", Seq("-jar", (crossTarget.value / "foo.jar").toString)) val out = (process!!) @@ -12,4 +11,3 @@ lazy val root = (project in file(".")). () } ) - diff --git a/src/sbt-test/sbt-assembly/simple/test b/src/sbt-test/sbt-assembly/simple/test index 2d509a53..a74309b0 100644 --- a/src/sbt-test/sbt-assembly/simple/test +++ b/src/sbt-test/sbt-assembly/simple/test @@ -1,6 +1,6 @@ # check if the file gets created > assembly -$ exists target/scala-2.10/foo.jar +$ exists target/scala-2.12/foo.jar # check if it says hello > check diff --git a/src/sbt-test/shading/directories/build.sbt b/src/sbt-test/shading/directories/build.sbt index 654eaca7..6d0bb502 100644 --- a/src/sbt-test/shading/directories/build.sbt +++ b/src/sbt-test/shading/directories/build.sbt @@ -1,19 +1,22 @@ -scalaVersion := "2.10.7" +scalaVersion in ThisBuild := "2.10.7" -crossPaths := false - -assemblyJarName in assembly := "assembly.jar" - -assemblyShadeRules in assembly := Seq( +assemblyShadeRules in ThisBuild := Seq( ShadeRule.rename("somepackage.**" -> "shaded.@1").inAll ) -TaskKey[Unit]("check") := { - val expected = "Hello shaded.SomeClass" - val output = sys.process.Process("java", Seq("-jar", assembly.value.absString)).!!.trim - if (output != expected) sys.error("Unexpected output: " + output) -} +lazy val root = (project in file(".")) + .settings( + crossPaths := false, + + assemblyJarName in assembly := "assembly.jar", + + TaskKey[Unit]("check") := { + val expected = "Hello shaded.SomeClass" + val output = sys.process.Process("java", Seq("-jar", assembly.value.absString)).!!.trim + if (output != expected) sys.error("Unexpected output: " + output) + }, -TaskKey[Unit]("unzip") := { - IO.unzip((assemblyOutputPath in assembly).value, crossTarget.value / "unzipped") -} + TaskKey[Unit]("unzip") := { + IO.unzip((assemblyOutputPath in assembly).value, crossTarget.value / "unzipped") + } + )