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
20 changes: 17 additions & 3 deletions .github/workflows/scala.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,15 @@ on:
jobs:
build:

runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
jobtype: 1
- os: ubuntu-latest
jobtype: 2
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v5
Expand All @@ -21,5 +29,11 @@ jobs:
cache: sbt
- name: Install sbt
uses: sbt/setup-sbt@v1
- name: Run tests
run: sbt "^ scripted"
- name: Build and test
if: ${{ matrix.jobtype == 1 }}
shell: bash
run: sbt -v '++ 2.12.x' scripted
- name: Scala 3
if: ${{ matrix.jobtype == 2 }}
shell: bash
run: sbt -v '++ 3.x' 'scripted agent/*'
25 changes: 22 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
* Copyright © 2016-2017 Lightbend, Inc. <http://www.lightbend.com>
*/

// sbt cross build
crossSbtVersions := Seq("1.11.4")

// dependencies
val packagerVersion = "1.11.3"
val packager19xVersion = "1.9.16"

val scala212 = "2.12.20"
val scala3 = "3.7.2"
Copy link
Contributor

@gaeljw gaeljw Sep 8, 2025

Choose a reason for hiding this comment

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

Naive question out of curiosity: we can use Scala 3.7 in the plugin because SBT 2.x itself is in Scala 3.7, right? I ask because I was expecting to see a Scala 3 LTS version (3.3.x) to ensure the plugin can be consumed in various version of SBT 2.x but I guess SBT 2.x (not RC) won't be released until the next LTS anyway?

Copy link
Member Author

Choose a reason for hiding this comment

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

sbt 2.x doesn't follow LTS since it on its own isn't a library, and sbt using the latest allows greater flexibility in the plugin ecosystem.


addSbtPlugin(
"com.github.sbt" % "sbt-native-packager" % packagerVersion % "provided"
)
Expand Down Expand Up @@ -42,6 +42,13 @@ lazy val `sbt-javaagent` = (project.in(file(".")))
.settings(
name := "sbt-javaagent",
organization := "com.github.sbt",
crossScalaVersions := Seq(scala212, scala3),
scalacOptions ++= {
scalaBinaryVersion.value match {
case "2.12" => Seq("-Xsource:3")
case _ => Nil
}
},
scriptedBufferLog := false,
scriptedLaunchOpts ++= Seq(
"-Dproject.version=" + version.value,
Expand All @@ -51,6 +58,18 @@ lazy val `sbt-javaagent` = (project.in(file(".")))
scriptedDependencies := {
(maxwell / publishLocal).value
publishLocal.value
},
(pluginCrossBuild / sbtVersion) := {
scalaBinaryVersion.value match {
case "2.12" => "1.11.6"
case _ => "2.0.0-RC4"
}
},
scriptedSbt := {
scalaBinaryVersion.value match {
case "2.12" => "1.11.6"
case _ => (pluginCrossBuild / sbtVersion).value
}
}
)

Expand Down
14 changes: 14 additions & 0 deletions src/main/scala-2.12/AgentModule.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.lightbend.sbt.javaagent

import sbt.*
import scala.language.implicitConversions

case class AgentScope(compile: Boolean = false, test: Boolean = false, run: Boolean = false, dist: Boolean = true)

case class AgentModule(name: String, module: ModuleID, scope: AgentScope, arguments: String)

case class ResolvedAgent(agent: AgentModule, artifact: File)

object AgentModule {
implicit def moduleToAgentModule(module: ModuleID): AgentModule = JavaAgent(module)
}
25 changes: 25 additions & 0 deletions src/main/scala-2.12/PluginCompat.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.lightbend.sbt.javaagent

import sbt.*
import java.nio.file.{ Path => NioPath }
import xsbti.FileConverter

private[javaagent] object PluginCompat {
type FileRef = java.io.File
type Out = java.io.File

def toNioPath(a: Attributed[File])(implicit conv: FileConverter): NioPath =
a.data.toPath()
def toFile(a: Attributed[File])(implicit conv: FileConverter): File =
a.data
def toNioPaths(cp: Seq[Attributed[File]])(implicit conv: FileConverter): Vector[NioPath] =
cp.map(_.data.toPath()).toVector
def toFiles(cp: Seq[Attributed[File]])(implicit conv: FileConverter): Vector[File] =
cp.map(_.data).toVector
def toFileRef(a: File)(implicit conv: FileConverter): File = a

// This adds `Def.uncached(...)`
implicit class DefOp(singleton: Def.type) {
def uncached[A1](a: A1): A1 = a
}
}
15 changes: 15 additions & 0 deletions src/main/scala-3/AgentModule.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.lightbend.sbt.javaagent

import sbt.*
import scala.language.implicitConversions

case class AgentScope(compile: Boolean = false, test: Boolean = false, run: Boolean = false, dist: Boolean = true)

case class AgentModule(name: String, module: ModuleID, scope: AgentScope, arguments: String)

case class ResolvedAgent(agent: AgentModule, artifact: File)

object AgentModule {
given Conversion[ModuleID, AgentModule] with
def apply(module: ModuleID): AgentModule = JavaAgent(module)
}
21 changes: 21 additions & 0 deletions src/main/scala-3/PluginCompat.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.lightbend.sbt.javaagent

import java.nio.file.{ Path => NioPath }
import sbt.*
import xsbti.{ FileConverter, HashedVirtualFileRef, VirtualFile }

private[javaagent] object PluginCompat:
type FileRef = HashedVirtualFileRef
type Out = VirtualFile

def toNioPath(a: Attributed[HashedVirtualFileRef])(using conv: FileConverter): NioPath =
conv.toPath(a.data)
inline def toFile(a: Attributed[HashedVirtualFileRef])(using conv: FileConverter): File =
toNioPath(a).toFile()
def toNioPaths(cp: Seq[Attributed[HashedVirtualFileRef]])(using conv: FileConverter): Vector[NioPath] =
cp.map(toNioPath).toVector
inline def toFiles(cp: Seq[Attributed[HashedVirtualFileRef]])(using conv: FileConverter): Vector[File] =
toNioPaths(cp).map(_.toFile())
def toFileRef(a: File)(using conv: FileConverter): HashedVirtualFileRef =
conv.toVirtualFile(a.toPath())
end PluginCompat
30 changes: 14 additions & 16 deletions src/main/scala/com/lightbend/sbt/javaagent/JavaAgent.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

package com.lightbend.sbt.javaagent

import sbt._
import sbt.Keys._
import sbt.*
import sbt.Keys.*
import xsbti.FileConverter
import PluginCompat.{ *, given }

/**
* Plugin for adding Java agents to projects in a general way.
Expand All @@ -17,22 +19,13 @@ object JavaAgent extends JavaAgent {

object JavaAgentKeys {
val javaAgents = settingKey[Seq[AgentModule]]("Java agent modules enabled for this project.")

@transient
val resolvedJavaAgents = taskKey[Seq[ResolvedAgent]]("Java agent modules with resolved artifacts.")
}

val AgentConfig = config("javaagent").hide

case class AgentScope(compile: Boolean = false, test: Boolean = false, run: Boolean = false, dist: Boolean = true)

case class AgentModule(name: String, module: ModuleID, scope: AgentScope, arguments: String)

case class ResolvedAgent(agent: AgentModule, artifact: File)

object AgentModule {
import scala.language.implicitConversions
implicit def moduleToAgentModule(module: ModuleID): AgentModule = JavaAgent(module)
}

/**
* Create an agent module from a module dependency.
* Scope is also derived from the given module configuration.
Expand Down Expand Up @@ -69,7 +62,11 @@ class JavaAgent extends AutoPlugin {
Test/fork := enableFork(Test/fork, _.scope.test).value,
run/javaOptions ++= agentOptions(_.agent.scope.run).value,
Test/javaOptions ++= agentOptions(_.agent.scope.test).value,
Test/fullClasspath := filterAgents((Test/fullClasspath).value, resolvedJavaAgents.value)
Test/fullClasspath := Def.uncached {
val conv = fileConverter.value
implicit val conv0: xsbti.FileConverter = conv
filterAgents((Test/fullClasspath).value, resolvedJavaAgents.value)
}
)

private def resolveAgents = Def.task[Seq[ResolvedAgent]] {
Expand Down Expand Up @@ -102,8 +99,9 @@ class JavaAgent extends AutoPlugin {
}
}

def filterAgents(classpath: Classpath, resolvedAgents: Seq[ResolvedAgent]): Classpath = {
def filterAgents(classpath: Classpath, resolvedAgents: Seq[ResolvedAgent])(implicit conv: FileConverter): Classpath = {
val agents = resolvedAgents.map(resolved => resolved.artifact.absolutePath)
classpath.filter(aFile => !agents.contains(aFile.data.getAbsolutePath))
classpath
.filter(entry => !agents.contains(PluginCompat.toFile(entry).getAbsolutePath))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

package com.lightbend.sbt.javaagent

import sbt._
import sbt.Keys._
import sbt.*
import sbt.Keys.*
import com.typesafe.sbt.packager.universal.UniversalPlugin.autoImport.Universal
import java.io.File

Expand All @@ -24,7 +24,11 @@ object JavaAgentPackaging extends AutoPlugin {
override def projectSettings = {
import com.typesafe.sbt.packager.{ Keys => PackagerKeys }
Seq(
Universal / mappings ++= agentMappings.value.map(m => m._1 -> m._2),
Universal / mappings ++= {
val conv = fileConverter.value
implicit val conv0: xsbti.FileConverter = conv
agentMappings.value.map(m => PluginCompat.toFileRef(m._1) -> m._2)
},
PackagerKeys.bashScriptExtraDefines ++= agentBashScriptOptions.value,
PackagerKeys.batScriptExtraDefines ++= agentBatScriptOptions.value
)
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion src/sbt-test/agent/compile/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ lazy val agentCompile =

javaAgents += "sbt.javaagent.test" % "maxwell" % sys.props(
"project.version"
) % "compile"
) % Compile