Skip to content

Commit

Permalink
Move JarManifest to mill.api
Browse files Browse the repository at this point in the history
Kept deprecated forwarders in `mill.scalalib.Jvm` to maintain source compatibility.
  • Loading branch information
lefou committed Feb 18, 2023
1 parent d0e4308 commit f98513c
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 60 deletions.
15 changes: 15 additions & 0 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,21 @@ object main extends MillModule {
Deps.upickle,
Deps.sbtTestInterface
)
def generatedBuildInfo: T[Seq[PathRef]] = T {
val dest = T.dest
val code =
s"""package mill.main.api
|
|/** Generated at built-time by Mill. */
|object BuildInfo {
| /** Mill version. */
| val millVersion: String = "${millVersion()}"
|}
|""".stripMargin
os.write(dest / "mill" / "main" / "api" / "BuildInfo.scala", code, createFolders = true)
Seq(PathRef(dest))
}
override def generatedSources: T[Seq[PathRef]] = super.generatedSources() ++ generatedBuildInfo()
}
object util extends MillApiModule with MillAutoTestSetup {
override def moduleDeps = Seq(api)
Expand Down
67 changes: 67 additions & 0 deletions main/api/src/mill/api/JarManifest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package mill.api

import mill.main.api.BuildInfo
import upickle.default.ReadWriter

import java.util.jar.{Attributes, Manifest}

/**
* Represents a JAR manifest.
*
* @param main the main manifest attributes
* @param groups additional attributes for named entries
*/
final class JarManifest private (
val main: Map[String, String],
val groups: Map[String, Map[String, String]]
) {
def add(entries: (String, String)*): JarManifest = copy(main = main ++ entries)

def addGroup(group: String, entries: (String, String)*): JarManifest =
copy(groups = groups + (group -> (groups.getOrElse(group, Map.empty) ++ entries)))

private def copy(
main: Map[String, String] = main,
groups: Map[String, Map[String, String]] = groups
): JarManifest = JarManifest(main, groups)

override def toString: String = Seq(
"main" -> main,
"groups" -> groups
).map(p => s"${p._1}=${p._2}").mkString(getClass().getSimpleName + "(", ",", ")")

/** Constructs a [[java.util.jar.Manifest]] from this JarManifest. */
def build: Manifest = {
val manifest = new Manifest
val mainAttributes = manifest.getMainAttributes
main.foreach { case (key, value) => mainAttributes.putValue(key, value) }
val entries = manifest.getEntries
for ((group, attribs) <- groups) {
val attrib = new Attributes
attribs.foreach { case (key, value) => attrib.putValue(key, value) }
entries.put(group, attrib)
}
manifest
}
}

object JarManifest {

final val Empty = JarManifest()

final val MillDefault = JarManifest(
main = Map[String, String](
java.util.jar.Attributes.Name.MANIFEST_VERSION.toString -> "1.0",
"Created-By" -> s"Mill ${BuildInfo.millVersion}",
"Tool" -> s"Mill-${BuildInfo.millVersion}"
)
)

def apply(
main: Map[String, String] = Map.empty,
groups: Map[String, Map[String, String]] = Map.empty
): JarManifest = new JarManifest(main, groups)

implicit val jarManifestRW: ReadWriter[JarManifest] = upickle.default.macroRW

}
37 changes: 35 additions & 2 deletions main/api/src/mill/api/JarOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,46 @@ trait JarOps {
def jar(
jar: os.Path,
inputPaths: Agg[os.Path],
manifest: Manifest,
manifest: JarManifest = JarManifest.Empty,
fileFilter: (os.Path, os.RelPath) => Boolean = (_, _) => true,
includeDirs: Boolean = false,
timestamp: Option[Long] = None
): Unit = {
): Unit = this.jar(
jar = jar,
inputPaths = inputPaths,
manifest = manifest.build,
fileFilter = fileFilter,
includeDirs = includeDirs,
timestamp = timestamp
)

/**
* Create a JAR file with default inflation level.
* d
*
* @param jar The final JAR file
* @param inputPaths The input paths resembling the content of the JAR file.
* Files will be directly included in the root of the archive,
* whereas for directories their content is added to the root of the archive.
* @param manifest The JAR Manifest
* @param fileFilter A filter to support exclusions of selected files
* @param includeDirs If `true` the JAR archive will contain directory entries.
* According to the ZIP specification, directory entries are not required.
* In the Java ecosystem, most JARs have directory entries, so including them may reduce compatibility issues.
* Directory entry names will result with a trailing `/`.
* @param timestamp If specified, this timestamp is used as modification timestamp (mtime) for all entries in the JAR file.
* Having a stable timestamp may result in reproducible files, if all other content, including the JAR Manifest, keep stable.
*/
def jar(
jar: os.Path,
inputPaths: Agg[os.Path],
manifest: Manifest,
fileFilter: (os.Path, os.RelPath) => Boolean,
includeDirs: Boolean,
timestamp: Option[Long] = None
): Unit = {
val curTime = timestamp.getOrElse(System.currentTimeMillis())

def mTime(file: os.Path) = timestamp.getOrElse(os.mtime(file))

os.makeDir.all(jar / os.up)
Expand Down
62 changes: 13 additions & 49 deletions main/src/mill/modules/Jvm.scala
Original file line number Diff line number Diff line change
Expand Up @@ -242,17 +242,10 @@ object Jvm extends CoursierSupport {
)
}

def createManifest(mainClass: Option[String]): JarManifest = {
val main =
Map[String, String](
java.util.jar.Attributes.Name.MANIFEST_VERSION.toString -> "1.0",
"Created-By" -> s"Mill ${BuildInfo.millVersion}",
"Tool" -> s"Mill-${BuildInfo.millVersion}"
) ++
mainClass.map(mc => Map(java.util.jar.Attributes.Name.MAIN_CLASS.toString -> mc)).getOrElse(
Map.empty
)
JarManifest(main)
def createManifest(mainClass: Option[String]): mill.api.JarManifest = {
mainClass.foldLeft(mill.api.JarManifest.MillDefault)((m, c) =>
m.add((java.util.jar.Attributes.Name.MAIN_CLASS.toString, c))
)
}

/**
Expand All @@ -269,7 +262,7 @@ object Jvm extends CoursierSupport {
*/
def createJar(
inputPaths: Agg[os.Path],
manifest: JarManifest = JarManifest.Default,
manifest: mill.api.JarManifest = mill.api.JarManifest.MillDefault,
fileFilter: (os.Path, os.RelPath) => Boolean = (_, _) => true
)(implicit ctx: Ctx.Dest): PathRef = {
val outputPath = ctx.dest / "out.jar"
Expand All @@ -285,16 +278,16 @@ object Jvm extends CoursierSupport {
def createJar(
jar: os.Path,
inputPaths: Agg[os.Path],
manifest: JarManifest,
manifest: mill.api.JarManifest,
fileFilter: (os.Path, os.RelPath) => Boolean
): Unit =
JarOps.jar(jar, inputPaths, manifest.build, fileFilter, includeDirs = true, timestamp = None)
JarOps.jar(jar, inputPaths, manifest, fileFilter, includeDirs = true, timestamp = None)

def createClasspathPassingJar(jar: os.Path, classpath: Agg[os.Path]): Unit = {
createJar(
jar = jar,
inputPaths = Agg(),
manifest = JarManifest.Default.add(
manifest = mill.api.JarManifest.MillDefault.add(
"Class-Path" -> classpath.iterator.map(_.toNIO.toUri().toURL().toExternalForm()).mkString(
" "
)
Expand All @@ -305,7 +298,7 @@ object Jvm extends CoursierSupport {

def createAssembly(
inputPaths: Agg[os.Path],
manifest: JarManifest = JarManifest.Default,
manifest: mill.api.JarManifest = mill.api.JarManifest.MillDefault,
prependShellScript: String = "",
base: Option[os.Path] = None,
assemblyRules: Seq[Assembly.Rule] = Assembly.defaultRules
Expand Down Expand Up @@ -449,38 +442,9 @@ object Jvm extends CoursierSupport {
PathRef(outputPath)
}


object JarManifest {
implicit val jarManifestRW: RW[JarManifest] = upickle.default.macroRW
final val Default = createManifest(None)
}

/**
* Represents a JAR manifest.
* @param main the main manifest attributes
* @param groups additional attributes for named entries
*/
final case class JarManifest(
main: Map[String, String] = Map.empty,
groups: Map[String, Map[String, String]] = Map.empty
) {
def add(entries: (String, String)*): JarManifest = copy(main = main ++ entries)
def addGroup(group: String, entries: (String, String)*): JarManifest =
copy(groups = groups + (group -> (groups.getOrElse(group, Map.empty) ++ entries)))

/** Constructs a [[java.util.jar.Manifest]] from this JarManifest. */
def build: Manifest = {
val manifest = new Manifest
val mainAttributes = manifest.getMainAttributes
main.foreach { case (key, value) => mainAttributes.putValue(key, value) }
val entries = manifest.getEntries
for ((group, attribs) <- groups) {
val attrib = new Attributes
attribs.foreach { case (key, value) => attrib.putValue(key, value) }
entries.put(group, attrib)
}
manifest
}
}
@deprecated("Use mill.api.JarManifest instead", "Mill after 0.11.0-M4")
type JarManifest = mill.api.JarManifest
@deprecated("Use mill.api.JarManifest instead", "Mill after 0.11.0-M4")
val JarManifest = mill.api.JarManifest

}
5 changes: 2 additions & 3 deletions main/test/src/eval/JavaCompileJarTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ package mill.eval

import mill.define.{Discover, Input, Target, Task}
import mill.modules.Jvm
import mill.modules.Jvm.JarManifest
import mill.api.Ctx.Dest
import mill.{Module, T}
import mill.util.{DummyLogger, TestEvaluator, TestUtil}
import mill.api.Strict.Agg
import mill.api.Loose
import mill.api.{JarManifest, Loose}
import utest._
import mill._

Expand Down Expand Up @@ -50,7 +49,7 @@ object JavaCompileJarTests extends TestSuite {
def filterJar(fileFilter: (os.Path, os.RelPath) => Boolean) = T {
Jvm.createJar(
Loose.Agg(classFiles().path, readme().path) ++ resourceRoot().map(_.path),
JarManifest.Default,
JarManifest.MillDefault,
fileFilter
)
}
Expand Down
4 changes: 2 additions & 2 deletions scalalib/src/JavaModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import coursier.parse.ModuleParser
import coursier.util.ModuleMatcher
import mainargs.Flag
import mill.api.Loose.Agg
import mill.api.{PathRef, Result, internal}
import mill.api.{JarManifest, PathRef, Result, internal}
import mill.define.{Command, Sources, Target, Task, TaskModule}
import mill.eval.EvaluatorPathsResolver
import mill.modules.{Assembly, Jvm}
Expand Down Expand Up @@ -398,7 +398,7 @@ trait JavaModule
* Creates a manifest representation which can be modified or replaced
* The default implementation just adds the `Manifest-Version`, `Main-Class` and `Created-By` attributes
*/
def manifest: T[Jvm.JarManifest] = T {
def manifest: T[JarManifest] = T {
Jvm.createManifest(finalMainClassOpt().toOption)
}

Expand Down
4 changes: 2 additions & 2 deletions scalalib/src/PublishModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package mill
package scalalib

import mill.define.{Command, ExternalModule, Target, Task}
import mill.api.{PathRef, Result}
import mill.api.{JarManifest, PathRef, Result}
import mill.main.Tasks
import mill.modules.Jvm
import mill.scalalib.PublishModule.checkSonatypeCreds
Expand Down Expand Up @@ -199,7 +199,7 @@ trait PublishModule extends JavaModule { outer =>
).publish(artifacts.map { case (a, b) => (a.path, b) }, artifactInfo, release)
}

override def manifest: T[Jvm.JarManifest] = T {
override def manifest: T[JarManifest] = T {
import java.util.jar.Attributes.Name
val pom = pomSettings()
super.manifest().add(
Expand Down
4 changes: 2 additions & 2 deletions scalalib/src/ScalaModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package scalalib

import scala.annotation.nowarn
import mill.define.{Command, Sources, Target, Task}
import mill.api.{DummyInputStream, PathRef, Result, internal}
import mill.api.{DummyInputStream, JarManifest, PathRef, Result, internal}
import mill.modules.Jvm
import mill.modules.Jvm.createJar
import mill.api.Loose.Agg
Expand Down Expand Up @@ -501,7 +501,7 @@ trait ScalaModule extends JavaModule { outer =>
}
}

override def manifest: T[Jvm.JarManifest] = T {
override def manifest: T[JarManifest] = T {
super.manifest().add("Scala-Version" -> scalaVersion())
}

Expand Down

0 comments on commit f98513c

Please sign in to comment.