Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,5 @@ sbt-pack is an SBT plugin for creating distributable Scala packages. It bundles
- Version conflict resolution uses custom `VersionString` comparison
- Supports multi-module projects with different main classes
- Handles both application and library packaging scenarios
- Special Docker-friendly features for container deployment
- Special Docker-friendly features for container deployment
- before making pull requests, ensure formatting code with ./sbt scalafmtAll
54 changes: 33 additions & 21 deletions src/main/scala/xerial/sbt/pack/PackPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ import sbt.{given, _}
object PackPlugin extends AutoPlugin with PackArchive {
override def trigger = noTrigger

/** Helper class for logging with level checking */
private class LevelAwareLogger(logger: Logger, level: Level.Value) {
def info(message: => String): Unit = if (level <= Level.Info) logger.info(message)
def debug(message: => String): Unit = if (level <= Level.Debug) logger.debug(message)
def warn(message: => String): Unit = if (level <= Level.Warn) logger.warn(message)
def error(message: => String): Unit = if (level <= Level.Error) logger.error(message)
}

case class ModuleEntry(
org: String,
name: String,
Expand Down Expand Up @@ -188,11 +196,13 @@ object PackPlugin extends AutoPlugin with PackArchive {
Def.derive(
packModuleEntries := {
val out = streams.value
val level = logLevel.value
val log = new LevelAwareLogger(out.log, level)
val jarExcludeFilter: Seq[Regex] = packExcludeJars.value.map(_.r)
def isExcludeJar(name: String): Boolean = {
val toExclude = jarExcludeFilter.exists(pattern => pattern.findFirstIn(name).isDefined)
if (toExclude) {
out.log.info(s"Exclude $name from the package")
log.info(s"Exclude $name from the package")
}
toExclude
}
Expand Down Expand Up @@ -227,8 +237,7 @@ object PackPlugin extends AutoPlugin with PackArchive {
val latestRevision = revisions.last
packDuplicateJarStrategy.value match {
case "latest" =>
out.log
.debug(s"Version conflict on $key. Using ${latestRevision} (found ${revisions.mkString(", ")})")
log.debug(s"Version conflict on $key. Using ${latestRevision} (found ${revisions.mkString(", ")})")
entries.filter(_.revision == latestRevision)
case "exit" =>
sys.error(s"Version conflict on $key (found ${revisions.mkString(", ")})")
Expand All @@ -243,7 +252,8 @@ object PackPlugin extends AutoPlugin with PackArchive {
packCopyDependenciesTarget := target.value / "lib",
Def.derive(
packCopyDependencies := {
val log = streams.value.log
val level = logLevel.value
val log = new LevelAwareLogger(streams.value.log, level)

val distinctDpJars = packModuleEntries.value.map(_.file)
val unmanaged = packAllUnmanagedJars.value.flatMap(_._1).map(x => toFile(x.data))
Expand All @@ -270,11 +280,13 @@ object PackPlugin extends AutoPlugin with PackArchive {
packEnvVars := Map.empty,
Def.derive(pack := {
val out = streams.value
val level = logLevel.value
val log = new LevelAwareLogger(out.log, level)
val logPrefix = "[" + name.value + "] "
val base: File = new File(".") // Using the working directory as base for readability

val distDir: File = packTargetDir.value / packDir.value
out.log.info(logPrefix + "Creating a distributable package in " + rpath(base, distDir))
log.info(logPrefix + "Creating a distributable package in " + rpath(base, distDir))
IO.delete(distDir)
distDir.mkdirs()

Expand All @@ -283,9 +295,9 @@ object PackPlugin extends AutoPlugin with PackArchive {
libDir.mkdirs()

// Copy project jars
out.log.info(logPrefix + "Copying libraries to " + rpath(base, libDir))
log.info(logPrefix + "Copying libraries to " + rpath(base, libDir))
val libs: Seq[FileRef] = packLibJars.value.map(_._1)
out.log.info(logPrefix + "project jars:\n" + libs.map(path => rpath(base, new io.RichFile(path))).mkString("\n"))
log.info(logPrefix + "project jars:\n" + libs.map(path => rpath(base, new io.RichFile(path))).mkString("\n"))
val projectJars = libs.map(l => {
val dest = libDir / l.getName()
IO.copyFile(l, dest)
Expand All @@ -295,30 +307,30 @@ object PackPlugin extends AutoPlugin with PackArchive {
// Copy dependent jars

val distinctDpJars = packModuleEntries.value
out.log.info(logPrefix + "Copying project dependencies:")
log.info(logPrefix + "Copying project dependencies:")
val jarNameConvention = packJarNameConvention.value
val projectDepsJars = for (m <- distinctDpJars) yield {
val targetFileName = resolveJarName(m, jarNameConvention)
val dest = libDir / targetFileName
out.log.info(s"${m}")
log.info(s"${m}")
IO.copyFile(m.file, dest, true)
dest
}

// Copy unmanaged jars in ${baseDir}/lib folder
out.log.info(logPrefix + "Copying unmanaged dependencies:")
log.info(logPrefix + "Copying unmanaged dependencies:")
val unmanagedDepsJars = for ((m, projectRef) <- packAllUnmanagedJars.value; um <- m; f = um.data) yield {
out.log.info(f.getPath)
log.info(f.getPath)
val dest = libDir / f.getName()
sbt.IO.copyFile(f, dest, true)
dest
}

// Copy explicitly added dependencies
val mapped: Seq[(FileRef, String)] = mappings.value
out.log.info(logPrefix + "Copying explicit dependencies:")
log.info(logPrefix + "Copying explicit dependencies:")
val explicitDepsJars = for ((file, path) <- mapped) yield {
out.log.info(file.getPath)
log.info(file.getPath)
val dest = distDir / path
IO.copyFile(file, dest, true)
dest
Expand All @@ -340,20 +352,20 @@ object PackPlugin extends AutoPlugin with PackArchive {

// Create target/pack/bin folder
val binDir = distDir / "bin"
out.log.info(logPrefix + "Create a bin folder: " + rpath(base, binDir))
log.info(logPrefix + "Create a bin folder: " + rpath(base, binDir))
binDir.mkdirs()

def write(path: String, content: String): Unit = {
val p = distDir / path
out.log.info(logPrefix + "Generating %s".format(rpath(base, p)))
log.info(logPrefix + "Generating %s".format(rpath(base, p)))
IO.write(p, content)
}

// Create launch scripts
out.log.info(logPrefix + "Generating launch scripts")
log.info(logPrefix + "Generating launch scripts")
val mainTable: Map[String, String] = packMain.value
if (mainTable.isEmpty) {
out.log.warn(
log.warn(
logPrefix + "No mapping (program name) -> MainClass is defined. Please set packMain variable (Map[String, String]) in your sbt project settings."
)
}
Expand All @@ -364,7 +376,7 @@ object PackPlugin extends AutoPlugin with PackArchive {
// Check the current Git revision
val gitRevision: String = Try {
if ((base / ".git").exists()) {
out.log.info(logPrefix + "Checking the git revision of the current project")
log.info(logPrefix + "Checking the git revision of the current project")
sys.process.Process("git rev-parse HEAD").!!
} else {
"unknown"
Expand All @@ -376,7 +388,7 @@ object PackPlugin extends AutoPlugin with PackArchive {

// Render script via Scalate template
for ((name, mainClass) <- mainTable) {
out.log.info(logPrefix + "main class for %s: %s".format(name, mainClass))
log.info(logPrefix + "main class for %s: %s".format(name, mainClass))
def extraClasspath(sep: String): String =
packExtraClasspath.value.get(name).map(_.mkString("", sep, sep)).getOrElse("")
def expandedClasspath(sep: String): String = {
Expand Down Expand Up @@ -441,7 +453,7 @@ object PackPlugin extends AutoPlugin with PackArchive {
// Copy resources
val otherResourceDirs = packResourceDir.value
val binScriptsDir = otherResourceDirs.map(_._1 / "bin").filter(_.exists)
out.log.info(logPrefix + s"packed resource directories = ${otherResourceDirs.map(_._1).mkString(",")}")
log.info(logPrefix + s"packed resource directories = ${otherResourceDirs.map(_._1).mkString(",")}")

def linkToScript(name: String) =
"\t" + """ln -sf "../$(PROG)/current/bin/%s" "$(PREFIX)/bin/%s"""".format(name, name)
Expand Down Expand Up @@ -479,7 +491,7 @@ object PackPlugin extends AutoPlugin with PackArchive {
// chmod +x the scripts in bin directory
binDir.listFiles.foreach(_.setExecutable(true, false))

out.log.info(logPrefix + "done.")
log.info(logPrefix + "done.")
distDir
}),
Def.derive(packInstall := {
Expand Down
18 changes: 18 additions & 0 deletions src/sbt-test/sbt-pack/log-level/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
enablePlugins(PackPlugin)

name := "log-level-test"

version := "0.1.0"

scalaVersion := "2.13.15"

// Test dependencies
libraryDependencies ++= Seq(
"org.scala-lang" % "scala-library" % scalaVersion.value
)

packMain := Map("hello" -> "HelloWorld")

// Test that logLevel setting is respected
// When set to Warn, info messages should not appear
Test / logLevel := Level.Warn
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logLevel setting here is scoped to the Test configuration, which means it will affect tasks like test, but not the pack tasks which are typically in the Compile scope. The scripted test file correctly sets Compile / pack / logLevel, so this line seems to be either redundant or not having the intended effect described in the comment. If the goal is to test the pack tasks, this line can probably be removed to avoid confusion, as the test script handles the log level setting explicitly.

1 change: 1 addition & 0 deletions src/sbt-test/sbt-pack/log-level/project/build.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sbt.version=1.10.11
1 change: 1 addition & 0 deletions src/sbt-test/sbt-pack/log-level/project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
addSbtPlugin("xerial.sbt" % "sbt-pack" % sys.props("project.version"))
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
object HelloWorld {
def main(args: Array[String]): Unit = {
println("Hello World!")
}
}
18 changes: 18 additions & 0 deletions src/sbt-test/sbt-pack/log-level/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Test that log level settings are respected

# Test with default log level (should show info messages)
> Compile / pack

# Test with Warn log level (should NOT show info messages)
> set Compile / pack / logLevel := Level.Warn
> clean
> Compile / pack

# Test packCopyDependencies with Warn log level
> set Compile / packCopyDependencies / logLevel := Level.Warn
> clean
> Compile / packCopyDependencies

# Verify the pack was successful even with Warn log level
$ exists target/pack/bin/hello
$ exists target/pack/lib
Loading